#include <cstring>
#include <inttypes.h>
#include <stdarg.h>
#include <assert.h>
#include <stdio.h>
#include "adl.h"
#include "debug.h"
#ifdef ADL_DEBUG
# define warning(...) AdPlug_LogWrite(__VA_ARGS__); \
AdPlug_LogWrite("\n")
# define debugC(i1, i2, ...) AdPlug_LogWrite(__VA_ARGS__); \
AdPlug_LogWrite("\n")
#else
# define kDebugLevelSound 1
static inline void warning(const char *str, ...)
{
}
static inline void debugC(int i1, int i2, const char *str, ...)
{
}
#endif
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
#define CALLBACKS_PER_SECOND 72
typedef uint8_t uint8;
typedef int8_t int8;
typedef uint16_t uint16;
typedef int16_t int16;
typedef uint32_t uint32;
typedef int32_t int32;
typedef uint8_t byte;
static inline uint16 READ_LE_UINT16(const void *ptr) {
const byte *b = (const byte *)ptr;
return (b[1] << 8) + b[0];
}
static inline uint16 READ_BE_UINT16(const void *ptr) {
const byte *b = (const byte *)ptr;
return (b[0] << 8) + b[1];
}
class AdlibDriver {
public:
AdlibDriver(Copl *opl);
~AdlibDriver();
int callback(int opcode, ...);
void callback();
bool isStereo() const { return false; }
bool endOfData() const { return false; }
struct OpcodeEntry {
typedef int (AdlibDriver::*DriverOpcode)(va_list &list);
DriverOpcode function;
const char *name;
};
void setupOpcodeList();
const OpcodeEntry *_opcodeList;
int _opcodesEntries;
int snd_ret0x100(va_list &list);
int snd_ret0x1983(va_list &list);
int snd_initDriver(va_list &list);
int snd_deinitDriver(va_list &list);
int snd_setSoundData(va_list &list);
int snd_unkOpcode1(va_list &list);
int snd_startSong(va_list &list);
int snd_unkOpcode2(va_list &list);
int snd_unkOpcode3(va_list &list);
int snd_readByte(va_list &list);
int snd_writeByte(va_list &list);
int snd_getSoundTrigger(va_list &list);
int snd_unkOpcode4(va_list &list);
int snd_dummy(va_list &list);
int snd_getNullvar4(va_list &list);
int snd_setNullvar3(va_list &list);
int snd_setFlag(va_list &list);
int snd_clearFlag(va_list &list);
struct Channel {
uint8 opExtraLevel2;
uint8 *dataptr;
uint8 duration;
uint8 repeatCounter;
int8 baseOctave;
uint8 priority;
uint8 dataptrStackPos;
uint8 *dataptrStack[4];
int8 baseNote;
uint8 unk29;
uint8 unk31;
uint16 unk30;
uint16 unk37;
uint8 unk33;
uint8 unk34;
uint8 unk35;
uint8 unk36;
uint8 unk32;
uint8 unk41;
uint8 unk38;
uint8 opExtraLevel1;
uint8 spacing2;
uint8 baseFreq;
uint8 tempo;
uint8 position;
uint8 regAx;
uint8 regBx;
typedef void (AdlibDriver::*Callback)(Channel&);
Callback primaryEffect;
Callback secondaryEffect;
uint8 fractionalSpacing;
uint8 opLevel1;
uint8 opLevel2;
uint8 opExtraLevel3;
uint8 twoChan;
uint8 unk39;
uint8 unk40;
uint8 spacing1;
uint8 durationRandomness;
uint8 unk19;
uint8 unk18;
int8 unk20;
int8 unk21;
uint8 unk22;
uint16 offset;
uint8 tempoReset;
uint8 rawNote;
int8 unk16;
};
void primaryEffect1(Channel &channel);
void primaryEffect2(Channel &channel);
void secondaryEffect1(Channel &channel);
void resetAdlibState();
void writeOPL(byte reg, byte val);
void initChannel(Channel &channel);
void noteOff(Channel &channel);
void unkOutput2(uint8 num);
uint16 getRandomNr();
void setupDuration(uint8 duration, Channel &channel);
void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
void setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel);
void noteOn(Channel &channel);
void adjustVolume(Channel &channel);
uint8 calculateOpLevel1(Channel &channel);
uint8 calculateOpLevel2(Channel &channel);
uint16 checkValue(int16 val) {
if (val < 0)
val = 0;
else if (val > 0x3F)
val = 0x3F;
return val;
}
uint8 *getProgram(int progId) {
return _soundData + READ_LE_UINT16(_soundData + 2 * progId);
}
uint8 *getInstrument(int instrumentId) {
uint16 instOffset = 0;
switch (ADLVer)
{
case 1:
instOffset = 150 * 2;
break;
case 2:
instOffset = 250 * 2;
break;
case 3:
instOffset = 500 * 2;
break;
}
return _soundData + READ_LE_UINT16(_soundData + instOffset + 2 * instrumentId);
}
void setupPrograms();
void executePrograms();
struct ParserOpcode {
typedef int (AdlibDriver::*POpcode)(uint8 *&dataptr, Channel &channel, uint8 value);
POpcode function;
const char *name;
};
void setupParserOpcodeTable();
const ParserOpcode *_parserOpcodeTable;
int _parserOpcodeTableSize;
int update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value);
int update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value);
int update_jump(uint8 *&dataptr, Channel &channel, uint8 value);
int update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value);
int update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value);
int update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value);
int update_playRest(uint8 *&dataptr, Channel &channel, uint8 value);
int update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
int update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value);
int update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
int update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value);
int update_playNote(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value);
int update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value);
int update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value);
int update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value);
int update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value);
int update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value);
int update_nop1(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value);
int update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value);
int update_nop2(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
int update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
int update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value);
int update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value);
int updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value);
uint8 ADLVer;
int32 _samplesPerCallback;
int32 _samplesPerCallbackRemainder;
int32 _samplesTillCallback;
int32 _samplesTillCallbackRemainder;
int _lastProcessed;
int8 _flagTrigger;
int _curChannel;
uint8 _soundTrigger;
int _soundsPlaying;
uint16 _rnd;
uint8 _unkValue1;
uint8 _unkValue2;
uint8 _unkValue3;
uint8 _unkValue4;
uint8 _unkValue5;
uint8 _unkValue6;
uint8 _unkValue7;
uint8 _unkValue8;
uint8 _unkValue9;
uint8 _unkValue10;
uint8 _unkValue11;
uint8 _unkValue12;
uint8 _unkValue13;
uint8 _unkValue14;
uint8 _unkValue15;
uint8 _unkValue16;
uint8 _unkValue17;
uint8 _unkValue18;
uint8 _unkValue19;
uint8 _unkValue20;
int _flags;
uint8 *_soundData;
uint8 _soundIdTable[0x10];
Channel _channels[10];
uint8 _vibratoAndAMDepthBits;
uint8 _rhythmSectionBits;
uint8 _curRegOffset;
uint8 _tempo;
const uint8 *_tablePtr1;
const uint8 *_tablePtr2;
static const uint8 _regOffset[];
static const uint16 _unkTable[];
static const uint8 *_unkTable2[];
static const uint8 _unkTable2_1[];
static const uint8 _unkTable2_2[];
static const uint8 _unkTable2_3[];
static const uint8 _unkTables[][32];
Copl *opl;
};
AdlibDriver::AdlibDriver(Copl *newopl)
: opl(newopl)
{
setupOpcodeList();
setupParserOpcodeTable();
ADLVer = 0;
_flags = 0;
memset(_channels, 0, sizeof(_channels));
_soundData = 0;
_vibratoAndAMDepthBits = _curRegOffset = 0;
_lastProcessed = _flagTrigger = _curChannel = _rhythmSectionBits = 0;
_soundsPlaying = 0;
_rnd = 0x1234;
_tempo = 0;
_soundTrigger = 0;
_unkValue3 = 0xFF;
_unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0;
_unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0;
_unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 =
_unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0;
_tablePtr1 = _tablePtr2 = 0;
_samplesTillCallback = 0;
_samplesTillCallbackRemainder = 0;
}
AdlibDriver::~AdlibDriver() {
}
int AdlibDriver::callback(int opcode, ...) {
if (opcode >= _opcodesEntries || opcode < 0) {
warning("AdlibDriver: calling unknown opcode '%d'", opcode);
return 0;
}
debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d)", _opcodeList[opcode].name, opcode);
va_list args;
va_start(args, opcode);
int returnValue = (this->*(_opcodeList[opcode].function))(args);
va_end(args);
return returnValue;
}
int AdlibDriver::snd_ret0x100(va_list &list) {
return 0x100;
}
int AdlibDriver::snd_ret0x1983(va_list &list) {
return 0x1983;
}
int AdlibDriver::snd_initDriver(va_list &list) {
_lastProcessed = _soundsPlaying = 0;
resetAdlibState();
return 0;
}
int AdlibDriver::snd_deinitDriver(va_list &list) {
resetAdlibState();
return 0;
}
int AdlibDriver::snd_setSoundData(va_list &list) {
if (_soundData) {
delete [] _soundData;
_soundData = 0;
}
_soundData = va_arg(list, uint8*);
return 0;
}
int AdlibDriver::snd_unkOpcode1(va_list &list) {
warning("unimplemented snd_unkOpcode1");
return 0;
}
int AdlibDriver::snd_startSong(va_list &list) {
int songId = va_arg(list, int);
_flags |= 8;
_flagTrigger = 1;
uint8 *ptr = getProgram(songId);
uint8 chan = *ptr;
if ((songId << 1) != 0) {
if (chan == 9) {
if (_flags & 2)
return 0;
} else {
if (_flags & 1)
return 0;
}
}
_soundIdTable[_soundsPlaying++] = songId;
_soundsPlaying &= 0x0F;
return 0;
}
int AdlibDriver::snd_unkOpcode2(va_list &list) {
warning("unimplemented snd_unkOpcode2");
return 0;
}
int AdlibDriver::snd_unkOpcode3(va_list &list) {
int value = va_arg(list, int);
int loop = value;
if (value < 0) {
value = 0;
loop = 9;
}
loop -= value;
++loop;
while (loop--) {
_curChannel = value;
Channel &channel = _channels[_curChannel];
channel.priority = 0;
channel.dataptr = 0;
if (value != 9) {
noteOff(channel);
}
++value;
}
return 0;
}
int AdlibDriver::snd_readByte(va_list &list) {
int a = va_arg(list, int);
int b = va_arg(list, int);
uint8 *ptr = getProgram(a) + b;
return *ptr;
}
int AdlibDriver::snd_writeByte(va_list &list) {
int a = va_arg(list, int);
int b = va_arg(list, int);
int c = va_arg(list, int);
uint8 *ptr = getProgram(a) + b;
uint8 oldValue = *ptr;
*ptr = (uint8)c;
return oldValue;
}
int AdlibDriver::snd_getSoundTrigger(va_list &list) {
return _soundTrigger;
}
int AdlibDriver::snd_unkOpcode4(va_list &list) {
warning("unimplemented snd_unkOpcode4");
return 0;
}
int AdlibDriver::snd_dummy(va_list &list) {
return 0;
}
int AdlibDriver::snd_getNullvar4(va_list &list) {
warning("unimplemented snd_getNullvar4");
return 0;
}
int AdlibDriver::snd_setNullvar3(va_list &list) {
warning("unimplemented snd_setNullvar3");
return 0;
}
int AdlibDriver::snd_setFlag(va_list &list) {
int oldFlags = _flags;
_flags |= va_arg(list, int);
return oldFlags;
}
int AdlibDriver::snd_clearFlag(va_list &list) {
int oldFlags = _flags;
_flags &= ~(va_arg(list, int));
return oldFlags;
}
void AdlibDriver::callback() {
--_flagTrigger;
if (_flagTrigger < 0)
_flags &= ~8;
setupPrograms();
executePrograms();
uint8 temp = _unkValue3;
_unkValue3 += _tempo;
if (_unkValue3 < temp) {
if (!(--_unkValue2)) {
_unkValue2 = _unkValue1;
++_unkValue4;
}
}
}
void AdlibDriver::setupPrograms() {
while (_lastProcessed != _soundsPlaying) {
uint8 *ptr = getProgram(_soundIdTable[_lastProcessed]);
uint8 chan = *ptr++;
uint8 priority = *ptr++;
Channel &channel = _channels[chan];
if (priority >= channel.priority) {
initChannel(channel);
channel.priority = priority;
channel.dataptr = ptr;
channel.tempo = 0xFF;
channel.position = 0xFF;
channel.duration = 1;
unkOutput2(chan);
}
++_lastProcessed;
_lastProcessed &= 0x0F;
}
}
void AdlibDriver::executePrograms() {
for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
int result = 1;
if (!_channels[_curChannel].dataptr) {
continue;
}
Channel &channel = _channels[_curChannel];
if (_curChannel != 9) {
_curRegOffset = _regOffset[_curChannel];
}
if (channel.tempoReset) {
channel.tempo = _tempo;
}
uint8 backup = channel.position;
channel.position += channel.tempo;
if (channel.position < backup) {
if (--channel.duration) {
if (channel.duration == channel.spacing2)
noteOff(channel);
if (channel.duration == channel.spacing1 && _curChannel != 9)
noteOff(channel);
} else {
uint8 *dataptr = channel.dataptr;
while (dataptr) {
uint8 opcode = *dataptr++;
uint8 param = *dataptr++;
if (opcode & 0x80) {
opcode &= 0x7F;
if (opcode >= _parserOpcodeTableSize)
opcode = _parserOpcodeTableSize - 1;
debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel);
result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param);
channel.dataptr = dataptr;
if (result)
break;
} else {
debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel);
setupNote(opcode, channel);
noteOn(channel);
setupDuration(param, channel);
if (param) {
channel.dataptr = dataptr;
break;
}
}
}
}
}
if (result == 1) {
if (channel.primaryEffect)
(this->*(channel.primaryEffect))(channel);
if (channel.secondaryEffect)
(this->*(channel.secondaryEffect))(channel);
}
}
}
void AdlibDriver::resetAdlibState() {
debugC(9, kDebugLevelSound, "resetAdlibState()");
_rnd = 0x1234;
writeOPL(0x01, 0x20);
writeOPL(0x08, 0x00);
writeOPL(0xBD, 0x00);
int loop = 10;
while (loop--) {
if (loop != 9) {
writeOPL(0x40 + _regOffset[loop], 0x3F);
writeOPL(0x43 + _regOffset[loop], 0x3F);
}
initChannel(_channels[loop]);
}
}
void AdlibDriver::writeOPL(byte reg, byte val) {
opl->write(reg, val);
}
void AdlibDriver::initChannel(Channel &channel) {
debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels));
memset(&channel.dataptr, 0, sizeof(Channel) - ((char*)&channel.dataptr - (char*)&channel));
channel.tempo = 0xFF;
channel.priority = 0;
channel.primaryEffect = 0;
channel.secondaryEffect = 0;
channel.spacing1 = 1;
}
void AdlibDriver::noteOff(Channel &channel) {
debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels));
if (_curChannel >= 9)
return;
if (_rhythmSectionBits && _curChannel >= 6)
return;
channel.regBx &= 0xDF;
writeOPL(0xB0 + _curChannel, channel.regBx);
}
void AdlibDriver::unkOutput2(uint8 chan) {
debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan);
if (chan >= 9)
return;
if (_rhythmSectionBits && chan >= 6)
return;
uint8 offset = _regOffset[chan];
writeOPL(0x60 + offset, 0xFF);
writeOPL(0x63 + offset, 0xFF);
writeOPL(0x80 + offset, 0xFF);
writeOPL(0x83 + offset, 0xFF);
writeOPL(0xB0 + chan, 0x00);
writeOPL(0xB0 + chan, 0x20);
}
uint16 AdlibDriver::getRandomNr() {
_rnd += 0x9248;
uint16 lowBits = _rnd & 7;
_rnd >>= 3;
_rnd |= (lowBits << 13);
return _rnd;
}
void AdlibDriver::setupDuration(uint8 duration, Channel &channel) {
debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels));
if (channel.durationRandomness) {
channel.duration = duration + (getRandomNr() & channel.durationRandomness);
return;
}
if (channel.fractionalSpacing) {
channel.spacing2 = (duration >> 3) * channel.fractionalSpacing;
}
channel.duration = duration;
}
void AdlibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) {
debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels));
channel.rawNote = rawNote;
int8 note = (rawNote & 0x0F) + channel.baseNote;
int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F;
if (note >= 12) {
note -= 12;
octave++;
} else if (note < 0) {
note += 12;
octave--;
}
uint16 freq = _unkTable[note] + channel.baseFreq;
if (channel.unk16 || flag) {
const uint8 *table;
if (channel.unk16 >= 0) {
table = _unkTables[(channel.rawNote & 0x0F) + 2];
freq += table[channel.unk16];
} else {
table = _unkTables[channel.rawNote & 0x0F];
freq -= table[-channel.unk16];
}
}
channel.regAx = freq & 0xFF;
channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03);
writeOPL(0xA0 + _curChannel, channel.regAx);
writeOPL(0xB0 + _curChannel, channel.regBx);
}
void AdlibDriver::setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel) {
debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels));
writeOPL(0x20 + regOffset, *dataptr++);
writeOPL(0x23 + regOffset, *dataptr++);
uint8 temp = *dataptr++;
writeOPL(0xC0 + _curChannel, temp);
channel.twoChan = temp & 1;
writeOPL(0xE0 + regOffset, *dataptr++);
writeOPL(0xE3 + regOffset, *dataptr++);
channel.opLevel1 = *dataptr++;
channel.opLevel2 = *dataptr++;
writeOPL(0x40 + regOffset, calculateOpLevel1(channel));
writeOPL(0x43 + regOffset, calculateOpLevel2(channel));
writeOPL(0x60 + regOffset, *dataptr++);
writeOPL(0x63 + regOffset, *dataptr++);
writeOPL(0x80 + regOffset, *dataptr++);
writeOPL(0x83 + regOffset, *dataptr++);
}
void AdlibDriver::noteOn(Channel &channel) {
debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels));
channel.regBx |= 0x20;
writeOPL(0xB0 + _curChannel, channel.regBx);
int8 shift = 9 - channel.unk33;
uint16 temp = channel.regAx | (channel.regBx << 8);
channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF;
channel.unk38 = channel.unk36;
}
void AdlibDriver::adjustVolume(Channel &channel) {
debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels));
writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel));
if (channel.twoChan)
writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel));
}
void AdlibDriver::primaryEffect1(Channel &channel) {
debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel);
uint8 temp = channel.unk31;
channel.unk31 += channel.unk29;
if (channel.unk31 >= temp)
return;
uint16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx;
uint16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C);
int16 unk3 = (int16)channel.unk30;
if (unk3 >= 0) {
unk1 += unk3;
if (unk1 >= 734) {
unk1 >>= 1;
if (!(unk1 & 0x3FF))
++unk1;
unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF);
unk2 &= 0xFF1C;
}
} else {
unk1 += unk3;
if (unk1 < 388) {
unk1 <<= 1;
if (!(unk1 & 0x3FF))
--unk1;
unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF);
unk2 &= 0xFF1C;
}
}
unk1 &= 0x3FF;
writeOPL(0xA0 + _curChannel, unk1 & 0xFF);
channel.regAx = unk1 & 0xFF;
uint8 value = unk1 >> 8;
value |= (unk2 >> 8) & 0xFF;
value |= unk2 & 0xFF;
writeOPL(0xB0 + _curChannel, value);
channel.regBx = value;
}
void AdlibDriver::primaryEffect2(Channel &channel) {
debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel);
if (channel.unk38) {
--channel.unk38;
return;
}
uint8 temp = channel.unk41;
channel.unk41 += channel.unk32;
if (channel.unk41 < temp) {
uint16 unk1 = channel.unk37;
if (!(--channel.unk34)) {
unk1 ^= 0xFFFF;
++unk1;
channel.unk37 = unk1;
channel.unk34 = channel.unk35;
}
uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF;
unk2 += unk1;
channel.regAx = unk2 & 0xFF;
channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8);
writeOPL(0xA0 + _curChannel, channel.regAx);
writeOPL(0xB0 + _curChannel, channel.regBx);
}
}
void AdlibDriver::secondaryEffect1(Channel &channel) {
debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel);
uint8 temp = channel.unk18;
channel.unk18 += channel.unk19;
if (channel.unk18 < temp) {
if (--channel.unk21 < 0) {
channel.unk21 = channel.unk20;
}
writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]);
}
}
uint8 AdlibDriver::calculateOpLevel1(Channel &channel) {
int8 value = channel.opLevel1 & 0x3F;
if (channel.twoChan) {
value += channel.opExtraLevel1;
value += channel.opExtraLevel2;
value += channel.opExtraLevel3;
}
return checkValue(value) | (channel.opLevel1 & 0xC0);
}
uint8 AdlibDriver::calculateOpLevel2(Channel &channel) {
int8 value = channel.opLevel2 & 0x3F;
value += channel.opExtraLevel1;
value += channel.opExtraLevel2;
value += channel.opExtraLevel3;
return checkValue(value) | (channel.opLevel2 & 0xC0);
}
int AdlibDriver::update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.repeatCounter = value;
return 0;
}
int AdlibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value) {
++dataptr;
if (--channel.repeatCounter) {
int16 add = READ_LE_UINT16(dataptr - 2);
dataptr += add;
}
return 0;
}
int AdlibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value) {
if (value == 0xFF)
return 0;
uint8 *ptr = getProgram(value);
uint8 chan = *ptr++;
uint8 priority = *ptr++;
Channel &channel2 = _channels[chan];
if (priority >= channel2.priority) {
_flagTrigger = 1;
_flags |= 8;
initChannel(channel2);
channel2.priority = priority;
channel2.dataptr = ptr;
channel2.tempo = 0xFF;
channel2.position = 0xFF;
channel2.duration = 1;
unkOutput2(chan);
}
return 0;
}
int AdlibDriver::update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.spacing1 = value;
return 0;
}
int AdlibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
dataptr += add;
return 0;
}
int AdlibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
int16 add = READ_LE_UINT16(dataptr); dataptr += 2;
channel.dataptrStack[channel.dataptrStackPos++] = dataptr;
dataptr += add;
return 0;
}
int AdlibDriver::update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) {
dataptr = channel.dataptrStack[--channel.dataptrStackPos];
return 0;
}
int AdlibDriver::update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.baseOctave = value;
return 0;
}
int AdlibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.priority = 0;
if (_curChannel != 9) {
noteOff(channel);
}
dataptr = 0;
return 2;
}
int AdlibDriver::update_playRest(uint8 *&dataptr, Channel &channel, uint8 value) {
setupDuration(value, channel);
noteOff(channel);
return (value != 0);
}
int AdlibDriver::update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value) {
writeOPL(value, *dataptr++);
return 0;
}
int AdlibDriver::update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value) {
setupNote(value, channel);
value = *dataptr++;
setupDuration(value, channel);
return (value != 0);
}
int AdlibDriver::update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.baseNote = value;
return 0;
}
int AdlibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.unk18 = value;
channel.unk19 = value;
channel.unk20 = channel.unk21 = *dataptr++;
channel.unk22 = *dataptr++;
channel.offset = READ_LE_UINT16(dataptr); dataptr += 2;
channel.secondaryEffect = &AdlibDriver::secondaryEffect1;
return 0;
}
int AdlibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value) {
Channel &channel2 = _channels[value];
channel2.duration = 0;
channel2.priority = 0;
channel2.dataptr = 0;
return 0;
}
int AdlibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value) {
uint8 *ptr = getProgram(value);
uint8 chan = *ptr;
if (!_channels[chan].dataptr) {
return 0;
}
dataptr -= 2;
return 2;
}
int AdlibDriver::update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value) {
setupInstrument(_curRegOffset, getInstrument(value), channel);
return 0;
}
int AdlibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.unk29 = value;
channel.unk30 = READ_BE_UINT16(dataptr);
dataptr += 2;
channel.primaryEffect = &AdlibDriver::primaryEffect1;
channel.unk31 = 0xFF;
return 0;
}
int AdlibDriver::update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
channel.primaryEffect = 0;
channel.unk30 = 0;
return 0;
}
int AdlibDriver::update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.baseFreq = value;
return 0;
}
int AdlibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.unk32 = value;
channel.unk33 = *dataptr++;
uint8 temp = *dataptr++;
channel.unk34 = temp + 1;
channel.unk35 = temp << 1;
channel.unk36 = *dataptr++;
channel.primaryEffect = &AdlibDriver::primaryEffect2;
return 0;
}
int AdlibDriver::update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.priority = value;
return 0;
}
int AdlibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value) {
value >>= 1;
_unkValue1 = _unkValue2 = value;
_unkValue3 = 0xFF;
_unkValue4 = _unkValue5 = 0;
return 0;
}
int AdlibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value) {
if (_unkValue5) {
if (_unkValue4 & value) {
_unkValue5 = 0;
return 0;
}
}
if (!(value & _unkValue4)) {
++_unkValue5;
}
dataptr -= 2;
channel.duration = 1;
return 2;
}
int AdlibDriver::update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.opExtraLevel1 = value;
adjustVolume(channel);
return 0;
}
int AdlibDriver::update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value) {
setupDuration(value, channel);
return (value != 0);
}
int AdlibDriver::update_playNote(uint8 *&dataptr, Channel &channel, uint8 value) {
setupDuration(value, channel);
noteOn(channel);
return (value != 0);
}
int AdlibDriver::update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.fractionalSpacing = value & 7;
return 0;
}
int AdlibDriver::update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
_tempo = value;
return 0;
}
int AdlibDriver::update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
channel.secondaryEffect = 0;
return 0;
}
int AdlibDriver::update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.tempo = value;
return 0;
}
int AdlibDriver::update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.opExtraLevel3 = value;
return 0;
}
int AdlibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) {
int channelBackUp = _curChannel;
_curChannel = value;
Channel &channel2 = _channels[value];
channel2.opExtraLevel2 = *dataptr++;
adjustVolume(channel2);
_curChannel = channelBackUp;
return 0;
}
int AdlibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) {
int channelBackUp = _curChannel;
_curChannel = value;
Channel &channel2 = _channels[value];
channel2.opExtraLevel2 += *dataptr++;
adjustVolume(channel2);
_curChannel = channelBackUp;
return 0;
}
int AdlibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value) {
if (value & 1)
_vibratoAndAMDepthBits |= 0x80;
else
_vibratoAndAMDepthBits &= 0x7F;
writeOPL(0xBD, _vibratoAndAMDepthBits);
return 0;
}
int AdlibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value) {
if (value & 1)
_vibratoAndAMDepthBits |= 0x40;
else
_vibratoAndAMDepthBits &= 0xBF;
writeOPL(0xBD, _vibratoAndAMDepthBits);
return 0;
}
int AdlibDriver::update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.opExtraLevel1 += value;
adjustVolume(channel);
return 0;
}
int AdlibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value) {
int channelBackUp = _curChannel;
_curChannel = value;
Channel &channel2 = _channels[value];
channel2.duration = channel2.priority = 0;
channel2.dataptr = 0;
channel2.opExtraLevel2 = 0;
if (value != 9) {
uint8 outValue = _regOffset[value];
writeOPL(0xC0 + _curChannel, 0x00);
writeOPL(0x43 + outValue, 0x3F);
writeOPL(0x83 + outValue, 0xFF);
writeOPL(0xB0 + _curChannel, 0x00);
}
_curChannel = channelBackUp;
return 0;
}
int AdlibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) {
uint16 unk = *dataptr++;
unk |= value << 8;
unk &= getRandomNr();
uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx;
unk2 += unk;
unk2 |= ((channel.regBx & 0x20) << 8);
writeOPL(0xA0 + _curChannel, unk2 & 0xFF);
writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8);
return 0;
}
int AdlibDriver::update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
channel.primaryEffect = 0;
return 0;
}
int AdlibDriver::updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.unk16 = value;
setupNote(channel.rawNote, channel, true);
return 0;
}
int AdlibDriver::update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
channel.tempo = _tempo;
return 0;
}
int AdlibDriver::update_nop1(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
return 0;
}
int AdlibDriver::update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.durationRandomness = value;
return 0;
}
int AdlibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) {
int tempo = channel.tempo + (int8)value;
if (tempo <= 0)
tempo = 1;
else if (tempo > 255)
tempo = 255;
channel.tempo = tempo;
return 0;
}
int AdlibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value) {
uint8 entry = *dataptr++;
_tablePtr1 = _unkTable2[entry++];
_tablePtr2 = _unkTable2[entry];
if (value == 2) {
writeOPL(0xA0, _tablePtr2[0]);
}
return 0;
}
int AdlibDriver::update_nop2(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
return 0;
}
int AdlibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
int channelBackUp = _curChannel;
int regOffsetBackUp = _curRegOffset;
_curChannel = 6;
_curRegOffset = _regOffset[6];
setupInstrument(_curRegOffset, getInstrument(value), channel);
_unkValue6 = channel.opLevel2;
_curChannel = 7;
_curRegOffset = _regOffset[7];
setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel);
_unkValue7 = channel.opLevel1;
_unkValue8 = channel.opLevel2;
_curChannel = 8;
_curRegOffset = _regOffset[8];
setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel);
_unkValue9 = channel.opLevel1;
_unkValue10 = channel.opLevel2;
_channels[6].regBx = *dataptr++ & 0x2F;
writeOPL(0xB6, _channels[6].regBx);
writeOPL(0xA6, *dataptr++);
_channels[7].regBx = *dataptr++ & 0x2F;
writeOPL(0xB7, _channels[7].regBx);
writeOPL(0xA7, *dataptr++);
_channels[8].regBx = *dataptr++ & 0x2F;
writeOPL(0xB8, _channels[8].regBx);
writeOPL(0xA8, *dataptr++);
_rhythmSectionBits = 0x20;
_curRegOffset = regOffsetBackUp;
_curChannel = channelBackUp;
return 0;
}
int AdlibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20);
_rhythmSectionBits |= value;
writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits);
return 0;
}
int AdlibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
--dataptr;
_rhythmSectionBits = 0;
writeOPL(0xBD, _vibratoAndAMDepthBits);
return 0;
}
int AdlibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) {
uint8 value2 = *dataptr++;
if (value & 1) {
_unkValue12 = value2;
writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12));
}
if (value & 2) {
_unkValue14 = value2;
writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14));
}
if (value & 4) {
_unkValue15 = value2;
writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15));
}
if (value & 8) {
_unkValue18 = value2;
writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18));
}
if (value & 16) {
_unkValue20 = value2;
writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20));
}
return 0;
}
int AdlibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value) {
uint8 value2 = *dataptr++;
if (value & 1) {
_unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12);
writeOPL(0x51, _unkValue11);
}
if (value & 2) {
_unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14);
writeOPL(0x55, _unkValue13);
}
if (value & 4) {
_unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15);
writeOPL(0x52, _unkValue16);
}
if (value & 8) {
_unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18);
writeOPL(0x54, _unkValue17);
}
if (value & 16) {
_unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20);
writeOPL(0x53, _unkValue19);
}
return 0;
}
int AdlibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) {
uint8 value2 = *dataptr++;
if (value & 1) {
_unkValue11 = value2;
writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12));
}
if (value & 2) {
_unkValue13 = value2;
writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14));
}
if (value & 4) {
_unkValue16 = value2;
writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15));
}
if (value & 8) {
_unkValue17 = value2;
writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18));
}
if (value & 16) {
_unkValue19 = value2;
writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20));
}
return 0;
}
int AdlibDriver::update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value) {
_soundTrigger = value;
return 0;
}
int AdlibDriver::update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.tempoReset = value;
return 0;
}
int AdlibDriver::updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value) {
channel.unk39 = value;
channel.unk40 = *dataptr++;
return 0;
}
#define COMMAND(x) { &AdlibDriver::x, #x }
void AdlibDriver::setupOpcodeList() {
static const OpcodeEntry opcodeList[] = {
COMMAND(snd_ret0x100),
COMMAND(snd_ret0x1983),
COMMAND(snd_initDriver),
COMMAND(snd_deinitDriver),
COMMAND(snd_setSoundData),
COMMAND(snd_unkOpcode1),
COMMAND(snd_startSong),
COMMAND(snd_unkOpcode2),
COMMAND(snd_unkOpcode3),
COMMAND(snd_readByte),
COMMAND(snd_writeByte),
COMMAND(snd_getSoundTrigger),
COMMAND(snd_unkOpcode4),
COMMAND(snd_dummy),
COMMAND(snd_getNullvar4),
COMMAND(snd_setNullvar3),
COMMAND(snd_setFlag),
COMMAND(snd_clearFlag)
};
_opcodeList = opcodeList;
_opcodesEntries = ARRAYSIZE(opcodeList);
}
void AdlibDriver::setupParserOpcodeTable() {
static const ParserOpcode parserOpcodeTable[] = {
COMMAND(update_setRepeat),
COMMAND(update_checkRepeat),
COMMAND(update_setupProgram),
COMMAND(update_setNoteSpacing),
COMMAND(update_jump),
COMMAND(update_jumpToSubroutine),
COMMAND(update_returnFromSubroutine),
COMMAND(update_setBaseOctave),
COMMAND(update_stopChannel),
COMMAND(update_playRest),
COMMAND(update_writeAdlib),
COMMAND(update_setupNoteAndDuration),
COMMAND(update_setBaseNote),
COMMAND(update_setupSecondaryEffect1),
COMMAND(update_stopOtherChannel),
COMMAND(update_waitForEndOfProgram),
COMMAND(update_setupInstrument),
COMMAND(update_setupPrimaryEffect1),
COMMAND(update_removePrimaryEffect1),
COMMAND(update_setBaseFreq),
COMMAND(update_stopChannel),
COMMAND(update_setupPrimaryEffect2),
COMMAND(update_stopChannel),
COMMAND(update_stopChannel),
COMMAND(update_stopChannel),
COMMAND(update_stopChannel),
COMMAND(update_setPriority),
COMMAND(update_stopChannel),
COMMAND(updateCallback23),
COMMAND(updateCallback24),
COMMAND(update_setExtraLevel1),
COMMAND(update_stopChannel),
COMMAND(update_setupDuration),
COMMAND(update_playNote),
COMMAND(update_stopChannel),
COMMAND(update_stopChannel),
COMMAND(update_setFractionalNoteSpacing),
COMMAND(update_stopChannel),
COMMAND(update_setTempo),
COMMAND(update_removeSecondaryEffect1),
COMMAND(update_stopChannel),
COMMAND(update_setChannelTempo),
COMMAND(update_stopChannel),
COMMAND(update_setExtraLevel3),
COMMAND(update_setExtraLevel2),
COMMAND(update_changeExtraLevel2),
COMMAND(update_setAMDepth),
COMMAND(update_setVibratoDepth),
COMMAND(update_changeExtraLevel1),
COMMAND(update_stopChannel),
COMMAND(update_stopChannel),
COMMAND(updateCallback38),
COMMAND(update_stopChannel),
COMMAND(updateCallback39),
COMMAND(update_removePrimaryEffect2),
COMMAND(update_stopChannel),
COMMAND(update_stopChannel),
COMMAND(updateCallback41),
COMMAND(update_resetToGlobalTempo),
COMMAND(update_nop1),
COMMAND(update_setDurationRandomness),
COMMAND(update_changeChannelTempo),
COMMAND(update_stopChannel),
COMMAND(updateCallback46),
COMMAND(update_nop2),
COMMAND(update_setupRhythmSection),
COMMAND(update_playRhythmSection),
COMMAND(update_removeRhythmSection),
COMMAND(updateCallback51),
COMMAND(updateCallback52),
COMMAND(updateCallback53),
COMMAND(update_setSoundTrigger),
COMMAND(update_setTempoReset),
COMMAND(updateCallback56),
COMMAND(update_stopChannel)
};
_parserOpcodeTable = parserOpcodeTable;
_parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable);
}
#undef COMMAND
const uint8 AdlibDriver::_regOffset[] = {
0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11,
0x12
};
const uint16 AdlibDriver::_unkTable[] = {
0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9,
0x0207, 0x0225, 0x0246
};
const uint8 *AdlibDriver::_unkTable2[] = {
AdlibDriver::_unkTable2_1,
AdlibDriver::_unkTable2_2,
AdlibDriver::_unkTable2_1,
AdlibDriver::_unkTable2_2,
AdlibDriver::_unkTable2_3,
AdlibDriver::_unkTable2_2
};
const uint8 AdlibDriver::_unkTable2_1[] = {
0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D,
0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49,
0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45,
0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41,
0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D,
0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39,
0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35,
0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31,
0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D,
0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29,
0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25,
0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21,
0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D,
0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19,
0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15,
0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
0x10, 0x10
};
const uint8 AdlibDriver::_unkTable2_2[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F
};
const uint8 AdlibDriver::_unkTable2_3[] = {
0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E,
0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B,
0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39,
0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36,
0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33,
0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E,
0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B,
0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29,
0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26,
0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23,
0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21,
0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E,
0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B,
0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19,
0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16,
0x16, 0x15
};
const uint8 AdlibDriver::_unkTables[][32] = {
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19,
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A,
0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09,
0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A,
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A },
{ 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D },
{ 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15,
0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24,
0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 },
{ 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C,
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18,
0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25,
0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 },
{ 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D,
0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A,
0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28,
0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 },
{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B,
0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29,
0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 },
{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E,
0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E,
0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D,
0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C },
{ 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F,
0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E,
0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E,
0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F },
{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10,
0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21,
0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32,
0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 },
{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11,
0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23,
0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35,
0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 }
};
const int CadlPlayer::_kyra1SoundTriggers[] = {
0, 4, 5, 3
};
const int CadlPlayer::_kyra1NumSoundTriggers = ARRAYSIZE(CadlPlayer::_kyra1SoundTriggers);
CadlPlayer::CadlPlayer(Copl *newopl)
: CPlayer(newopl), numsubsongs(0), _trackEntries(), _trackEntries16(), _soundDataPtr(0)
{
_version = 0;
memset(_trackEntries, 0, sizeof(_trackEntries));
memset(_trackEntries16, 0, sizeof(_trackEntries16));
_driver = new AdlibDriver(newopl);
assert(_driver);
_sfxPlayingSound = -1;
_soundTriggers = _kyra1SoundTriggers;
_numSoundTriggers = _kyra1NumSoundTriggers;
}
CadlPlayer::~CadlPlayer() {
delete [] _soundDataPtr;
delete _driver;
}
bool CadlPlayer::init() {
_driver->callback(2);
_driver->callback(16, int(4));
return true;
}
void CadlPlayer::process() {
uint8 trigger = _driver->callback(11);
if (trigger < _numSoundTriggers) {
int soundId = _soundTriggers[trigger];
if (soundId) {
playTrack(soundId);
}
} else {
warning("Unknown sound trigger %d", trigger);
}
}
void CadlPlayer::playTrack(uint16_t track) {
play(track);
}
void CadlPlayer::playSoundEffect(uint16_t track) {
play(track);
}
void CadlPlayer::play(uint16_t track) {
uint16 soundId = 0;
if (_version < 3)
{
soundId = _trackEntries[track];
if ((int8)soundId == -1 || !_soundDataPtr)
return;
soundId &= 0xFF;
}
else
{
soundId = _trackEntries16[track];
if ((int16)soundId == -1 || !_soundDataPtr)
return;
soundId &= 0xFFFF;
}
_driver->ADLVer = _version;
_driver->callback(16, 0);
if (_sfxPlayingSound != -1) {
_driver->callback(10, _sfxPlayingSound, int(1), int(_sfxPriority));
_driver->callback(10, _sfxPlayingSound, int(3), int(_sfxFourthByteOfSong));
_sfxPlayingSound = -1;
}
if (READ_LE_UINT16(_soundDataPtr + 2 * soundId) == 0xffff)
return;
int chan = _driver->callback(9, soundId, int(0));
if (chan != 9) {
_sfxPlayingSound = soundId;
_sfxPriority = _driver->callback(9, soundId, int(1));
_sfxFourthByteOfSong = _driver->callback(9, soundId, int(3));
int newVal = ((((-_sfxFourthByteOfSong) + 63) * 0xFF) >> 8) & 0xFF;
newVal = -newVal + 63;
_driver->callback(10, soundId, int(3), newVal);
newVal = ((_sfxPriority * 0xFF) >> 8) & 0xFF;
_driver->callback(10, soundId, int(1), newVal);
}
_driver->callback(6, soundId);
}
bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp)
{
binistream *f = fp.open(filename);
if(!f || !fp.extension(filename, ".adl")) {
fp.close(f);
return false;
}
if (fp.filesize(f) < 720)
{ fp.close(f);
return false;
}
uint8 *file_data = 0; uint32 file_size = 0;
unk2();
unk1();
_version = 3; for (int i = 0; i < 120; i += 2)
{
uint16_t w = f->readInt(2);
if (w >= 500 && w < 0xffff)
{
_version = 1; break;
}
}
if (_version == 1)
{ f->seek(120);
_version = 2; for (int i = 0; i < 150; i += 2)
{
uint16_t w = f->readInt(2);
if (w > 0 && w < 600)
{ fp.close(f);
return false;
}
if (w > 0 && w < 1000)
_version = 1;
}
}
if (_version == 2 && fp.filesize(f) < 1120)
{ fp.close(f);
return false;
}
if (_version == 3 && fp.filesize(f) < 2500)
{ fp.close(f);
return false;
}
f->seek(0);
file_size = fp.filesize(f);
file_data = new uint8 [file_size];
f->readString((char *)file_data, file_size);
_soundDataPtr = 0;
uint8 *p = file_data;
uint16 _EntriesSize;
if (_version < 3)
{
_EntriesSize = 120 * sizeof(uint8);
memcpy(_trackEntries, p, _EntriesSize);
}
else
{
_EntriesSize = 250 * sizeof(uint16);
memcpy(_trackEntries16, p, _EntriesSize);
}
p += _EntriesSize;
int soundDataSize = file_size - _EntriesSize;
_soundDataPtr = new uint8[soundDataSize];
assert(_soundDataPtr);
memcpy(_soundDataPtr, p, soundDataSize*sizeof(uint8));
delete [] file_data;
file_data = p = 0;
file_size = 0;
_driver->callback(4, _soundDataPtr);
uint16_t maxEntry = 0xffff;
switch (_version)
{
case 1:
maxEntry = 150 - 1;
break;
case 2:
maxEntry = 250 - 1;
break;
case 3:
maxEntry = 500 - 1;
break;
}
if (_version < 3)
{
for (int i = 120-1; i >= 0; i--)
if (_trackEntries[i] <= maxEntry) {
numsubsongs = i + 1;
break;
}
}
else
{
for (int i = 250-1; i >= 0; i--)
if (_trackEntries16[i] <= maxEntry) {
numsubsongs = i + 1;
break;
}
}
fp.close(f);
rewind(-1);
return true;
}
void CadlPlayer::rewind(int subsong)
{
if(subsong == -1)
{
init();
_driver->callback(8, int(-1));
opl->init();
opl->write(1,32);
subsong = 2;
}
cursubsong = subsong;
loadcursubsong = true;
}
unsigned int CadlPlayer::getsubsongs()
{
return numsubsongs;
}
bool CadlPlayer::update()
{
if (loadcursubsong)
{
loadcursubsong = false;
playSoundEffect(cursubsong);
}
bool songend = true;
_driver->callback();
for(int i = 0; i < 10; i++)
if(_driver->_channels[i].dataptr != NULL)
songend = false;
return !songend;
}
void CadlPlayer::unk1() {
playSoundEffect(0);
}
void CadlPlayer::unk2() {
playSoundEffect(0);
}
std::string CadlPlayer::gettype()
{
char tmpstr[25];
sprintf(tmpstr, "Westwood ADL (version %d)", _version);
return std::string(tmpstr);
}
CPlayer *CadlPlayer::factory(Copl *newopl)
{
return new CadlPlayer(newopl);
}