2020-03-12 10:06:56 +08:00
|
|
|
/*
|
|
|
|
|
* sfall
|
|
|
|
|
* Copyright (C) 2008-2020 The sfall team
|
|
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
2015-06-30 00:07:12 +07:00
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
//#include <unordered_map>
|
2020-09-01 16:09:29 +08:00
|
|
|
#include <algorithm>
|
2020-03-12 10:06:56 +08:00
|
|
|
#include <dshow.h>
|
|
|
|
|
|
|
|
|
|
#include "main.h"
|
2015-11-16 10:40:26 +08:00
|
|
|
#include "FalloutEngine.h"
|
2020-03-12 10:06:56 +08:00
|
|
|
#include "Graphics.h"
|
|
|
|
|
|
|
|
|
|
#include "Sound.h"
|
|
|
|
|
|
2020-03-12 11:58:48 +08:00
|
|
|
#define SAFERELEASE(a) { if (a) { a->Release(); } }
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
enum SoundType : DWORD {
|
2020-03-17 13:00:07 +08:00
|
|
|
SNDTYPE_sfx_loop = 0, // sfall
|
|
|
|
|
SNDTYPE_sfx_single = 1, // sfall
|
|
|
|
|
SNDTYPE_game_sfx = 2,
|
|
|
|
|
SNDTYPE_game_master = 3
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum SoundMode : long {
|
2020-07-22 12:27:18 +08:00
|
|
|
SNDMODE_single_play = 0, // uses sfx volume
|
|
|
|
|
SNDMODE_loop_play = 1, // uses sfx volume
|
|
|
|
|
SNDMODE_music_play = 2, // uses background volume
|
|
|
|
|
SNDMODE_speech_play = 3, // uses speech volume
|
2020-03-17 13:00:07 +08:00
|
|
|
SNDMODE_engine_music_play = -1 // used when playing game music in an alternative format (not used from scripts)
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
enum SoundFlags : DWORD {
|
2020-03-17 13:00:07 +08:00
|
|
|
SNDFLAG_looping = 0x10000000,
|
2020-07-22 12:27:18 +08:00
|
|
|
SNDFLAG_on_stop = 0x20000000,
|
2020-03-17 13:00:07 +08:00
|
|
|
SNDFLAG_restore = 0x40000000, // restore background game music on stop play
|
|
|
|
|
SNDFLAG_engine = 0x80000000
|
|
|
|
|
};
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
struct sDSSound {
|
2020-03-12 11:58:48 +08:00
|
|
|
DWORD id; // should be first
|
2020-03-12 10:06:56 +08:00
|
|
|
IGraphBuilder *pGraph;
|
|
|
|
|
IMediaControl *pControl;
|
|
|
|
|
IMediaSeeking *pSeek;
|
|
|
|
|
IMediaEventEx *pEvent;
|
|
|
|
|
IBasicAudio *pAudio;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static std::vector<sDSSound*> playingSounds;
|
|
|
|
|
static std::vector<sDSSound*> loopingSounds;
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
//static std::unordered_map<std::string, std::wstring> sfxSoundsFiles;
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
DWORD playID = 0;
|
|
|
|
|
DWORD loopID = 0;
|
|
|
|
|
|
|
|
|
|
static HWND soundwindow = 0;
|
2020-07-26 08:53:31 +08:00
|
|
|
|
|
|
|
|
static sDSSound* speechSound = nullptr; // currently playing sfall speech sound
|
2020-03-17 13:00:07 +08:00
|
|
|
static sDSSound* backgroundMusic = nullptr; // currently playing sfall background music
|
2020-03-12 10:06:56 +08:00
|
|
|
//static char playingMusicFile[256];
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
static bool deathSceneSpeech = false;
|
|
|
|
|
static bool lipsPlaying = false;
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
static void FreeSound(sDSSound* sound) {
|
|
|
|
|
sound->pEvent->SetNotifyWindow(0, WM_APP, 0);
|
|
|
|
|
SAFERELEASE(sound->pAudio);
|
|
|
|
|
SAFERELEASE(sound->pEvent);
|
|
|
|
|
SAFERELEASE(sound->pSeek);
|
|
|
|
|
SAFERELEASE(sound->pControl);
|
|
|
|
|
SAFERELEASE(sound->pGraph);
|
|
|
|
|
delete sound;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WipeSounds() {
|
2020-03-12 11:58:48 +08:00
|
|
|
for (size_t i = 0; i < playingSounds.size(); i++) FreeSound(playingSounds[i]);
|
|
|
|
|
for (size_t i = 0; i < loopingSounds.size(); i++) FreeSound(loopingSounds[i]);
|
2020-03-12 10:06:56 +08:00
|
|
|
playingSounds.clear();
|
|
|
|
|
loopingSounds.clear();
|
2020-03-17 13:00:07 +08:00
|
|
|
backgroundMusic = nullptr;
|
2020-07-26 08:53:31 +08:00
|
|
|
speechSound = nullptr;
|
2020-03-12 11:58:48 +08:00
|
|
|
playID = 0;
|
|
|
|
|
loopID = 0;
|
2020-07-26 08:53:31 +08:00
|
|
|
lipsPlaying = false;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LRESULT CALLBACK SoundWndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l) {
|
|
|
|
|
if (msg == WM_APP) {
|
2020-09-01 16:09:29 +08:00
|
|
|
if (!(l & 0xA0000000)) return 0;
|
|
|
|
|
sDSSound* sound = reinterpret_cast<sDSSound*>(l & ~0xA0000000);
|
2020-03-12 10:06:56 +08:00
|
|
|
LONG e = 0;
|
|
|
|
|
LONG_PTR p1 = 0, p2 = 0;
|
2020-09-01 16:09:29 +08:00
|
|
|
if (!FAILED(sound->pEvent->GetEvent(&e, &p1, &p2, 0))) {
|
2020-03-12 10:06:56 +08:00
|
|
|
if (e == EC_COMPLETE) {
|
2020-09-01 16:09:29 +08:00
|
|
|
if (sound->id & SNDFLAG_looping) {
|
2020-03-12 10:06:56 +08:00
|
|
|
LONGLONG pos = 0;
|
2020-03-12 11:58:48 +08:00
|
|
|
sound->pSeek->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning);
|
|
|
|
|
sound->pControl->Run();
|
2020-09-01 16:09:29 +08:00
|
|
|
sound->pEvent->FreeEventParams(e, p1, p2);
|
2020-03-12 10:06:56 +08:00
|
|
|
} else {
|
2020-09-01 16:09:29 +08:00
|
|
|
sound->pEvent->FreeEventParams(e, p1, p2);
|
|
|
|
|
if (sound->id & SNDFLAG_on_stop) { // speech sound playback is completed
|
2020-07-26 08:53:31 +08:00
|
|
|
*ptr_main_death_voiceover_done = 1;
|
|
|
|
|
*ptr_endgame_subtitle_done = 1;
|
|
|
|
|
lipsPlaying = false;
|
|
|
|
|
speechSound = nullptr;
|
2020-07-22 12:27:18 +08:00
|
|
|
}
|
2020-09-01 16:09:29 +08:00
|
|
|
playingSounds.erase(
|
|
|
|
|
std::find_if(playingSounds.cbegin(), playingSounds.cend(), [&](const sDSSound* snd) { return snd->id == sound->id; })
|
|
|
|
|
);
|
|
|
|
|
FreeSound(sound);
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return DefWindowProc(wnd, msg, w, l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void CreateSndWnd() {
|
2020-09-01 16:09:29 +08:00
|
|
|
dlog("Creating sfall sound callback window.", DL_INIT);
|
2020-03-12 10:06:56 +08:00
|
|
|
if (GraphicsMode == 0) CoInitialize(0);
|
|
|
|
|
|
|
|
|
|
WNDCLASSEX wcx;
|
2020-03-17 13:00:07 +08:00
|
|
|
std::memset(&wcx, 0, sizeof(wcx));
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
wcx.cbSize = sizeof(wcx);
|
|
|
|
|
wcx.lpfnWndProc = SoundWndProc;
|
|
|
|
|
wcx.hInstance = GetModuleHandleA(0);
|
|
|
|
|
wcx.lpszClassName = "sfallSndWnd";
|
|
|
|
|
|
|
|
|
|
RegisterClassEx(&wcx);
|
2020-09-01 16:09:29 +08:00
|
|
|
soundwindow = CreateWindowA("sfallSndWnd", "SndWnd", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, wcx.hInstance, 0);
|
2020-03-12 10:06:56 +08:00
|
|
|
dlogr(" Done", DL_INIT);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
// Get sound duration in seconds
|
2020-07-29 14:20:04 +08:00
|
|
|
static DWORD GetSpeechDurationTime() {
|
2020-07-30 10:42:08 +08:00
|
|
|
if (!speechSound || !speechSound->pSeek) return 0;
|
2020-07-26 08:53:31 +08:00
|
|
|
speechSound->pSeek->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
|
2020-07-31 20:42:46 +08:00
|
|
|
__int64 outVal = -1;
|
2020-07-26 08:53:31 +08:00
|
|
|
speechSound->pSeek->GetDuration(&outVal);
|
2020-07-31 20:42:46 +08:00
|
|
|
return (outVal != -1) ? static_cast<DWORD>(outVal / 10000000) + 1 : 0;
|
2020-07-26 08:53:31 +08:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 14:20:04 +08:00
|
|
|
static DWORD GetSpeechPlayingPosition() {
|
2020-07-26 08:53:31 +08:00
|
|
|
if (!speechSound) return 0;
|
|
|
|
|
__int64 pos;
|
|
|
|
|
speechSound->pSeek->GetCurrentPosition(&pos);
|
|
|
|
|
return static_cast<DWORD>(pos << 1);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 10:42:08 +08:00
|
|
|
void __fastcall PauseSfallSound(sDSSound* sound) {
|
|
|
|
|
if (sound) sound->pControl->Pause();
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
2020-07-30 10:42:08 +08:00
|
|
|
void __fastcall ResumeSfallSound(sDSSound* sound) {
|
|
|
|
|
if (sound) sound->pControl->Run();
|
2020-03-17 13:00:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall PauseAllSfallSound() {
|
2020-03-19 12:52:37 +08:00
|
|
|
std::vector<sDSSound*>::const_iterator it;
|
2020-03-17 14:48:50 +08:00
|
|
|
sDSSound* sound = nullptr;
|
2020-03-17 13:00:07 +08:00
|
|
|
for (it = loopingSounds.begin(); it != loopingSounds.end(); ++it) {
|
2020-03-17 14:48:50 +08:00
|
|
|
sound = *it;
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->pControl->Pause();
|
|
|
|
|
}
|
|
|
|
|
for (it = playingSounds.begin(); it != playingSounds.end(); ++it) {
|
2020-03-17 14:48:50 +08:00
|
|
|
sound = *it;
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->pControl->Pause();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall ResumeAllSfallSound() {
|
2020-03-19 12:52:37 +08:00
|
|
|
std::vector<sDSSound*>::const_iterator it;
|
2020-03-17 14:48:50 +08:00
|
|
|
sDSSound* sound = nullptr;
|
2020-03-17 13:00:07 +08:00
|
|
|
for (it = loopingSounds.begin(); it != loopingSounds.end(); ++it) {
|
2020-03-17 14:48:50 +08:00
|
|
|
sound = *it;
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->pControl->Run();
|
|
|
|
|
}
|
|
|
|
|
for (it = playingSounds.begin(); it != playingSounds.end(); ++it) {
|
2020-03-17 14:48:50 +08:00
|
|
|
sound = *it;
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->pControl->Run();
|
|
|
|
|
}
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 14:20:04 +08:00
|
|
|
long Sound_CalculateVolumeDB(long masterVolume, long passVolume) {
|
2020-03-17 13:00:07 +08:00
|
|
|
if (masterVolume <= 0 || passVolume <= 0) return -9999; // mute
|
|
|
|
|
|
|
|
|
|
const int volOffset = -100; // adjust the maximum volume
|
2020-09-01 16:09:29 +08:00
|
|
|
const int minVolume = -2100;
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-09-01 16:09:29 +08:00
|
|
|
passVolume = masterVolume * passVolume / 32767;
|
|
|
|
|
long volume = static_cast<long>(minVolume * ((float)passVolume / 32767.0f)); // calculate % value
|
|
|
|
|
return (minVolume - volume) + volOffset;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
/*
|
2020-07-26 08:53:31 +08:00
|
|
|
master_volume: sound=null type=game_master passVolume=background_volume
|
2020-03-17 13:00:07 +08:00
|
|
|
background_volume: sound=backgroundMusic type=background_loop passVolume=background_volume
|
2020-07-26 08:53:31 +08:00
|
|
|
sfx_volume: sound=null type=game_sfx passVolume=sndfx_volume
|
|
|
|
|
speech_volume: sound=sound type=sfx_single passVolume=speech_volume
|
2020-07-22 12:27:18 +08:00
|
|
|
*/
|
2020-09-01 16:09:29 +08:00
|
|
|
static void __cdecl SetSoundVolume(sDSSound* sound, SoundType type, long passVolume) {
|
2020-03-17 13:00:07 +08:00
|
|
|
long volume, sfxVolume, masterVolume = *ptr_master_volume;
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-07-29 14:20:04 +08:00
|
|
|
volume = Sound_CalculateVolumeDB(masterVolume, passVolume);
|
|
|
|
|
if (type == SNDTYPE_game_master) sfxVolume = Sound_CalculateVolumeDB(masterVolume, *ptr_sndfx_volume);
|
2020-03-12 10:06:56 +08:00
|
|
|
|
|
|
|
|
if (sound) {
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->pAudio->put_Volume(volume);
|
2020-03-12 10:06:56 +08:00
|
|
|
} else {
|
2020-03-17 13:00:07 +08:00
|
|
|
if (type == SNDTYPE_game_sfx) sfxVolume = volume;
|
|
|
|
|
bool sfx_or_master = (type == SNDTYPE_game_sfx || type == SNDTYPE_game_master);
|
|
|
|
|
for (size_t i = 0; i < loopingSounds.size(); i++) {
|
|
|
|
|
if (sfx_or_master) {
|
|
|
|
|
if ((loopingSounds[i]->id & 0xF0000000) == SNDFLAG_looping) { // only for sfx loop mode
|
|
|
|
|
loopingSounds[i]->pAudio->put_Volume(sfxVolume);
|
|
|
|
|
continue;
|
|
|
|
|
} else
|
|
|
|
|
if (type == SNDTYPE_game_sfx) continue;
|
|
|
|
|
}
|
|
|
|
|
loopingSounds[i]->pAudio->put_Volume(volume);
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
2020-03-17 13:00:07 +08:00
|
|
|
if (sfx_or_master) {
|
|
|
|
|
for (size_t i = 0; i < playingSounds.size(); i++) {
|
2020-03-12 10:06:56 +08:00
|
|
|
playingSounds[i]->pAudio->put_Volume(sfxVolume);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
static bool IsMute(SoundMode mode) {
|
2020-03-17 13:00:07 +08:00
|
|
|
//if (*ptr_master_volume == 0) return true;
|
2020-07-22 12:27:18 +08:00
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
|
case SNDMODE_single_play:
|
|
|
|
|
return (*ptr_sndfx_volume == 0);
|
|
|
|
|
case SNDMODE_speech_play:
|
|
|
|
|
return (*ptr_speech_volume == 0);
|
|
|
|
|
default:
|
|
|
|
|
return (*ptr_background_volume == 0);
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
/*
|
|
|
|
|
single_play: mode 0 - single sound playback (with sound overlay)
|
|
|
|
|
loop_play: mode 1 - loop sound playback (with sound overlay)
|
|
|
|
|
music_play: mode 2 - loop sound playback with the background game music turned off
|
2020-07-22 12:27:18 +08:00
|
|
|
speech_play: mode 3 -
|
2020-03-17 13:00:07 +08:00
|
|
|
*/
|
2020-09-01 16:09:29 +08:00
|
|
|
static sDSSound* PlayingSound(const wchar_t* pathFile, SoundMode mode, long adjustVolume = 0, bool isPaused = false) {
|
2020-03-12 10:06:56 +08:00
|
|
|
if (!soundwindow) CreateSndWnd();
|
2020-03-12 11:58:48 +08:00
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
if (IsMute(mode)) return nullptr;
|
|
|
|
|
bool isLoop = (mode != SNDMODE_single_play && mode != SNDMODE_speech_play);
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-03-12 11:58:48 +08:00
|
|
|
sDSSound* sound = new sDSSound();
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-03-12 11:58:48 +08:00
|
|
|
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC, IID_IGraphBuilder, (void**)&sound->pGraph);
|
2020-03-12 10:06:56 +08:00
|
|
|
if (hr != S_OK) {
|
|
|
|
|
dlog_f("Error CoCreateInstance: %d", DL_INIT, hr);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2020-03-12 11:58:48 +08:00
|
|
|
sound->pGraph->QueryInterface(IID_IMediaControl, (void**)&sound->pControl);
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
if (mode == SNDMODE_speech_play || isLoop)
|
2020-03-12 11:58:48 +08:00
|
|
|
sound->pGraph->QueryInterface(IID_IMediaSeeking, (void**)&sound->pSeek);
|
2020-03-12 10:06:56 +08:00
|
|
|
else
|
2020-03-12 11:58:48 +08:00
|
|
|
sound->pSeek = nullptr;
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-03-12 11:58:48 +08:00
|
|
|
sound->pGraph->QueryInterface(IID_IMediaEventEx, (void**)&sound->pEvent);
|
|
|
|
|
sound->pGraph->QueryInterface(IID_IBasicAudio, (void**)&sound->pAudio);
|
2020-07-26 08:53:31 +08:00
|
|
|
sound->pControl->RenderFile((BSTR)pathFile);
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->id = (isLoop) ? ++loopID : ++playID;
|
|
|
|
|
if (isLoop) sound->id |= SNDFLAG_looping; // sfx loop sound
|
|
|
|
|
|
2020-09-01 16:09:29 +08:00
|
|
|
switch (mode) {
|
|
|
|
|
case SNDMODE_engine_music_play:
|
|
|
|
|
sound->id |= SNDFLAG_engine; // engine play
|
|
|
|
|
break;
|
|
|
|
|
case SNDMODE_music_play:
|
2020-03-17 13:00:07 +08:00
|
|
|
sound->id |= SNDFLAG_restore; // restore background game music on stop
|
|
|
|
|
if (backgroundMusic)
|
|
|
|
|
StopSfallSound(backgroundMusic->id);
|
|
|
|
|
else {
|
|
|
|
|
__asm call gsound_background_stop_;
|
|
|
|
|
}
|
|
|
|
|
backgroundMusic = sound;
|
2020-09-01 16:09:29 +08:00
|
|
|
break;
|
|
|
|
|
case SNDMODE_speech_play:
|
|
|
|
|
sound->pSeek->SetTimeFormat(&TIME_FORMAT_SAMPLE);
|
|
|
|
|
sound->id |= SNDFLAG_on_stop;
|
|
|
|
|
break;
|
2020-03-17 13:00:07 +08:00
|
|
|
}
|
|
|
|
|
|
2020-09-01 16:09:29 +08:00
|
|
|
sound->pEvent->SetNotifyWindow((OAHWND)soundwindow, WM_APP, ((unsigned long)sound | 0xA0000000));
|
2020-03-12 11:58:48 +08:00
|
|
|
sound->pControl->Run();
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
if (isLoop) {
|
2020-09-01 16:09:29 +08:00
|
|
|
SetSoundVolume(sound, SNDTYPE_sfx_loop, ((mode == SNDMODE_loop_play) ? *ptr_sndfx_volume : *ptr_background_volume) - adjustVolume);
|
2020-07-26 08:53:31 +08:00
|
|
|
loopingSounds.push_back(sound);
|
2020-03-12 10:06:56 +08:00
|
|
|
} else {
|
2020-09-01 16:09:29 +08:00
|
|
|
SetSoundVolume(sound, SNDTYPE_sfx_single, ((mode == SNDMODE_speech_play) ? *ptr_speech_volume : *ptr_sndfx_volume) - adjustVolume);
|
2020-07-26 08:53:31 +08:00
|
|
|
if (mode == SNDMODE_speech_play) speechSound = sound;
|
2020-07-30 10:42:08 +08:00
|
|
|
if (isPaused) {
|
|
|
|
|
sound->pControl->Pause(); // for delayed playback
|
|
|
|
|
}
|
2020-07-26 08:53:31 +08:00
|
|
|
playingSounds.push_back(sound);
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
2020-03-12 11:58:48 +08:00
|
|
|
return sound;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
enum PlayType : signed char {
|
|
|
|
|
PLAYTYPE_sfx = 0,
|
|
|
|
|
PLAYTYPE_music = 1,
|
2020-07-26 08:53:31 +08:00
|
|
|
PLAYTYPE_lips = 2,
|
2020-07-30 10:42:08 +08:00
|
|
|
PLAYTYPE_speech = 3,
|
|
|
|
|
PLAYTYPE_slides = 4 // speech for endgame slideshow
|
2020-07-22 12:27:18 +08:00
|
|
|
};
|
2020-03-12 10:06:56 +08:00
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
static const wchar_t *SoundExtensions[] = { L"wav", L"mp3", L"wma" };
|
2020-07-22 12:27:18 +08:00
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
/*
|
|
|
|
|
TODO: For sfx sounds in wav format, playback must be performed using the game functions (DirectSound)
|
|
|
|
|
because there is a small delay (~50-100ms) when using DirectShow
|
2020-08-13 15:22:20 +08:00
|
|
|
sfx - environment effects must set their volume relative to the location from the player (see gsound_compute_relative_volume_)
|
2020-07-26 08:53:31 +08:00
|
|
|
*/
|
2020-07-22 12:27:18 +08:00
|
|
|
static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
|
|
|
|
|
if (!path) return false;
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
/*if (playType == PLAYTYPE_sfx) {
|
2020-10-03 21:01:54 +08:00
|
|
|
std::unordered_map<std::string, std::wstring>::iterator it = sfxSoundsFiles.find(path);
|
2020-07-26 08:53:31 +08:00
|
|
|
if (it != sfxSoundsFiles.cend()) {
|
|
|
|
|
return (PlayingSound(it->second.c_str(), SNDMODE_single_play) != nullptr);
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
int len = 0;
|
|
|
|
|
while (len < 4 && path[len] != '\0') len++; // X.acm0
|
|
|
|
|
if (len <= 3) return false; // 012345
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
wchar_t buf[MAX_PATH];
|
|
|
|
|
if (playType != PLAYTYPE_music) {
|
|
|
|
|
char* master_patches = *ptr_patches; // all sfx/speech sounds must be placed in patches folder
|
|
|
|
|
while (master_patches[len]) buf[len] = master_patches[len++];
|
|
|
|
|
buf[len++] = L'\\';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* _path = path - len;
|
|
|
|
|
while (len < MAX_PATH && _path[len]) buf[len] = _path[len++];
|
|
|
|
|
if (len >= MAX_PATH) return false;
|
|
|
|
|
buf[len] = L'\0';
|
|
|
|
|
len -= 3; // the position of the first character of the file extension
|
2020-03-12 10:06:56 +08:00
|
|
|
|
|
|
|
|
bool isExist = false;
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
2020-07-22 12:27:18 +08:00
|
|
|
int j = len;
|
|
|
|
|
buf[j++] = SoundExtensions[i][0];
|
|
|
|
|
buf[j++] = SoundExtensions[i][1];
|
|
|
|
|
buf[j] = SoundExtensions[i][2];
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
if (GetFileAttributesW(buf) & FILE_ATTRIBUTE_DIRECTORY) continue; // also file not found
|
|
|
|
|
isExist = true;
|
2020-07-26 08:53:31 +08:00
|
|
|
|
|
|
|
|
//if (playType == PLAYTYPE_sfx) sfxSoundsFiles.insert(std::make_pair(path, buf));
|
2020-03-12 10:06:56 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
if (playType == PLAYTYPE_music && backgroundMusic != nullptr) {
|
2020-03-12 10:06:56 +08:00
|
|
|
//if (found && strcmp(path, playingMusicFile) == 0) return true; // don't stop music
|
2020-03-17 13:00:07 +08:00
|
|
|
StopSfallSound(backgroundMusic->id);
|
|
|
|
|
backgroundMusic = nullptr;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
if (!isExist) return false;
|
|
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
if (playType == PLAYTYPE_music) {
|
2020-03-12 10:06:56 +08:00
|
|
|
//strcpy_s(playingMusicFile, path);
|
2020-03-17 13:00:07 +08:00
|
|
|
backgroundMusic = PlayingSound(buf, SNDMODE_engine_music_play); // background music loop
|
|
|
|
|
if (!backgroundMusic) return false;
|
2020-03-12 10:06:56 +08:00
|
|
|
} else {
|
2020-09-01 16:09:29 +08:00
|
|
|
if (!PlayingSound(buf, ((playType >= PLAYTYPE_lips) ? SNDMODE_speech_play : SNDMODE_single_play), 0, (playType == PLAYTYPE_slides))) {
|
2020-07-30 10:42:08 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-26 08:53:31 +08:00
|
|
|
if (playType == PLAYTYPE_lips) {
|
|
|
|
|
lipsPlaying = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-22 12:27:18 +08:00
|
|
|
deathSceneSpeech = false;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __fastcall MakeMusicPath(const char* file) {
|
|
|
|
|
const char* pathFmt = "%s%s.ACM";
|
2020-07-26 08:53:31 +08:00
|
|
|
char pathBuf[MAX_PATH];
|
2020-03-12 10:06:56 +08:00
|
|
|
|
|
|
|
|
sprintf_s(pathBuf, pathFmt, *ptr_sound_music_path1, file);
|
2020-07-22 12:27:18 +08:00
|
|
|
if (SoundFileLoad(PLAYTYPE_music, pathBuf)) return;
|
2020-03-12 10:06:56 +08:00
|
|
|
|
|
|
|
|
sprintf_s(pathBuf, pathFmt, *ptr_sound_music_path2, file);
|
2020-07-22 12:27:18 +08:00
|
|
|
SoundFileLoad(PLAYTYPE_music, pathBuf);
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
DWORD __stdcall PlaySfallSound(const char* path, long mode) {
|
2020-07-26 08:53:31 +08:00
|
|
|
wchar_t buf[MAX_PATH];
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
|
|
while (len < MAX_PATH && path[len]) {
|
|
|
|
|
char ch = path[len];
|
|
|
|
|
if (ch == ':' || (ch == '.' && path[len + 1] == '.')) return 0;
|
|
|
|
|
buf[len++] = ch;
|
|
|
|
|
}
|
|
|
|
|
if (len <= 3 || len >= MAX_PATH) return 0;
|
|
|
|
|
buf[len] = L'\0';
|
|
|
|
|
|
2020-09-01 16:09:29 +08:00
|
|
|
short volAdjust = (mode & 0x7FFF0000) >> 16;
|
|
|
|
|
mode &= 0xF;
|
2020-07-26 08:53:31 +08:00
|
|
|
if (mode > SNDMODE_music_play) mode = SNDMODE_music_play;
|
2020-09-01 16:09:29 +08:00
|
|
|
sDSSound* sound = PlayingSound(buf, (SoundMode)mode, volAdjust);
|
|
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
return (mode && sound) ? sound->id : 0;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
|
2020-03-12 11:58:48 +08:00
|
|
|
void __stdcall StopSfallSound(DWORD id) {
|
2020-03-17 13:00:07 +08:00
|
|
|
if (!(id & 0xFFFFFF)) return;
|
2020-03-12 11:58:48 +08:00
|
|
|
for (size_t i = 0; i < loopingSounds.size(); i++) {
|
|
|
|
|
if (loopingSounds[i]->id == id) {
|
|
|
|
|
sDSSound* sound = loopingSounds[i];
|
|
|
|
|
sound->pControl->Stop();
|
|
|
|
|
FreeSound(sound);
|
2020-03-12 10:06:56 +08:00
|
|
|
loopingSounds.erase(loopingSounds.begin() + i);
|
2020-03-17 13:00:07 +08:00
|
|
|
if (!(id & SNDFLAG_engine) && id & SNDFLAG_restore) {
|
|
|
|
|
__asm call gsound_background_restart_last_;
|
|
|
|
|
}
|
2020-03-12 10:06:56 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
static void __fastcall ReleaseSound(sDSSound* sound) {
|
|
|
|
|
sound->pControl->Stop();
|
|
|
|
|
std::vector<sDSSound*>::const_iterator itEl = std::find(playingSounds.cbegin(), playingSounds.cend(), sound);
|
|
|
|
|
if (itEl != playingSounds.cend()) {
|
|
|
|
|
playingSounds.erase(itEl);
|
|
|
|
|
} else {
|
|
|
|
|
/*itEl = std::find(loopingSounds.cbegin(), loopingSounds.cend(), sound);
|
|
|
|
|
if (itEl != loopingSounds.cend()) {
|
|
|
|
|
loopingSounds.erase(itEl);
|
|
|
|
|
}*/
|
|
|
|
|
}
|
|
|
|
|
FreeSound(sound);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
static void __declspec(naked) soundLoad_hack() {
|
|
|
|
|
static const DWORD SoundLoadHackRet = 0x4AD49E;
|
|
|
|
|
static const DWORD SoundLoadHackEnd = 0x4AD4B6;
|
|
|
|
|
__asm {
|
|
|
|
|
push esi;
|
|
|
|
|
push edi;
|
|
|
|
|
push ebp;
|
|
|
|
|
mov ebx, eax;
|
|
|
|
|
// end engine code
|
|
|
|
|
push ecx;
|
2020-07-30 10:42:08 +08:00
|
|
|
push edx; // path to file
|
2020-07-22 12:27:18 +08:00
|
|
|
mov eax, [esp + 24];
|
2020-07-30 10:42:08 +08:00
|
|
|
cmp eax, 0x450926 + 5; // called from gsound_background_play_
|
|
|
|
|
sete cl; // PLAYTYPE_sfx / PLAYTYPE_music
|
2020-07-22 12:27:18 +08:00
|
|
|
je skip;
|
2020-07-30 10:42:08 +08:00
|
|
|
cmp eax, 0x47B6B5 + 5; // called from lips_make_speech_
|
2020-07-26 08:53:31 +08:00
|
|
|
je jlips;
|
2020-07-30 10:42:08 +08:00
|
|
|
cmp eax, 0x450EB4 + 5; // called from gsound_speech_play_
|
2020-07-22 12:27:18 +08:00
|
|
|
jne skip;
|
2020-07-30 10:42:08 +08:00
|
|
|
cmp dword ptr [esp + 24 + 0x114 + 4], 0x440200 + 5; // called from endgame_load_voiceover_
|
|
|
|
|
sete cl;
|
2020-07-26 08:53:31 +08:00
|
|
|
inc cl;
|
|
|
|
|
jlips:
|
2020-07-30 10:42:08 +08:00
|
|
|
add cl, 2; // PLAYTYPE_lips / PLAYTYPE_speech / PLAYTYPE_slides
|
2020-07-22 12:27:18 +08:00
|
|
|
skip:
|
2020-03-12 10:06:56 +08:00
|
|
|
call SoundFileLoad;
|
|
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
|
|
|
|
test al, al;
|
|
|
|
|
jnz playSfall;
|
|
|
|
|
jmp SoundLoadHackRet; // play acm
|
|
|
|
|
playSfall:
|
|
|
|
|
jmp SoundLoadHackEnd; // don't play acm (force error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 10:42:08 +08:00
|
|
|
static void __declspec(naked) gsound_background_play_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov esi, eax; // store
|
|
|
|
|
mov ecx, ebp; // file
|
|
|
|
|
call MakeMusicPath;
|
|
|
|
|
mov eax, esi; // restore eax
|
|
|
|
|
jmp soundDelete_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
static void __declspec(naked) main_death_scene_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov deathSceneSpeech, 1;
|
|
|
|
|
call gsound_speech_play_;
|
|
|
|
|
cmp deathSceneSpeech, 0
|
|
|
|
|
je playSfall;
|
|
|
|
|
mov deathSceneSpeech, 0;
|
2020-07-26 08:53:31 +08:00
|
|
|
retn; // play acm
|
2020-07-22 12:27:18 +08:00
|
|
|
playSfall:
|
|
|
|
|
xor eax, eax;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 10:42:08 +08:00
|
|
|
//////////////////////// SLIDES SPEECH SOUND CONTROL //////////////////////////
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
static void __declspec(naked) endgame_load_voiceover_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
cmp speechSound, 0;
|
|
|
|
|
jnz skip;
|
|
|
|
|
mov [esp + 0x118 - 0xC + 4], esi;
|
|
|
|
|
retn;
|
|
|
|
|
skip:
|
|
|
|
|
call GetSpeechDurationTime;
|
|
|
|
|
push eax;
|
|
|
|
|
fild dword ptr [esp];
|
|
|
|
|
fild dword ptr ds:[_endgame_subtitle_characters];
|
|
|
|
|
add esp, 4;
|
|
|
|
|
fdivp st(1), st;
|
|
|
|
|
fstp qword ptr [esp + 0x118 - 0x8 + 4];
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 10:42:08 +08:00
|
|
|
static void __declspec(naked) endgame_pan_desert_hack() {
|
2020-03-12 10:06:56 +08:00
|
|
|
__asm {
|
2020-07-30 10:42:08 +08:00
|
|
|
mov ecx, speechSound;
|
|
|
|
|
test ecx, ecx;
|
|
|
|
|
jnz skip;
|
|
|
|
|
mov ecx, dword ptr ds:[_endgame_voiceover_loaded];
|
|
|
|
|
skip:
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) endgame_pan_desert_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
cmp speechSound, 0;
|
|
|
|
|
jnz GetSpeechDurationTime;
|
|
|
|
|
jmp gsound_speech_length_get_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) endgame_display_image_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov ecx, speechSound;
|
|
|
|
|
call ResumeSfallSound;
|
|
|
|
|
mov edx, dword ptr ds:[_endgame_voiceover_loaded];
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) endgame_pan_desert_hack_play() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov ecx, speechSound;
|
|
|
|
|
call ResumeSfallSound;
|
|
|
|
|
mov eax, dword ptr ds:[_endgame_voiceover_loaded];
|
|
|
|
|
xor ecx, ecx;
|
|
|
|
|
retn;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-26 08:53:31 +08:00
|
|
|
//////////////////////// LIPS SPEECH SOUND CONTROL ////////////////////////////
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) lips_play_speech_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
cmp lipsPlaying, 0;
|
|
|
|
|
jnz skip;
|
|
|
|
|
jmp soundPlay_;
|
|
|
|
|
skip:
|
|
|
|
|
xor eax, eax;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gdialog_bk_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
cmp lipsPlaying, 0;
|
|
|
|
|
jnz skip;
|
|
|
|
|
jmp soundPlaying_;
|
|
|
|
|
skip:
|
|
|
|
|
or eax, 1;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) lips_bkg_proc_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
cmp lipsPlaying, 0;
|
|
|
|
|
jnz skip;
|
|
|
|
|
jmp soundGetPosition_;
|
|
|
|
|
skip:
|
|
|
|
|
jmp GetSpeechPlayingPosition;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gdialogFreeSpeech_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
cmp lipsPlaying, 0;
|
|
|
|
|
jz skip;
|
|
|
|
|
push ecx;
|
|
|
|
|
push edx;
|
|
|
|
|
mov ecx,speechSound;
|
|
|
|
|
call ReleaseSound;
|
|
|
|
|
mov speechSound, 0;
|
|
|
|
|
mov lipsPlaying, 0;
|
|
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
|
|
|
|
skip:
|
|
|
|
|
cmp ds:[_gdialog_speech_playing], 0;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////// VOLUME AND PLAYBACK SOUND CONTROL ////////////////////////
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gsound_speech_stop_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov ecx, speechSound;
|
|
|
|
|
test ecx, ecx;
|
|
|
|
|
jz skip;
|
|
|
|
|
push edx;
|
|
|
|
|
call ReleaseSound;
|
|
|
|
|
mov speechSound, 0;
|
|
|
|
|
pop edx;
|
|
|
|
|
skip:
|
|
|
|
|
mov ecx, dword ptr ds:[_gsound_speech_tag];
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
static void __declspec(naked) gmovie_play_hook_stop() {
|
|
|
|
|
__asm {
|
2020-03-17 13:00:07 +08:00
|
|
|
mov eax, backgroundMusic;
|
2020-03-12 10:06:56 +08:00
|
|
|
test eax, eax;
|
|
|
|
|
jz skip;
|
2020-03-17 13:00:07 +08:00
|
|
|
mov eax, [eax]; // backgroundMusic->id
|
2020-03-12 10:06:56 +08:00
|
|
|
push ecx;
|
|
|
|
|
push edx;
|
|
|
|
|
push eax;
|
|
|
|
|
call StopSfallSound;
|
|
|
|
|
xor eax, eax;
|
2020-03-17 13:00:07 +08:00
|
|
|
mov backgroundMusic, eax;
|
2020-03-12 10:06:56 +08:00
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
|
|
|
|
retn;
|
|
|
|
|
skip:
|
|
|
|
|
jmp gsound_background_stop_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gmovie_play_hook_pause() {
|
|
|
|
|
__asm {
|
|
|
|
|
push ecx;
|
|
|
|
|
push edx;
|
2020-03-17 13:00:07 +08:00
|
|
|
call PauseAllSfallSound;
|
2020-03-12 10:06:56 +08:00
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
2020-03-17 13:00:07 +08:00
|
|
|
cmp dword ptr backgroundMusic, 0;
|
|
|
|
|
jnz skip;
|
2020-03-12 10:06:56 +08:00
|
|
|
jmp gsound_background_pause_;
|
2020-03-17 13:00:07 +08:00
|
|
|
skip:
|
|
|
|
|
retn;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gmovie_play_hook_unpause() {
|
|
|
|
|
__asm {
|
|
|
|
|
push ecx;
|
|
|
|
|
push edx;
|
2020-03-17 13:00:07 +08:00
|
|
|
call ResumeAllSfallSound;
|
2020-03-12 10:06:56 +08:00
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
2020-03-17 13:00:07 +08:00
|
|
|
cmp dword ptr backgroundMusic, 0;
|
|
|
|
|
jnz skip;
|
2020-03-12 10:06:56 +08:00
|
|
|
jmp gsound_background_unpause_;
|
2020-03-17 13:00:07 +08:00
|
|
|
skip:
|
|
|
|
|
retn;
|
2020-03-12 10:06:56 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gsound_background_volume_set_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov dword ptr ds:[_background_volume], eax;
|
|
|
|
|
push ecx;
|
2020-03-17 13:00:07 +08:00
|
|
|
mov ecx, backgroundMusic;
|
2020-03-12 10:06:56 +08:00
|
|
|
test ecx, ecx;
|
|
|
|
|
jz skip;
|
|
|
|
|
push edx;
|
|
|
|
|
push eax;
|
2020-03-17 13:00:07 +08:00
|
|
|
push 0; // SNDTYPE_sfx_loop
|
|
|
|
|
push ecx; // set volume for background music
|
2020-09-01 16:09:29 +08:00
|
|
|
call SetSoundVolume;
|
2020-03-12 10:06:56 +08:00
|
|
|
add esp, 8;
|
|
|
|
|
pop eax;
|
|
|
|
|
pop edx;
|
|
|
|
|
skip:
|
|
|
|
|
pop ecx;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __declspec(naked) gsound_master_volume_set_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov dword ptr ds:[_master_volume], edx;
|
|
|
|
|
push eax;
|
|
|
|
|
push ecx;
|
|
|
|
|
push edx;
|
|
|
|
|
push dword ptr ds:[_background_volume];
|
2020-03-17 13:00:07 +08:00
|
|
|
push 3; // SNDTYPE_game_master
|
|
|
|
|
push 0; // set volume for all sounds
|
2020-09-01 16:09:29 +08:00
|
|
|
call SetSoundVolume;
|
2020-03-12 10:06:56 +08:00
|
|
|
add esp, 12;
|
|
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
|
|
|
|
pop eax;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
static void __declspec(naked) gsound_set_sfx_volume_hack() {
|
|
|
|
|
__asm {
|
|
|
|
|
push ecx;
|
|
|
|
|
push edx;
|
|
|
|
|
mov dword ptr ds:[_sndfx_volume], eax;
|
|
|
|
|
push eax;
|
|
|
|
|
push 2; // SNDTYPE_game_sfx
|
|
|
|
|
push 0; // set volume for all sounds
|
2020-09-01 16:09:29 +08:00
|
|
|
call SetSoundVolume;
|
2020-03-17 13:00:07 +08:00
|
|
|
add esp, 12;
|
|
|
|
|
pop edx;
|
|
|
|
|
pop ecx;
|
|
|
|
|
retn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-13 22:33:30 +08:00
|
|
|
void Sound_SoundLostFocus(long isActive) {
|
2020-03-26 14:13:22 +08:00
|
|
|
if (!loopingSounds.empty() || !playingSounds.empty()) {
|
|
|
|
|
if (isActive) {
|
|
|
|
|
ResumeAllSfallSound();
|
|
|
|
|
} else {
|
|
|
|
|
PauseAllSfallSound();
|
|
|
|
|
}
|
2020-03-17 13:00:07 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2015-06-30 00:07:12 +07:00
|
|
|
|
2019-01-08 13:28:00 +08:00
|
|
|
static char attackerSnd[9] = {0};
|
2020-03-12 10:06:56 +08:00
|
|
|
static char targetSnd[9] = {0};
|
2015-06-30 00:07:12 +07:00
|
|
|
|
2019-01-08 13:28:00 +08:00
|
|
|
static void __declspec(naked) combatai_msg_hook() {
|
2015-06-30 00:07:12 +07:00
|
|
|
__asm {
|
2019-01-08 13:28:00 +08:00
|
|
|
mov edi, [esp + 0xC]; // lip file from msg
|
|
|
|
|
push eax;
|
|
|
|
|
cmp eax, _target_str;
|
|
|
|
|
jne attacker;
|
|
|
|
|
lea eax, targetSnd;
|
|
|
|
|
jmp skip;
|
2015-06-30 00:07:12 +07:00
|
|
|
attacker:
|
2019-01-08 13:28:00 +08:00
|
|
|
lea eax, attackerSnd;
|
|
|
|
|
skip:
|
|
|
|
|
push edx;
|
|
|
|
|
push ebx;
|
|
|
|
|
mov edx, edi;
|
|
|
|
|
mov ebx, 8;
|
2015-11-16 10:40:26 +08:00
|
|
|
call strncpy_;
|
2019-01-08 13:28:00 +08:00
|
|
|
pop ebx;
|
|
|
|
|
pop edx;
|
|
|
|
|
pop eax;
|
|
|
|
|
jmp strncpy_;
|
2015-06-30 00:07:12 +07:00
|
|
|
}
|
|
|
|
|
}
|
2019-01-08 13:28:00 +08:00
|
|
|
|
|
|
|
|
static void __declspec(naked) ai_print_msg_hook() {
|
2015-06-30 00:07:12 +07:00
|
|
|
__asm {
|
2019-01-08 13:28:00 +08:00
|
|
|
push eax;
|
|
|
|
|
cmp edx, _target_str;
|
|
|
|
|
jne attacker;
|
|
|
|
|
lea eax, targetSnd;
|
|
|
|
|
jmp skip;
|
2015-06-30 00:07:12 +07:00
|
|
|
attacker:
|
2019-01-08 13:28:00 +08:00
|
|
|
lea eax, attackerSnd;
|
2015-06-30 00:07:12 +07:00
|
|
|
skip:
|
2019-01-08 13:28:00 +08:00
|
|
|
push ecx;
|
|
|
|
|
mov ecx, [eax];
|
|
|
|
|
test cl, cl;
|
|
|
|
|
jz end;
|
|
|
|
|
call gsound_play_sfx_file_;
|
|
|
|
|
end:
|
|
|
|
|
pop ecx;
|
|
|
|
|
pop eax;
|
|
|
|
|
jmp text_object_create_;
|
2015-06-30 00:07:12 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-01 16:09:29 +08:00
|
|
|
static void __declspec(naked) soundStartInterpret_hook() {
|
|
|
|
|
__asm {
|
|
|
|
|
mov ebp, eax; // keep sound data
|
|
|
|
|
call soundSetCallback_;
|
|
|
|
|
xor ebx, ebx;
|
|
|
|
|
mov eax, [esp + 0x18 - 0x18 + 4]; // play mode flags
|
|
|
|
|
and eax, 0x80000000; // check raw format flag (for backward compatibility mode)
|
|
|
|
|
jnz rawFile;
|
|
|
|
|
push ecx;
|
|
|
|
|
push audioFileSize_;
|
|
|
|
|
mov ecx, audioRead_;
|
|
|
|
|
push eax;
|
|
|
|
|
mov ebx, audioCloseFile_;
|
|
|
|
|
push audioSeek_;
|
|
|
|
|
mov edx, audioOpen_;
|
|
|
|
|
push eax;
|
|
|
|
|
mov eax, ebp;
|
|
|
|
|
call soundSetFileIO_;
|
|
|
|
|
pop ecx;
|
2020-09-02 14:21:30 +08:00
|
|
|
mov bx, [esp + 0x18 - 0x18 + 4+2]; // get volume adjustment: 0 - max volume, 32767 - mute
|
2020-09-01 16:09:29 +08:00
|
|
|
and bx, ~0x8000;
|
|
|
|
|
rawFile:
|
|
|
|
|
xor edx, edx;
|
|
|
|
|
mov eax, ds:[_sndfx_volume];
|
|
|
|
|
sub ax, bx; // reduce volume
|
|
|
|
|
cmovg edx, eax; // volume > 0
|
|
|
|
|
mov eax, ebp;
|
|
|
|
|
jmp soundVolume_; // set sfx volume
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-13 22:33:30 +08:00
|
|
|
//static const int SampleRate = 44100; // 44.1kHz
|
2020-09-01 16:09:29 +08:00
|
|
|
|
2020-09-02 14:21:30 +08:00
|
|
|
//void SetSoundSampleRate() {
|
|
|
|
|
// *ptr_sampleRate = SampleRate / 2; // Revert to 22kHz for secondary sound buffers
|
|
|
|
|
//}
|
|
|
|
|
|
2015-06-30 00:07:12 +07:00
|
|
|
void SoundInit() {
|
2020-09-01 16:09:29 +08:00
|
|
|
// Set the sample rate for the primary sound buffer
|
|
|
|
|
//SafeWrite32(0x44FDBC, SampleRate);
|
2020-09-02 14:21:30 +08:00
|
|
|
// SetSoundSampleRate will be run after game initialization
|
2020-09-01 16:09:29 +08:00
|
|
|
|
2020-03-17 13:00:07 +08:00
|
|
|
HookCall(0x44E816, gmovie_play_hook_pause);
|
|
|
|
|
HookCall(0x44EA84, gmovie_play_hook_unpause);
|
|
|
|
|
MakeCall(0x450525, gsound_background_volume_set_hack);
|
|
|
|
|
MakeCall(0x4503CA, gsound_master_volume_set_hack, 1);
|
|
|
|
|
MakeCall(0x45042C, gsound_set_sfx_volume_hack);
|
|
|
|
|
|
2020-04-23 12:42:20 +08:00
|
|
|
int allowDShowSound = GetConfigInt("Sound", "AllowDShowSound", 0);
|
|
|
|
|
if (allowDShowSound > 0) {
|
2020-07-30 10:42:08 +08:00
|
|
|
MakeJump(0x4AD499, soundLoad_hack); // main hook
|
|
|
|
|
|
2020-04-11 00:26:48 +08:00
|
|
|
const DWORD gmoviePlayStopAddr[] = {0x44E80A, 0x445280};
|
|
|
|
|
HookCalls(gmovie_play_hook_stop, gmoviePlayStopAddr); // only play looping music
|
2020-07-30 10:42:08 +08:00
|
|
|
if (allowDShowSound > 1) {
|
|
|
|
|
HookCall(0x450851, gsound_background_play_hook);
|
|
|
|
|
}
|
2020-07-26 08:53:31 +08:00
|
|
|
|
2020-07-22 12:27:18 +08:00
|
|
|
HookCall(0x4813EE, main_death_scene_hook);
|
2020-07-26 08:53:31 +08:00
|
|
|
MakeCall(0x451038, gsound_speech_stop_hack, 1);
|
|
|
|
|
MakeCall(0x440286, endgame_load_voiceover_hack, 2);
|
2020-07-30 10:42:08 +08:00
|
|
|
MakeCall(0x43FCED, endgame_pan_desert_hack, 1);
|
|
|
|
|
HookCall(0x43FCF9, endgame_pan_desert_hook);
|
|
|
|
|
// play slideshow speech
|
|
|
|
|
MakeCall(0x43FEF3, endgame_pan_desert_hack_play, 1);
|
|
|
|
|
MakeCall(0x43FF37, endgame_pan_desert_hack_play, 1);
|
|
|
|
|
MakeCall(0x4400B2, endgame_display_image_hack, 1);
|
2020-07-26 08:53:31 +08:00
|
|
|
|
|
|
|
|
// lips sounds hacks
|
|
|
|
|
HookCall(0x47ACDE, lips_play_speech_hook);
|
|
|
|
|
HookCall(0x47AAF6, lips_bkg_proc_hook);
|
|
|
|
|
HookCall(0x447B68, gdialog_bk_hook);
|
|
|
|
|
MakeCall(0x4450C5, gdialogFreeSpeech_hack, 2);
|
|
|
|
|
|
2020-03-12 10:06:56 +08:00
|
|
|
CreateSndWnd();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 12:42:20 +08:00
|
|
|
int sBuff = GetConfigInt("Sound", "NumSoundBuffers", 0);
|
2020-07-31 20:42:46 +08:00
|
|
|
if (sBuff > 0) {
|
|
|
|
|
SafeWrite8(0x451129, (sBuff > 32) ? (BYTE)32 : (BYTE)sBuff);
|
2015-06-30 00:07:12 +07:00
|
|
|
}
|
|
|
|
|
|
2019-10-14 23:55:54 +08:00
|
|
|
if (GetConfigInt("Sound", "AllowSoundForFloats", 0)) {
|
2019-01-08 13:28:00 +08:00
|
|
|
HookCall(0x42B7C7, combatai_msg_hook); // copy msg
|
|
|
|
|
HookCall(0x42B849, ai_print_msg_hook);
|
2015-06-30 00:07:12 +07:00
|
|
|
|
2019-01-08 13:28:00 +08:00
|
|
|
//Yes, I did leave this in on purpose. Will be of use to anyone trying to add in the sound effects
|
2019-10-16 13:06:43 +08:00
|
|
|
if (isDebug && iniGetInt("Debugging", "Test_ForceFloats", 0, ddrawIniDef)) {
|
2020-07-21 12:42:46 +08:00
|
|
|
SafeWrite8(0x42B6F5, CODETYPE_JumpShort); // bypass chance
|
2019-01-08 13:28:00 +08:00
|
|
|
}
|
2015-06-30 00:07:12 +07:00
|
|
|
}
|
2020-09-01 16:09:29 +08:00
|
|
|
|
2020-09-02 14:21:30 +08:00
|
|
|
// Support for ACM audio file playback and volume control for the soundplay script function
|
2020-09-01 16:09:29 +08:00
|
|
|
HookCall(0x4661B3, soundStartInterpret_hook);
|
2019-01-08 13:28:00 +08:00
|
|
|
}
|
2020-03-12 10:06:56 +08:00
|
|
|
|
|
|
|
|
void SoundExit() {
|
|
|
|
|
if (soundwindow && GraphicsMode == 0) CoUninitialize();
|
|
|
|
|
}
|