/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * u6m.cpp - Ultima 6 Music Player by Marc Winterrowd.
 * This code extends the Adlib Winamp plug-in by Simon Peter <dn.tlp@gmx.net>
 */

#include "u6m.h"

// Makes security checks on output buffer before writing
#define SAVE_OUTPUT_ROOT(c, d, p) \
if(p < d.size) \
  output_root(c, d.data, p); \
else \
  return false;

CPlayer *
Cu6mPlayer::factory (Copl * newopl)
{
  return new Cu6mPlayer (newopl);
}

bool
Cu6mPlayer::load (VFSFile * fd, const CFileProvider & fp)
{
  // file validation section
  // this section only checks a few *necessary* conditions
  unsigned long filesize, decompressed_filesize;
  binistream *f;

  f = fp.open (fd);
  if (!f)
    return false;
  filesize = fp.filesize (f);

  if (filesize >= 6)
  {
    // check if the file has a valid pseudo-header
    unsigned char pseudo_header[6];
    f->readString ((char *) pseudo_header, 6);
    decompressed_filesize = pseudo_header[0] + (pseudo_header[1] << 8);

    if (!((pseudo_header[2] == 0) && (pseudo_header[3] == 0) &&
          (pseudo_header[4] + ((pseudo_header[5] & 0x1) << 8) == 0x100) &&
          (decompressed_filesize > (filesize - 4))))
    {
      fp.close (f);
      return (false);
    }
  }
  else
  {
    fp.close (f);
    return (false);
  }

  // load section
  song_data = new unsigned char[decompressed_filesize];
  unsigned char *compressed_song_data = new unsigned char[filesize - 3];

  f->seek (4);
  f->readString ((char *) compressed_song_data, filesize - 4);
  fp.close (f);

  // attempt to decompress the song data
  // if unsuccessful, deallocate song_data[] on the spot, and return(false)
  data_block source, destination;
  source.size = filesize - 4;
  source.data = compressed_song_data;
  destination.size = decompressed_filesize;
  destination.data = song_data;

  if (!lzw_decompress (source, destination))
  {
    delete[]compressed_song_data;
    compressed_song_data = 0;
    delete[]song_data;
    song_data = 0;
    return (false);
  }

  // deallocation section
  delete[]compressed_song_data;
  compressed_song_data = 0;

  rewind (0);
  return (true);
}


bool
Cu6mPlayer::update ()
{
  if (!driver_active)
  {
    driver_active = true;
    dec_clip (read_delay);
    if (read_delay == 0)
    {
      command_loop ();
    }

    // on all Adlib channels: freq slide/vibrato, mute factor slide
    for (int i = 0; i < 9; i++)
    {
      if (channel_freq_signed_delta[i] != 0)
        // frequency slide + mute factor slide
      {
        // freq slide
        freq_slide (i);

        // mute factor slide
        if (carrier_mf_signed_delta[i] != 0)
        {
          mf_slide (i);
        }
      }
      else
        // vibrato + mute factor slide
      {
        // vibrato
        if ((vb_multiplier[i] != 0) && ((channel_freq[i].hi & 0x20) == 0x20))
        {
          vibrato (i);
        }

        // mute factor slide
        if (carrier_mf_signed_delta[i] != 0)
        {
          mf_slide (i);
        }
      }
    }

    driver_active = false;
  }

  return !songend;
}


void
Cu6mPlayer::rewind (int subsong)
{
  played_ticks = 0;
  songend = false;

  // set the driver's internal variables
  byte_pair freq_word = { 0, 0 };

  driver_active = false;
  song_pos = 0;
  loop_position = 0;            // position of the loop point
  read_delay = 0;               // delay (in timer ticks) before further song data is read

  for (int i = 0; i < 9; i++)
  {
    // frequency
    channel_freq_signed_delta[i] = 0;
    channel_freq[i] = freq_word;    // Adlib freq settings for each channel

    // vibrato ("vb")
    vb_current_value[i] = 0;
    vb_double_amplitude[i] = 0;
    vb_multiplier[i] = 0;
    vb_direction_flag[i] = 0;

    // mute factor ("mf") == ~(volume)
    carrier_mf[i] = 0;
    carrier_mf_signed_delta[i] = 0;
    carrier_mf_mod_delay_backup[i] = 0;
    carrier_mf_mod_delay[i] = 0;
  }

  while (!subsong_stack.empty ())   // empty subsong stack
    subsong_stack.pop ();

  opl->init ();
  out_adlib (1, 32);            // go to OPL2 mode
}


