Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "audio.h"
  2. #include "resource.h"
  3.  
  4. #include "3d.h"
  5. #include "backends/backend.h"
  6. #include "harness/config.h"
  7. #include "harness/os.h"
  8. #include "harness/trace.h"
  9. #include "s3/s3.h"
  10. #include "s3cda.h"
  11. #include "s3music.h"
  12. #include "s3sound.h"
  13. #include <ctype.h>
  14. #include <errno.h>
  15. #include <stddef.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19.  
  20. extern int PDGetTotalTime(void);
  21.  
  22. int gS3_enabled;
  23. int gS3_noutlets;
  24. int gS3_nsound_sources;
  25. int gS3_next_outlet_id;
  26. tS3_outlet* gS3_outlets;
  27. tS3_sound_source* gS3_sound_sources;
  28. tS3_descriptor* gS3_descriptors;
  29. tS3_descriptor* gS3_root_descriptor;
  30. int gS3_opened_output_devices;
  31. int gS3_last_service_time;
  32. int gS3_service_time_delta;
  33. tS3_channel* gS3_unbound_channels;
  34. tS3_channel* gS3_last_unbound_channel;
  35. int gS3_last_error;
  36. int gS3_default_pitch;
  37. tS3_hardware_info gS3_hardware_info;
  38. char* gS3_soundbank_buffer;
  39. int gS3_soundbank_buffer_len;
  40. int gS3_inside_cockpit;
  41.  
  42. tS3_channel gS3_channel_template;
  43. int gS3_tag_seed;
  44.  
  45. char gS3_directory_separator[4];
  46. char gS3_directory_name[8];
  47. int gS3_have_current_dir;
  48. char gS3_current_dir[260];
  49.  
  50. int dword_5216C0;
  51.  
  52. int S3Init(char* pPath, int pLow_memory_mode) {
  53.     tS3_descriptor* root_descriptor;
  54.  
  55.     gS3_noutlets = 0;
  56.     gS3_nsound_sources = 0;
  57.     gS3_next_outlet_id = 0;
  58.     gS3_outlets = NULL;
  59.     gS3_sound_sources = NULL;
  60.     gS3_descriptors = NULL;
  61.     gS3_root_descriptor = NULL;
  62.  
  63.     S3Disable();
  64.     S3DisableMIDI();
  65.     S3DisableCDA();
  66.     gS3_sample_filter_funcs_registered = 0;
  67.     gS3_sample_filter_func = NULL;
  68.     gS3_sample_filter_disable_func = NULL;
  69.     if (S3OpenOutputDevices() == 0) {
  70.         return 1;
  71.     }
  72.     gS3_opened_output_devices = 1;
  73.     root_descriptor = S3MemAllocate(sizeof(tS3_descriptor), kMem_S3_sentinel);
  74.     if (!root_descriptor) {
  75.         return 3;
  76.     }
  77.     memset(root_descriptor, 0, sizeof(tS3_descriptor));
  78.     root_descriptor->id = 2495081;
  79.     gS3_root_descriptor = root_descriptor;
  80.     gS3_descriptors = root_descriptor;
  81.     if (S3LoadSoundbank(pPath, pLow_memory_mode)) {
  82.         return 5;
  83.     }
  84.     gS3_last_service_time = PDGetTotalTime();
  85.     gS3_unbound_channels = 0;
  86.     gS3_last_unbound_channel = 0;
  87.     return 0;
  88. }
  89.  
  90. void S3Shutdown(void) {
  91.     tS3_outlet* outlet;              // [esp+10h] [ebp-10h]
  92.     tS3_outlet* next_outlet;         // [esp+14h] [ebp-Ch]
  93.     tS3_descriptor* next_descriptor; // [esp+18h] [ebp-8h]
  94.     tS3_descriptor* descriptor;      // [esp+1Ch] [ebp-4h]
  95.  
  96.     S3StopAllOutletSounds();
  97.     S3DisableMIDI();
  98.     S3DisableCDA();
  99.     if (gS3_enabled) {
  100.         S3Disable();
  101.         for (descriptor = gS3_descriptors; descriptor != NULL; descriptor = next_descriptor) {
  102.             next_descriptor = descriptor->next;
  103.             S3ReleaseSound(descriptor->id);
  104.             S3MemFree(descriptor);
  105.         }
  106.         for (outlet = gS3_outlets; outlet != NULL; outlet = next_outlet) {
  107.             next_outlet = outlet->next;
  108.             S3ReleaseOutlet(outlet);
  109.         }
  110.         S3ReleaseUnboundChannels();
  111.     }
  112.     if (gS3_opened_output_devices) {
  113.         S3CloseDevices();
  114.     }
  115. }
  116.  
  117. void S3Enable(void) {
  118.     gS3_enabled = 1;
  119. }
  120.  
  121. void S3Disable(void) {
  122.     S3StopAllOutletSounds();
  123.     gS3_enabled = 0;
  124. }
  125.  
  126. int S3OpenOutputDevices(void) {
  127.  
  128.     // strcpy(gS3_directory_separator, "\\");
  129.     strcpy(gS3_directory_separator, "/");
  130.     strcpy(gS3_directory_name, "SOUND");
  131.     memset(&gS3_hardware_info, 0, sizeof(gS3_hardware_info));
  132.     if (S3OpenSampleDevice() == 0) {
  133.         return 0;
  134.     }
  135.     S3OpenCDADevice();
  136.     gS3_hardware_info.timer_installed = 0;
  137.     gS3_hardware_info.device_installed = 1;
  138.     gS3_hardware_info.independent_pitch = 0;
  139.     return 1;
  140. }
  141.  
  142. int S3OpenSampleDevice(void) {
  143.  
  144.     // if (DirectSoundCreate(0, &gS3_direct_sound_ptr, 0)) {
  145.     //     return 0;
  146.     // }
  147.     // if (gS3_direct_sound_ptr->lpVtbl->SetCooperativeLevel(gS3_direct_sound_ptr, gWin32_hwnd, 3)) {
  148.     //     return 0;
  149.     // }
  150.  
  151.     if (AudioBackend_Init() != eAB_success) {
  152.         return 0;
  153.     }
  154.     S3Enable();
  155.     return 1;
  156. }
  157.  
  158. // returns 0 on failure, 1 on success
  159. int S3OpenCDADevice(void) {
  160.     // gS3_cda_device.lpstrDeviceType = (LPCSTR)516;
  161.     // if (mciSendCommandA(0, 0x803u, 0x3000u, (DWORD_PTR)&gS3_cda_device)
  162.     //     && mciSendCommandA(0, 0x803u, 0x3100u, (DWORD_PTR)&gS3_cda_device)) {
  163.     //     return 0;
  164.     // }
  165.     // stru_550560.dwTimeFormat = 10; // MCI_FORMAT_TMSF
  166.     // mciSendCommandA(gS3_cda_device.wDeviceID, 0x80Du, 0x400u, (DWORD_PTR)&stru_550560);
  167.  
  168.     if (AudioBackend_InitCDA() != eAB_success) {
  169.         return 0;
  170.     }
  171.  
  172.     S3EnableCDA();
  173.     return 1;
  174. }
  175.  
  176. void S3CloseDevices(void) {
  177.     if (gS3_hardware_info.device_installed) {
  178.         // gS3_direct_sound_ptr->lpVtbl->Release(gS3_direct_sound_ptr);
  179.         // gS3_direct_sound_ptr = NULL;
  180.  
  181.         AudioBackend_UnInit();
  182.     }
  183.     // if (gS3_cda_device.wDeviceID) {
  184.     //     mciSendCommandA(gS3_cda_device.wDeviceID, 0x804u, 0, 0); // MCI_CLOSE
  185.     // }
  186.  
  187.     AudioBackend_UnInitCDA();
  188. }
  189.  
  190. int S3ReleaseSound(tS3_sound_id id) {
  191.     tS3_channel* c;       // [esp+Ch] [ebp-10h]
  192.     tS3_outlet* o;        // [esp+10h] [ebp-Ch]
  193.     tS3_descriptor* desc; // [esp+14h] [ebp-8h]
  194.     tS3_sample* sample_ptr;
  195.  
  196.     if (!gS3_enabled) {
  197.         return 0;
  198.     }
  199.  
  200.     desc = S3GetDescriptorByID(id);
  201.     if (desc == NULL) {
  202.         return eS3_error_bad_id;
  203.     }
  204.     if (desc->type == eS3_ST_midi) {
  205.         for (o = gS3_outlets; o; o = o->next) {
  206.             for (c = o->channel_list; c; c = c->next) {
  207.                 if (c->descriptor && c->descriptor->id == id) {
  208.                     S3ReleaseMIDI(c->tag);
  209.                 }
  210.             }
  211.         }
  212.     } else if (desc->type == eS3_ST_sample) {
  213.         sample_ptr = (tS3_sample*)desc->sound_data;
  214.         if (sample_ptr == NULL) {
  215.             return 0;
  216.         }
  217.         S3MemFree(sample_ptr->freeptr);
  218.         S3MemFree(sample_ptr);
  219.         desc->sound_data = NULL;
  220.     }
  221.     return 0;
  222. }
  223.  
  224. int S3LoadSoundbank(const char* pSoundbank_filename, int pLow_memory_mode) {
  225.     char soundbank_filename[256];    // [esp+Ch] [ebp-218h] BYREF
  226.     void* buffer;                    // [esp+10Ch] [ebp-118h]
  227.     tS3_soundbank_read_ctx read_ctx; // [esp+110h] [ebp-114h] BYREF
  228.     char* cur_dir;                   // [esp+120h] [ebp-104h]
  229.     char dir_name[256];              // [esp+124h] [ebp-100h] BYREF
  230.  
  231.     if (gS3_enabled) {
  232.         dir_name[0] = 0;
  233.         soundbank_filename[0] = 0;
  234.         cur_dir = S3GetCurrentDir();
  235.         strcpy(dir_name, cur_dir);
  236.         strcat(dir_name, gS3_directory_separator);
  237.         strcat(dir_name, "DATA");
  238.         strcat(dir_name, gS3_directory_separator);
  239.         strcat(dir_name, gS3_directory_name);
  240.         strcat(dir_name, gS3_directory_separator);
  241.         strcpy(soundbank_filename, pSoundbank_filename);
  242.         buffer = S3LoadSoundBankFile(soundbank_filename);
  243.         if (!buffer) {
  244.             return gS3_last_error;
  245.         }
  246.         read_ctx.data = (char*)buffer;
  247.         read_ctx.data_len = gS3_soundbank_buffer_len;
  248.         read_ctx.nlines = 0;
  249.         S3SoundBankReaderSkipWhitespace(&read_ctx);
  250.         while (S3SoundBankReadEntry(&read_ctx, dir_name, pLow_memory_mode)) {
  251.             ;
  252.         }
  253.         S3MemFree(buffer);
  254.     }
  255.     return 0;
  256. }
  257.  
  258. char* S3LoadSoundBankFile(char* pThe_path) {
  259.     size_t bytes_read;
  260.     // int fd;                   // [esp+Ch] [ebp-2Ch]
  261.     char* buffer;
  262.     // struct _stat stat_result;
  263.  
  264.     FILE* fd;
  265.     size_t file_size;
  266.  
  267.     // fd = _open(pThe_path, 0x8000);
  268.     fd = OS_fopen(pThe_path, "rb");
  269.     if (!fd) {
  270.         gS3_last_error = eS3_error_readfile;
  271.         return 0;
  272.     }
  273.  
  274.     // if (_fstat(fd, &stat_result)) {
  275.     //     gS3_last_error = eS3_error_readfile;
  276.     //     return 0;
  277.     // }
  278.  
  279.     if (fseek(fd, 0, SEEK_END) != 0) {
  280.         gS3_last_error = eS3_error_readfile;
  281.         return 0;
  282.     }
  283.     file_size = ftell(fd);
  284.     fseek(fd, 0, SEEK_SET);
  285.  
  286.     buffer = S3MemAllocate(file_size + 1, kMem_S3_sample);
  287.     if (buffer == NULL) {
  288.         fclose(fd);
  289.         gS3_last_error = eS3_error_memory;
  290.         return 0;
  291.     }
  292.     buffer[file_size] = 0;
  293.     bytes_read = fread(buffer, 1, file_size, fd);
  294.     if (bytes_read == file_size) {
  295.         gS3_soundbank_buffer = buffer;
  296.         gS3_soundbank_buffer_len = file_size;
  297.         fclose(fd);
  298.         return buffer;
  299.     }
  300.     gS3_last_error = eS3_error_readfile;
  301.     return 0;
  302. }
  303.  
  304. void S3SoundBankReaderNextLine(tS3_soundbank_read_ctx* ctx) {
  305.     S3SoundBankReaderSkipToNewline(ctx);
  306.     S3SoundBankReaderSkipWhitespace(ctx);
  307. }
  308.  
  309. void S3SoundBankReaderSkipWhitespace(tS3_soundbank_read_ctx* ctx) {
  310.     while (ctx->data_len) {
  311.         if (isalnum(*ctx->data) || *ctx->data == '-') {
  312.             break;
  313.         }
  314.         S3SoundBankReaderSkipToNewline(ctx);
  315.     }
  316. }
  317.  
  318. void S3SoundBankReaderSkipToNewline(tS3_soundbank_read_ctx* ctx) {
  319.     char* newline_ptr; // [esp+Ch] [ebp-4h]
  320.  
  321.     newline_ptr = memchr(ctx->data, '\n', ctx->data_len);
  322.     if (newline_ptr) {
  323.         S3SoundBankReaderAdvance(ctx, newline_ptr + 1 - ctx->data);
  324.         ctx->nlines++;
  325.     } else {
  326.         ctx->data_len = 0;
  327.     }
  328. }
  329.  
  330. void S3SoundBankReaderAdvance(tS3_soundbank_read_ctx* buffer, int bytes_read) {
  331.     buffer->data += bytes_read;
  332.     buffer->data_len -= bytes_read;
  333. }
  334.  
  335. int S3SoundBankReadEntry(tS3_soundbank_read_ctx* ctx, char* dir_name, int low_memory_mode) {
  336.     int nmemory_proxies;  // [esp+Ch] [ebp-24h] BYREF
  337.     int i;                // [esp+10h] [ebp-20h]
  338.     int proxy_id;         // [esp+14h] [ebp-1Ch] BYREF
  339.     tS3_descriptor* desc; // [esp+18h] [ebp-18h]
  340.     double tmp1;          // [esp+1Ch] [ebp-14h] BYREF
  341.     double tmp2;          // [esp+24h] [ebp-Ch] BYREF
  342.     int char_count;       // [esp+2Ch] [ebp-4h] BYREF
  343.     char cda_dir_name[4];
  344.  
  345.     desc = S3CreateDescriptor();
  346.     if (!desc) {
  347.         return gS3_last_error;
  348.     }
  349.     if (sscanf(ctx->data, "%i%n", &desc->id, &char_count) != 1) {
  350.         return 0;
  351.     }
  352.     S3SoundBankReaderAdvance(ctx, char_count);
  353.     S3SoundBankReaderNextLine(ctx);
  354.     if (sscanf(ctx->data, "%i,%i%n", &desc->type, &desc->flags, &char_count) != 2) {
  355.         return 0;
  356.     }
  357.     S3SoundBankReaderAdvance(ctx, char_count);
  358.     S3SoundBankReaderNextLine(ctx);
  359.     if (desc->type == eS3_ST_cda) {
  360.         dir_name = cda_dir_name;
  361.         cda_dir_name[0] = '\0';
  362.     }
  363.     if (!S3SoundBankReaderReadFilename(&desc->filename, ctx, dir_name)) {
  364.         return 0;
  365.     }
  366.     S3SoundBankReaderNextLine(ctx);
  367.     if (sscanf(ctx->data, "%i%n", (int*)&desc->priority, &char_count) != 1) {
  368.         return 0;
  369.     }
  370.     S3SoundBankReaderAdvance(ctx, char_count);
  371.     S3SoundBankReaderNextLine(ctx);
  372.     if (sscanf(ctx->data, "%i%n", &desc->repeats, &char_count) != 1) {
  373.         return 0;
  374.     }
  375.     S3SoundBankReaderAdvance(ctx, char_count);
  376.     S3SoundBankReaderNextLine(ctx);
  377.     if (sscanf(ctx->data, "%i,%i%n", &desc->min_volume, &desc->max_volume, &char_count) != 2) {
  378.         return 0;
  379.     }
  380.     S3SoundBankReaderAdvance(ctx, char_count);
  381.     S3SoundBankReaderNextLine(ctx);
  382.     if (sscanf(ctx->data, "%lf,%lf%n", &tmp1, &tmp2, &char_count) != 2) {
  383.         return 0;
  384.     }
  385.     S3SoundBankReaderAdvance(ctx, char_count);
  386.     S3SoundBankReaderNextLine(ctx);
  387.     if (tmp1 == 0.0f) {
  388.         tmp1 = 1.0f;
  389.     }
  390.     if (tmp2 == 0.0f) {
  391.         tmp2 = 1.0f;
  392.     }
  393.     desc->min_pitch = ldexpf(tmp1, 16);
  394.     desc->max_pitch = ldexpf(tmp2, 16);
  395.     if (sscanf(ctx->data, "%lf,%lf%n", &tmp1, &tmp2, &char_count) != 2) {
  396.         return 0;
  397.     }
  398.     S3SoundBankReaderAdvance(ctx, char_count);
  399.     S3SoundBankReaderNextLine(ctx);
  400.     if (tmp1 == 0.0) {
  401.         tmp1 = 1.0;
  402.     }
  403.     if (tmp2 == 0.0) {
  404.         tmp2 = 1.0;
  405.     }
  406.     desc->min_speed = ldexpf(tmp1, 16);
  407.     desc->max_speed = ldexpf(tmp2, 16);
  408.     if (sscanf(ctx->data, "%i%n", &desc->special_fx, &char_count) != 1) {
  409.         return 0;
  410.     }
  411.     S3SoundBankReaderAdvance(ctx, char_count);
  412.     S3SoundBankReaderNextLine(ctx);
  413.     if (sscanf(ctx->data, "%d%n", &nmemory_proxies, &char_count) != 1) {
  414.         return 0;
  415.     }
  416.     S3SoundBankReaderAdvance(ctx, char_count);
  417.     S3SoundBankReaderNextLine(ctx);
  418.     desc->memory_proxy = -1;
  419.     for (i = 0; i < nmemory_proxies; i++) {
  420.         if (sscanf(ctx->data, "%d%n", &proxy_id, &char_count) != 1) {
  421.             return 0;
  422.         }
  423.         if (low_memory_mode == i + 1) {
  424.             desc->memory_proxy = proxy_id;
  425.         }
  426.         S3SoundBankReaderAdvance(ctx, char_count);
  427.         S3SoundBankReaderNextLine(ctx);
  428.     }
  429.     if ((desc->flags & 1) != 0 && desc->memory_proxy == -1) {
  430.         if (desc->type == eS3_ST_midi) {
  431.             desc->sound_data = NULL;
  432.         } else if (S3LoadSample(desc->id) != 0) {
  433.             printf("\nSound bank file: couldn't load '%s'\n", desc->filename);
  434.             ctx->data_len = 1;
  435.             return 0;
  436.         }
  437.     }
  438.     return 1;
  439. }
  440.  
  441. tS3_descriptor* S3CreateDescriptor(void) {
  442.     tS3_descriptor* root;
  443.     tS3_descriptor* d;
  444.  
  445.     d = S3MemAllocate(sizeof(tS3_descriptor), kMem_S3_descriptor);
  446.     if (!d) {
  447.         gS3_last_error = eS3_error_memory;
  448.         return NULL;
  449.     }
  450.     memset(d, 0, sizeof(tS3_descriptor));
  451.     root = gS3_root_descriptor;
  452.     gS3_root_descriptor->next = d;
  453.     d->prev = root;
  454.     gS3_root_descriptor = d;
  455.     return d;
  456. }
  457.  
  458. int S3SoundBankReaderReadFilename(char** filename, tS3_soundbank_read_ctx* ctx, const char* dir_name) {
  459.     char* data_start;          // [esp+10h] [ebp-Ch]
  460.     unsigned int bytes_read;   // [esp+14h] [ebp-8h]
  461.     unsigned int dir_name_len; // [esp+18h] [ebp-4h]
  462.  
  463.     data_start = ctx->data;
  464.     dir_name_len = strlen(dir_name);
  465.     while (ctx->data_len) {
  466.         if (isspace(*ctx->data)) {
  467.             break;
  468.         }
  469.         S3SoundBankReaderAdvance(ctx, 1);
  470.     }
  471.     bytes_read = ctx->data - data_start;
  472.     if (!bytes_read) {
  473.         return 0;
  474.     }
  475.     *filename = S3MemAllocate(bytes_read + dir_name_len + 1, kMem_S3_scan_name);
  476.     if (!*filename) {
  477.         return 0;
  478.     }
  479.     strcpy(*filename, dir_name);
  480.     memcpy(&(*filename)[dir_name_len], data_start, bytes_read);
  481.     (*filename)[bytes_read + dir_name_len] = '\0';
  482.     return 1;
  483. }
  484.  
  485. tS3_outlet* S3CreateOutlet(int unk1, int pChannel_count) {
  486.     tS3_outlet* o;
  487.     int nchannels;
  488.     tS3_outlet* outlet;
  489.     int channels_remaining;
  490.  
  491.     // nchannels = (int)operator new(unk1, (void*)pChannel_count);
  492.     nchannels = pChannel_count;
  493.  
  494.     if (nchannels == 0) {
  495.         gS3_last_error = eS3_error_channel_alloc;
  496.         return NULL;
  497.     }
  498.     outlet = S3MemAllocate(sizeof(tS3_outlet), kMem_S3_outlet);
  499.     if (outlet == NULL) {
  500.         gS3_last_error = eS3_error_memory;
  501.         return 0;
  502.     }
  503.     memset(outlet, 0, sizeof(tS3_outlet));
  504.     channels_remaining = S3CreateOutletChannels(outlet, nchannels);
  505.     if (channels_remaining == nchannels) {
  506.         S3MemFree(outlet);
  507.         return NULL;
  508.     }
  509.     o = gS3_outlets;
  510.     if (gS3_outlets) {
  511.         while (o->next) {
  512.             o = o->next;
  513.         }
  514.         o->next = outlet;
  515.         outlet->prev = o;
  516.     } else {
  517.         gS3_outlets = outlet;
  518.     }
  519.     outlet->max_channels = nchannels - channels_remaining;
  520.     outlet->id = gS3_next_outlet_id;
  521.     gS3_next_outlet_id++;
  522.     outlet->independent_pitch = gS3_hardware_info.independent_pitch;
  523.     gS3_noutlets++;
  524.     return outlet;
  525. }
  526.  
  527. int S3CreateOutletChannels(tS3_outlet* outlet, int pChannel_count) {
  528.     tS3_channel* chan;      // [esp+Ch] [ebp-8h]
  529.     tS3_channel* last_chan; // [esp+10h] [ebp-4h]
  530.  
  531.     last_chan = NULL;
  532.     while (pChannel_count) {
  533.         chan = (tS3_channel*)S3MemAllocate(sizeof(tS3_channel), kMem_S3_channel);
  534.         if (!chan) {
  535.             return pChannel_count;
  536.         }
  537.         memset(chan, 0, sizeof(tS3_channel));
  538.         chan->owner_outlet = outlet;
  539.  
  540.         if (S3CreateTypeStructs(chan) == 0) {
  541.             S3MemFree(chan);
  542.             return pChannel_count;
  543.         }
  544.         chan->volume_multiplier = 1.0f;
  545.         if (last_chan) {
  546.             last_chan->next = chan;
  547.         } else {
  548.             outlet->channel_list = chan;
  549.         }
  550.         last_chan = chan;
  551.         pChannel_count--;
  552.     }
  553.     return 0;
  554. }
  555.  
  556. void S3ReleaseOutlet(tS3_outlet* outlet) {
  557.     tS3_outlet* next;
  558.     tS3_outlet* prev;
  559.  
  560.     if (outlet) {
  561.         S3UnbindChannels(outlet);
  562.         prev = outlet->prev;
  563.         next = outlet->next;
  564.         if (prev != NULL) {
  565.             prev->next = next;
  566.         } else {
  567.             gS3_outlets = outlet->next;
  568.         }
  569.         if (next != NULL) {
  570.             next->prev = prev;
  571.         }
  572.         if (gS3_noutlets) {
  573.             gS3_noutlets--;
  574.             if (gS3_noutlets == 0) {
  575.                 gS3_outlets = NULL;
  576.             }
  577.         }
  578.         S3MemFree(outlet);
  579.     }
  580. }
  581.  
  582. int S3UnbindChannels(tS3_outlet* outlet) {
  583.     tS3_channel* chan;
  584.     tS3_channel* next;
  585.  
  586.     for (chan = outlet->channel_list; chan; chan = next) {
  587.         next = chan->next;
  588.         S3ReleaseTypeStructs(chan);
  589.         if (gS3_unbound_channels) {
  590.             gS3_last_unbound_channel->next = chan;
  591.         } else {
  592.             gS3_unbound_channels = chan;
  593.         }
  594.         gS3_last_unbound_channel = chan;
  595.         memset(chan, 0, sizeof(tS3_channel));
  596.     }
  597.     outlet->channel_list = NULL;
  598.     return 1;
  599. }
  600.  
  601. void S3ReleaseUnboundChannels(void) {
  602.     tS3_channel* channel;      // [esp+Ch] [ebp-8h]
  603.     tS3_channel* next_channel; // [esp+10h] [ebp-4h]
  604.  
  605.     for (channel = gS3_unbound_channels; channel != NULL; channel = next_channel) {
  606.         next_channel = channel->next;
  607.         S3MemFree(channel);
  608.     }
  609. }
  610.  
  611. tS3_channel* S3AllocateChannel(tS3_outlet* outlet, int priority) {
  612.     tS3_channel* c;                    // [esp+Ch] [ebp-10h]
  613.     int lowest_priority;               // [esp+10h] [ebp-Ch] MAPDST
  614.     int this_priority;                 // [esp+14h] [ebp-8h]
  615.     tS3_channel* lowest_priority_chan; // [esp+18h] [ebp-4h]
  616.  
  617.     lowest_priority_chan = outlet->channel_list;
  618.     c = outlet->channel_list;
  619.     if (lowest_priority_chan == NULL) {
  620.         return NULL;
  621.     }
  622.     while (c) {
  623.         if (!c->active || c->needs_service) {
  624.             if (!c->needs_service) {
  625.                 c->active = 1;
  626.                 return c;
  627.             }
  628.         } else {
  629.             if (lowest_priority_chan->descriptor) {
  630.                 lowest_priority = lowest_priority_chan->descriptor->priority
  631.                     * (lowest_priority_chan->right_volume + lowest_priority_chan->left_volume + 1);
  632.             } else {
  633.                 lowest_priority = 0;
  634.             }
  635.             if (c->descriptor) {
  636.                 this_priority = c->descriptor->priority * (c->left_volume + c->right_volume + 1);
  637.             } else {
  638.                 this_priority = 0;
  639.             }
  640.             if (this_priority <= lowest_priority) {
  641.                 lowest_priority_chan = c;
  642.             }
  643.         }
  644.         c = c->next;
  645.     }
  646.     if (!lowest_priority_chan->descriptor || lowest_priority_chan->needs_service) {
  647.         lowest_priority = 0;
  648.     } else {
  649.         lowest_priority = lowest_priority_chan->descriptor->priority * (lowest_priority_chan->right_volume + lowest_priority_chan->left_volume + 1);
  650.     }
  651.     if (priority > lowest_priority && !lowest_priority_chan->needs_service) {
  652.         lowest_priority_chan->termination_reason = 2;
  653.         S3StopChannel(lowest_priority_chan);
  654.         lowest_priority_chan->active = 1;
  655.     }
  656.  
  657.     return NULL;
  658. }
  659.  
  660. int S3StopChannel(tS3_channel* chan) {
  661.     if (!chan->tag) {
  662.         return eS3_error_bad_stag;
  663.     }
  664.     chan->termination_reason = eS3_tr_stopped;
  665.     if (chan->active) {
  666.         chan->needs_service = 1;
  667.     }
  668.     if (chan->type == eS3_ST_sample) {
  669.         if (chan->sound_source_ptr) {
  670.             chan->sound_source_ptr->tag = 0;
  671.             chan->sound_source_ptr->channel = 0;
  672.             chan->sound_source_ptr->volume = 0;
  673.         }
  674.         if (S3StopSample(chan) == 0) {
  675.             return eS3_error_function_failed;
  676.         }
  677.     } else if (chan->type == eS3_ST_midi) {
  678.         if (S3StopMIDI(chan) != 0) {
  679.             return eS3_error_function_failed;
  680.         }
  681.     } else if (chan->type == eS3_ST_cda) {
  682.         if (S3StopCDA(chan) != 0) {
  683.             return eS3_error_function_failed;
  684.         }
  685.     }
  686.  
  687.     if ((chan->descriptor->flags & 2) != 0) {
  688.         S3ReleaseSound(chan->descriptor->id);
  689.     }
  690.     chan->repetitions = 1;
  691.     return 0;
  692. }
  693.  
  694. tS3_sound_source* S3CreateSoundSourceBR(br_vector3* pPosition, br_vector3* pVelocity, tS3_outlet* pBound_outlet) {
  695.     tS3_sound_source* source; // [esp+Ch] [ebp-4h]
  696.  
  697.     if (!gS3_enabled) {
  698.         return 0;
  699.     }
  700.     source = S3CreateSoundSource(pPosition, pVelocity, pBound_outlet);
  701.     if (source != NULL) {
  702.         source->brender_vector = 1;
  703.     }
  704.     return source;
  705. }
  706.  
  707. tS3_sound_source* S3CreateSoundSource(void* pPosition, void* pVelocity, tS3_outlet* pBound_outlet) {
  708.     tS3_sound_source* src; // [esp+Ch] [ebp-8h]
  709.     tS3_sound_source* s;   // [esp+10h] [ebp-4h]
  710.  
  711.     src = S3MemAllocate(sizeof(tS3_sound_source), kMem_S3_source);
  712.     if (src == NULL) {
  713.         gS3_last_error = eS3_error_memory;
  714.         return 0;
  715.     }
  716.     memset(src, 0, sizeof(tS3_sound_source));
  717.     src->bound_outlet = pBound_outlet;
  718.     src->position_ptr = pPosition;
  719.     src->velocity_ptr = pVelocity;
  720.     s = gS3_sound_sources;
  721.     if (gS3_sound_sources) {
  722.         while (s->next) {
  723.             s = s->next;
  724.         }
  725.         s->next = src;
  726.         src->prev = s;
  727.     } else {
  728.         gS3_sound_sources = src;
  729.     }
  730.     gS3_nsound_sources++;
  731.     return src;
  732. }
  733.  
  734. int S3ReleaseSoundSource(tS3_sound_source* src) {
  735.     tS3_sound_source* prev; // [esp+Ch] [ebp-8h]
  736.     tS3_sound_source* next; // [esp+10h] [ebp-4h]
  737.  
  738.     if (!gS3_enabled) {
  739.         return 0;
  740.     }
  741.  
  742.     if (src) {
  743.         prev = src->prev;
  744.         next = src->next;
  745.         if (prev) {
  746.             prev->next = next;
  747.         } else {
  748.             gS3_sound_sources = src->next;
  749.         }
  750.         if (next) {
  751.             next->prev = prev;
  752.         }
  753.         if (gS3_nsound_sources) {
  754.             gS3_nsound_sources--;
  755.             if (gS3_nsound_sources == 0) {
  756.                 gS3_sound_sources = NULL;
  757.             }
  758.         }
  759.         S3StopSoundSource(src);
  760.         S3MemFree(src);
  761.     }
  762.     return 0;
  763. }
  764.  
  765. void S3Service(int inside_cockpit, int unk1) {
  766.  
  767.     int now;        // [esp+Ch] [ebp-10h]
  768.     tS3_channel* c; // [esp+10h] [ebp-Ch]
  769.     tS3_outlet* o;  // [esp+14h] [ebp-8h]
  770.     int v5;         // [esp+18h] [ebp-4h]
  771.  
  772.     v5 = 0;
  773.     gS3_inside_cockpit = inside_cockpit;
  774.     if (!gS3_enabled) {
  775.         return;
  776.     }
  777.     now = PDGetTotalTime();
  778.     gS3_service_time_delta = now - gS3_last_service_time;
  779.     gS3_last_service_time = now;
  780.     S3ServiceOutlets();
  781.     if (unk1 == 1) {
  782.         S3UpdateListenerVectors();
  783.         S3ServiceAmbientSoundSources();
  784.     }
  785.     for (o = gS3_outlets; o; o = o->next) {
  786.         for (c = o->channel_list; c; c = c->next) {
  787.             if (c->needs_service) {
  788.                 c->needs_service = 0;
  789.                 if (c->descriptor && c->descriptor->flags == 2) {
  790.                     S3ReleaseSound(c->descriptor->id);
  791.                 }
  792.                 c->active = 0;
  793.                 if (c->type != eS3_ST_midi) {
  794.                     c->tag = 0;
  795.                 }
  796.             } else if (c->spatial_sound && c->active) {
  797.                 if (S3UpdateSpatialSound(c)) {
  798.                     if (c->sound_source_ptr && c->sound_source_ptr->ambient && !S3SoundStillPlaying(c->tag)) {
  799.                         S3UpdateSoundSource(NULL, -1, c->sound_source_ptr, -1.0f, -1, -1, 0, -1, -1);
  800.                     }
  801.                 } else if (c->sound_source_ptr) {
  802.                     if (c->sound_source_ptr->ambient) {
  803.                         S3UpdateSoundSource(NULL, -1, c->sound_source_ptr, -1.0f, -1, -1, 0, -1, -1);
  804.                     }
  805.                 } else {
  806.                     S3StopChannel(c);
  807.                 }
  808.             } else if (c->type == eS3_ST_midi && c->active) {
  809.                 // sub_4124BE(c);
  810.             }
  811.             if (unk1 < 2 && gS3_last_service_time > dword_5216C0) {
  812.                 v5 = 1;
  813.                 if (!c->active && c->spatial_sound == 2) {
  814.                     // null_unknown_libname_8();  /* no-op */
  815.                 }
  816.             }
  817.         }
  818.     }
  819.     if (v5) {
  820.         dword_5216C0 = gS3_last_service_time;
  821.     }
  822. }
  823.  
  824. void S3ServiceOutlets(void) {
  825.     tS3_channel* c; // [esp+Ch] [ebp-8h]
  826.     tS3_outlet* o;  // [esp+10h] [ebp-4h]
  827.  
  828.     for (o = gS3_outlets; o; o = o->next) {
  829.         for (c = o->channel_list; c; c = c->next) {
  830.             S3ServiceChannel(c);
  831.         }
  832.     }
  833. }
  834.  
  835. int S3ServiceChannel(tS3_channel* chan) {
  836.     if (chan->type == eS3_ST_sample) {
  837.         if (AudioBackend_SoundIsPlaying(chan)) {
  838.             return 1;
  839.         }
  840.         S3StopSample(chan);
  841.         return 0;
  842.     } else if (chan->type == eS3_ST_midi) {
  843.         return !S3IsMIDIStopped(chan);
  844.     } else if (chan->type == eS3_ST_cda) {
  845.         return S3IsCDAPlaying();
  846.     } else {
  847.         return 0;
  848.     }
  849. }
  850.  
  851. void S3StopAllOutletSounds(void) {
  852.     tS3_outlet* o; // [esp+Ch] [ebp-4h]
  853.  
  854.     if (!gS3_enabled) {
  855.         return;
  856.     }
  857.  
  858.     for (o = gS3_outlets; o; o = o->next) {
  859.         S3StopOutletSound(o);
  860.     }
  861. }
  862.  
  863. tS3_sound_tag S3StartSound(tS3_outlet* pOutlet, tS3_sound_id pSound) {
  864.     int repetitions;      // eax
  865.     tS3_channel* chan;    // [esp+14h] [ebp-Ch]
  866.     tS3_descriptor* desc; // [esp+1Ch] [ebp-4h]
  867.  
  868.     if (!gS3_enabled) {
  869.         return 0;
  870.     }
  871.     if (!pOutlet) {
  872.         gS3_last_error = eS3_error_bad_id;
  873.         return 0;
  874.     }
  875.     desc = S3GetDescriptorByID(pSound);
  876.     if (!desc) {
  877.         gS3_last_error = eS3_error_bad_id;
  878.         return 0;
  879.     }
  880.     memset(&gS3_channel_template, 0, sizeof(gS3_channel_template));
  881.     S3CalculateRandomizedFields(&gS3_channel_template, desc);
  882.     chan = S3AllocateChannel(pOutlet, desc->priority * (gS3_channel_template.right_volume + gS3_channel_template.left_volume + 1));
  883.     if (!chan) {
  884.         gS3_last_error = eS3_error_channel_alloc;
  885.         return 0;
  886.     }
  887.     chan->left_volume = gS3_channel_template.left_volume * chan->volume_multiplier;
  888.     chan->right_volume = gS3_channel_template.right_volume * chan->volume_multiplier;
  889.     chan->rate = gS3_channel_template.rate;
  890.     if (desc->type == eS3_ST_sample && (!desc->sound_data || desc->flags == 2)) {
  891.         if (!S3LoadSample(pSound)) {
  892.             chan->needs_service = 1;
  893.             gS3_last_error = eS3_error_load_sound;
  894.             return 0;
  895.         }
  896.     }
  897.     if (chan->descriptor && chan->descriptor->type == 1 && chan->descriptor->id != pSound) {
  898.         S3ReleaseMIDI(chan->tag);
  899.     }
  900.     chan->spatial_sound = 0;
  901.     chan->sound_source_ptr = 0;
  902.     chan->descriptor = desc;
  903.     chan->type = desc->type;
  904.     repetitions = desc->repeats;
  905.     if (repetitions <= 0) {
  906.         repetitions = 0;
  907.     }
  908.     chan->repetitions = repetitions;
  909.     chan->needs_service = 0;
  910.     chan->termination_reason = eS3_tr_natural;
  911.     chan->tag = S3GenerateTag(pOutlet);
  912.     if (desc->type == eS3_ST_midi && !desc->sound_data) {
  913.         if (S3MIDILoadSong(chan)) {
  914.             chan->needs_service = 1;
  915.             return 0;
  916.         }
  917.     }
  918.     if (chan->type == eS3_ST_sample) {
  919.         S3ExecuteSampleFilterFuncs(chan);
  920.         if (S3PlaySample(chan) == 0) {
  921.             gS3_last_error = eS3_error_start_sound;
  922.             chan->needs_service = 1;
  923.             return 0;
  924.         }
  925.     } else if (chan->type == eS3_ST_midi) {
  926.         if (S3PlayMIDI(chan)) {
  927.             chan->needs_service = 1;
  928.             gS3_last_error = eS3_error_start_song;
  929.             return 0;
  930.         }
  931.         S3SetMIDIVolume(chan);
  932.     } else if (chan->type == eS3_ST_cda) {
  933.         if (S3PlayCDA(chan)) {
  934.             chan->needs_service = 1;
  935.             gS3_last_error = eS3_error_start_cda;
  936.             return 0;
  937.         }
  938.     }
  939.  
  940.     return chan->tag;
  941. }
  942.  
  943. tS3_sound_tag S3StartSound2(tS3_outlet* pOutlet, tS3_sound_id pSound, tS3_repeats pRepeats, tS3_volume pLVolume, tS3_volume pRVolume, tS3_pitch pPitch, tS3_speed pSpeed) {
  944.     tS3_channel* chan;    // [esp+30h] [ebp-Ch]
  945.     tS3_descriptor* desc; // [esp+38h] [ebp-4h]
  946.  
  947.     if (!gS3_enabled) {
  948.         return 0;
  949.     }
  950.     desc = S3GetDescriptorByID(pSound);
  951.     if (!desc) {
  952.         gS3_last_error = eS3_error_bad_id;
  953.         return 0;
  954.     }
  955.     if (pLVolume < 0) {
  956.         pLVolume = 128;
  957.     }
  958.     if (pRVolume < 0) {
  959.         pRVolume = 128;
  960.     }
  961.     if (pLVolume > 255) {
  962.         pLVolume = 255;
  963.     }
  964.     if (pRVolume > 255) {
  965.         pRVolume = 255;
  966.     }
  967.     chan = S3AllocateChannel(pOutlet, desc->priority * (pLVolume + pRVolume + 1));
  968.     if (chan == NULL) {
  969.         gS3_last_error = eS3_error_channel_alloc;
  970.         return 0;
  971.     }
  972.     if (desc->type == eS3_ST_sample) {
  973.         if (desc->sound_data == NULL && (!S3LoadSample(pSound) || (desc->flags & 2) != 0)) {
  974.             chan->needs_service = 1;
  975.             gS3_last_error = eS3_error_load_sound;
  976.             return 0;
  977.         }
  978.     }
  979.  
  980.     if (chan->descriptor && chan->descriptor->type == eS3_ST_midi) {
  981.         if (chan->descriptor->id != pSound) {
  982.             S3ReleaseMIDI(chan->tag);
  983.         }
  984.     }
  985.     chan->spatial_sound = 0;
  986.     chan->descriptor = desc;
  987.     chan->needs_service = 0;
  988.     chan->termination_reason = eS3_tr_natural;
  989.     chan->type = desc->type;
  990.     chan->sound_source_ptr = NULL;
  991.     chan->repetitions = MAX(pRepeats, 0);
  992.     S3CalculateRandomizedFields(chan, desc);
  993.     chan->left_volume = pLVolume * chan->volume_multiplier;
  994.     chan->right_volume = pRVolume * chan->volume_multiplier;
  995.     chan->tag = S3GenerateTag(pOutlet);
  996.     if (pPitch == -1) {
  997.         pPitch = 0x10000;
  998.     }
  999.     if (pSpeed == -1) {
  1000.         pSpeed = 0x10000;
  1001.     }
  1002.     chan->rate = ldexpf(pPitch, -16) * chan->rate;
  1003.     if (!pOutlet->independent_pitch) {
  1004.         chan->rate = ldexpf(pSpeed, -16) * chan->rate;
  1005.     }
  1006.     if (desc->type == eS3_ST_midi && desc->sound_data == NULL && S3MIDILoadSong(chan)) {
  1007.         chan->needs_service = 1;
  1008.         return 0;
  1009.     }
  1010.  
  1011.     switch (chan->type) {
  1012.     case eS3_ST_sample:
  1013.         S3ExecuteSampleFilterFuncs(chan);
  1014.         if (!S3PlaySample(chan)) {
  1015.             chan->needs_service = 1;
  1016.             gS3_last_error = eS3_error_start_sound;
  1017.             return 0;
  1018.         }
  1019.         break;
  1020.  
  1021.     case eS3_ST_midi:
  1022.         if (S3PlayMIDI(chan)) {
  1023.             chan->needs_service = 1;
  1024.             gS3_last_error = eS3_error_start_song;
  1025.             return 0;
  1026.         }
  1027.         S3SetMIDIVolume(chan);
  1028.         break;
  1029.  
  1030.     case eS3_ST_cda:
  1031.         if (S3PlayCDA(chan)) {
  1032.             chan->needs_service = 1;
  1033.             gS3_last_error = eS3_error_start_cda;
  1034.             return 0;
  1035.         }
  1036.         break;
  1037.     }
  1038.  
  1039.     return chan->tag;
  1040. }
  1041.  
  1042. void S3CalculateRandomizedFields(tS3_channel* chan, tS3_descriptor* desc) {
  1043.     int vol; // eax
  1044.  
  1045.     vol = S3IRandomBetween(desc->min_volume, desc->max_volume, 128);
  1046.     chan->left_volume = vol;
  1047.     chan->right_volume = vol;
  1048.     if (desc->type == eS3_ST_sample) {
  1049. #if defined(DETHRACE_FIX_BUGS)
  1050.         /* Avoid a possible NULL pointer dereference. */
  1051.         if (desc->sound_data == NULL) {
  1052.             chan->rate = desc->min_pitch;
  1053.             return;
  1054.         }
  1055. #endif
  1056.         chan->rate = S3IRandomBetweenLog(desc->min_pitch, desc->max_pitch, ((tS3_sample*)desc->sound_data)->rate);
  1057.     }
  1058. }
  1059.  
  1060. // duplicate of S3IRandomBetween2
  1061. int S3IRandomBetween(int pMin, int pMax, int pDefault) {
  1062.     if (pMin == -1) {
  1063.         return pDefault;
  1064.     }
  1065.     if (pMax <= pMin) {
  1066.         return pMin;
  1067.     }
  1068.     return rand() % (pMax - pMin) + pMin;
  1069. }
  1070.  
  1071. // duplicate of S3IRandomBetweenLog2
  1072. int S3IRandomBetweenLog(int pMin, int pMax, int pDefault) {
  1073.     float v4; // st7
  1074.  
  1075.     if (pMin == -1 || pMin >= pMax) {
  1076.         return pDefault;
  1077.     }
  1078.     v4 = S3FRandomBetween(log(pMin), log(pMax));
  1079.     return ldexp(exp(v4), -16) * pDefault;
  1080. }
  1081.  
  1082. // duplicate of S3FRandomBetween2
  1083. double S3FRandomBetween(double pMin, double pMax) {
  1084.     return (double)rand() * (pMax - pMin) / (double)RAND_MAX + pMin;
  1085. }
  1086.  
  1087. int S3GenerateTag(tS3_outlet* outlet) {
  1088.     gS3_tag_seed += 256;
  1089.     return gS3_tag_seed | outlet->id;
  1090. }
  1091.  
  1092. int S3SoundStillPlaying(tS3_sound_tag pTag) {
  1093.     tS3_channel* chan;
  1094.  
  1095.     if (!gS3_enabled) {
  1096.         return 0;
  1097.     }
  1098.     if (!pTag) {
  1099.         return 0;
  1100.     }
  1101.     chan = S3GetChannelForTag(pTag);
  1102.     if (!chan) {
  1103.         return 0;
  1104.     }
  1105.     return S3ServiceChannel(chan) != 0;
  1106. }
  1107.  
  1108. int S3ChangePitchSpeed(tS3_sound_tag pTag, tS3_pitch pNew_pitch) {
  1109.     tS3_channel* chan;
  1110.  
  1111.     if (!gS3_enabled) {
  1112.         return 0;
  1113.     }
  1114.     if (pNew_pitch == -1) {
  1115.         pNew_pitch = 0x10000;
  1116.     }
  1117.     chan = S3GetChannelForTag(pTag);
  1118.     if (chan == NULL) {
  1119.         return eS3_error_bad_stag;
  1120.     }
  1121.     if (chan->type != eS3_ST_sample) {
  1122.         return 0;
  1123.     }
  1124.     chan->rate = ldexp(pNew_pitch, -16) * ((tS3_sample*)chan->descriptor->sound_data)->rate;
  1125.     if (S3SyncSampleRate(chan)) {
  1126.         return 0;
  1127.     } else {
  1128.         return eS3_error_function_failed;
  1129.     }
  1130. }
  1131.  
  1132. int S3StopSound(tS3_sound_tag pTag) {
  1133.     tS3_channel* chan; // [esp+Ch] [ebp-4h]
  1134.  
  1135.     if (!gS3_enabled) {
  1136.         return 0;
  1137.     }
  1138.     if (!pTag) {
  1139.         return eS3_error_bad_stag;
  1140.     }
  1141.     chan = S3GetChannelForTag(pTag);
  1142.     if (!chan) {
  1143.         return eS3_error_bad_stag;
  1144.     }
  1145.     chan->termination_reason = eS3_tr_stopped;
  1146.     chan->initial_volume = 0;
  1147.     if (chan->active) {
  1148.         chan->needs_service = 1;
  1149.     }
  1150.     if (chan->type == eS3_ST_sample) {
  1151.         if (chan->sound_source_ptr) {
  1152.             chan->sound_source_ptr->tag = 0;
  1153.             chan->sound_source_ptr->channel = NULL;
  1154.             chan->sound_source_ptr->volume = 0;
  1155.         }
  1156.         if (!S3StopSample(chan)) {
  1157.             return eS3_error_function_failed;
  1158.         }
  1159.     } else if (chan->type == eS3_ST_midi) {
  1160.         if (S3StopMIDI(chan)) {
  1161.             return eS3_error_function_failed;
  1162.         }
  1163.     } else if (chan->type == eS3_ST_cda) {
  1164.         if (S3StopCDA(chan)) {
  1165.             return eS3_error_function_failed;
  1166.         }
  1167.     }
  1168.  
  1169.     if ((chan->descriptor->flags & 2) != 0) {
  1170.         S3ReleaseSound(chan->descriptor->id);
  1171.     }
  1172.     chan->repetitions = 1;
  1173.     return 0;
  1174. }
  1175.  
  1176. int S3StopOutletSound(tS3_outlet* pOutlet) {
  1177.     tS3_channel* c; // [esp+Ch] [ebp-4h]
  1178.  
  1179.     if (!gS3_enabled) {
  1180.         return 0;
  1181.     }
  1182.     for (c = pOutlet->channel_list; c; c = c->next) {
  1183.         // null_unknown_libname_8();
  1184.         if (c->active) {
  1185.             c->spatial_sound = 0;
  1186.             S3StopChannel(c);
  1187.             c->needs_service = 1;
  1188.         }
  1189.     }
  1190.     return 0;
  1191. }
  1192.  
  1193. char* S3GetCurrentDir(void) {
  1194.     if (!gS3_have_current_dir) {
  1195.         if (getcwd(gS3_current_dir, 260) == NULL) {
  1196.             LOG_PANIC("failed to call getcwd"); // added by dethrace
  1197.         };
  1198.         gS3_have_current_dir = 1;
  1199.     }
  1200.     return gS3_current_dir;
  1201. }
  1202.  
  1203. tS3_descriptor* S3GetDescriptorByID(tS3_sound_tag id) {
  1204.     tS3_descriptor* d; // [esp+Ch] [ebp-4h]
  1205.  
  1206.     for (d = gS3_descriptors;; d = d->next) {
  1207.         if (!d) {
  1208.             return 0;
  1209.         }
  1210.         if (d->id == id) {
  1211.             break;
  1212.         }
  1213.     }
  1214.     if (d->memory_proxy < 0) {
  1215.         return d;
  1216.     } else {
  1217.         return S3GetDescriptorByID(d->memory_proxy);
  1218.     }
  1219. }
  1220.  
  1221. int S3SetOutletVolume(tS3_outlet* pOutlet, tS3_volume pVolume) {
  1222.     tS3_channel* c; // [esp+10h] [ebp-4h]
  1223.  
  1224.     if (!gS3_enabled) {
  1225.         return 0;
  1226.     }
  1227.     if (pVolume > 255) {
  1228.         pVolume = 255;
  1229.     }
  1230.     if (pVolume < 10) {
  1231.         pVolume = 10;
  1232.     }
  1233.     if (!pOutlet) {
  1234.         return 0;
  1235.     }
  1236.     for (c = pOutlet->channel_list; c; c = c->next) {
  1237.         c->volume_multiplier = pVolume / 150.0f;
  1238.         if (c->active) {
  1239.             S3ChangeVolume(c->tag, pVolume);
  1240.         }
  1241.     }
  1242.     return 0;
  1243. }
  1244.  
  1245. tS3_channel* S3GetChannelForTag(tS3_sound_tag tag) {
  1246.     tS3_channel* c; // [esp+Ch] [ebp-Ch]
  1247.     tS3_outlet* o;  // [esp+14h] [ebp-4h]
  1248.  
  1249.     if (!tag) {
  1250.         return 0;
  1251.     }
  1252.     for (o = gS3_outlets; o && o->id != (uint8_t)tag; o = o->next) {
  1253.         ;
  1254.     }
  1255.     if (!o) {
  1256.         return 0;
  1257.     }
  1258.     for (c = o->channel_list; c; c = c->next) {
  1259.         if (c->tag == tag) {
  1260.             return c;
  1261.         }
  1262.     }
  1263.     return 0;
  1264. }
  1265.  
  1266. int S3ChangeVolume(tS3_sound_tag pTag, tS3_volume pVolume) {
  1267.     tS3_channel* chan; // [esp+14h] [ebp-4h]
  1268.  
  1269.     if (!gS3_enabled) {
  1270.         return 0;
  1271.     }
  1272.     chan = S3GetChannelForTag(pTag);
  1273.     if (!chan) {
  1274.         return eS3_error_bad_stag;
  1275.     }
  1276.     if (pVolume < 0) {
  1277.         pVolume = 128;
  1278.     }
  1279.     if (pVolume > 255) {
  1280.         pVolume = 255;
  1281.     }
  1282.     if (chan->type == eS3_ST_sample) {
  1283.         chan->left_volume = pVolume * chan->volume_multiplier;
  1284.         chan->right_volume = pVolume * chan->volume_multiplier;
  1285.         if (!S3SyncSampleVolumeAndPan(chan)) {
  1286.             return eS3_error_function_failed;
  1287.         }
  1288.     } else if (chan->type == eS3_ST_midi) {
  1289.         S3SetMIDIVolume(chan);
  1290.     } else if (chan->type == eS3_ST_cda) {
  1291.         S3SetCDAVolume(chan, pVolume);
  1292.     }
  1293.  
  1294.     return 0;
  1295. }
  1296.