102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
mss_sdl.c
Go to the documentation of this file.
1#include "mss_private.h"
2#include "sidecar_private.h"
3
4#include <dttr_core.h>
5#include <dttr_log.h>
6
7#include <SDL3/SDL.h>
8
9#include <stdbool.h>
10#include <stdint.h>
11#include <string.h>
12#include <windows.h>
13
14typedef struct {
15 const char *hook_name;
16 const char *import_name;
17 void *callback;
20
22 {"dttr_hook_mss_ail_allocate_sample_handle",
23 "_AIL_allocate_sample_handle@4",
25 {"dttr_hook_mss_ail_close_stream", "_AIL_close_stream@4", dttr_mss_ail_close_stream},
26 {"dttr_hook_mss_ail_end_sample", "_AIL_end_sample@4", dttr_mss_ail_end_sample},
27 {"dttr_hook_mss_ail_get_preference",
28 "_AIL_get_preference@4",
30 {"dttr_hook_mss_ail_init_sample", "_AIL_init_sample@4", dttr_mss_ail_init_sample},
31 {"dttr_hook_mss_ail_open_stream", "_AIL_open_stream@12", dttr_mss_ail_open_stream},
32 {"dttr_hook_mss_ail_pause_stream", "_AIL_pause_stream@8", dttr_mss_ail_pause_stream},
33 {"dttr_hook_mss_ail_release_sample_handle",
34 "_AIL_release_sample_handle@4",
36 {"dttr_hook_mss_ail_sample_playback_rate",
37 "_AIL_sample_playback_rate@4",
39 {"dttr_hook_mss_ail_sample_status",
40 "_AIL_sample_status@4",
42 {"dttr_hook_mss_ail_set_digital_master_volume",
43 "_AIL_set_digital_master_volume@8",
45 {"dttr_hook_mss_ail_set_preference",
46 "_AIL_set_preference@8",
48 {"dttr_hook_mss_ail_set_sample_file",
49 "_AIL_set_sample_file@12",
51 {"dttr_hook_mss_ail_set_sample_loop_count",
52 "_AIL_set_sample_loop_count@8",
54 {"dttr_hook_mss_ail_set_sample_pan",
55 "_AIL_set_sample_pan@8",
57 {"dttr_hook_mss_ail_set_sample_playback_rate",
58 "_AIL_set_sample_playback_rate@8",
60 {"dttr_hook_mss_ail_set_sample_volume",
61 "_AIL_set_sample_volume@8",
63 {"dttr_hook_mss_ail_set_stream_loop_count",
64 "_AIL_set_stream_loop_count@8",
66 {"dttr_hook_mss_ail_set_stream_volume",
67 "_AIL_set_stream_volume@8",
69 {"dttr_hook_mss_ail_shutdown", "_AIL_shutdown@0", dttr_mss_ail_shutdown},
70 {"dttr_hook_mss_ail_start_sample", "_AIL_start_sample@4", dttr_mss_ail_start_sample},
71 {"dttr_hook_mss_ail_start_stream", "_AIL_start_stream@4", dttr_mss_ail_start_stream},
72 {"dttr_hook_mss_ail_startup", "_AIL_startup@0", dttr_mss_ail_startup},
73 {"dttr_hook_mss_ail_stop_sample", "_AIL_stop_sample@4", dttr_mss_ail_stop_sample},
74 {"dttr_hook_mss_ail_stream_status",
75 "_AIL_stream_status@4",
77 {"dttr_hook_mss_ail_waveOutClose", "_AIL_waveOutClose@4", dttr_mss_ail_waveOutClose},
78 {"dttr_hook_mss_ail_waveOutOpen", "_AIL_waveOutOpen@16", dttr_mss_ail_waveOutOpen},
79};
80
81// Converts a Miles WAVEFORMAT block into the SDL mixer spec.
82static bool wave_format_spec(const void *format, SDL_AudioSpec *spec) {
83 if (!format || !spec) {
84 return false;
85 }
86
87 const uint8_t *bytes = format;
88 const uint16_t format_tag = dttr_mss_wave_read_u16le(bytes);
89 const uint16_t channels = dttr_mss_wave_read_u16le(bytes + 2);
90 const uint32_t sample_rate = dttr_mss_wave_read_u32le(bytes + 4);
91 const uint16_t bits_per_sample = dttr_mss_wave_read_u16le(bytes + 14);
92
93 if (format_tag != DTTR_MSS_WAVE_FORMAT_PCM || channels == 0 || sample_rate == 0) {
94 return false;
95 }
96
97 if (!dttr_mss_wave_bits_supported(bits_per_sample)) {
98 return false;
99 }
100
101 spec->format = DTTR_MSS_MIXER_FORMAT;
102 spec->channels = DTTR_MSS_MIXER_CHANNELS;
103 spec->freq = (int)sample_rate;
104 return true;
105}
106
107// Tears down the SDL-backed MSS mixer and resets driver state.
115
116// Installs one pointer hook for a Miles Sound System import.
118 const DTTR_Mods_Context *ctx,
119 const char *name,
120 DTTR_Core_Hook **handle,
121 uintptr_t addr,
122 void *callback
123) {
124 if (*handle) {
125 return true;
126 }
127
129 result = DTTR_Core_HookPointer(&ctx->runtime, addr, callback, NULL, handle);
130
131 if (!DTTR_ResultOK(result)) {
133 ctx,
134 "%s: pointer hook failed: %s",
135 name,
137 );
138 return false;
139 }
140
141 DTTR_MODS_LOG_DEBUG(ctx, "Installed %s at 0x%08X", name, (unsigned)addr);
142 return true;
143}
144
145// Installs the SDL replacement for one named Miles Sound System import.
147 const DTTR_Mods_Context *ctx,
148 const char *name,
149 uintptr_t site
150) {
151 for (size_t i = 0; i < DTTR_ARRAY_COUNT(mss_import_hooks); i++) {
153
154 if (strcmp(name, hook->import_name) != 0) {
155 continue;
156 }
157
159 ctx,
160 hook->hook_name,
161 &hook->handle,
162 site,
163 hook->callback
164 );
165 }
166
167 return false;
168}
169
170// Installs SDL replacements for the imports in one Miles descriptor.
172 const DTTR_Mods_Context *ctx,
173 uint8_t *base,
174 IMAGE_IMPORT_DESCRIPTOR *desc
175) {
176 bool ok = true;
177 IMAGE_THUNK_DATA *name_thunk = (IMAGE_THUNK_DATA *)(base + desc->OriginalFirstThunk);
178 IMAGE_THUNK_DATA *addr_thunk = (IMAGE_THUNK_DATA *)(base + desc->FirstThunk);
179
180 for (; name_thunk->u1.AddressOfData; name_thunk++, addr_thunk++) {
181 if (IMAGE_SNAP_BY_ORDINAL(name_thunk->u1.Ordinal)) {
182 continue;
183 }
184
185 IMAGE_IMPORT_BY_NAME
186 *import_name = (IMAGE_IMPORT_BY_NAME *)(base + name_thunk->u1.AddressOfData);
188 ctx,
189 (const char *)import_name->Name,
190 (uintptr_t)&addr_thunk->u1.Function
191 )) {
193 ctx,
194 "Unhandled or unhooked MSS32 import: %s",
195 import_name->Name
196 );
197 ok = false;
198 }
199 }
200
201 return ok;
202}
203
204// Releases Miles import hooks owned by the SDL-backed MSS shim.
206 for (size_t i = 0; i < DTTR_ARRAY_COUNT(mss_import_hooks); i++) {
208
209 if (!hook->handle) {
210 continue;
211 }
212
213 DTTR_Result result = DTTR_Core_Unhook(hook->handle);
214 if (!DTTR_ResultOK(result)) {
216 "%s: pointer unhook failed: %s",
217 hook->hook_name,
219 );
220 continue;
221 }
222
223 hook->handle = NULL;
224 }
225}
226
227// Installs SDL-backed replacements for the game's mss32.dll imports.
229 HMODULE module = ctx->runtime.game_module;
230 uint8_t *base = (uint8_t *)module;
231 IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)base;
232 IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(base + dos->e_lfanew);
233 IMAGE_DATA_DIRECTORY imports_dir = nt->OptionalHeader
234 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
235 IMAGE_IMPORT_DESCRIPTOR
236 *desc = (IMAGE_IMPORT_DESCRIPTOR *)(base + imports_dir.VirtualAddress);
237
238 for (; desc && desc->Name; desc++) {
239 if (_stricmp((const char *)(base + desc->Name), "mss32.dll") != 0) {
240 continue;
241 }
242
244 }
245
246 DTTR_MODS_LOG_ERROR(ctx, "mss32.dll import descriptor not found");
247 return false;
248}
249
250// Converts Miles master volume into SDL mixer gain.
251static float master_gain_for_volume(int volume) {
252 if (volume <= 0) {
253 return 0.0f;
254 }
255
256 if (volume >= DTTR_MSS_DEFAULT_VOLUME) {
257 return 1.0f;
258 }
259
260 return (float)volume / DTTR_MSS_MAX_VOLUME;
261}
262
267
268void __stdcall dttr_mss_ail_shutdown() {
270}
271
272int __stdcall dttr_mss_ail_set_preference(unsigned int preference, int value) {
273 return dttr_mss_core_set_preference(preference, value);
274}
275
276int __stdcall dttr_mss_ail_get_preference(unsigned int preference) {
277 return dttr_mss_core_get_preference(preference);
278}
279
280// Opens the SDL-backed mixer for Miles AIL waveOut.
282 void **driver_out,
283 void *wave_out,
284 int device_id,
285 const void *format
286) {
288 "MSS AIL_waveOutOpen(driver_out=%p, wave_out=%p, device_id=%d, format=%p)",
289 driver_out,
290 wave_out,
291 device_id,
292 format
293 );
294
295 if (driver_out) {
296 *driver_out = NULL;
297 }
298
299 SDL_AudioSpec desired_spec = {0};
300
301 if (wave_format_spec(format, &desired_spec)) {
304 "MSS AIL_waveOutOpen desired spec: format=%u channels=%d freq=%d",
305 (unsigned)desired_spec.format,
306 desired_spec.channels,
307 desired_spec.freq
308 );
309 }
310
312 DTTR_LOG_TRACE("MSS AIL_waveOutOpen -> -1 (mixer unavailable)");
313 return -1;
314 }
315
317
318 if (driver_out) {
319 *driver_out = dttr_mss_core_mixer();
320 }
321
323 "MSS AIL_waveOutOpen -> 0 driver=%p open_count=%d",
326 );
327 return 0;
328}
329
330// Closes one Miles AIL waveOut driver reference.
331void __stdcall dttr_mss_ail_waveOutClose(void *driver) {
333 "MSS AIL_waveOutClose(driver=%p, mixer=%p, open_count=%d)",
334 driver,
337 );
338
339 if (driver && driver != dttr_mss_core_mixer()) {
340 DTTR_LOG_ERROR("Ignoring AIL_waveOutClose for unknown driver %p", driver);
341 return;
342 }
343
346 }
347
349 "MSS AIL_waveOutClose open_count -> %d",
351 );
352
354 return;
355 }
356
358}
359
360// Applies Miles digital master volume to all SDL-backed playback.
void void * ctx
DTTR_Graphics_COM_DirectDraw7 DWORD void * desc
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
#define DTTR_ARRAY_COUNT(array)
Definition dttr_core.h:18
DTTR_Result DTTR_Core_HookPointer(const DTTR_Core_Context *ctx, uintptr_t address, void *new_value, void **out_original, DTTR_Core_Hook **out_hook)
Definition core.c:472
DTTR_Result DTTR_Core_Unhook(DTTR_Core_Hook *hook)
Definition core.c:517
#define DTTR_LOG_TRACE(...)
Definition dttr_log.h:27
#define DTTR_LOG_ERROR(...)
Definition dttr_log.h:31
#define DTTR_MODS_LOG_ERROR(ctx,...)
Definition dttr_mods.h:225
#define DTTR_MODS_LOG_DEBUG(ctx,...)
Definition dttr_mods.h:219
bool DTTR_ResultOK(DTTR_Result result)
Definition core.c:78
void dttr_mss_core_reset_driver_open_count()
Definition mss_core.c:180
void dttr_mss_core_set_desired_spec(const SDL_AudioSpec *spec)
Definition mss_core.c:154
void dttr_mss_core_set_master_gain(float gain)
Definition mss_core.c:188
void dttr_mss_core_ensure_preferences()
Definition mss_core.c:41
int dttr_mss_core_set_preference(unsigned int preference, int value)
Definition mss_core.c:59
void dttr_mss_core_decrement_driver_open_count()
Definition mss_core.c:172
bool dttr_mss_core_ensure_mixer()
Definition mss_core.c:93
static SDL_AudioSpec desired_spec
Definition mss_core.c:14
void dttr_mss_core_destroy_mixer()
Definition mss_core.c:131
void dttr_mss_core_increment_driver_open_count()
Definition mss_core.c:168
bool dttr_mss_core_ensure_mix_initialized()
Definition mss_core.c:79
int dttr_mss_core_get_preference(unsigned int preference)
Definition mss_core.c:49
int dttr_mss_core_driver_open_count()
Definition mss_core.c:164
MIX_Mixer * dttr_mss_core_mixer()
Definition mss_core.c:146
void dttr_mss_ail_shutdown()
Definition mss_sdl.c:268
int dttr_mss_ail_sample_status(void *sample)
Definition mss_sample.c:550
void dttr_mss_sample_shutdown_all()
Definition mss_sample.c:312
int dttr_mss_ail_stream_status(void *stream)
Definition mss_stream.c:269
void dttr_mss_ail_start_sample(void *sample)
Definition mss_sample.c:483
void dttr_mss_ail_set_sample_pan(void *sample, int pan)
Definition mss_sample.c:582
void dttr_mss_ail_waveOutClose(void *driver)
Definition mss_sdl.c:331
void dttr_mss_ail_set_sample_playback_rate(void *sample, int rate)
Definition mss_sample.c:592
int dttr_mss_ail_sample_playback_rate(void *sample)
Definition mss_sample.c:646
void * dttr_mss_ail_open_stream(void *driver, const char *path, int stream_mem)
Definition mss_stream.c:167
void dttr_mss_ail_pause_stream(void *stream, int pause)
Definition mss_stream.c:281
void dttr_mss_ail_start_stream(void *stream)
Definition mss_stream.c:242
#define DTTR_MSS_DEFAULT_VOLUME
Definition mss_private.h:25
uint32_t dttr_mss_wave_read_u32le(const uint8_t *p)
Definition mss_wave.c:15
int dttr_mss_ail_startup()
Definition mss_sdl.c:263
#define DTTR_MSS_MIXER_CHANNELS
Definition mss_private.h:30
void dttr_mss_ail_set_digital_master_volume(void *driver, int volume)
Definition mss_sdl.c:361
void dttr_mss_ail_set_stream_loop_count(void *stream, int loops)
Definition mss_stream.c:310
void dttr_mss_stream_shutdown_all()
Definition mss_stream.c:135
int dttr_mss_ail_set_preference(unsigned int preference, int value)
Definition mss_sdl.c:272
#define DTTR_MSS_MIXER_FORMAT
Definition mss_private.h:31
void dttr_mss_ail_set_sample_loop_count(void *sample, int loops)
Definition mss_sample.c:560
void dttr_mss_ail_release_sample_handle(void *sample)
Definition mss_sample.c:363
#define DTTR_MSS_DEFAULT_MASTER_GAIN
Definition mss_private.h:23
void dttr_mss_ail_stop_sample(void *sample)
Definition mss_sample.c:530
void dttr_mss_stream_apply_master_gain()
Definition mss_stream.c:142
void dttr_mss_ail_close_stream(void *stream)
Definition mss_stream.c:230
#define DTTR_MSS_WAVE_FORMAT_PCM
Definition mss_private.h:27
void dttr_mss_ail_init_sample(void *sample)
Definition mss_sample.c:379
void dttr_mss_sample_apply_master_gain()
Definition mss_sample.c:329
void * dttr_mss_ail_allocate_sample_handle(void *driver)
Definition mss_sample.c:339
uint16_t dttr_mss_wave_read_u16le(const uint8_t *p)
Definition mss_wave.c:11
#define DTTR_MSS_MAX_VOLUME
Definition mss_private.h:33
void dttr_mss_ail_set_sample_volume(void *sample, int volume)
Definition mss_sample.c:572
static bool dttr_mss_wave_bits_supported(uint16_t bits_per_sample)
Definition mss_private.h:65
void dttr_mss_ail_set_stream_volume(void *stream, int volume)
Definition mss_stream.c:298
int dttr_mss_ail_get_preference(unsigned int preference)
Definition mss_sdl.c:276
int dttr_mss_ail_waveOutOpen(void **driver_out, void *wave_out, int device_id, const void *format)
Definition mss_sdl.c:281
int dttr_mss_ail_set_sample_file(void *sample, const void *file_image, int block)
Definition mss_sample.c:391
void dttr_mss_ail_end_sample(void *sample)
Definition mss_sample.c:546
void dttr_mss_ail_shutdown()
Definition mss_sdl.c:268
static bool install_mss_import_descriptor(const DTTR_Mods_Context *ctx, uint8_t *base, IMAGE_IMPORT_DESCRIPTOR *desc)
Definition mss_sdl.c:171
void dttr_mss_ail_waveOutClose(void *driver)
Definition mss_sdl.c:331
void dttr_mss_sdl_shutdown()
Definition mss_sdl.c:108
static bool wave_format_spec(const void *format, SDL_AudioSpec *spec)
Definition mss_sdl.c:82
static bool install_pointer_hook(const DTTR_Mods_Context *ctx, const char *name, DTTR_Core_Hook **handle, uintptr_t addr, void *callback)
Definition mss_sdl.c:117
int dttr_mss_ail_startup()
Definition mss_sdl.c:263
void dttr_mss_ail_set_digital_master_volume(void *driver, int volume)
Definition mss_sdl.c:361
static mss_import_hook mss_import_hooks[]
Definition mss_sdl.c:21
int dttr_mss_ail_set_preference(unsigned int preference, int value)
Definition mss_sdl.c:272
bool dttr_mss_sdl_install_hooks(const DTTR_Mods_Context *ctx)
Definition mss_sdl.c:228
void dttr_mss_sdl_release_hooks()
Definition mss_sdl.c:205
static float master_gain_for_volume(int volume)
Definition mss_sdl.c:251
int dttr_mss_ail_get_preference(unsigned int preference)
Definition mss_sdl.c:276
int dttr_mss_ail_waveOutOpen(void **driver_out, void *wave_out, int device_id, const void *format)
Definition mss_sdl.c:281
static bool install_mss_import_hook(const DTTR_Mods_Context *ctx, const char *name, uintptr_t site)
Definition mss_sdl.c:146
static const char * dttr_sidecar_result_detail(DTTR_Result result)
const char * import_name
Definition mss_sdl.c:16
void * callback
Definition mss_sdl.c:17
const char * hook_name
Definition mss_sdl.c:15
DTTR_Core_Hook * handle
Definition mss_sdl.c:18