float
Cu6mPlayer::getrefresh ()
{
  return ((float) 60);          // the Ultima 6 music driver expects to be called at 60 Hz
}


// ============================================================================================
//
//
//    Functions called by load()
//
//
// ============================================================================================


// decompress from memory to memory
bool
Cu6mPlayer::lzw_decompress (Cu6mPlayer::data_block source,
                            Cu6mPlayer::data_block dest)
{
  bool end_marker_reached = false;
  int codeword_size = 9;
  long bits_read = 0;
  int next_free_codeword = 0x102;
  int dictionary_size = 0x200;
  MyDict dictionary = MyDict ();
  std::stack < unsigned char >root_stack;

  long bytes_written = 0;

  int cW;
  int pW;
  unsigned char C;

  while (!end_marker_reached)
  {
    cW = get_next_codeword (bits_read, source.data, codeword_size);
    switch (cW)
    {
      // re-init the dictionary
    case 0x100:
      codeword_size = 9;
      next_free_codeword = 0x102;
      dictionary_size = 0x200;
      dictionary.reset ();
      cW = get_next_codeword (bits_read, source.data, codeword_size);
      SAVE_OUTPUT_ROOT ((unsigned char) cW, dest, bytes_written);
      break;
      // end of compressed file has been reached
    case 0x101:
      end_marker_reached = true;
      break;
      // (cW <> 0x100) && (cW <> 0x101)
    default:
      if (cW < next_free_codeword)  // codeword is already in the dictionary
      {
        // create the string associated with cW (on the stack)
        get_string (cW, dictionary, root_stack);
        C = root_stack.top ();
        // output the string represented by cW
        while (!root_stack.empty ())
        {
          SAVE_OUTPUT_ROOT (root_stack.top (), dest, bytes_written);
          root_stack.pop ();
        }
        // add pW+C to the dictionary
        dictionary.add (C, pW);

        next_free_codeword++;
        if (next_free_codeword >= dictionary_size)
        {
          if (codeword_size < max_codeword_length)
          {
            codeword_size += 1;
            dictionary_size *= 2;
          }
        }
      }
      else                      // codeword is not yet defined
      {
        // create the string associated with pW (on the stack)
        get_string (pW, dictionary, root_stack);
        C = root_stack.top ();
        // output the string represented by pW
        while (!root_stack.empty ())
        {
          SAVE_OUTPUT_ROOT (root_stack.top (), dest, bytes_written);
          root_stack.pop ();
        }
        // output the char C
        SAVE_OUTPUT_ROOT (C, dest, bytes_written);

        // the new dictionary entry must correspond to cW
        // if it doesn't, something is wrong with the lzw-compressed data.
        if (cW != next_free_codeword)
        {
          /*                        printf("cW != next_free_codeword!\n");
             exit(-1); */
          return false;
        }
        // add pW+C to the dictionary
        dictionary.add (C, pW);

        next_free_codeword++;
        if (next_free_codeword >= dictionary_size)
        {
          if (codeword_size < max_codeword_length)
          {
            codeword_size += 1;
            dictionary_size *= 2;
          }
        }
      };
      break;
    }
    // shift roles - the current cW becomes the new pW
    pW = cW;
  }

  return (true);                // indicate successful decompression
}


// --------------------
// Additional functions
// --------------------


// Read the next code word from the source buffer
int
Cu6mPlayer::get_next_codeword (long &bits_read, unsigned char *source,
                               int codeword_size)
{
  unsigned char b0, b1, b2;
  int codeword;

  b0 = source[bits_read / 8];
  b1 = source[bits_read / 8 + 1];
  b2 = source[bits_read / 8 + 2];

  codeword = ((b2 << 16) + (b1 << 8) + b0);
  codeword = codeword >> (bits_read % 8);
  switch (codeword_size)
  {
  case 0x9:
    codeword = codeword & 0x1ff;
    break;
  case 0xa:
    codeword = codeword & 0x3ff;
    break;
  case 0xb:
    codeword = codeword & 0x7ff;
    break;
  case 0xc:
    codeword = codeword & 0xfff;
    break;
  default:
    codeword = -1;              // indicates that an error has occurred
    break;
  }

  bits_read += codeword_size;
  return (codeword);
}


// output a root to memory
void
Cu6mPlayer::output_root (unsigned char root, unsigned char *destination,
                         long &position)
{
  destination[position] = root;
  position++;
}


