#include "u6m.h"
#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(const std::string &filename, const CFileProvider &fp)
{
unsigned long filesize, decompressed_filesize;
binistream *f;
f = fp.open(filename); if(!f) return false;
filesize = fp.filesize(f);
if (filesize >= 6)
{
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);
}
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);
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;
delete[] song_data;
return(false);
}
delete[] compressed_song_data;
rewind(0);
return (true);
}
bool Cu6mPlayer::update()
{
if (!driver_active)
{
driver_active = true;
dec_clip(read_delay);
if (read_delay == 0)
{
command_loop();
}
for (int i = 0; i < 9; i++)
{
if (channel_freq_signed_delta[i]!=0)
{
freq_slide(i);
if (carrier_mf_signed_delta[i]!=0)
{
mf_slide(i);
}
}
else
{
if ((vb_multiplier[i]!=0) && ((channel_freq[i].hi & 0x20)==0x20))
{
vibrato(i);
}
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;
byte_pair freq_word = {0,0};
driver_active = false;
song_pos = 0;
loop_position = 0; read_delay = 0;
for (int i = 0; i < 9; i++)
{
channel_freq_signed_delta[i] = 0;
channel_freq[i] = freq_word;
vb_current_value[i] = 0;
vb_double_amplitude[i] = 0;
vb_multiplier[i] = 0;
vb_direction_flag[i] = 0;
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()) subsong_stack.pop();
opl->init();
out_adlib(1,32); }
float Cu6mPlayer::getrefresh()
{
return ((float)60); }
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)
{
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;
case 0x101:
end_marker_reached = true;
break;
default:
if (cW < next_free_codeword) {
get_string(cW,dictionary,root_stack);
C = root_stack.top();
while (!root_stack.empty())
{
SAVE_OUTPUT_ROOT(root_stack.top(), dest, bytes_written);
root_stack.pop();
}
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 {
get_string(pW,dictionary,root_stack);
C = root_stack.top();
while (!root_stack.empty())
{
SAVE_OUTPUT_ROOT(root_stack.top(), dest, bytes_written);
root_stack.pop();
}
SAVE_OUTPUT_ROOT(C, dest, bytes_written);
if (cW != next_free_codeword)
{
return false;
}
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;
}
pW = cW;
}
return(true); }
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; break;
}
bits_read += codeword_size;
return (codeword);
}
void Cu6mPlayer::output_root(unsigned char root, unsigned char *destination, long& position)
{
destination[position] = root;
position++;
}
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);
}
root_stack.push((unsigned char)current_codeword);
}
void Cu6mPlayer::command_loop()
{
unsigned char command_byte; int command_nibble_hi; int command_nibble_lo; bool repeat_loop = true;
do
{
command_byte = read_song_byte(); 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; }
break;
case 0xE: command_E(); break;
case 0xF: command_F(); break;
default: break; }
} while (repeat_loop);
}
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);
}
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; set_adlib_freq(channel,freq_word);
}
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; set_adlib_freq(channel,freq_word);
}
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);
}
void Cu6mPlayer::command_4(int channel)
{
unsigned char mf_byte;
mf_byte = read_song_byte();
set_modulator_mf(channel,mf_byte);
}
void Cu6mPlayer::command_5(int channel)
{
channel_freq_signed_delta[channel] = read_signed_song_byte();
}
void Cu6mPlayer::command_6(int channel)
{
unsigned char vb_parameters;
vb_parameters = read_song_byte();
vb_double_amplitude[channel] = vb_parameters >> 4; vb_multiplier[channel] = vb_parameters & 0xF; }
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));
}
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;
}
void Cu6mPlayer::command_82()
{
read_delay = read_song_byte();
}
void Cu6mPlayer::command_83()
{
unsigned char instrument_number = read_song_byte();
instrument_offsets[instrument_number] = song_pos;
song_pos += 11;
}
void Cu6mPlayer::command_85()
{
unsigned char data_byte = read_song_byte();
int channel = data_byte >> 4; unsigned char slide_delay = data_byte & 0xF; carrier_mf_signed_delta[channel] = +1;
carrier_mf_mod_delay[channel] = slide_delay + 1;
carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
}
void Cu6mPlayer::command_86()
{
unsigned char data_byte = read_song_byte();
int channel = data_byte >> 4; unsigned char slide_delay = data_byte & 0xF; carrier_mf_signed_delta[channel] = -1;
carrier_mf_mod_delay[channel] = slide_delay + 1;
carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
}
void Cu6mPlayer::command_E()
{
loop_position = song_pos;
}
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;
}
}
void Cu6mPlayer::dec_clip(int& param)
{
param--;
if (param < 0) { param = 0; }
}
unsigned char Cu6mPlayer::read_song_byte()
{
unsigned char song_byte;
song_byte = song_data[song_pos];
song_pos++;
return(song_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;
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);
channel_freq[channel] = freq_word;
}
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);
}
}
Cu6mPlayer::MyDict::MyDict()
{
dict_size = default_dict_size;
dictionary = new dict_entry[dict_size-0x100]; contains = 0x102;
}
Cu6mPlayer::MyDict::MyDict(int max_size)
{
dict_size = max_size;
dictionary = new dict_entry[dict_size-0x100]; contains = 0x102;
}
Cu6mPlayer::MyDict::~MyDict()
{
delete [] dictionary;
}
void Cu6mPlayer::MyDict::reset()
{
contains = 0x102;
}
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);
}