/* TDKPIN Win16 waveform resource lifecycle. */ #include "tdkpin_sound.h" #include "tdkpin_borland_runtime.h" #include "tdkpin_command_line.h" #include "tdkpin_global_memory.h" #include "tdkpin_shortstrings.h" #include "tdkpin_strings.h" #include enum { DGROUP_SOUND_SIZE_INDEX_BASE = 0x03e6, DGROUP_INI_SUFFIX = 0x0442, DGROUP_INI_SECTION_SETTINGS = 0x0446, DGROUP_INI_KEY_SOUND = 0x044f, DGROUP_WAVE_RESOURCE_TYPE = 0x0455, DGROUP_GAME_TILTED = 0x07c6, DGROUP_WAVE_CAPABILITIES = 0x43be, DGROUP_WAVE_DEVICE_COUNT = 0x43ee, DGROUP_WAVE_DEVICE_ID = 0x43f0, DGROUP_SOUND_INDEX_BASE = 0x43f2, DGROUP_SOUND_ENABLED = 0x43f2, DGROUP_SOUND_PLAYING = 0x43f3, DGROUP_SOUND_VOLUME_STEP = 0x43f4, CODE2_EXTENSION_DOT = 0x0f2c, WAVE_RESOURCE_ID_BASE = 2000, SOUND_FIRST = 1, SOUND_LAST = 22, GMEM_MOVEABLE_ZEROINIT = 0x0042, WAVECAPS_VOLUME = 0x0004, SND_ASYNC_NODEFAULT = 0x0005, }; /* * 1008:0002 — central gameplay sound dispatcher. Tilt suppresses every sound; * the duplicated Enabled test is retained even though 1008:1131 tests it too. */ void tdkpin_play_sound_if_not_tilted(uint16_t sound_number) { if (win16_read_u8(win16_dgroup_pointer(DGROUP_GAME_TILTED)) == 0 && win16_read_u8(win16_dgroup_pointer(DGROUP_SOUND_ENABLED)) != 0) { (void)tdkpin_play_sound(sound_number); } } /* Preserve the original 16-bit shift and DGROUP-offset wrapping. */ static uint16_t sound_index_byte_offset(uint16_t sound_number) { return (uint16_t)(sound_number << 2); } static uint32_t sound_resource_byte_count(uint16_t sound_number) { uint16_t offset = (uint16_t)( DGROUP_SOUND_SIZE_INDEX_BASE + sound_index_byte_offset(sound_number)); return win16_read_u32(win16_dgroup_pointer(offset)); } static Win16FarPtr sound_buffer(uint16_t sound_number) { return win16_read_far_pointer( win16_dgroup_pointer(DGROUP_SOUND_INDEX_BASE), sound_index_byte_offset(sound_number)); } static void set_sound_buffer(uint16_t sound_number, Win16FarPtr buffer) { Win16FarPtr base = win16_dgroup_pointer(DGROUP_SOUND_INDEX_BASE); win16_write_u32(base, sound_index_byte_offset(sound_number), buffer); } /* * 1008:0f2e — initialize optional waveform playback. * * The two ParamStr(0) calls and Borland string operations deliberately mirror * the binary: they replace the first dot in the module path with "INI". The * routine then reads [Settings] Sound (default 1), probes wave device zero, * records volume support, and copies each present WAV resource into its own * GMEM_MOVEABLE|GMEM_ZEROINIT block. The original has no resource/allocation * failure checks and this reconstruction intentionally does not add any. */ void tdkpin_initialize_sounds(void) { uint8_t parameter_string[512]; uint8_t ini_file_name[102]; uint8_t module_file_name[102]; win16_write_u8(win16_dgroup_pointer(DGROUP_SOUND_ENABLED), 0); win16_write_u8(win16_dgroup_pointer(DGROUP_SOUND_PLAYING), 0); win16_write_u16( win16_dgroup_pointer(DGROUP_WAVE_DEVICE_COUNT), 0, 0); Win16FarPtr parameter = win16_stack_pointer(parameter_string); Win16FarPtr module_file = win16_stack_pointer(module_file_name); Win16FarPtr ini_file = win16_stack_pointer(ini_file_name); borland_parameter_string(0, parameter); (void)borland_short_to_c_string(module_file, parameter); borland_parameter_string(0, parameter); uint16_t extension_position = borland_short_position( win16_relocated_code_pointer(2, CODE2_EXTENSION_DOT), parameter); (void)borland_far_strncpy( ini_file, module_file, extension_position); (void)borland_far_strcat( ini_file, win16_dgroup_pointer(DGROUP_INI_SUFFIX)); if (GetPrivateProfileInt16( win16_dgroup_pointer(DGROUP_INI_SECTION_SETTINGS), win16_dgroup_pointer(DGROUP_INI_KEY_SOUND), 1, ini_file) == 0) { return; } UINT16 device_count = WaveOutGetNumDevs16(); win16_write_u16( win16_dgroup_pointer(DGROUP_WAVE_DEVICE_COUNT), 0, device_count); if (device_count == 0) { return; } UINT16 device_id = win16_read_u16(win16_dgroup_pointer(DGROUP_WAVE_DEVICE_ID)); if (WaveOutGetDevCaps16( device_id, win16_dgroup_pointer(DGROUP_WAVE_CAPABILITIES), sizeof(WAVEOUTCAPS16)) != 0) { win16_write_u16( win16_dgroup_pointer(DGROUP_WAVE_DEVICE_COUNT), 0, 0); return; } uint32_t support = win16_read_u32( win16_dgroup_pointer( (uint16_t)(DGROUP_WAVE_CAPABILITIES + offsetof(WAVEOUTCAPS16, support)))); if ((support & WAVECAPS_VOLUME) == 0) { win16_write_u8( win16_dgroup_pointer(DGROUP_SOUND_VOLUME_STEP), 0xff); } else { uint32_t volume = 0; (void)WaveOutGetVolume16(device_id, win16_stack_pointer(&volume)); uint8_t high_byte = (uint8_t)(volume >> 8); win16_write_u8( win16_dgroup_pointer(DGROUP_SOUND_VOLUME_STEP), (uint8_t)((high_byte / 10) * 10)); } for (uint16_t sound_number = SOUND_FIRST; sound_number <= SOUND_LAST; sound_number++) { uint32_t byte_count = sound_resource_byte_count(sound_number); if (byte_count == 0) { continue; } HRSRC16 found = FindResource16( g_win16_instance, win16_make_far_pointer( 0, (uint16_t)(sound_number + WAVE_RESOURCE_ID_BASE)), win16_dgroup_pointer(DGROUP_WAVE_RESOURCE_TYPE)); HGLOBAL16 resource = LoadResource16(g_win16_instance, found); Win16FarPtr allocation = tdkpin_global_allocate_locked(GMEM_MOVEABLE_ZEROINIT, byte_count); set_sound_buffer(sound_number, allocation); Win16FarPtr resource_bytes = LockResource16(resource); borland_move_bytes( allocation, resource_bytes, (uint16_t)byte_count); (void)GlobalUnlock16(resource); (void)FreeResource16(resource); } win16_write_u8(win16_dgroup_pointer(DGROUP_SOUND_ENABLED), 1); } /* * 1008:10e3 — stop playback, release sound slots 1..22, clear Playing. * Device count, enabled state, and the freed far pointers remain unchanged. */ void tdkpin_unload_sounds(void) { if (win16_read_u16( win16_dgroup_pointer(DGROUP_WAVE_DEVICE_COUNT)) == 0) { return; } if (win16_read_u8(win16_dgroup_pointer(DGROUP_SOUND_PLAYING)) != 0) { (void)SndPlaySound16(0, 0); } for (uint16_t sound_number = SOUND_FIRST; sound_number <= SOUND_LAST; sound_number++) { tdkpin_global_unlock_and_free(sound_buffer(sound_number)); } win16_write_u8(win16_dgroup_pointer(DGROUP_SOUND_PLAYING), 0); } /* 1008:1131 — play one loaded sound with SND_ASYNC|SND_NODEFAULT. */ uint16_t tdkpin_play_sound(uint16_t sound_number) { if (win16_read_u16(win16_dgroup_pointer(DGROUP_WAVE_DEVICE_COUNT)) != 0 && win16_read_u8(win16_dgroup_pointer(DGROUP_SOUND_ENABLED)) != 0 && sound_resource_byte_count(sound_number) != 0) { if (win16_read_u8(win16_dgroup_pointer(DGROUP_SOUND_PLAYING)) != 0) { (void)SndPlaySound16(0, 0); } BOOL16 played = SndPlaySound16( sound_buffer(sound_number), SND_ASYNC_NODEFAULT); win16_write_u8( win16_dgroup_pointer(DGROUP_SOUND_PLAYING), played != 0); } return win16_indeterminate_u16(); } /* * 1008:118f — stop only while a wave device exists and a sound is marked * playing. The target does not clear Playing and returns an uninitialized word. */ uint16_t tdkpin_stop_sound_if_active(void) { if (win16_read_u16(win16_dgroup_pointer(DGROUP_WAVE_DEVICE_COUNT)) != 0 && win16_read_u8(win16_dgroup_pointer(DGROUP_SOUND_PLAYING)) != 0) { (void)SndPlaySound16(0, 0); } return win16_indeterminate_u16(); }