// output the string represented by a codeword
void
Cu6mPlayer::get_string (int codeword, Cu6mPlayer::MyDict & dictionary,
                        std::stack < unsigned char >&root_stack)
{
  unsigned char root;
  int current_codeword;

  current_codeword = codeword;

  while (current_codeword > 0xff)
  {
    root = dictionary.get_root (current_codeword);
    current_codeword = dictionary.get_codeword (current_codeword);
    root_stack.push (root);
  }

  // push the root at the leaf
  root_stack.push ((unsigned char) current_codeword);
}


// ============================================================================================
//
//
//    Functions called by update()
//
//
// ============================================================================================


// This function reads the song data and executes the embedded commands.
void
Cu6mPlayer::command_loop ()
{
  unsigned char command_byte;   // current command byte
  int command_nibble_hi;        // command byte, bits 4-7
  int command_nibble_lo;        // command byte, bite 0-3
  bool repeat_loop = true;      //

  do
  {
    // extract low and high command nibbles
    command_byte = read_song_byte ();   // implicitly increments song_pos
    command_nibble_hi = command_byte >> 4;
    command_nibble_lo = command_byte & 0xf;

    switch (command_nibble_hi)
    {
    case 0x0:
      command_0 (command_nibble_lo);
      break;
    case 0x1:
      command_1 (command_nibble_lo);
      break;
    case 0x2:
      command_2 (command_nibble_lo);
      break;
    case 0x3:
      command_3 (command_nibble_lo);
      break;
    case 0x4:
      command_4 (command_nibble_lo);
      break;
    case 0x5:
      command_5 (command_nibble_lo);
      break;
    case 0x6:
      command_6 (command_nibble_lo);
      break;
    case 0x7:
      command_7 (command_nibble_lo);
      break;
    case 0x8:
      switch (command_nibble_lo)
      {
      case 1:
        command_81 ();
        break;
      case 2:
        command_82 ();
        repeat_loop = false;
        break;
      case 3:
        command_83 ();
        break;
      case 5:
        command_85 ();
        break;
      case 6:
        command_86 ();
        break;
      default:
        break;                  // maybe generate an error?
      }
      break;
    case 0xE:
      command_E ();
      break;
    case 0xF:
      command_F ();
      break;
    default:
      break;                    // maybe generate an error?
    }

  } while (repeat_loop);
}


// --------------------------------------------------------
//    The commands supported by the U6 music file format
// --------------------------------------------------------

// ----------------------------------------
// Set octave and frequency, note off
// Format: 0c nn
// c = channel, nn = packed Adlib frequency
// ----------------------------------------
void
Cu6mPlayer::command_0 (int channel)
{
  unsigned char freq_byte;
  byte_pair freq_word;

  freq_byte = read_song_byte ();
  freq_word = expand_freq_byte (freq_byte);
  set_adlib_freq (channel, freq_word);
}


// ---------------------------------------------------
// Set octave and frequency, old note off, new note on
// Format: 1c nn
// c = channel, nn = packed Adlib frequency
// ---------------------------------------------------
void
Cu6mPlayer::command_1 (int channel)
{
  unsigned char freq_byte;
  byte_pair freq_word;

  vb_direction_flag[channel] = 0;
  vb_current_value[channel] = 0;

  freq_byte = read_song_byte ();
  freq_word = expand_freq_byte (freq_byte);
  set_adlib_freq (channel, freq_word);

  freq_word.hi = freq_word.hi | 0x20;   // note on
  set_adlib_freq (channel, freq_word);
}


// ----------------------------------------
// Set octave and frequency, note on
// Format: 2c nn
// c = channel, nn = packed Adlib frequency
// ----------------------------------------
void
Cu6mPlayer::command_2 (int channel)
{
  unsigned char freq_byte;
  byte_pair freq_word;

  freq_byte = read_song_byte ();
  freq_word = expand_freq_byte (freq_byte);
  freq_word.hi = freq_word.hi | 0x20;   // note on
  set_adlib_freq (channel, freq_word);
}


// --------------------------------------
// Set "carrier mute factor"==not(volume)
// Format: 3c nn
// c = channel, nn = mute factor
// --------------------------------------
void
Cu6mPlayer::command_3 (int channel)
{
  unsigned char mf_byte;

  carrier_mf_signed_delta[channel] = 0;
  mf_byte = read_song_byte ();
  set_carrier_mf (channel, mf_byte);
}


