WIP: Fixed sfx/speech playback for alternative sound files

This commit is contained in:
NovaRain
2020-07-22 12:24:20 +08:00
parent b178e15b31
commit 29da941582
5 changed files with 97 additions and 35 deletions
+2
View File
@@ -135,6 +135,7 @@
#define FO_VAR_LSData 0x613D30
#define FO_VAR_lsgwin 0x6142C4
#define FO_VAR_main_ctd 0x56D2B0
#define FO_VAR_main_death_voiceover_done 0x614838
#define FO_VAR_main_window 0x5194F0
#define FO_VAR_map_elevation 0x519578
#define FO_VAR_map_global_vars 0x51956C
@@ -223,6 +224,7 @@
#define FO_VAR_sneak_working 0x56D77C // DWORD var
#define FO_VAR_sound_music_path1 0x518E78
#define FO_VAR_sound_music_path2 0x518E7C
#define FO_VAR_speech_volume 0x518E8C
#define FO_VAR_square 0x631E40
#define FO_VAR_squares 0x66BE08
#define FO_VAR_stack 0x59E86C
+2
View File
@@ -107,6 +107,7 @@ VAR_(loadingGame, DWORD)
VAR_(LSData, DWORD)
VAR_(lsgwin, DWORD)
VAR_(main_ctd, DWORD)
VAR_(main_death_voiceover_done, DWORD)
VAR_(main_window, DWORD)
VAR_(map_elevation, DWORD)
VAR_(map_global_vars, long*) // array
@@ -182,6 +183,7 @@ VAR_(sndfx_volume, DWORD)
VAR_(sneak_working, DWORD) // DWORD var
VAR_(sound_music_path1, char*)
VAR_(sound_music_path2, char*)
VAR_(speech_volume, DWORD)
VAR_(square, DWORD)
VAR_(squares, DWORD*)
VARA(stack, DWORD, 10)
+86 -30
View File
@@ -41,11 +41,13 @@ enum SoundMode : long {
single_play = 0, // uses sfx volume
loop_play = 1, // uses sfx volume
music_play = 2, // uses background volume
speech_play = 3, // uses speech volume
engine_music_play = -1 // used when playing game music in an alternative format (not used from scripts)
};
enum SoundFlags : unsigned long {
looping = 0x10000000,
on_stop = 0x20000000,
restore = 0x40000000, // restore background game music on stop play
engine = 0x80000000
};
@@ -123,6 +125,9 @@ LRESULT CALLBACK SoundWndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l) {
} else {
FreeSound(sound);
playingSounds.erase(playingSounds.begin() + index);
if (id & SoundFlags::on_stop) {
fo::var::main_death_voiceover_done = 1; // death speech sound playback is completed
}
}
}
}
@@ -166,7 +171,7 @@ void __stdcall ResumeAllSfallSound() {
for (sDSSound* sound : playingSounds) sound->pControl->Run();
}
static long CalculateVolumeDB(long masterVolume, long passVolume) {
long Sound::CalculateVolumeDB(long masterVolume, long passVolume) {
if (masterVolume <= 0 || passVolume <= 0) return -9999; // mute
const int volOffset = -100; // adjust the maximum volume
@@ -186,8 +191,8 @@ static long CalculateVolumeDB(long masterVolume, long passVolume) {
static void __cdecl SfallSoundVolume(sDSSound* sound, SoundType type, long passVolume) {
long volume, sfxVolume, masterVolume = fo::var::master_volume;
volume = CalculateVolumeDB(masterVolume, passVolume);
if (type == SoundType::game_master) sfxVolume = CalculateVolumeDB(masterVolume, fo::var::sndfx_volume);
volume = Sound::CalculateVolumeDB(masterVolume, passVolume);
if (type == SoundType::game_master) sfxVolume = Sound::CalculateVolumeDB(masterVolume, fo::var::sndfx_volume);
if (sound) {
sound->pAudio->put_Volume(volume);
@@ -212,27 +217,32 @@ static void __cdecl SfallSoundVolume(sDSSound* sound, SoundType type, long passV
}
}
static bool IsMute(bool type) {
static bool IsMute(SoundMode mode) {
//if (fo::var::master_volume == 0) return true;
int value;
if (type) {
value = fo::var::background_volume;
} else {
value = fo::var::sndfx_volume;
switch (mode) {
case SoundMode::single_play:
return (fo::var::sndfx_volume == 0);
case SoundMode::speech_play:
return (fo::var::speech_volume == 0);
default:
return (fo::var::background_volume == 0);
}
return (value == 0);
}
static bool deathSceneSpeech = false;
/*
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
speech_play: mode 3 -
*/
static sDSSound* PlayingSound(wchar_t* path, SoundMode mode) {
if (!soundwindow) CreateSndWnd();
bool isLoop = (mode != SoundMode::single_play);
if (IsMute(isLoop)) return nullptr;
if (IsMute(mode)) return nullptr;
bool isLoop = (mode != SoundMode::single_play && mode != SoundMode::speech_play);
sDSSound* sound = new sDSSound();
@@ -264,6 +274,7 @@ static sDSSound* PlayingSound(wchar_t* path, SoundMode mode) {
backgroundMusic = sound;
}
else if (mode == SoundMode::engine_music_play) sound->id |= SoundFlags::engine; // engine play
else if (deathSceneSpeech && mode == SoundMode::speech_play) sound->id |= SoundFlags::on_stop;
sound->pEvent->SetNotifyWindow((OAHWND)soundwindow, WM_APP, sound->id);
sound->pControl->RenderFile(path);
@@ -274,42 +285,66 @@ static sDSSound* PlayingSound(wchar_t* path, SoundMode mode) {
SfallSoundVolume(sound, SoundType::sfx_loop, (mode == SoundMode::loop_play) ? fo::var::sndfx_volume : fo::var::background_volume);
} else {
playingSounds.push_back(sound);
SfallSoundVolume(sound, SoundType::sfx_single, fo::var::sndfx_volume);
SfallSoundVolume(sound, SoundType::sfx_single, (mode == SoundMode::speech_play) ? fo::var::speech_volume : fo::var::sndfx_volume);
}
return sound;
}
static const wchar_t *SoundExtensions[] = { L"mp3", L"wma", L"wav" };
enum PlayType : signed char {
sfx = 0,
music = 1,
speech = 2,
};
static bool __cdecl SoundFileLoad(DWORD called, const char* path) {
if (!path || strlen(path) < 4) return false;
wchar_t buf[256];
mbstowcs_s(0, buf, path, 256);
static const wchar_t *SoundExtensions[] = { L"mp3", L"wav", L"wma" };
static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
if (!path) return false;
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 = fo::var::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
bool isExist = false;
int len = wcslen(buf) - 3;
for (int i = 0; i < 3; i++) {
buf[len] = 0;
wcscat_s(buf, SoundExtensions[i]);
int j = len;
buf[j++] = SoundExtensions[i][0];
buf[j++] = SoundExtensions[i][1];
buf[j] = SoundExtensions[i][2];
if (GetFileAttributesW(buf) & FILE_ATTRIBUTE_DIRECTORY) continue; // also file not found
isExist = true;
break;
}
bool music = (called == 0x45092B); // from gsound_background_play_
if (music && backgroundMusic != nullptr) {
if (playType == PlayType::music && backgroundMusic != nullptr) {
//if (found && strcmp(path, playingMusicFile) == 0) return true; // don't stop music
Sound::StopSfallSound(backgroundMusic->id);
backgroundMusic = nullptr;
}
if (!isExist) return false;
if (music) {
if (playType == PlayType::music) {
//strcpy_s(playingMusicFile, path);
backgroundMusic = PlayingSound(buf, SoundMode::engine_music_play); // background music loop
if (!backgroundMusic) return false;
} else {
if (!PlayingSound(buf, SoundMode::single_play)) return false;
if (!PlayingSound(buf, (playType == PlayType::speech) ? SoundMode::speech_play : SoundMode::single_play)) return false;
deathSceneSpeech = false;
}
return true;
}
@@ -319,10 +354,10 @@ static void __fastcall MakeMusicPath(const char* file) {
char pathBuf[256];
sprintf_s(pathBuf, pathFmt, fo::var::sound_music_path1, file);
if (SoundFileLoad(0x45092B, pathBuf)) return;
if (SoundFileLoad(PlayType::music, pathBuf)) return;
sprintf_s(pathBuf, pathFmt, fo::var::sound_music_path2, file);
SoundFileLoad(0x45092B, pathBuf);
SoundFileLoad(PlayType::music, pathBuf);
}
DWORD Sound::PlaySfallSound(const char* path, long mode) {
@@ -358,10 +393,16 @@ static void __declspec(naked) soundLoad_hack() {
mov ebx, eax;
// end engine code
push ecx;
push edx;
push [esp + 24];
push edx; // path to file
mov eax, [esp + 24];
cmp eax, 0x45092B; // called from gsound_background_play_
sete cl; // PlayType::sfx / PlayType::music
je skip;
cmp eax, 0x450EB9; // called from gsound_speech_play_
jne skip;
mov cl, 2; // PlayType::speech
skip:
call SoundFileLoad;
add esp, 4;
pop edx;
pop ecx;
test al, al;
@@ -372,6 +413,20 @@ playSfall:
}
}
static void __declspec(naked) main_death_scene_hook() {
__asm {
mov deathSceneSpeech, 1;
call fo::funcoffs::gsound_speech_play_;
cmp deathSceneSpeech, 0
je playSfall;
mov deathSceneSpeech, 0;
retn;
playSfall:
xor eax, eax;
retn;
}
}
static void __declspec(naked) gsound_background_play_hook() {
__asm {
mov esi, eax; // store
@@ -569,6 +624,7 @@ void Sound::init() {
if (allowDShowSound > 0) {
MakeJump(0x4AD499, soundLoad_hack);
HookCalls(gmovie_play_hook_stop, {0x44E80A, 0x445280}); // only play looping music
HookCall(0x4813EE, main_death_scene_hook);
if (allowDShowSound > 1) {
HookCall(0x450851, gsound_background_play_hook);
}
+2
View File
@@ -31,6 +31,8 @@ public:
static DWORD PlaySfallSound(const char* path, long mode);
static void __stdcall StopSfallSound(DWORD id);
static long CalculateVolumeDB(long masterVolume, long passVolume);
};
}