mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
WIP: Fixed sfx/speech playback for alternative sound files
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2831,7 +2831,7 @@ void BugFixes::init()
|
||||
|
||||
// Fix for negative values in Skilldex window ("S")
|
||||
dlog("Applying fix for negative values in Skilldex window.", DL_INIT);
|
||||
SafeWrite8(0x4AC377, 0x7F); // jg
|
||||
SafeWrite8(0x4AC377, 0x7F); // jg
|
||||
dlogr(" Done", DL_INIT);
|
||||
|
||||
// Fix for negative SPECIAL values in character creation
|
||||
|
||||
+90
-34
@@ -38,14 +38,16 @@ enum SoundType : unsigned long {
|
||||
};
|
||||
|
||||
enum SoundMode : long {
|
||||
single_play = 0, // uses sfx volume
|
||||
loop_play = 1, // uses sfx volume
|
||||
music_play = 2, // uses background volume
|
||||
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
|
||||
@@ -182,12 +187,12 @@ static long CalculateVolumeDB(long masterVolume, long passVolume) {
|
||||
master_volume: sound=0 type=game_master passVolume=background_volume
|
||||
background_volume: sound=backgroundMusic type=background_loop passVolume=background_volume
|
||||
sfx_volume: sound=0 type=game_sfx passVolume=sndfx_volume
|
||||
*/
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user