// ----------------------------------------
// set "modulator mute factor"==not(volume)
// Format: 4c nn
// c = channel, nn = mute factor
// ----------------------------------------
void
Cu6mPlayer::command_4 (int channel)
{
  unsigned char mf_byte;

  mf_byte = read_song_byte ();
  set_modulator_mf (channel, mf_byte);
}


// --------------------------------------------
// Set portamento (pitch slide)
// Format: 5c nn
// c = channel, nn = signed channel pitch delta
// --------------------------------------------
void
Cu6mPlayer::command_5 (int channel)
{
  channel_freq_signed_delta[channel] = read_signed_song_byte ();
}


// --------------------------------------------
// Set vibrato paramters
// Format: 6c mn
// c = channel
// m = vibrato double amplitude
// n = vibrato multiplier
// --------------------------------------------
void
Cu6mPlayer::command_6 (int channel)
{
  unsigned char vb_parameters;

  vb_parameters = read_song_byte ();
  vb_double_amplitude[channel] = vb_parameters >> 4;    // high nibble
  vb_multiplier[channel] = vb_parameters & 0xF; // low nibble
}


// ----------------------------------------
// Assign Adlib instrument to Adlib channel
// Format: 7c nn
// c = channel, nn = instrument number
// ----------------------------------------
void
Cu6mPlayer::command_7 (int channel)
{
  int instrument_offset = instrument_offsets[read_song_byte ()];
  out_adlib_opcell (channel, false, 0x20,
                    *(song_data + instrument_offset + 0));
  out_adlib_opcell (channel, false, 0x40,
                    *(song_data + instrument_offset + 1));
  out_adlib_opcell (channel, false, 0x60,
                    *(song_data + instrument_offset + 2));
  out_adlib_opcell (channel, false, 0x80,
                    *(song_data + instrument_offset + 3));
  out_adlib_opcell (channel, false, 0xE0,
                    *(song_data + instrument_offset + 4));
  out_adlib_opcell (channel, true, 0x20,
                    *(song_data + instrument_offset + 5));
  out_adlib_opcell (channel, true, 0x40,
                    *(song_data + instrument_offset + 6));
  out_adlib_opcell (channel, true, 0x60,
                    *(song_data + instrument_offset + 7));
  out_adlib_opcell (channel, true, 0x80,
                    *(song_data + instrument_offset + 8));
  out_adlib_opcell (channel, true, 0xE0,
                    *(song_data + instrument_offset + 9));
  out_adlib (0xC0 + channel, *(song_data + instrument_offset + 10));
}


// -------------------------------------------
// Branch to a new subsong
// Format: 81 nn aa bb
// nn == number of times to repeat the subsong
// aa == subsong offset (low byte)
// bb == subsong offset (high byte)
// -------------------------------------------
void
Cu6mPlayer::command_81 ()
{
  subsong_info new_ss_info;

  new_ss_info.subsong_repetitions = read_song_byte ();
  new_ss_info.subsong_start = read_song_byte ();
  new_ss_info.subsong_start += read_song_byte () << 8;
  new_ss_info.continue_pos = song_pos;

  subsong_stack.push (new_ss_info);
  song_pos = new_ss_info.subsong_start;
}


// ------------------------------------------------------------
// Stop interpreting commands for this timer tick
// Format: 82 nn
// nn == delay (in timer ticks) until further data will be read
// ------------------------------------------------------------
void
Cu6mPlayer::command_82 ()
{
  read_delay = read_song_byte ();
}


// -----------------------------
// Adlib instrument data follows
// Format: 83 nn <11 bytes>
// nn == instrument number
// -----------------------------
void
Cu6mPlayer::command_83 ()
{
  unsigned char instrument_number = read_song_byte ();
  instrument_offsets[instrument_number] = song_pos;
  song_pos += 11;
}


// ----------------------------------------------
// Set -1 mute factor slide (upward volume slide)
// Format: 85 cn
// c == channel
// n == slide delay
// ----------------------------------------------
void
Cu6mPlayer::command_85 ()
{
  unsigned char data_byte = read_song_byte ();
  int channel = data_byte >> 4; // high nibble
  unsigned char slide_delay = data_byte & 0xF;  // low nibble
  carrier_mf_signed_delta[channel] = +1;
  carrier_mf_mod_delay[channel] = slide_delay + 1;
  carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
}


