Subversion Repositories Games.Carmageddon

Rev

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