From d0946b46cf617467f014a25e264fd952698a13f9 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Tue, 23 Apr 2024 20:23:03 +0300 Subject: [PATCH] Add Allegro code --- doomgeneric/doomgeneric_allegro.c | 453 ++++++++++++++++++++++++++++++ doomgeneric/i_allegromusic.c | 308 ++++++++++++++++++++ doomgeneric/i_allegrosound.c | 433 ++++++++++++++++++++++++++++ 3 files changed, 1194 insertions(+) create mode 100644 doomgeneric/doomgeneric_allegro.c create mode 100644 doomgeneric/i_allegromusic.c create mode 100644 doomgeneric/i_allegrosound.c diff --git a/doomgeneric/doomgeneric_allegro.c b/doomgeneric/doomgeneric_allegro.c new file mode 100644 index 0000000..b120366 --- /dev/null +++ b/doomgeneric/doomgeneric_allegro.c @@ -0,0 +1,453 @@ +// doomgeneric for Allegro library + +#include "doomkeys.h" +#include "i_system.h" +#include "i_video.h" +#include "m_argv.h" +#include "doomgeneric.h" + +#include +#include +#include +#include + +#define ALLEGRO_NO_KEY_DEFINES 1 +#include +#undef uint32_t // ugly hack because Allegro is Old and doesn't know modern GCC has stdint.h + + +static bool videomode_set = false; + +static BITMAP *temp_bitmap = NULL; + +static PALETTE pal; + +#define KEYQUEUE_SIZE 16 + +static volatile unsigned int s_KeyQueue[KEYQUEUE_SIZE]; +static volatile unsigned int s_KeyQueueWriteIndex = 0; +static unsigned int s_KeyQueueReadIndex = 0; + +static volatile unsigned int s_ticks = 0; + + +void key_callback(int scancode) { + // this is in interrupt context, avoid heavy processing and be careful about volatile writes + s_KeyQueue[s_KeyQueueWriteIndex] = scancode; + s_KeyQueueWriteIndex = (s_KeyQueueWriteIndex + 1) % KEYQUEUE_SIZE; +} +END_OF_FUNCTION(key_callback); + + +void timer_callback(void) { + // also in interrupt context + s_ticks++; +} +END_OF_FUNCTION(timer_callback); + + +void DG_Init() { + int result; + + printf("Initializing Allegro\n"); + + result = allegro_init(); + if (result != 0) + { + I_Error("Allegro init failed: %d %s\n", result, allegro_error); + } + + I_AtExit(allegro_exit, true); + + LOCK_FUNCTION(key_callback); + LOCK_VARIABLE(s_KeyQueue); + LOCK_VARIABLE(s_KeyQueueWriteIndex); + LOCK_VARIABLE(s_KeyQueueReadIndex); + + LOCK_FUNCTION(timer_callback); + LOCK_VARIABLE(s_ticks); + + install_keyboard(); + keyboard_lowlevel_callback = key_callback; + + install_timer(); + result = install_int(timer_callback, 1); + if (result < 0) { + I_Error("Unable to install timer: %d %s\n", result, allegro_error); + } + + // don't set video mode yet so initialization messages stay on screen +} + + +static void back_to_text_mode(void) { + set_gfx_mode(GFX_TEXT, 80, 25, 0, 0); +} + + +void DG_DrawFrame() +{ + if (!DG_ScreenBuffer) { + return; + } + + if (!videomode_set) { + int y; + int result; + +#ifdef CMAP256 + set_color_depth(8); +#else // CMAP256 + // does not seem to work on real DOS hardware + set_color_depth(32); +#endif // CMAP256 + + result = set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, DOOMGENERIC_RESX, DOOMGENERIC_RESY, 0, 0); + if (result < 0) { + I_Error("Failed to set video mode: %s\n", allegro_error); + } + + // register an exit handler to return to text mode + // while this is also done by allegro_exit handler registered earlier + // that's too late for ENDOOM screen to work correctly + I_AtExit(back_to_text_mode, true); + + if (!screen) { + I_Error("screen is null\n"); + } + + temp_bitmap = create_bitmap(DOOMGENERIC_RESX, DOOMGENERIC_RESY); + if (!temp_bitmap) { + I_Error("Failed to create temp bitmap\n"); + } + + // this is an evil hack + // replace the internal line pointers with pointers into DG_ScreenBuffer + // this avoids some copying + // it might crash when freeing the bitmap but we never do + for (y = 0; y < DOOMGENERIC_RESY; y++) { + temp_bitmap->line[y] = (unsigned char *) &DG_ScreenBuffer[y * DOOMGENERIC_RESX]; + } + + clear_bitmap(temp_bitmap); + + videomode_set = true; + palette_changed = true; + } + +#ifdef CMAP256 + + // changing palette implicitly waits for vsync + // TODO: should do it explicitly in case where we don't change palette + if (palette_changed) { + int i; + + for (i = 0; i < 256; i++) { + // allegro uses VGA range 0..63 + pal[i].r = colors[i].r >> 2; + pal[i].g = colors[i].g >> 2; + pal[i].b = colors[i].b >> 2; + } + set_palette(pal); + + palette_changed = false; + } + +#endif // CMAP256 + + blit(temp_bitmap, screen, 0, 0, 0, 0, DOOMGENERIC_RESX, DOOMGENERIC_RESY); +} + + +void DG_SleepMs(uint32_t ms) +{ + rest(ms); +} + + +uint32_t DG_GetTicksMs() +{ + return s_ticks; +} + + +int DG_GetKey(int *pressed, unsigned char *doomKey) +{ + if (s_KeyQueueReadIndex == s_KeyQueueWriteIndex){ + //key queue is empty + return 0; + } else { + bool released; + short keyData = 0; + int scancode = s_KeyQueue[s_KeyQueueReadIndex]; + s_KeyQueueReadIndex++; + s_KeyQueueReadIndex %= KEYQUEUE_SIZE; + + released = scancode & 0x80; + *pressed = !released; + + scancode = scancode & 0x7F; + + switch (scancode) { + case __allegro_KEY_RIGHT: + keyData = KEY_RIGHTARROW; + break; + + case __allegro_KEY_LEFT: + keyData = KEY_LEFTARROW; + break; + + case __allegro_KEY_UP: + keyData = KEY_UPARROW; + break; + + case __allegro_KEY_DOWN: + keyData = KEY_DOWNARROW; + break; + + case __allegro_KEY_COMMA: + keyData = KEY_STRAFE_L; + break; + + case __allegro_KEY_STOP: + keyData = KEY_STRAFE_R; + break; + + case __allegro_KEY_SPACE: + keyData = KEY_USE; + break; + + case __allegro_KEY_LCONTROL: + keyData = KEY_FIRE; + break; + + case __allegro_KEY_ESC: + keyData = KEY_ESCAPE; + break; + + case __allegro_KEY_ENTER: + keyData = KEY_ENTER; + break; + + case __allegro_KEY_TAB: + keyData = KEY_TAB; + break; + + case __allegro_KEY_F1: + keyData = KEY_F1; + break; + + case __allegro_KEY_F2: + keyData = KEY_F2; + break; + + case __allegro_KEY_F3: + keyData = KEY_F3; + break; + + case __allegro_KEY_F4: + keyData = KEY_F4; + break; + + case __allegro_KEY_F5: + keyData = KEY_F5; + break; + + case __allegro_KEY_F6: + keyData = KEY_F6; + break; + + case __allegro_KEY_F7: + keyData = KEY_F7; + break; + + case __allegro_KEY_F8: + keyData = KEY_F8; + break; + + case __allegro_KEY_F9: + keyData = KEY_F9; + break; + + case __allegro_KEY_F10: + keyData = KEY_F10; + break; + + case __allegro_KEY_F11: + keyData = KEY_F11; + break; + + case __allegro_KEY_F12: + keyData = KEY_F12; + break; + + case __allegro_KEY_BACKSPACE: + keyData = KEY_BACKSPACE; + break; + + case __allegro_KEY_PAUSE: + keyData = KEY_PAUSE; + break; + + case __allegro_KEY_EQUALS: + keyData = KEY_EQUALS; + break; + + case __allegro_KEY_MINUS: + keyData = KEY_MINUS; + break; + + case __allegro_KEY_LSHIFT: + case __allegro_KEY_RSHIFT: + keyData = KEY_RSHIFT; + break; + + case __allegro_KEY_RCONTROL: + keyData = KEY_RCTRL; + break; + + case __allegro_KEY_ALT: + keyData = KEY_RALT; + break; + + case __allegro_KEY_CAPSLOCK: + keyData = KEY_CAPSLOCK; + break; + + case __allegro_KEY_NUMLOCK: + keyData = KEY_NUMLOCK; + break; + + case __allegro_KEY_SCRLOCK: + keyData = KEY_SCRLCK; + break; + + case __allegro_KEY_PRTSCR: + keyData = KEY_PRTSCR; + break; + + case __allegro_KEY_HOME: + keyData = KEY_HOME; + break; + + case __allegro_KEY_END: + keyData = KEY_END; + break; + + case __allegro_KEY_PGUP: + keyData = KEY_PGUP; + break; + + case __allegro_KEY_PGDN: + keyData = KEY_PGDN; + break; + + case __allegro_KEY_INSERT: + keyData = KEY_INS; + break; + + case __allegro_KEY_DEL: + keyData = KEY_DEL; + break; + + case __allegro_KEY_0_PAD: + keyData = KEYP_0; + break; + + case __allegro_KEY_1_PAD: + keyData = KEYP_1; + break; + + case __allegro_KEY_2_PAD: + keyData = KEYP_2; + break; + + case __allegro_KEY_3_PAD: + keyData = KEYP_3; + break; + + case __allegro_KEY_4_PAD: + keyData = KEYP_4; + break; + + case __allegro_KEY_5_PAD: + keyData = KEYP_5; + break; + + case __allegro_KEY_6_PAD: + keyData = KEYP_6; + break; + + case __allegro_KEY_7_PAD: + keyData = KEYP_7; + break; + + case __allegro_KEY_8_PAD: + keyData = KEYP_8; + break; + + case __allegro_KEY_9_PAD: + keyData = KEYP_9; + break; + + case __allegro_KEY_SLASH_PAD: + keyData = KEYP_DIVIDE; + break; + + case __allegro_KEY_PLUS_PAD: + keyData = KEYP_PLUS; + break; + + case __allegro_KEY_MINUS_PAD: + keyData = KEYP_MINUS; + break; + + case __allegro_KEY_ASTERISK: + keyData = KEYP_MULTIPLY; + break; + + case __allegro_KEY_DEL_PAD: + keyData = KEYP_PERIOD; + break; + + case __allegro_KEY_EQUALS_PAD: + keyData = KEYP_EQUALS; + break; + + case __allegro_KEY_ENTER_PAD: + keyData = KEYP_ENTER; + break; + + default: + keyData = scancode_to_ascii(scancode); + break; + } + + if (keyData != 0) { + *doomKey = keyData & 0xFF; + } + + return 1; + } + + return 0; +} + + +void DG_SetWindowTitle(const char *title) +{ + set_window_title(title); +} + + +int main(int argc, char **argv) +{ + doomgeneric_Create(argc, argv); + + while (true) + { + doomgeneric_Tick(); + } + + return 0; +} diff --git a/doomgeneric/i_allegromusic.c b/doomgeneric/i_allegromusic.c new file mode 100644 index 0000000..2c6c055 --- /dev/null +++ b/doomgeneric/i_allegromusic.c @@ -0,0 +1,308 @@ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005-2014 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// System interface for music. +// + + +#include +#include +#include +#include + +#include "config.h" +#include "doomtype.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_str.h" +#include "gusconf.h" +#include "i_sound.h" +#include "i_system.h" +#include "i_swap.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_wad.h" +#include "z_zone.h" + +#include +#include +#include +#include + + +#define MAXMIDLENGTH (96 * 1024) +#define MID_HEADER_MAGIC "MThd" +#define MUS_HEADER_MAGIC "MUS\x1a" + + +static boolean music_initialized = false; + + +static boolean musicpaused = false; +static int current_music_volume; + + +// Currently playing music track. +MIDI *current_track_music = NULL; + +// If true, the currently playing track is being played on loop. +static boolean current_track_loop; + + + +// Shutdown music +static void I_Allegro_ShutdownMusic(void) +{ + // nothing here + // TODO: stop song? +} + + +// Initialize music subsystem +static boolean I_Allegro_InitMusic(void) +{ + // nothing here, it's all done by I_Allegro_InitSound + music_initialized = true; + + return true; +} + + +// Set music volume (0 - 127) +static void I_Allegro_SetMusicVolume(int volume) +{ + int digivol = -1; + get_volume(&digivol, NULL); + // allegro range 0 - 255 + set_volume(digivol, volume * 2); +} + + +// Start playing a mid +static void I_Allegro_PlaySong(void *handle, boolean looping) +{ + int retval; + + if (!music_initialized) + { + return; + } + + if (handle == NULL) + { + return; + } + + current_track_music = (MIDI *) handle; + current_track_loop = looping; + + retval = play_midi(current_track_music, looping); + if (retval < 0) { + fprintf(stderr, "Error playing midi: %d \"%s\"\n", retval, allegro_error); + } +} + + +static void I_Allegro_PauseSong(void) +{ + if (!music_initialized) + { + return; + } + + musicpaused = true; + + midi_pause(); +} + + +static void I_Allegro_ResumeSong(void) +{ + if (!music_initialized) + { + return; + } + + musicpaused = false; + + midi_resume(); +} + + +static void I_Allegro_StopSong(void) +{ + if (!music_initialized) + { + return; + } + + stop_midi(); + + current_track_music = NULL; +} + + +static void I_Allegro_UnRegisterSong(void *handle) +{ + MIDI *midi = (MIDI *) handle; + + if (!music_initialized) + { + return; + } + + if (handle == NULL) + { + return; + } + + destroy_midi(midi); +} + + +// Determine whether memory block is a .mid file +static boolean IsMid(byte *mem, int len) +{ + return len > 4 && !memcmp(mem, "MThd", 4); +} + + +static boolean ConvertMus(byte *musdata, int len, char *filename) +{ + MEMFILE *instream; + MEMFILE *outstream; + void *outbuf; + size_t outbuf_len; + int result; + + instream = mem_fopen_read(musdata, len); + outstream = mem_fopen_write(); + + result = mus2mid(instream, outstream); + + if (result == 0) + { + mem_get_buf(outstream, &outbuf, &outbuf_len); + + M_WriteFile(filename, outbuf, outbuf_len); + } + + mem_fclose(instream); + mem_fclose(outstream); + + return result; +} + + +static void *I_Allegro_RegisterSong(void *data, int len) +{ + char *filename; + MIDI *music; + + if (!music_initialized) + { + return NULL; + } + + filename = M_TempFile("doom.mid"); + + if (IsMid(data, len) && len < MAXMIDLENGTH) + { + M_WriteFile(filename, data, len); + } + else + { + // Assume a MUS file and try to convert + ConvertMus(data, len, filename); + } + + // Load the MIDI. In an ideal world we'd load it directly from memory but Allegro + // doesn't support that so we have to generate a temporary file. + + music = load_midi(filename); + + if (music == NULL) + { + // Failed to load + fprintf(stderr, "Error loading midi: %s\n", allegro_error); + } + + // Remove the temporary MIDI file; + remove(filename); + + free(filename); + + return music; +} + + +// Is the song playing? +static boolean I_Allegro_MusicIsPlaying(void) +{ + if (!music_initialized) + { + return false; + } + + return (current_track_music != NULL) && (midi_pos > 0); +} + + +// Get position in substitute music track, in seconds since start of track. +static double GetMusicPosition(void) +{ + return midi_time; +} + + +// Poll music position; if we have passed the loop point end position +// then we need to go back. +static void I_Allegro_PollMusic(void) +{ + // nothing here, allegro takes care of it +} + + +static snddevice_t music_allegro_devices[] = +{ + SNDDEVICE_PAS, + SNDDEVICE_GUS, + SNDDEVICE_WAVEBLASTER, + SNDDEVICE_SOUNDCANVAS, + SNDDEVICE_GENMIDI, + SNDDEVICE_AWE32, +}; + + +music_module_t DG_music_module = +{ + music_allegro_devices, + arrlen(music_allegro_devices), + I_Allegro_InitMusic, + I_Allegro_ShutdownMusic, + I_Allegro_SetMusicVolume, + I_Allegro_PauseSong, + I_Allegro_ResumeSong, + I_Allegro_RegisterSong, + I_Allegro_UnRegisterSong, + I_Allegro_PlaySong, + I_Allegro_StopSong, + I_Allegro_MusicIsPlaying, + I_Allegro_PollMusic, +}; + diff --git a/doomgeneric/i_allegrosound.c b/doomgeneric/i_allegrosound.c new file mode 100644 index 0000000..d8f5714 --- /dev/null +++ b/doomgeneric/i_allegrosound.c @@ -0,0 +1,433 @@ +// +// Copyright(C) 1993-1996 Id Software, Inc. +// Copyright(C) 2005-2014 Simon Howard +// Copyright(C) 2008 David Flater +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// System interface for sound. +// + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "deh_str.h" +#include "i_sound.h" +#include "i_system.h" +#include "i_swap.h" +#include "m_argv.h" +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "doomtype.h" + +#include +#include +#include + + +#define NUM_CHANNELS 16 + +#define NUM_MIDI_CHANNELS 16 + + +static boolean sound_initialized = false; + +static sfxinfo_t *channels_playing[NUM_CHANNELS]; + +static int allegro_voices[NUM_CHANNELS]; + +static SAMPLE *dummy_sample = NULL; + +static boolean use_sfx_prefix; + + +// We don't support libsamplerate with Allegro but these have to be here since +// other code requires them +int use_libsamplerate = 0; + +// Scale factor used when converting libsamplerate floating point numbers +// to integers. Too high means the sounds can clip; too low means they +// will be too quiet. This is an amount that should avoid clipping most +// of the time: with all the Doom IWAD sound effects, at least. If a PWAD +// is used, clipping might occur. + +float libsamplerate_scale = 0.65f; + + +// Load and convert a sound effect +// Returns true if successful + +static boolean CacheSFX(sfxinfo_t *sfxinfo) +{ + int lumpnum; + unsigned int lumplen; + int samplerate; + unsigned int length; + byte *data; + SAMPLE *sample; + + // need to load the sound + + lumpnum = sfxinfo->lumpnum; + data = W_CacheLumpNum(lumpnum, PU_STATIC); + lumplen = W_LumpLength(lumpnum); + + // Check the header, and ensure this is a valid sound + + if (lumplen < 8 + || data[0] != 0x03 || data[1] != 0x00) + { + // Invalid sound + + return false; + } + + // 16 bit sample rate field, 32 bit length field + + samplerate = (data[3] << 8) | data[2]; + length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; + + // If the header specifies that the length of the sound is greater than + // the length of the lump itself, this is an invalid sound lump + + // We also discard sound lumps that are less than 49 samples long, + // as this is how DMX behaves - although the actual cut-off length + // seems to vary slightly depending on the sample rate. This needs + // further investigation to better understand the correct + // behavior. + + if (length > lumplen - 8 || length <= 48) + { + return false; + } + + // The DMX sound library seems to skip the first 16 and last 16 + // bytes of the lump - reason unknown. + + data += 16; + length -= 32; + + sample = create_sample(8, 0, samplerate, length); + if (sample == NULL) { + return false; + } + memcpy(sample->data, data, length); + + sfxinfo->driver_data = sample; + + // don't need the original lump any more + + W_ReleaseLumpNum(lumpnum); + + return true; +} + + +static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len) +{ + // Linked sfx lumps? Get the lump number for the sound linked to. + + if (sfx->link != NULL) + { + sfx = sfx->link; + } + + // Doom adds a DS* prefix to sound lumps; Heretic and Hexen don't + // do this. + + if (use_sfx_prefix) + { + M_snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name)); + } + else + { + M_StringCopy(buf, DEH_String(sfx->name), buf_len); + } +} + + +static void I_Allegro_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) +{ + char namebuf[9]; + int i; + + printf("I_Allegro_PrecacheSounds: Precaching all sound effects.."); + + for (i=0; i= NUM_CHANNELS) + { + return; + } + + if (channels_playing[handle] == NULL) { + return; + } + + voice_set_volume(allegro_voices[handle], vol); + voice_set_pan(allegro_voices[handle], sep); +} + +// +// Starting a sound means adding it +// to the current list of active sounds +// in the internal channels. +// As the SFX info struct contains +// e.g. a pointer to the raw data, +// it is ignored. +// As our sound handling does not handle +// priority, it is ignored. +// Pitching (that is, increased speed of playback) +// is set, but currently not used by mixing. +// + +static int I_Allegro_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) +{ + if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS) + { + return -1; + } + + // Release a sound effect if there is already one playing + // on this channel + if (channels_playing[channel]) { + voice_stop(allegro_voices[channel]); + channels_playing[channel] = NULL; + } + + // Get the sound data + if (sfxinfo->driver_data == NULL) + { + if (!CacheSFX(sfxinfo)) + { + return -1; + } + } + assert(sfxinfo->driver_data); + + // play sound + reallocate_voice(allegro_voices[channel], sfxinfo->driver_data); + voice_set_volume(allegro_voices[channel], vol); + voice_set_pan(allegro_voices[channel], sep); + voice_start(allegro_voices[channel]); + + channels_playing[channel] = sfxinfo; + + return channel; +} + + +static void I_Allegro_StopSound(int handle) +{ + if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) + { + return; + } + + if (channels_playing[handle] == NULL) { + return; + } + + voice_stop(allegro_voices[handle]); + channels_playing[handle] = NULL; +} + + +static boolean I_Allegro_SoundIsPlaying(int handle) +{ + int position; + + if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) + { + return false; + } + + if (channels_playing[handle] == NULL) { + return false; + } + + position = voice_get_position(allegro_voices[handle]); + if (position < 0) { + // finished + return false; + } + + // still playing + return true; +} + +// +// Periodically called to update the sound system +// + +static void I_Allegro_UpdateSound(void) +{ + int i; + + // loop through all channels which have sample, check if they're finished + for (i = 0; i < NUM_CHANNELS; i++) { + if (channels_playing[i]) { + int position = voice_get_position(allegro_voices[i]); + if (position < 0) { + // finished + channels_playing[i] = NULL; + } + } + } +} + + +static void I_Allegro_ShutdownSound(void) +{ + int i; + + if (!sound_initialized) + { + return; + } + + for (i = 0; i < NUM_CHANNELS; i++) { + if (channels_playing[i] != NULL) { + voice_stop(allegro_voices[i]); + channels_playing[i] = NULL; + } + deallocate_voice(allegro_voices[i]); + allegro_voices[i] = -1; + } + + destroy_sample(dummy_sample); + dummy_sample = NULL; + + remove_sound(); + + sound_initialized = false; +} + + +static boolean I_Allegro_InitSound(boolean _use_sfx_prefix) +{ + int i; + + use_sfx_prefix = _use_sfx_prefix; + + // No sounds yet + + for (i=0; i