// ------------------------------------------------
// Set +1 mute factor slide (downward volume slide)
// Format: 86 cn
// c == channel
// n == slide speed
// ------------------------------------------------
void
Cu6mPlayer::command_86 ()
{
  unsigned char data_byte = read_song_byte ();
  int channel = data_byte >> 4; // high nibble
  unsigned char slide_delay = data_byte & 0xF;  // low nibble
  carrier_mf_signed_delta[channel] = -1;
  carrier_mf_mod_delay[channel] = slide_delay + 1;
  carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
}


// --------------
// Set loop point
// Format: E?
// --------------
void
Cu6mPlayer::command_E ()
{
  loop_position = song_pos;
}


// ---------------------------
// Return from current subsong
// Format: F?
// ---------------------------
void
Cu6mPlayer::command_F ()
{
  if (!subsong_stack.empty ())
  {
    subsong_info temp = subsong_stack.top ();
    subsong_stack.pop ();
    temp.subsong_repetitions--;
    if (temp.subsong_repetitions == 0)
    {
      song_pos = temp.continue_pos;
    }
    else
    {
      song_pos = temp.subsong_start;
      subsong_stack.push (temp);
    }
  }
  else
  {
    song_pos = loop_position;
    songend = true;
  }
}


// --------------------
// Additional functions
// --------------------

// This function decrements its argument, without allowing it to become negative.
void
Cu6mPlayer::dec_clip (int &param)
{
  param--;
  if (param < 0)
  {
    param = 0;
  }
}


// Returns the byte at the current song position.
// Side effect: increments song_pos.
unsigned char
Cu6mPlayer::read_song_byte ()
{
  unsigned char song_byte;
  song_byte = song_data[song_pos];
  song_pos++;
  return (song_byte);
}


// Same as read_song_byte(), except that it returns a signed byte
signed char
Cu6mPlayer::read_signed_song_byte ()
{
  unsigned char song_byte;
  int signed_value;
  song_byte = *(song_data + song_pos);
  song_pos++;
  if (song_byte <= 127)
  {
    signed_value = song_byte;
  }
  else
  {
    signed_value = (int) song_byte - 0x100;
  }
  return ((signed char) signed_value);
}


Cu6mPlayer::byte_pair Cu6mPlayer::expand_freq_byte (unsigned char freq_byte)
{
  const byte_pair
    freq_table[24] = {
    {0x00, 0x00}, {0x58, 0x01}, {0x82, 0x01}, {0xB0, 0x01},
    {0xCC, 0x01}, {0x03, 0x02}, {0x41, 0x02}, {0x86, 0x02},
    {0x00, 0x00}, {0x6A, 0x01}, {0x96, 0x01}, {0xC7, 0x01},
    {0xE4, 0x01}, {0x1E, 0x02}, {0x5F, 0x02}, {0xA8, 0x02},
    {0x00, 0x00}, {0x47, 0x01}, {0x6E, 0x01}, {0x9A, 0x01},
    {0xB5, 0x01}, {0xE9, 0x01}, {0x24, 0x02}, {0x66, 0x02}
  };

  int
    packed_freq;
  int
    octave;
  byte_pair
    freq_word;

  packed_freq = freq_byte & 0x1F;
  octave = freq_byte >> 5;

  // range check (not present in the original U6 music driver)
  if (packed_freq >= 24)
  {
    packed_freq = 0;
  }

  freq_word.hi = freq_table[packed_freq].hi + (octave << 2);
  freq_word.lo = freq_table[packed_freq].lo;

  return (freq_word);
}


void
Cu6mPlayer::set_adlib_freq (int channel, Cu6mPlayer::byte_pair freq_word)
{
  out_adlib (0xA0 + channel, freq_word.lo);
  out_adlib (0xB0 + channel, freq_word.hi);
  // update the Adlib register backups
  channel_freq[channel] = freq_word;
}


// this function sets the Adlib frequency, but does not update the register backups
void
Cu6mPlayer::set_adlib_freq_no_update (int channel,
                                      Cu6mPlayer::byte_pair freq_word)
{
  out_adlib (0xA0 + channel, freq_word.lo);
  out_adlib (0xB0 + channel, freq_word.hi);
}


void
Cu6mPlayer::set_carrier_mf (int channel, unsigned char mute_factor)
{
  out_adlib_opcell (channel, true, 0x40, mute_factor);
  carrier_mf[channel] = mute_factor;
}


void
Cu6mPlayer::set_modulator_mf (int channel, unsigned char mute_factor)
{
  out_adlib_opcell (channel, false, 0x40, mute_factor);
}


