Added support for ACM audio file playback to soundplay script function

(with volume control)

Added a volume control to reduce the volume for play_sfall_sound.
Added descriptions and constant defines to documents.
Some code optimization.
Updated version number.
This commit is contained in:
NovaRain
2020-09-01 16:12:00 +08:00
parent 57f4deb5cf
commit ed9c6c7508
10 changed files with 151 additions and 84 deletions
+8 -8
View File
@@ -1,5 +1,5 @@
;sfall configuration settings ;sfall configuration settings
;v3.8.27 ;v3.8.28
[Main] [Main]
;Change to 1 if you want to use command line args to tell sfall to use another ini file. ;Change to 1 if you want to use command line args to tell sfall to use another ini file.
@@ -28,7 +28,7 @@ SpeedMulti9=900
;The initial speed at game startup ;The initial speed at game startup
SpeedMultiInitial=100 SpeedMultiInitial=100
;Set to 1 to also affect the playback speed of mve video files without an audio track ;Set to 1 to also affect the playback speed of MVE video files without an audio track
AffectPlayback=0 AffectPlayback=0
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -68,8 +68,8 @@ GPUBlt=0
;Requires DX9 graphics mode and v2.0 pixel shader support (see GPUBlt option) ;Requires DX9 graphics mode and v2.0 pixel shader support (see GPUBlt option)
Use32BitHeadGraphics=0 Use32BitHeadGraphics=0
;Set to 1 to automatically search for alternative avi video files when Fallout tries to play the game movies ;Set to 1 to automatically search for alternative AVI video files when Fallout tries to play the game movies
;Set to 2 to force avi videos to fit the screen width ;Set to 2 to force AVI videos to fit the screen width
;Requires DX9 graphics mode ;Requires DX9 graphics mode
AllowDShowMovies=0 AllowDShowMovies=0
@@ -100,8 +100,8 @@ NumSoundBuffers=0
;Set to 1 to allow attaching sound files to combat float messages ;Set to 1 to allow attaching sound files to combat float messages
AllowSoundForFloats=0 AllowSoundForFloats=0
;Set to 1 to automatically search for alternative formats (mp3/wma/wav) when Fallout tries to play an acm ;Set to 1 to automatically search for alternative formats (mp3/wma/wav) when Fallout tries to play an ACM
;Set to 2 to play alternative music files even if original acm files are not present in the music folder ;Set to 2 to play alternative music files even if original ACM files are not present in the music folder
;This does not effect the play_sfall_sound and stop_sfall_sound script functions ;This does not effect the play_sfall_sound and stop_sfall_sound script functions
AllowDShowSound=0 AllowDShowSound=0
@@ -355,9 +355,9 @@ SaveInCombatFix=1
;Player's level is capped once the highest specified level is reached ;Player's level is capped once the highest specified level is reached
;XPTable=50,100,200 ;XPTable=50,100,200
;Set to 1 to enable additional weapon animation codes from o-t ;Set to 1 to enable additional weapon animation codes from o to t
;The 4 byte value at 0x39 of weapon protos may range from 0 to 15 rather than 0 to 10 ;The 4 byte value at 0x39 of weapon protos may range from 0 to 15 rather than 0 to 10
;Since the letters 'n' and 'r' are in use for other animations, an animation code of 11 corrisponds to 's' and 15 to 't' ;Since the letters 'n' and 'r' are in use for other animations, an animation code of 11 corresponds to 's' and 15 to 't'
AdditionalWeaponAnims=1 AdditionalWeaponAnims=1
;Uncomment these lines to modify the default modifiers for aimed shots at specific bodyparts ;Uncomment these lines to modify the default modifiers for aimed shots at specific bodyparts
@@ -340,4 +340,18 @@
#define OBJ_DATA_DAMAGE_LAST_TURN (0x48) #define OBJ_DATA_DAMAGE_LAST_TURN (0x48)
#define OBJ_DATA_WHO_HIT_ME (0x54) // current target of the critter #define OBJ_DATA_WHO_HIT_ME (0x54) // current target of the critter
/* Playback mode defines for the soundplay function */
#define soundraw (0x80000000) // sfall flag
#define Stereo8bit (soundstereo)
#define Stereo8bitLoop (soundstereo bwor soundloop)
#define Mono16bit (sound16bit)
#define Mono16bitLoop (sound16bit bwor soundloop)
#define Stereo16bit (soundstereo bwor sound16bit)
#define Stereo16bitLoop (soundstereo bwor sound16bit bwor soundloop)
// Volume reduction: 0x0000XXXX - min, 0x7FFFXXXX - mute
#define SoundVolume25 (0x20000000)
#define SoundVolumeHalf (0x40000000)
#define SoundVolume75 (0x60000000)
#endif // DEFINE_EXTRA_H #endif // DEFINE_EXTRA_H
+2 -1
View File
@@ -67,6 +67,7 @@ get/set_proto_data are used to manipulate the in memory copies of the .pro files
the list_xxx functions can be used to loop over all items on a map. list_begin takes an argument telling sfall what you want to list. (Defined in sfall.h) It returns a list pointer, which you iterate through with list_next. Finally, when you've finished with the list use list_end on it. Not calling list_end will result in a memory leak. Alternatively, use list_as_array to get the whole list at once as a temp array variable, which can be looped over using len_array and which you don't need to remember to free afterwards. the list_xxx functions can be used to loop over all items on a map. list_begin takes an argument telling sfall what you want to list. (Defined in sfall.h) It returns a list pointer, which you iterate through with list_next. Finally, when you've finished with the list use list_end on it. Not calling list_end will result in a memory leak. Alternatively, use list_as_array to get the whole list at once as a temp array variable, which can be looped over using len_array and which you don't need to remember to free afterwards.
play_sfall_sound and stop_sfall_sound are used to play mp3/wav/wma files. The path given is relative to the Fallout folder. Specify mode as 1 to loop the file continuously, 2 to replace the current background game music with playing the specified file in loop mode, or 0 to play the file once. If you don't wish to loop, play_sfall_sound returns 0. If you do loop, it returns an id which can be passed back to stop_sfall_sound when you want to stop the effect. All sounds effects will be stopped on game reload, looping or not. These functions do not require 'AllowDShowSound' to be set to 1 in ddraw.ini. play_sfall_sound and stop_sfall_sound are used to play mp3/wav/wma files. The path given is relative to the Fallout folder. Specify mode as 1 to loop the file continuously, 2 to replace the current background game music with playing the specified file in loop mode, or 0 to play the file once. If you don't wish to loop, play_sfall_sound returns 0. If you do loop, it returns an id which can be passed back to stop_sfall_sound when you want to stop the effect. All sounds effects will be stopped on game reload, looping or not. These functions do not require 'AllowDShowSound' to be set to 1 in ddraw.ini.
Starting from sfall 4.2.8/3.8.28, you can pass a value in range from 0 to 32767 in the 'mode' argument for a reduced sound volume; the value 32767 is the minimum volume (mute). To set the volume, You need to convert the number to hexadecimal and use the argument format 0xZZZZ000Y, where ZZZZ is the volume value, and Y is the playback mode.
arrays are created and manipulated with the xxx_array functions. An array must first be created with create_array or temp_array, specifying how many data elements the array can hold. You can store any of ints, floats and strings in an array, and can mix all 3 in a single array. The id returned by create/temp_array can then be used with the other array functions. Arrays are shared between all scripts. (i.e. you can call create_array from one script, and then use the returned id from another script.) They are also saved across savegames. You must remember to free any arrays you create with create_array when you are done with them, or you will leak memory. arrays created with temp_array will be automatically freed at the end of the frame. These functions are safe, in that supplying a bad id or trying to access out of range elements will not crash the script. create_array is the only function that returns a permanent array, all other functions which return arrays (string_split, list_as_array etc,) all return temp arrays. You can use fix_array to make a temp array permanent. arrays are created and manipulated with the xxx_array functions. An array must first be created with create_array or temp_array, specifying how many data elements the array can hold. You can store any of ints, floats and strings in an array, and can mix all 3 in a single array. The id returned by create/temp_array can then be used with the other array functions. Arrays are shared between all scripts. (i.e. you can call create_array from one script, and then use the returned id from another script.) They are also saved across savegames. You must remember to free any arrays you create with create_array when you are done with them, or you will leak memory. arrays created with temp_array will be automatically freed at the end of the frame. These functions are safe, in that supplying a bad id or trying to access out of range elements will not crash the script. create_array is the only function that returns a permanent array, all other functions which return arrays (string_split, list_as_array etc,) all return temp arrays. You can use fix_array to make a temp array permanent.
@@ -418,7 +419,7 @@ Some utility/math functions are available:
> void sfall_func2("set_outline", object, int color) > void sfall_func2("set_outline", object, int color)
- sets the outline color of an object (see OUTLINE_* constants in sfall.h) - sets the outline color of an object (see OUTLINE_* constants in sfall.h)
- can also set a custom color from the game palette by shifting the color index value 8 bits left: 0xCC00 where CC is the palette index (available since sfall 4.2.7/3.8.27) - can also set a custom color from the game palette by shifting the color index value left by 8 bits: 0xCC00 where CC is the palette index (available since sfall 4.2.7/3.8.27)
- passing 0 will disable the outline - passing 0 will disable the outline
- call "tile_refresh_display" after changing outline of objects to properly redraw the scene - call "tile_refresh_display" after changing outline of objects to properly redraw the scene
+1 -1
View File
@@ -3486,9 +3486,9 @@ void BugFixesInit()
HookCall(0x481409, main_death_scene_hook); HookCall(0x481409, main_death_scene_hook);
// Fix for trying to loot corpses with the "NoSteal" flag // Fix for trying to loot corpses with the "NoSteal" flag
MakeCall(0x4123F8, action_loot_container_hack, 1);
SafeWrite8(0x4123F2, 0x64); // protoId SafeWrite8(0x4123F2, 0x64); // protoId
BlockCall(0x4123F3); BlockCall(0x4123F3);
MakeCall(0x4123F8, action_loot_container_hack, 1);
// Fix the music volume when entering the dialog // Fix the music volume when entering the dialog
SafeWrite32(0x44525D, (DWORD)_background_volume); SafeWrite32(0x44525D, (DWORD)_background_volume);
+10
View File
@@ -190,6 +190,7 @@ DWORD* ptr_read_callback = reinterpret_cast<DWORD*>(_read_callbac
BYTE* ptr_RedColor = reinterpret_cast<BYTE*>(_RedColor); BYTE* ptr_RedColor = reinterpret_cast<BYTE*>(_RedColor);
DWORD* ptr_retvals = reinterpret_cast<DWORD*>(_retvals); DWORD* ptr_retvals = reinterpret_cast<DWORD*>(_retvals);
DWORD* ptr_rotation = reinterpret_cast<DWORD*>(_rotation); DWORD* ptr_rotation = reinterpret_cast<DWORD*>(_rotation);
DWORD* ptr_sampleRate = reinterpret_cast<DWORD*>(_sampleRate);
BoundRect* ptr_scr_size = reinterpret_cast<BoundRect*>(_scr_size); BoundRect* ptr_scr_size = reinterpret_cast<BoundRect*>(_scr_size);
ScriptListInfoItem** ptr_scriptListInfo = reinterpret_cast<ScriptListInfoItem**>(_scriptListInfo); // dynamic array ScriptListInfoItem** ptr_scriptListInfo = reinterpret_cast<ScriptListInfoItem**>(_scriptListInfo); // dynamic array
DWORD* ptr_skill_data = reinterpret_cast<DWORD*>(_skill_data); DWORD* ptr_skill_data = reinterpret_cast<DWORD*>(_skill_data);
@@ -294,6 +295,11 @@ const DWORD art_ptr_lock_ = 0x419160;
const DWORD art_ptr_lock_data_ = 0x419188; const DWORD art_ptr_lock_data_ = 0x419188;
const DWORD art_ptr_unlock_ = 0x419260; const DWORD art_ptr_unlock_ = 0x419260;
const DWORD attack_crit_success_ = 0x423EB4; const DWORD attack_crit_success_ = 0x423EB4;
const DWORD audioCloseFile_ = 0x41A50C;
const DWORD audioFileSize_ = 0x41A78C;
const DWORD audioOpen_ = 0x41A2EC;
const DWORD audioRead_ = 0x41A574;
const DWORD audioSeek_ = 0x41A5E0;
const DWORD automap_ = 0x41B8BC; const DWORD automap_ = 0x41B8BC;
const DWORD barter_compute_value_ = 0x474B2C; const DWORD barter_compute_value_ = 0x474B2C;
const DWORD barter_inventory_ = 0x4757F0; const DWORD barter_inventory_ = 0x4757F0;
@@ -499,6 +505,7 @@ const DWORD item_d_check_addict_ = 0x47A640;
const DWORD item_d_take_drug_ = 0x479F60; const DWORD item_d_take_drug_ = 0x479F60;
const DWORD item_drop_all_ = 0x477804; const DWORD item_drop_all_ = 0x477804;
const DWORD item_get_type_ = 0x477AFC; const DWORD item_get_type_ = 0x477AFC;
const DWORD item_hit_with_ = 0x477FF8;
const DWORD item_m_cell_pid_ = 0x479454; const DWORD item_m_cell_pid_ = 0x479454;
const DWORD item_m_dec_charges_ = 0x4795A4; const DWORD item_m_dec_charges_ = 0x4795A4;
const DWORD item_m_turn_off_ = 0x479898; const DWORD item_m_turn_off_ = 0x479898;
@@ -716,6 +723,9 @@ const DWORD soundDelete_ = 0x4AD8DC;
const DWORD soundGetPosition_ = 0x4AE634; const DWORD soundGetPosition_ = 0x4AE634;
const DWORD soundPlay_ = 0x4AD73C; const DWORD soundPlay_ = 0x4AD73C;
const DWORD soundPlaying_ = 0x4ADA84; const DWORD soundPlaying_ = 0x4ADA84;
const DWORD soundSetCallback_ = 0x4ADFF0;
const DWORD soundSetFileIO_ = 0x4AE2FC;
const DWORD soundVolume_ = 0x4ADE0C;
const DWORD sprintf_ = 0x4F0041; const DWORD sprintf_ = 0x4F0041;
const DWORD square_num_ = 0x4B1F04; const DWORD square_num_ = 0x4B1F04;
const DWORD stat_get_base_ = 0x4AF3E0; const DWORD stat_get_base_ = 0x4AF3E0;
+11 -1
View File
@@ -236,6 +236,7 @@
#define _retvals 0x43EA7C #define _retvals 0x43EA7C
#define _rotation 0x631D34 #define _rotation 0x631D34
#define _sad 0x530014 #define _sad 0x530014
#define _sampleRate 0x66815C
#define _scr_size 0x6AC9F0 #define _scr_size 0x6AC9F0
#define _script_engine_running 0x51C714 #define _script_engine_running 0x51C714
#define _scriptListInfo 0x51C7C8 #define _scriptListInfo 0x51C7C8
@@ -485,7 +486,7 @@ extern DWORD* ptr_read_callback;
extern BYTE* ptr_RedColor; extern BYTE* ptr_RedColor;
extern DWORD* ptr_retvals; extern DWORD* ptr_retvals;
extern DWORD* ptr_rotation; extern DWORD* ptr_rotation;
extern DWORD* ptr_sad; extern DWORD* ptr_sampleRate;
extern BoundRect* ptr_scr_size; extern BoundRect* ptr_scr_size;
extern ScriptListInfoItem** ptr_scriptListInfo; // dynamic array extern ScriptListInfoItem** ptr_scriptListInfo; // dynamic array
extern DWORD* ptr_skill_data; extern DWORD* ptr_skill_data;
@@ -573,6 +574,11 @@ extern const DWORD art_ptr_lock_;
extern const DWORD art_ptr_lock_data_; extern const DWORD art_ptr_lock_data_;
extern const DWORD art_ptr_unlock_; extern const DWORD art_ptr_unlock_;
extern const DWORD attack_crit_success_; extern const DWORD attack_crit_success_;
extern const DWORD audioCloseFile_;
extern const DWORD audioFileSize_;
extern const DWORD audioOpen_;
extern const DWORD audioRead_;
extern const DWORD audioSeek_;
extern const DWORD automap_; extern const DWORD automap_;
extern const DWORD barter_compute_value_; extern const DWORD barter_compute_value_;
extern const DWORD barter_inventory_; extern const DWORD barter_inventory_;
@@ -781,6 +787,7 @@ extern const DWORD item_d_check_addict_;
extern const DWORD item_d_take_drug_; extern const DWORD item_d_take_drug_;
extern const DWORD item_drop_all_; extern const DWORD item_drop_all_;
extern const DWORD item_get_type_; extern const DWORD item_get_type_;
extern const DWORD item_hit_with_;
extern const DWORD item_m_cell_pid_; extern const DWORD item_m_cell_pid_;
extern const DWORD item_m_dec_charges_; extern const DWORD item_m_dec_charges_;
extern const DWORD item_m_turn_off_; extern const DWORD item_m_turn_off_;
@@ -1004,6 +1011,9 @@ extern const DWORD soundDelete_;
extern const DWORD soundGetPosition_; extern const DWORD soundGetPosition_;
extern const DWORD soundPlay_; extern const DWORD soundPlay_;
extern const DWORD soundPlaying_; extern const DWORD soundPlaying_;
extern const DWORD soundSetCallback_;
extern const DWORD soundSetFileIO_;
extern const DWORD soundVolume_;
extern const DWORD sprintf_; extern const DWORD sprintf_;
extern const DWORD square_num_; extern const DWORD square_num_;
extern const DWORD stat_get_base_; extern const DWORD stat_get_base_;
+1 -2
View File
@@ -447,8 +447,7 @@ static void OverrideMusicDirPatch() {
if (overrideMode == 2) { if (overrideMode == 2) {
const DWORD musicOverride2Addr[] = {0x518E78, 0x518E7C}; const DWORD musicOverride2Addr[] = {0x518E78, 0x518E7C};
SafeWriteBatch<DWORD>((DWORD)musicOverridePath, musicOverride2Addr); SafeWriteBatch<DWORD>((DWORD)musicOverridePath, musicOverride2Addr);
__int64 data = 0x90909040EB; // jmp 0x44FD35 (skip paths initialization) SafeWrite16(0x44FCF3, 0x40EB); // jmp 0x44FD35 (skip paths initialization)
SafeWriteBytes(0x44FCF3, (BYTE*)&data, 5);
} }
} }
} }
+20 -21
View File
@@ -3,10 +3,10 @@
static void __stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) { static void __stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func) {
DWORD oldProtect, data = (DWORD)func - (addr + 5); DWORD oldProtect, data = (DWORD)func - (addr + 5);
VirtualProtect((void *)addr, 5, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
*((BYTE*)addr) = code; *((BYTE*)addr) = code;
*((DWORD*)(addr + 1)) = data; *((DWORD*)(addr + 1)) = data;
VirtualProtect((void *)addr, 5, oldProtect, &oldProtect); VirtualProtect((void*)addr, 5, oldProtect, &oldProtect);
} }
static __declspec(noinline) void __stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func, DWORD len) { static __declspec(noinline) void __stdcall SafeWriteFunc(BYTE code, DWORD addr, void* func, DWORD len) {
@@ -15,55 +15,55 @@ static __declspec(noinline) void __stdcall SafeWriteFunc(BYTE code, DWORD addr,
addrMem = addr + 5, addrMem = addr + 5,
data = (DWORD)func - addrMem; data = (DWORD)func - addrMem;
VirtualProtect((void *)addr, protectLen, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, protectLen, PAGE_EXECUTE_READWRITE, &oldProtect);
*((BYTE*)addr) = code; *((BYTE*)addr) = code;
*((DWORD*)(addr + 1)) = data; *((DWORD*)(addr + 1)) = data;
for (unsigned int i = 0; i < len; i++) { for (unsigned int i = 0; i < len; i++) {
*((BYTE*)(addrMem + i)) = CODETYPE_Nop; *((BYTE*)(addrMem + i)) = CODETYPE_Nop;
} }
VirtualProtect((void *)addr, protectLen, oldProtect, &oldProtect); VirtualProtect((void*)addr, protectLen, oldProtect, &oldProtect);
} }
void SafeWriteBytes(DWORD addr, BYTE* data, int count) { void SafeWriteBytes(DWORD addr, BYTE* data, int count) {
DWORD oldProtect; DWORD oldProtect;
VirtualProtect((void *)addr, count, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, count, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((void*)addr, data, count); memcpy((void*)addr, data, count);
VirtualProtect((void *)addr, count, oldProtect, &oldProtect); VirtualProtect((void*)addr, count, oldProtect, &oldProtect);
} }
void __stdcall SafeWrite8(DWORD addr, BYTE data) { void __stdcall SafeWrite8(DWORD addr, BYTE data) {
DWORD oldProtect; DWORD oldProtect;
VirtualProtect((void *)addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*((BYTE*)addr) = data; *((BYTE*)addr) = data;
VirtualProtect((void *)addr, 1, oldProtect, &oldProtect); VirtualProtect((void*)addr, 1, oldProtect, &oldProtect);
} }
void __stdcall SafeWrite16(DWORD addr, WORD data) { void __stdcall SafeWrite16(DWORD addr, WORD data) {
DWORD oldProtect; DWORD oldProtect;
VirtualProtect((void *)addr, 2, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, 2, PAGE_EXECUTE_READWRITE, &oldProtect);
*((WORD*)addr) = data; *((WORD*)addr) = data;
VirtualProtect((void *)addr, 2, oldProtect, &oldProtect); VirtualProtect((void*)addr, 2, oldProtect, &oldProtect);
} }
void __stdcall SafeWrite32(DWORD addr, DWORD data) { void __stdcall SafeWrite32(DWORD addr, DWORD data) {
DWORD oldProtect; DWORD oldProtect;
VirtualProtect((void *)addr, 4, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
*((DWORD*)addr) = data; *((DWORD*)addr) = data;
VirtualProtect((void *)addr, 4, oldProtect, &oldProtect); VirtualProtect((void*)addr, 4, oldProtect, &oldProtect);
} }
void __stdcall SafeWriteStr(DWORD addr, const char* data) { void __stdcall SafeWriteStr(DWORD addr, const char* data) {
DWORD oldProtect; DWORD oldProtect;
long len = strlen(data) + 1; long len = strlen(data) + 1;
VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect);
strcpy((char *)addr, data); strcpy((char*)addr, data);
VirtualProtect((void *)addr, len, oldProtect, &oldProtect); VirtualProtect((void*)addr, len, oldProtect, &oldProtect);
} }
void HookCall(DWORD addr, void* func) { void HookCall(DWORD addr, void* func) {
@@ -89,16 +89,15 @@ void MakeJump(DWORD addr, void* func, int len) {
void SafeMemSet(DWORD addr, BYTE val, int len) { void SafeMemSet(DWORD addr, BYTE val, int len) {
DWORD oldProtect; DWORD oldProtect;
VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect);
memset((void*)addr, val, len); memset((void*)addr, val, len);
VirtualProtect((void *)addr, len, oldProtect, &oldProtect); VirtualProtect((void*)addr, len, oldProtect, &oldProtect);
} }
void BlockCall(DWORD addr) { void BlockCall(DWORD addr) {
DWORD oldProtect; DWORD oldProtect;
VirtualProtect((void *)addr, 5, PAGE_EXECUTE_READWRITE, &oldProtect); VirtualProtect((void*)addr, 4, PAGE_EXECUTE_READWRITE, &oldProtect);
*((DWORD*)addr) = 0x00441F0F; // long NOP (0F1F440000) *((DWORD*)addr) = 0x00441F0F; // long NOP (0F1F4400-XX)
*((BYTE*)(addr + 4)) = 0x00; VirtualProtect((void*)addr, 4, oldProtect, &oldProtect);
VirtualProtect((void *)addr, 5, oldProtect, &oldProtect);
} }
+81 -47
View File
@@ -17,6 +17,7 @@
*/ */
//#include <unordered_map> //#include <unordered_map>
#include <algorithm>
#include <dshow.h> #include <dshow.h>
#include "main.h" #include "main.h"
@@ -99,44 +100,29 @@ void WipeSounds() {
LRESULT CALLBACK SoundWndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l) { LRESULT CALLBACK SoundWndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l) {
if (msg == WM_APP) { if (msg == WM_APP) {
DWORD id = l; if (!(l & 0xA0000000)) return 0;
sDSSound* sound = nullptr; sDSSound* sound = reinterpret_cast<sDSSound*>(l & ~0xA0000000);
long index = -1;
if (id & SNDFLAG_looping) {
for (size_t i = 0; i < loopingSounds.size(); i++) {
if (loopingSounds[i]->id == id) {
sound = loopingSounds[i];
break;
}
}
} else {
for (size_t i = 0; i < playingSounds.size(); i++) {
if (playingSounds[i]->id == id) {
sound = playingSounds[i];
index = i;
break;
}
}
}
LONG e = 0; LONG e = 0;
LONG_PTR p1 = 0, p2 = 0; LONG_PTR p1 = 0, p2 = 0;
if (sound && !FAILED(sound->pEvent->GetEvent(&e, &p1, &p2, 0))) { if (!FAILED(sound->pEvent->GetEvent(&e, &p1, &p2, 0))) {
sound->pEvent->FreeEventParams(e, p1, p2);
if (e == EC_COMPLETE) { if (e == EC_COMPLETE) {
if (id & SNDFLAG_looping) { if (sound->id & SNDFLAG_looping) {
LONGLONG pos = 0; LONGLONG pos = 0;
sound->pSeek->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning); sound->pSeek->SetPositions(&pos, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning);
sound->pControl->Run(); sound->pControl->Run();
sound->pEvent->FreeEventParams(e, p1, p2);
} else { } else {
FreeSound(sound); sound->pEvent->FreeEventParams(e, p1, p2);
playingSounds.erase(playingSounds.begin() + index); if (sound->id & SNDFLAG_on_stop) { // speech sound playback is completed
if (id & SNDFLAG_on_stop) { // speech sound playback is completed
*ptr_main_death_voiceover_done = 1; *ptr_main_death_voiceover_done = 1;
*ptr_endgame_subtitle_done = 1; *ptr_endgame_subtitle_done = 1;
lipsPlaying = false; lipsPlaying = false;
speechSound = nullptr; speechSound = nullptr;
} }
playingSounds.erase(
std::find_if(playingSounds.cbegin(), playingSounds.cend(), [&](const sDSSound* snd) { return snd->id == sound->id; })
);
FreeSound(sound);
} }
} }
} }
@@ -146,7 +132,7 @@ LRESULT CALLBACK SoundWndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l) {
} }
static void CreateSndWnd() { static void CreateSndWnd() {
dlog("Creating sfall sound callback windows.", DL_INIT); dlog("Creating sfall sound callback window.", DL_INIT);
if (GraphicsMode == 0) CoInitialize(0); if (GraphicsMode == 0) CoInitialize(0);
WNDCLASSEX wcx; WNDCLASSEX wcx;
@@ -158,7 +144,7 @@ static void CreateSndWnd() {
wcx.lpszClassName = "sfallSndWnd"; wcx.lpszClassName = "sfallSndWnd";
RegisterClassEx(&wcx); RegisterClassEx(&wcx);
soundwindow = CreateWindow("sfallSndWnd", "SndWnd", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, GetModuleHandleA(0), 0); soundwindow = CreateWindowA("sfallSndWnd", "SndWnd", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, wcx.hInstance, 0);
dlogr(" Done", DL_INIT); dlogr(" Done", DL_INIT);
} }
@@ -216,12 +202,11 @@ long Sound_CalculateVolumeDB(long masterVolume, long passVolume) {
if (masterVolume <= 0 || passVolume <= 0) return -9999; // mute if (masterVolume <= 0 || passVolume <= 0) return -9999; // mute
const int volOffset = -100; // adjust the maximum volume const int volOffset = -100; // adjust the maximum volume
const int minVolume = -2048; const int minVolume = -2100;
float multiply = (32767.0f / masterVolume); passVolume = masterVolume * passVolume / 32767;
float volume = (((float)passVolume / 32767.0f) * 100.0f) / multiply; // calculate % long volume = static_cast<long>(minVolume * ((float)passVolume / 32767.0f)); // calculate % value
volume = (minVolume * volume) / 100.0f; return (minVolume - volume) + volOffset;
return static_cast<long>(minVolume - volume) + volOffset;
} }
/* /*
@@ -230,7 +215,7 @@ long Sound_CalculateVolumeDB(long masterVolume, long passVolume) {
sfx_volume: sound=null type=game_sfx passVolume=sndfx_volume sfx_volume: sound=null type=game_sfx passVolume=sndfx_volume
speech_volume: sound=sound type=sfx_single passVolume=speech_volume speech_volume: sound=sound type=sfx_single passVolume=speech_volume
*/ */
static void __cdecl SfallSoundVolume(sDSSound* sound, SoundType type, long passVolume) { static void __cdecl SetSoundVolume(sDSSound* sound, SoundType type, long passVolume) {
long volume, sfxVolume, masterVolume = *ptr_master_volume; long volume, sfxVolume, masterVolume = *ptr_master_volume;
volume = Sound_CalculateVolumeDB(masterVolume, passVolume); volume = Sound_CalculateVolumeDB(masterVolume, passVolume);
@@ -278,7 +263,7 @@ static bool IsMute(SoundMode mode) {
music_play: mode 2 - loop sound playback with the background game music turned off music_play: mode 2 - loop sound playback with the background game music turned off
speech_play: mode 3 - speech_play: mode 3 -
*/ */
static sDSSound* PlayingSound(const wchar_t* pathFile, SoundMode mode, bool isPaused = false) { static sDSSound* PlayingSound(const wchar_t* pathFile, SoundMode mode, long adjustVolume = 0, bool isPaused = false) {
if (!soundwindow) CreateSndWnd(); if (!soundwindow) CreateSndWnd();
if (IsMute(mode)) return nullptr; if (IsMute(mode)) return nullptr;
@@ -305,7 +290,11 @@ static sDSSound* PlayingSound(const wchar_t* pathFile, SoundMode mode, bool isPa
sound->id = (isLoop) ? ++loopID : ++playID; sound->id = (isLoop) ? ++loopID : ++playID;
if (isLoop) sound->id |= SNDFLAG_looping; // sfx loop sound if (isLoop) sound->id |= SNDFLAG_looping; // sfx loop sound
if (mode == SNDMODE_music_play) { switch (mode) {
case SNDMODE_engine_music_play:
sound->id |= SNDFLAG_engine; // engine play
break;
case SNDMODE_music_play:
sound->id |= SNDFLAG_restore; // restore background game music on stop sound->id |= SNDFLAG_restore; // restore background game music on stop
if (backgroundMusic) if (backgroundMusic)
StopSfallSound(backgroundMusic->id); StopSfallSound(backgroundMusic->id);
@@ -313,19 +302,21 @@ static sDSSound* PlayingSound(const wchar_t* pathFile, SoundMode mode, bool isPa
__asm call gsound_background_stop_; __asm call gsound_background_stop_;
} }
backgroundMusic = sound; backgroundMusic = sound;
break;
case SNDMODE_speech_play:
sound->pSeek->SetTimeFormat(&TIME_FORMAT_SAMPLE);
sound->id |= SNDFLAG_on_stop;
break;
} }
else if (mode == SNDMODE_engine_music_play) sound->id |= SNDFLAG_engine; // engine play
else if (mode == SNDMODE_speech_play) sound->id |= SNDFLAG_on_stop;
if (sound->pSeek) sound->pSeek->SetTimeFormat(&TIME_FORMAT_SAMPLE); sound->pEvent->SetNotifyWindow((OAHWND)soundwindow, WM_APP, ((unsigned long)sound | 0xA0000000));
sound->pEvent->SetNotifyWindow((OAHWND)soundwindow, WM_APP, sound->id);
sound->pControl->Run(); sound->pControl->Run();
if (isLoop) { if (isLoop) {
SfallSoundVolume(sound, SNDTYPE_sfx_loop, (mode == SNDMODE_loop_play) ? *ptr_sndfx_volume : *ptr_background_volume); SetSoundVolume(sound, SNDTYPE_sfx_loop, ((mode == SNDMODE_loop_play) ? *ptr_sndfx_volume : *ptr_background_volume) - adjustVolume);
loopingSounds.push_back(sound); loopingSounds.push_back(sound);
} else { } else {
SfallSoundVolume(sound, SNDTYPE_sfx_single, (mode == SNDMODE_speech_play) ? *ptr_speech_volume : *ptr_sndfx_volume); SetSoundVolume(sound, SNDTYPE_sfx_single, ((mode == SNDMODE_speech_play) ? *ptr_speech_volume : *ptr_sndfx_volume) - adjustVolume);
if (mode == SNDMODE_speech_play) speechSound = sound; if (mode == SNDMODE_speech_play) speechSound = sound;
if (isPaused) { if (isPaused) {
sound->pControl->Pause(); // for delayed playback sound->pControl->Pause(); // for delayed playback
@@ -404,7 +395,7 @@ static bool __fastcall SoundFileLoad(PlayType playType, const char* path) {
backgroundMusic = PlayingSound(buf, SNDMODE_engine_music_play); // background music loop backgroundMusic = PlayingSound(buf, SNDMODE_engine_music_play); // background music loop
if (!backgroundMusic) return false; if (!backgroundMusic) return false;
} else { } else {
if (!PlayingSound(buf, ((playType >= PLAYTYPE_lips) ? SNDMODE_speech_play : SNDMODE_single_play), (playType == PLAYTYPE_slides))) { if (!PlayingSound(buf, ((playType >= PLAYTYPE_lips) ? SNDMODE_speech_play : SNDMODE_single_play), 0, (playType == PLAYTYPE_slides))) {
return false; return false;
} }
if (playType == PLAYTYPE_lips) { if (playType == PLAYTYPE_lips) {
@@ -439,8 +430,11 @@ DWORD __stdcall PlaySfallSound(const char* path, long mode) {
if (len <= 3 || len >= MAX_PATH) return 0; if (len <= 3 || len >= MAX_PATH) return 0;
buf[len] = L'\0'; buf[len] = L'\0';
short volAdjust = (mode & 0x7FFF0000) >> 16;
mode &= 0xF;
if (mode > SNDMODE_music_play) mode = SNDMODE_music_play; if (mode > SNDMODE_music_play) mode = SNDMODE_music_play;
sDSSound* sound = PlayingSound(buf, (SoundMode)mode); sDSSound* sound = PlayingSound(buf, (SoundMode)mode, volAdjust);
return (mode && sound) ? sound->id : 0; return (mode && sound) ? sound->id : 0;
} }
@@ -722,7 +716,7 @@ static void __declspec(naked) gsound_background_volume_set_hack() {
push eax; push eax;
push 0; // SNDTYPE_sfx_loop push 0; // SNDTYPE_sfx_loop
push ecx; // set volume for background music push ecx; // set volume for background music
call SfallSoundVolume; call SetSoundVolume;
add esp, 8; add esp, 8;
pop eax; pop eax;
pop edx; pop edx;
@@ -741,7 +735,7 @@ static void __declspec(naked) gsound_master_volume_set_hack() {
push dword ptr ds:[_background_volume]; push dword ptr ds:[_background_volume];
push 3; // SNDTYPE_game_master push 3; // SNDTYPE_game_master
push 0; // set volume for all sounds push 0; // set volume for all sounds
call SfallSoundVolume; call SetSoundVolume;
add esp, 12; add esp, 12;
pop edx; pop edx;
pop ecx; pop ecx;
@@ -758,7 +752,7 @@ static void __declspec(naked) gsound_set_sfx_volume_hack() {
push eax; push eax;
push 2; // SNDTYPE_game_sfx push 2; // SNDTYPE_game_sfx
push 0; // set volume for all sounds push 0; // set volume for all sounds
call SfallSoundVolume; call SetSoundVolume;
add esp, 12; add esp, 12;
pop edx; pop edx;
pop ecx; pop ecx;
@@ -831,7 +825,44 @@ end:
} }
} }
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;
mov bx, [esp + 0x18 - 0x18 + 4+2]; // get volume adjustment: 0 - max, 32767 - mute
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
}
}
static const int SampleRate = 44100; // 44.1kHz
void SoundInit() { void SoundInit() {
// Set the sample rate for the primary sound buffer
//SafeWrite32(0x44FDBC, SampleRate);
//LoadGameHook::OnAfterGameInit() += []() { *ptr_sampleRate = SampleRate / 2; }; // Revert to 22kHz for secondary sound buffers
HookCall(0x44E816, gmovie_play_hook_pause); HookCall(0x44E816, gmovie_play_hook_pause);
HookCall(0x44EA84, gmovie_play_hook_unpause); HookCall(0x44EA84, gmovie_play_hook_unpause);
MakeCall(0x450525, gsound_background_volume_set_hack); MakeCall(0x450525, gsound_background_volume_set_hack);
@@ -884,6 +915,9 @@ void SoundInit() {
SafeWrite8(0x42B6F5, CODETYPE_JumpShort); // bypass chance SafeWrite8(0x42B6F5, CODETYPE_JumpShort); // bypass chance
} }
} }
// Support for ACM audio file playback and volume adjustment for the soundplay script function
HookCall(0x4661B3, soundStartInterpret_hook);
} }
void SoundExit() { void SoundExit() {
+3 -3
View File
@@ -24,11 +24,11 @@
#define VERSION_MAJOR 3 #define VERSION_MAJOR 3
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_BUILD 27 #define VERSION_BUILD 28
#define VERSION_REV 0 #define VERSION_REV 0
#ifdef WIN2K #ifdef WIN2K
#define VERSION_STRING "3.8.27 win2k" #define VERSION_STRING "3.8.28 win2k"
#else #else
#define VERSION_STRING "3.8.27" #define VERSION_STRING "3.8.28"
#endif #endif