void
Cu6mPlayer::freq_slide (int channel)
{
  byte_pair freq = channel_freq[channel];

  long freq_word =
    freq.lo + (freq.hi << 8) + channel_freq_signed_delta[channel];
  if (freq_word < 0)
  {
    freq_word += 0x10000;
  }
  if (freq_word > 0xFFFF)
  {
    freq_word -= 0x10000;
  }

  freq.lo = freq_word & 0xFF;
  freq.hi = (freq_word >> 8) & 0xFF;
  set_adlib_freq (channel, freq);
}


void
Cu6mPlayer::vibrato (int channel)
{
  byte_pair freq;

  if (vb_current_value[channel] >= vb_double_amplitude[channel])
  {
    vb_direction_flag[channel] = 1;
  }
  else if (vb_current_value[channel] <= 0)
  {
    vb_direction_flag[channel] = 0;
  }

  if (vb_direction_flag[channel] == 0)
  {
    vb_current_value[channel]++;
  }
  else
  {
    vb_current_value[channel]--;
  }

  long freq_word = channel_freq[channel].lo + (channel_freq[channel].hi << 8);
  freq_word +=
    (vb_current_value[channel] -
     (vb_double_amplitude[channel] >> 1)) * vb_multiplier[channel];
  if (freq_word < 0)
  {
    freq_word += 0x10000;
  }
  if (freq_word > 0xFFFF)
  {
    freq_word -= 0x10000;
  }

  freq.lo = freq_word & 0xFF;
  freq.hi = (freq_word >> 8) & 0xFF;
  set_adlib_freq_no_update (channel, freq);
}


void
Cu6mPlayer::mf_slide (int channel)
{
  carrier_mf_mod_delay[channel]--;
  if (carrier_mf_mod_delay[channel] == 0)
  {
    carrier_mf_mod_delay[channel] = carrier_mf_mod_delay_backup[channel];
    int current_mf = carrier_mf[channel] + carrier_mf_signed_delta[channel];
    if (current_mf > 0x3F)
    {
      current_mf = 0x3F;
      carrier_mf_signed_delta[channel] = 0;
    }
    else if (current_mf < 0)
    {
      current_mf = 0;
      carrier_mf_signed_delta[channel] = 0;
    }

    set_carrier_mf (channel, (unsigned char) current_mf);
  }
}


void
Cu6mPlayer::out_adlib (unsigned char adlib_register, unsigned char adlib_data)
{
  opl->write (adlib_register, adlib_data);
}


void
Cu6mPlayer::out_adlib_opcell (int channel, bool carrier,
                              unsigned char adlib_register,
                              unsigned char out_byte)
{
  const unsigned char adlib_channel_to_carrier_offset[9] =
    { 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15 };
  const unsigned char adlib_channel_to_modulator_offset[9] =
    { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12 };

  if (carrier)
  {
    out_adlib (adlib_register + adlib_channel_to_carrier_offset[channel],
               out_byte);
  }
  else
  {
    out_adlib (adlib_register + adlib_channel_to_modulator_offset[channel],
               out_byte);
  }
}


// ============================================================================================
//
//
//    The Dictionary
//
//
// ============================================================================================


Cu6mPlayer::MyDict::MyDict ()
{
  dict_size = default_dict_size;
  dictionary = new dict_entry[dict_size - 0x100];   // don't allocate space for the roots
  contains = 0x102;
}


Cu6mPlayer::MyDict::MyDict (int max_size)
{
  dict_size = max_size;
  dictionary = new dict_entry[dict_size - 0x100];   // don't allocate space for the roots
  contains = 0x102;
}


Cu6mPlayer::MyDict::~MyDict ()
{
  delete[]dictionary;
  dictionary = 0;
}

// re-initializes the dictionary
void
Cu6mPlayer::MyDict::reset ()
{
  contains = 0x102;
}


// Note: If the dictionary is already full, this function does nothing.
void
Cu6mPlayer::MyDict::add (unsigned char root, int codeword)
{
  if (contains < dict_size)
  {
    dictionary[contains - 0x100].root = root;
    dictionary[contains - 0x100].codeword = codeword;
    contains++;
  }
}


unsigned char
Cu6mPlayer::MyDict::get_root (int codeword)
{
  return (dictionary[codeword - 0x100].root);
}


int
Cu6mPlayer::MyDict::get_codeword (int codeword)
{
  return (dictionary[codeword - 0x100].codeword);
}
