/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program 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 General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * LGPL License
 *
 * 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
 *
 */

#include "sound_adlib.h"

#include <algorithm>
#include <stdarg.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef __USE_SDL__
#include <SDL_mixer.h>
#include <SDL_endian.h>
#else
#include <allegro5/allegro.h>
#include <allegro5/allegro_audio.h>
#define AUDIO_S8	ALLEGRO_AUDIO_DEPTH_INT8
#define AUDIO_U8	ALLEGRO_AUDIO_DEPTH_UINT8
#define AUDIO_S16LSB	ALLEGRO_AUDIO_DEPTH_INT16
#endif


#include "fmopl.h"

// Basic AdLib Programming:
// http://www.gamedev.net/reference/articles/article446.asp

#define CALLBACKS_PER_SECOND 72

#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))


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;

const int kDebugLevelSound = 0;

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];
}

static inline void warning(const char *str, ...)
{
	va_list args;
	va_start(args, str);
	vfprintf(stderr,str,args);
	fprintf(stderr,"\n");
}

static inline void debugC(int level, int debugChannels, const char *str, ...)
{
/*
	va_list args;
	va_start(args, str);
	vfprintf(stderr,str,args);
	fprintf(stderr,"\n");
*/
}


class AdLibDriver {
public:
	AdLibDriver(int rate, bool bMAME);
	~AdLibDriver();

	int callback(int opcode, ...);
	void callback();

	// AudioStream API
	int readBuffer(int16 *buffer, const int numSamples) {
		int32 samplesLeft = numSamples;
		memset(buffer, 0, sizeof(int16) * numSamples);
		while (samplesLeft) {
			if (!_samplesTillCallback) {
				callback();
				_samplesTillCallback = _samplesPerCallback;
				_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
				if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) {
					_samplesTillCallback++;
					_samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND;
				}
			}

			int32 render = std::min(samplesLeft, _samplesTillCallback);
			samplesLeft -= render;
			_samplesTillCallback -= render;
			YM3812UpdateOne(_adlib, buffer, render);
			buffer += render;
		}
		return numSamples;
	}

	bool isStereo() const { return false; }
	bool endOfData() const { return false; }
	int getRate() const { return _rate; }

	void setSyncJumpMask(uint16 mask) { _syncJumpMask = mask; }

private:
	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_isChannelPlaying(va_list &list);
	int snd_stopChannel(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);

	// These variables have not yet been named, but some of them are partly
	// known nevertheless:
	//
	// unk16 - Sound-related. Possibly some sort of pitch bend.
	// unk18 - Sound-effect. Used for secondaryEffect1()
	// unk19 - Sound-effect. Used for secondaryEffect1()
	// unk20 - Sound-effect. Used for secondaryEffect1()
	// unk21 - Sound-effect. Used for secondaryEffect1()
	// unk22 - Sound-effect. Used for secondaryEffect1()
	// unk29 - Sound-effect. Used for primaryEffect1()
	// unk30 - Sound-effect. Used for primaryEffect1()
	// unk31 - Sound-effect. Used for primaryEffect1()
	// unk32 - Sound-effect. Used for primaryEffect2()
	// unk33 - Sound-effect. Used for primaryEffect2()
	// unk34 - Sound-effect. Used for primaryEffect2()
	// unk35 - Sound-effect. Used for primaryEffect2()
	// unk36 - Sound-effect. Used for primaryEffect2()
	// unk37 - Sound-effect. Used for primaryEffect2()
	// unk38 - Sound-effect. Used for primaryEffect2()
	// unk39 - Currently unused, except for updateCallback56()
	// unk40 - Currently unused, except for updateCallback56()
	// unk41 - Sound-effect. Used for primaryEffect2()

	struct Channel {
		bool lock;	// New to ScummVM
		uint8 opExtraLevel2;
		uint8 *dataptr;
		uint8 duration;
		uint8 repeatCounter;
		uint8 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;
	}

	// The sound data has at least two lookup tables:
	//
	// * One for programs, starting at offset 0.
	// * One for instruments, starting at offset 500.

	uint8 *getProgram(int progId) {
		uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
		//TODO: Check in LoL CD AdLib driver
		if (offset == 0xFFFF)
			return 0;
		return _soundData + READ_LE_UINT16(_soundData + 2 * progId);
	}

	uint8 *getInstrument(int instrumentId) {
		unsigned short tmp = READ_LE_UINT16(_soundData + (_v2 ? 1000 : 500) + 2 * instrumentId);
		if(tmp == 0xFFFF) {
		   return NULL;
		} else {
		   return _soundData + tmp;
		}
	}

	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_nop(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_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);
private:
	// These variables have not yet been named, but some of them are partly
	// known nevertheless:
	//
	// _unkValue1      - Unknown. Used for updating _unkValue2
	// _unkValue2      - Unknown. Used for updating _unkValue4
	// _unkValue4      - Unknown. Used for updating _unkValue5
	// _unkValue5      - Unknown. Used for controlling updateCallback24().
	// _unkValue6      - Unknown. Rhythm section volume?
	// _unkValue7      - Unknown. Rhythm section volume?
	// _unkValue8      - Unknown. Rhythm section volume?
	// _unkValue9      - Unknown. Rhythm section volume?
	// _unkValue10     - Unknown. Rhythm section volume?
	// _unkValue11     - Unknown. Rhythm section volume?
	// _unkValue12     - Unknown. Rhythm section volume?
	// _unkValue13     - Unknown. Rhythm section volume?
	// _unkValue14     - Unknown. Rhythm section volume?
	// _unkValue15     - Unknown. Rhythm section volume?
	// _unkValue16     - Unknown. Rhythm section volume?
	// _unkValue17     - Unknown. Rhythm section volume?
	// _unkValue18     - Unknown. Rhythm section volume?
	// _unkValue19     - Unknown. Rhythm section volume?
	// _unkValue20     - Unknown. Rhythm section volume?
	// _unkTable[]     - Probably frequences for the 12-tone scale.
	// _unkTable2[]    - Unknown. Currently only used by updateCallback46()
	// _unkTable2_1[]  - One of the tables in _unkTable2[]
	// _unkTable2_2[]  - One of the tables in _unkTable2[]
	// _unkTable2_3[]  - One of the tables in _unkTable2[]

	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 _callbackTimer;
	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;
	FM_OPL *_adlib;
	int _rate;

	uint8 *_soundData;

	uint8 _soundIdTable[0x10];
public:
	Channel _channels[10];

private:
	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 *const _unkTable2[];
	static const uint8 _unkTable2_1[];
	static const uint8 _unkTable2_2[];
	static const uint8 _unkTable2_3[];
	static const uint8 _unkTables[][32];

	uint16 _syncJumpMask;

	bool _v2;

	void lock() {  }
	void unlock() {  }
};

AdLibDriver::AdLibDriver(int rate, bool bMAME) {
	setupOpcodeList();
	setupParserOpcodeTable();

	_v2 = false;

	_rate = rate;

	_flags = 0;
	_adlib = makeAdLibOPL(_rate, bMAME);
	assert(_adlib);

	memset(_channels, 0, sizeof(_channels));
	_soundData = 0;

	_vibratoAndAMDepthBits = _curRegOffset = 0;

	_lastProcessed = _flagTrigger = _curChannel = _rhythmSectionBits = 0;
	_soundsPlaying = 0;
	_rnd = 0x1234;

	_tempo = 0;
	_soundTrigger = 0;

	_callbackTimer = 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;

	// HACK: We use MusicSoundType here for now so we can adjust the volume in the launcher dialog.
	// This affects SFX too, but if we want to support different volumes for SFX and music we would
	// have to change our player implementation, currently we setup the volume for an AdLib channel
	// in AdLibDriver::adjustVolume, so if that would be called, we would have to know if the channel
	// is used by SFX or music, and then adjust the volume accordingly. Since Kyrandia 2 supports
	// different volumes for SFX and music, looking at the disasm and checking how the original does it
	// would be a good idea.
	//_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);

	_samplesPerCallback = getRate() / CALLBACKS_PER_SECOND;
	_samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND;
	_samplesTillCallback = 0;
	_samplesTillCallbackRemainder = 0;

	_syncJumpMask = 0;
}

AdLibDriver::~AdLibDriver() {
	OPLDestroy(_adlib);
	_adlib = 0;
}

int AdLibDriver::callback(int opcode, ...) {
	lock();
	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);
	unlock();
	return returnValue;
}

// Opcodes

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);
	if(ptr == NULL)
		return 0;

	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_isChannelPlaying(va_list &list) {
	int channel = va_arg(list, int);
	assert(channel >= 0 && channel <= 9);
	return (_channels[channel].dataptr != 0) ? 1 : 0;
}

int AdLibDriver::snd_stopChannel(va_list &list) {
	int channel = va_arg(list, int);

	int maxChannel;
	if (channel < 0) {
		channel = 0;
		maxChannel = 9;
	} else {
		maxChannel = channel;
	}

	for (; channel <= maxChannel; ++channel) {
		_curChannel = channel;

		Channel &chan = _channels[_curChannel];
		chan.priority = 0;
		chan.dataptr = 0;

		if (channel != 9)
			noteOff(chan);
	}

	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;
	if(ptr == NULL) {
		warning("AdLibDriver::snd_readByte(): ptr == NULL!");
		return -1;
	}
	assert(ptr);
	return *ptr;
}

int AdLibDriver::snd_writeByte(va_list &list) {
	int a = va_arg(list, int);
	int b = va_arg(list, int);
	uint8 value = va_arg(list, int);
	uint8 *ptr = getProgram(a) + b;
	assert(ptr);
#if 0
	SWAP(*ptr, value);
	return value;
#else
	uint8 oldValue = *ptr;
	*ptr = (uint8)value;
	return oldValue;
#endif
}

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;
}

// timer callback

void AdLibDriver::callback() {
	lock();
	--_flagTrigger;
	if (_flagTrigger < 0)
		_flags &= ~8;
	setupPrograms();
	executePrograms();

	uint8 temp = _callbackTimer;
	_callbackTimer += _tempo;
	if (_callbackTimer < temp) {
		if (!(--_unkValue2)) {
			_unkValue2 = _unkValue1;
			++_unkValue4;
		}
	}
	unlock();
}

void AdLibDriver::setupPrograms() {
	while (_lastProcessed != _soundsPlaying) {
		uint8 *ptr = getProgram(_soundIdTable[_lastProcessed]);
		uint8 chan = *ptr++;
		uint8 priority = *ptr++;

		// Only start this sound if its priority is higher than the one
		// already playing.

		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);
		}

		// What we have set up now is, probably, the controlling
		// channel for the sound. It is assumed that this program will
		// set up all the other channels it needs, clearing their locks
		// along the way.

		++_lastProcessed;
		_lastProcessed &= 0x0F;
	}
}

// A few words on opcode parsing and timing:
//
// First of all, We simulate a timer callback 72 times per second. Each timeout
// we update each channel that has something to play.
//
// Each channel has its own individual tempo, which is added to its position.
// This will frequently cause the position to "wrap around" but that is
// intentional. In fact, it's the signal to go ahead and do more stuff with
// that channel.
//
// Each channel also has a duration, indicating how much time is left on the
// its current task. This duration is decreased by one. As long as it still has
// not reached zero, the only thing that can happen is that the note is turned
// off depending on manual or automatic note spacing. Once the duration reaches
// zero, a new set of musical opcodes are executed.
//
// An opcode is one byte, followed by a variable number of parameters. Since
// most opcodes have at least one one-byte parameter, we read that as well. Any
// opcode that doesn't have that one parameter is responsible for moving the
// data pointer back again.
//
// If the most significant bit of the opcode is 1, it's a function; call it.
// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do
// not run the effects callbacks).
//
// If the most significant bit of the opcode is 0, it's a note, and the first
// parameter is its duration. (There are cases where the duration is modified
// but that's an exception.) The note opcode is assumed to return 1, and is the
// last opcode unless its duration is zero.
//
// Finally, most of the times that the callback is called, it will invoke the
// effects callbacks. The final opcode in a set can prevent this, if it's a
// function and it returns anything other than 1.

void AdLibDriver::executePrograms() {
	// Each channel runs its own program. There are ten channels: One for
	// each AdLib channel (0-8), plus one "control channel" (9) which is
	// the one that tells the other channels what to do.

	// This is where we ensure that channels that are made to jump "in
	// sync" do so.

	if (_syncJumpMask) {
		bool forceUnlock = true;

		for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
			if ((_syncJumpMask & (1 << _curChannel)) == 0)
				continue;

			if (_channels[_curChannel].dataptr && !_channels[_curChannel].lock)
				forceUnlock = false;
		}

		if (forceUnlock) {
			for (_curChannel = 9; _curChannel >= 0; --_curChannel)
				if (_syncJumpMask & (1 << _curChannel))
					_channels[_curChannel].lock = false;
		}
	}

	for (_curChannel = 9; _curChannel >= 0; --_curChannel) {
		int result = 1;

		if (!_channels[_curChannel].dataptr)
			continue;

		if (_channels[_curChannel].lock && (_syncJumpMask & (1 << _curChannel)))
			continue;

		Channel &channel = _channels[_curChannel];
		if (_curChannel == 9)
			_curRegOffset = 0;
		else
			_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 {
				// An opcode is not allowed to modify its own
				// data pointer except through the 'dataptr'
				// parameter. To enforce that, we have to work
				// on a copy of the data pointer.
				//
				// This fixes a subtle music bug where the
				// wrong music would play when getting the
				// quill in Kyra 1.
				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) {
							// We need to make sure we are always running the
							// effects after this. Otherwise some sounds are
							// wrong. Like the sfx when bumping into a wall in
							// LoL.
							result = 1;
							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;

	// Authorize the control of the waveforms
	writeOPL(0x01, 0x20);

	// Select FM music mode
	writeOPL(0x08, 0x00);

	// I would guess the main purpose of this is to turn off the rhythm,
	// thus allowing us to use 9 melodic voices instead of 6.
	writeOPL(0xBD, 0x00);

	int loop = 10;
	while (loop--) {
		if (loop != 9) {
			// Silence the channel
			writeOPL(0x40 + _regOffset[loop], 0x3F);
			writeOPL(0x43 + _regOffset[loop], 0x3F);
		}
		initChannel(_channels[loop]);
	}
}

// Old calling style: output0x388(0xABCD)
// New calling style: writeOPL(0xAB, 0xCD)

void AdLibDriver::writeOPL(byte reg, byte val) {
	OPLWriteReg(_adlib, 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;
	// normally here are nullfuncs but we set 0 for now
	channel.primaryEffect = 0;
	channel.secondaryEffect = 0;
	channel.spacing1 = 1;
	channel.lock = false;
}

void AdLibDriver::noteOff(Channel &channel) {
	debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels));

	// The control channel has no corresponding AdLib channel

	if (_curChannel >= 9)
		return;

	// When the rhythm section is enabled, channels 6, 7 and 8 are special.

	if (_rhythmSectionBits && _curChannel >= 6)
		return;

	// This means the "Key On" bit will always be 0
	channel.regBx &= 0xDF;

	// Octave / F-Number / Key-On
	writeOPL(0xB0 + _curChannel, channel.regBx);
}

void AdLibDriver::unkOutput2(uint8 chan) {
	debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan);

	// The control channel has no corresponding AdLib channel

	if (chan >= 9)
		return;

	// I believe this has to do with channels 6, 7, and 8 being special
	// when AdLib's rhythm section is enabled.

	if (_rhythmSectionBits && chan >= 6)
		return;

	uint8 offset = _regOffset[chan];

	// The channel is cleared: First the attack/delay rate, then the
	// sustain level/release rate, and finally the note is turned off.

	writeOPL(0x60 + offset, 0xFF);
	writeOPL(0x63 + offset, 0xFF);

	writeOPL(0x80 + offset, 0xFF);
	writeOPL(0x83 + offset, 0xFF);

	writeOPL(0xB0 + chan, 0x00);

	// ...and then the note is turned on again, with whatever value is
	// still lurking in the A0 + chan register, but everything else -
	// including the two most significant frequency bit, and the octave -
	// set to zero.
	//
	// This is very strange behavior, and causes problems with the ancient
	// FMOPL code we borrowed from AdPlug. I've added a workaround. See
	// fmopl.cpp for more details.
	//
	// More recent versions of the MAME FMOPL don't seem to have this
	// problem, but cannot currently be used because of licensing and
	// performance issues.
	//
	// Ken Silverman's AdLib emulator (which can be found on his Web page -
	// http://www.advsys.net/ken - and as part of AdPlug) also seems to be
	// immune, but is apparently not as feature complete as MAME's.

	writeOPL(0xB0 + chan, 0x20);
}

// I believe this is a random number generator. It actually does seem to
// generate an even distribution of almost all numbers from 0 through 65535,
// though in my tests some numbers were never generated.

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;
}

// This function may or may not play the note. It's usually followed by a call
// to noteOn(), which will always play the current note.

void AdLibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) {
	debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels));

	if (_curChannel >= 9)
		return;

#if 0
	channel.rawNote = rawNote;

	int8 note = (rawNote & 0x0F) + channel.baseNote;
	int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F;
#else
	/* Revert scumm c571026a20b70b09d706dbefda22ce575c74c7e8. */
	/* unk1         => note (rawNote)
	 * unk2         => octave
	 * value        => freq
	 * state.unk10  => channel.baseOctave
	 * state.unk13  => channel.rawNote
	 * state.unk14  => channel.baseNote
	 * state.unk15  => channel.baseFreq
	 * state.unk17  => channel.regAx
	 * state.unkOutputValue1    => channel.regBx
	 */

	channel.rawNote = rawNote;
	uint8 octave = rawNote & 0xF0;
	octave += channel.baseOctave;
	uint8 note = rawNote;
	note &= 0x0F;
	note += channel.baseNote;
#endif

	// There are only twelve notes. If we go outside that, we have to
	// adjust the note and octave.

#if 0
	if (note >= 12) {
		note -= 12;
		octave++;
	} else if (note < 0) {
		note += 12;
		octave--;
	}
#else
	if ((int8)note >= 0x0C) {
		note -= 0x0C;
		octave += 0x10;
	} else if ((int8)note < 0) {
		note += 0x0C;
		octave -= 0x10;
	}
#endif

	// The calculation of frequency looks quite different from the original
	// disassembly at a first glance, but when you consider that the
	// largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if
	// baseFreq is unsigned), freq is still a 10-bit value, just as it
	// should be to fit in the Ax and Bx registers.
	//
	// If it were larger than that, it could have overflowed into the
	// octave bits, and that could possibly have been used in some sound.
	// But as it is now, I can't see any way it would happen.

	uint16 freq = _unkTable[note] + channel.baseFreq;

#if 1
	octave >>= 2;
	octave &= 0x1C;
	octave |= (freq & 0xFF00) >> 8;
	freq = (freq & 0xFF) | (octave << 8);
#endif

	// When called from callback 41, the behavior is slightly different:
	// We adjust the frequency, even when channel.unk16 is 0.

#if 0
	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];
		}
	}
#else
	if (channel.unk16 || flag) {
		if (channel.unk16 >= 0) {
			const uint8 *table = _unkTables[(channel.rawNote & 0x0F) + 2];
			freq += table[channel.unk16];
		} else {
			const uint8 *table = _unkTables[channel.rawNote & 0x0F];
			freq -= table[(channel.unk16 ^ 0xFF) + 1];
		}
	}
#endif

#if 0
	channel.regAx = freq & 0xFF;
	channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03);
#else
	channel.regBx = (channel.regBx & 0x20) | ((freq & 0xFF00) >> 8);
	channel.regAx = freq & 0xFF;
#endif

	// Keep the note on or off
	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));

	if(dataptr == NULL) {
		return;
	}

	// Amplitude Modulation / Vibrato / Envelope Generator Type /
	// Keyboard Scaling Rate / Modulator Frequency Multiple
	writeOPL(0x20 + regOffset, *dataptr++);
	writeOPL(0x23 + regOffset, *dataptr++);

	uint8 temp = *dataptr++;

	// Feedback / Algorithm

	// It is very likely that _curChannel really does refer to the same
	// channel as regOffset, but there's only one Cx register per channel.

	writeOPL(0xC0 + _curChannel, temp);

	// The algorithm bit. I don't pretend to understand this fully, but
	// "If set to 0, operator 1 modulates operator 2. In this case,
	// operator 2 is the only one producing sound. If set to 1, both
	// operators produce sound directly. Complex sounds are more easily
	// created if the algorithm is set to 0."

	channel.twoChan = temp & 1;

	// Waveform Select
	writeOPL(0xE0 + regOffset, *dataptr++);
	writeOPL(0xE3 + regOffset, *dataptr++);

	channel.opLevel1 = *dataptr++;
	channel.opLevel2 = *dataptr++;

	// Level Key Scaling / Total Level
	writeOPL(0x40 + regOffset, calculateOpLevel1(channel));
	writeOPL(0x43 + regOffset, calculateOpLevel2(channel));

	// Attack Rate / Decay Rate
	writeOPL(0x60 + regOffset, *dataptr++);
	writeOPL(0x63 + regOffset, *dataptr++);

	// Sustain Level / Release Rate
	writeOPL(0x80 + regOffset, *dataptr++);
	writeOPL(0x83 + regOffset, *dataptr++);
}

// Apart from playing the note, this function also updates the variables for
// primary effect 2.

void AdLibDriver::noteOn(Channel &channel) {
	debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels));

	// The "note on" bit is set, and the current note is played.

	if (_curChannel >= 9)
		return;

	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));

	if (_curChannel >= 9)
		return;

	// Level Key Scaling / Total Level

	writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel));
	if (channel.twoChan)
		writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel));
}

// This is presumably only used for some sound effects, e.g. Malcolm blowing up
// the trees in the intro (but not the effect where he "booby-traps" the big
// tree) and turning Kallak to stone. Related functions and variables:
//
// update_setupPrimaryEffect1()
//    - Initializes unk29, unk30 and unk31
//    - unk29 is not further modified
//    - unk30 is not further modified, except by update_removePrimaryEffect1()
//
// update_removePrimaryEffect1()
//    - Deinitializes unk30
//
// unk29 - determines how often the notes are played
// unk30 - modifies the frequency
// unk31 - determines how often the notes are played

void AdLibDriver::primaryEffect1(Channel &channel) {
	debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel);

	if (_curChannel >= 9)
		return;

	uint8 temp = channel.unk31;
	channel.unk31 += channel.unk29;
	if (channel.unk31 >= temp)
		return;

	// Initialize unk1 to the current frequency
	int16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx;

	// This is presumably to shift the "note on" bit so far to the left
	// that it won't be affected by any of the calculations below.
	int16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C);

	int16 unk3 = (int16)channel.unk30;

	if (unk3 >= 0) {
		unk1 += unk3;
		if (unk1 >= 734) {
			// The new frequency is too high. Shift it down and go
			// up one octave.
			unk1 >>= 1;
			if (!(unk1 & 0x3FF))
				++unk1;
			unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF);
			unk2 &= 0xFF1C;
		}
	} else {
		unk1 += unk3;
		if (unk1 < 388) {
			// The new frequency is too low. Shift it up and go
			// down one octave.
			unk1 <<= 1;
			if (!(unk1 & 0x3FF))
				--unk1;
			unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF);
			unk2 &= 0xFF1C;
		}
	}

	// Make sure that the new frequency is still a 10-bit value.
	unk1 &= 0x3FF;

	writeOPL(0xA0 + _curChannel, unk1 & 0xFF);
	channel.regAx = unk1 & 0xFF;

	// Shift down the "note on" bit again.
	uint8 value = unk1 >> 8;
	value |= (unk2 >> 8) & 0xFF;
	value |= unk2 & 0xFF;

	writeOPL(0xB0 + _curChannel, value);
	channel.regBx = value;
}

// This is presumably only used for some sound effects, e.g. Malcolm entering
// and leaving Kallak's hut. Related functions and variables:
//
// update_setupPrimaryEffect2()
//    - Initializes unk32, unk33, unk34, unk35 and unk36
//    - unk32 is not further modified
//    - unk33 is not further modified
//    - unk34 is a countdown that gets reinitialized to unk35 on zero
//    - unk35 is based on unk34 and not further modified
//    - unk36 is not further modified
//
// noteOn()
//    - Plays the current note
//    - Updates unk37 with a new (lower?) frequency
//    - Copies unk36 to unk38. The unk38 variable is a countdown.
//
// unk32 - determines how often the notes are played
// unk33 - modifies the frequency
// unk34 - countdown, updates frequency on zero
// unk35 - initializer for unk34 countdown
// unk36 - initializer for unk38 countdown
// unk37 - frequency
// unk38 - countdown, begins playing on zero
// unk41 - determines how often the notes are played
//
// Note that unk41 is never initialized. Not that it should matter much, but it
// is a bit sloppy.

void AdLibDriver::primaryEffect2(Channel &channel) {
	debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel);

	if (_curChannel >= 9)
		return;

	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);

		// Octave / F-Number / Key-On
		writeOPL(0xA0 + _curChannel, channel.regAx);
		writeOPL(0xB0 + _curChannel, channel.regBx);
	}
}

// I don't know where this is used. The same operation is performed several
// times on the current channel, using a chunk of the _soundData[] buffer for
// parameters. The parameters are used starting at the end of the chunk.
//
// Since we use _curRegOffset to specify the final register, it's quite
// unlikely that this function is ever used to play notes. It's probably only
// used to modify the sound. Another thing that supports this idea is that it
// can be combined with any of the effects callbacks above.
//
// Related functions and variables:
//
// update_setupSecondaryEffect1()
//    - Initialies unk18, unk19, unk20, unk21, unk22 and offset
//    - unk19 is not further modified
//    - unk20 is not further modified
//    - unk22 is not further modified
//    - offset is not further modified
//
// unk18 -  determines how often the operation is performed
// unk19 -  determines how often the operation is performed
// unk20 -  the start index into the data chunk
// unk21 -  the current index into the data chunk
// unk22 -  the operation to perform
// offset - the offset to the data chunk

void AdLibDriver::secondaryEffect1(Channel &channel) {
	debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel);

	if (_curChannel >= 9)
		return;

	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;
	}

	// Preserve the scaling level bits from opLevel1

	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;

	// Preserve the scaling level bits from opLevel2

	return checkValue(value) | (channel.opLevel2 & 0xC0);
}

// parser opcodes

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);
	//TODO: Check in LoL CD AdLib driver
	if (!ptr)
		return 0;
	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;
	if (_syncJumpMask & (1 << (&channel - _channels)))
		channel.lock = true;
	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;
	_callbackTimer = 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;
}

// Apart from initializing to zero, these two functions are the only ones that
// modify _vibratoAndAMDepthBits.

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];

		// Feedback strength / Connection type
		writeOPL(0xC0 + _curChannel, 0x00);

		// Key scaling level / Operator output level
		writeOPL(0x43 + outValue, 0x3F);

		// Sustain Level / Release Rate
		writeOPL(0x83 + outValue, 0xFF);

		// Key On / Octave / Frequency
		writeOPL(0xB0 + _curChannel, 0x00);
	}

	_curChannel = channelBackUp;
	return 0;
}

int AdLibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) {
	if (_curChannel >= 9)
		return 0;

	uint16 unk = *dataptr++;
	unk |= value << 8;
	unk &= getRandomNr();

	uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx;
	unk2 += unk;
	unk2 |= ((channel.regBx & 0x20) << 8);

	// Frequency
	writeOPL(0xA0 + _curChannel, unk2 & 0xFF);

	// Key On / Octave / Frequency
	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_nop(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) {
		// Frequency
		writeOPL(0xA0, _tablePtr2[0]);
	}
	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;

	// Octave / F-Number / Key-On for channels 6, 7 and 8

	_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) {
	// Any instrument that we want to play, and which was already playing,
	// is temporarily keyed off. Instruments that were off already, or
	// which we don't want to play, retain their old on/off status. This is
	// probably so that the instrument's envelope is played from its
	// beginning again...

	writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20);

	// ...but since we only set the rhythm instrument bits, and never clear
	// them (until the entire rhythm section is disabled), I'm not sure how
	// useful the cleverness above is. We could perhaps simply turn off all
	// the rhythm instruments instead.

	_rhythmSectionBits |= value;

	writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits);
	return 0;
}

int AdLibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) {
	--dataptr;
	_rhythmSectionBits = 0;

	// All the rhythm bits are cleared. The AM and Vibrato depth bits
	// remain unchanged.

	writeOPL(0xBD, _vibratoAndAMDepthBits);
	return 0;
}

int AdLibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) {
	uint8 value2 = *dataptr++;

	if (value & 1) {
		_unkValue12 = value2;

		// Channel 7, op1: Level Key Scaling / Total Level
		writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12));
	}

	if (value & 2) {
		_unkValue14 = value2;

		// Channel 8, op2: Level Key Scaling / Total Level
		writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14));
	}

	if (value & 4) {
		_unkValue15 = value2;

		// Channel 8, op1: Level Key Scaling / Total Level
		writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15));
	}

	if (value & 8) {
		_unkValue18 = value2;

		// Channel 7, op2: Level Key Scaling / Total Level
		writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18));
	}

	if (value & 16) {
		_unkValue20 = value2;

		// Channel 6, op2: Level Key Scaling / Total Level
		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);

		// Channel 7, op1: Level Key Scaling / Total Level
		writeOPL(0x51, _unkValue11);
	}

	if (value & 2) {
		_unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14);

		// Channel 8, op2: Level Key Scaling / Total Level
		writeOPL(0x55, _unkValue13);
	}

	if (value & 4) {
		_unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15);

		// Channel 8, op1: Level Key Scaling / Total Level
		writeOPL(0x52, _unkValue16);
	}

	if (value & 8) {
		_unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18);

		// Channel 7, op2: Level Key Scaling / Total Level
		writeOPL(0x54, _unkValue17);
	}

	if (value & 16) {
		_unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20);

		// Channel 6, op2: Level Key Scaling / Total Level
		writeOPL(0x53, _unkValue19);
	}

	return 0;
}

int AdLibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) {
	uint8 value2 = *dataptr++;

	if (value & 1) {
		_unkValue11 = value2;

		// Channel 7, op1: Level Key Scaling / Total Level
		writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12));
	}

	if (value & 2) {
		_unkValue13 = value2;

		// Channel 8, op2: Level Key Scaling / Total Level
		writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14));
	}

	if (value & 4) {
		_unkValue16 = value2;

		// Channel 8, op1: Level Key Scaling / Total Level
		writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15));
	}

	if (value & 8) {
		_unkValue17 = value2;

		// Channel 7, op2: Level Key Scaling / Total Level
		writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18));
	}

	if (value & 16) {
		_unkValue19 = value2;

		// Channel 6, op2: Level Key Scaling / Total Level
		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;
}

// static res

#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_isChannelPlaying),
		COMMAND(snd_stopChannel),
		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[] = {
		// 0
		COMMAND(update_setRepeat),
		COMMAND(update_checkRepeat),
		COMMAND(update_setupProgram),
		COMMAND(update_setNoteSpacing),

		// 4
		COMMAND(update_jump),
		COMMAND(update_jumpToSubroutine),
		COMMAND(update_returnFromSubroutine),
		COMMAND(update_setBaseOctave),

		// 8
		COMMAND(update_stopChannel),
		COMMAND(update_playRest),
		COMMAND(update_writeAdLib),
		COMMAND(update_setupNoteAndDuration),

		// 12
		COMMAND(update_setBaseNote),
		COMMAND(update_setupSecondaryEffect1),
		COMMAND(update_stopOtherChannel),
		COMMAND(update_waitForEndOfProgram),

		// 16
		COMMAND(update_setupInstrument),
		COMMAND(update_setupPrimaryEffect1),
		COMMAND(update_removePrimaryEffect1),
		COMMAND(update_setBaseFreq),

		// 20
		COMMAND(update_stopChannel),
		COMMAND(update_setupPrimaryEffect2),
		COMMAND(update_stopChannel),
		COMMAND(update_stopChannel),

		// 24
		COMMAND(update_stopChannel),
		COMMAND(update_stopChannel),
		COMMAND(update_setPriority),
		COMMAND(update_stopChannel),

		// 28
		COMMAND(updateCallback23),
		COMMAND(updateCallback24),
		COMMAND(update_setExtraLevel1),
		COMMAND(update_stopChannel),

		// 32
		COMMAND(update_setupDuration),
		COMMAND(update_playNote),
		COMMAND(update_stopChannel),
		COMMAND(update_stopChannel),

		// 36
		COMMAND(update_setFractionalNoteSpacing),
		COMMAND(update_stopChannel),
		COMMAND(update_setTempo),
		COMMAND(update_removeSecondaryEffect1),

		// 40
		COMMAND(update_stopChannel),
		COMMAND(update_setChannelTempo),
		COMMAND(update_stopChannel),
		COMMAND(update_setExtraLevel3),

		// 44
		COMMAND(update_setExtraLevel2),
		COMMAND(update_changeExtraLevel2),
		COMMAND(update_setAMDepth),
		COMMAND(update_setVibratoDepth),

		// 48
		COMMAND(update_changeExtraLevel1),
		COMMAND(update_stopChannel),
		COMMAND(update_stopChannel),
		COMMAND(updateCallback38),

		// 52
		COMMAND(update_stopChannel),
		COMMAND(updateCallback39),
		COMMAND(update_removePrimaryEffect2),
		COMMAND(update_stopChannel),

		// 56
		COMMAND(update_stopChannel),
		COMMAND(updateCallback41),
		COMMAND(update_resetToGlobalTempo),
		COMMAND(update_nop),

		// 60
		COMMAND(update_setDurationRandomness),
		COMMAND(update_changeChannelTempo),
		COMMAND(update_stopChannel),
		COMMAND(updateCallback46),

		// 64
		COMMAND(update_nop),
		COMMAND(update_setupRhythmSection),
		COMMAND(update_playRhythmSection),
		COMMAND(update_removeRhythmSection),

		// 68
		COMMAND(updateCallback51),
		COMMAND(updateCallback52),
		COMMAND(updateCallback53),
		COMMAND(update_setSoundTrigger),

		// 72
		COMMAND(update_setTempoReset),
		COMMAND(updateCallback56),
		COMMAND(update_stopChannel)
	};

	_parserOpcodeTable = parserOpcodeTable;
	_parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable);
}
#undef COMMAND

// This table holds the register offset for operator 1 for each of the nine
// channels. To get the register offset for operator 2, simply add 3.

const uint8 AdLibDriver::_regOffset[] = {
	0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11,
	0x12
};

// Given the size of this table, and the range of its values, it's probably the
// F-Numbers (10 bits) for the notes of the 12-tone scale. However, it does not
// match the table in the AdLib documentation I've seen.

const uint16 AdLibDriver::_unkTable[] = {
	0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9,
	0x0207, 0x0225, 0x0246
};

// These tables are currently only used by updateCallback46(), which only ever
// uses the first element of one of the sub-tables.

const uint8 *const 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
};

// no don't ask me WHY this table exsits!
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
};

// This table is used to modify the frequency of the notes, depending on the
// note value and unk16. In theory, we could very well try to access memory
// outside this table, but in reality that probably won't happen.
//
// This could be some sort of pitch bend, but I have yet to see it used for
// anything so it's hard to say.

const uint8 AdLibDriver::_unkTables[][32] = {
	// 0
	{ 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 },
	// 1
	{ 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 },
	// 2
	{ 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 },
	// 3
	{ 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 },
	// 4
	{ 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 },
	// 5
	{ 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 },
	// 6
	{ 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 },
	// 7
	{ 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 },
	// 8
	{ 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 },
	// 9
	{ 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 },
	// 10
	{ 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 },
	// 11
	{ 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 },
	// 12
	{ 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 },
	// 13
	{ 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 }
};

//#pragma mark -


#ifdef __USE_SDL__
SoundAdLibPC::SoundAdLibPC(SDL_RWops* rwop, bool bMAME) : _driver(0), _trackEntries(), _soundDataPtr(0) {
	memset(_trackEntries, 0, sizeof(_trackEntries));

	Mix_QuerySpec(&m_freq, &m_format, &m_channels);

	_driver = new AdLibDriver(m_freq, bMAME);
	assert(_driver);

	_sfxPlayingSound = -1;

	// TODO: Figure out if Kyra 2 uses sound triggers at all.
	_soundTriggers = 0;
	_numSoundTriggers = 0;

	bJustStartedPlaying = false;

	internalLoadFile(rwop);
}
#endif

SoundAdLibPC::SoundAdLibPC(ALLEGRO_FILE* rwop, int freq, bool bMAME) : _driver(0), _trackEntries(), _soundDataPtr(0) {
	memset(_trackEntries, 0, sizeof(_trackEntries));

	m_freq = freq;
	m_format = AUDIO_S16LSB;
	m_channels = 1;

	_driver = new AdLibDriver(m_freq, bMAME);
	assert(_driver);

	_sfxPlayingSound = -1;

	// TODO: Figure out if Kyra 2 uses sound triggers at all.
	_soundTriggers = 0;
	_numSoundTriggers = 0;

	bJustStartedPlaying = false;

	internalLoadFile(rwop);
}

SoundAdLibPC::~SoundAdLibPC() {
	delete _driver;
	delete[] _soundDataPtr;
}

std::vector<int> SoundAdLibPC::getSubsongs()
{
  std::vector<int> retvector;
  for(int i = 0; i < 120; i++) {
    if(_trackEntries[i] != 0xff) {
      retvector.push_back(i);
    }
  }
  return retvector;
}

bool SoundAdLibPC::init() {
	_driver->callback(2);
	_driver->callback(16, int(4));
	return true;
}

void SoundAdLibPC::process() {
	uint8 trigger = _driver->callback(11);

	if (trigger < _numSoundTriggers) {
		int soundId = _soundTriggers[trigger];

		if (soundId)
			playTrack(soundId);
	} else if(trigger == 1) {
		// ignore
	} else if(trigger != 0) {
		warning("Unknown sound trigger %d", trigger);
		// TODO: At this point, we really want to clear the trigger...
	}
}

void SoundAdLibPC::playTrack(uint8 track) {
	_driver->setSyncJumpMask(0);
	play(track);
}

void SoundAdLibPC::haltTrack() {
	playSoundEffect(0);
	playSoundEffect(0);

	bJustStartedPlaying = false;
}

bool SoundAdLibPC::isPlaying() const {
	return (bJustStartedPlaying == true) || (_driver->callback(7, int(0)) != 0);
}

void SoundAdLibPC::playSoundEffect(uint8 track) {
	play(track);
}

unsigned char SoundAdLibPC::getsampsize() {
	return m_channels * (m_format == AUDIO_U8 || m_format == AUDIO_S8 ? 1 : 2);
}

void SoundAdLibPC::callback(void *userdata, Uint8 *audiobuf, int len)
{
	SoundAdLibPC *self = (SoundAdLibPC *)userdata;

	self->process();

	int numSamples = len / (self->getsampsize());

	int16* tmpBuf = (int16*) calloc(numSamples,sizeof(int16));
	if(tmpBuf == NULL) {
		warning("Not enough memory!");
		return;
	}

	// write mono 16-bit signed samples (in system endianess)
	self->_driver->readBuffer((int16*) tmpBuf, numSamples);

	// now convert to target format
	switch(self->m_format) {
#ifdef __USE_SDL__
		case AUDIO_U8: {
			Uint8* out = audiobuf;

			for(int i=0;i<numSamples;i++) {
				for(int j=0;j<self->m_channels;j++,out++) {
					*out = ((tmpBuf[i]/2) >> 8) + 0x80;
				}
			}
		} break;

		case AUDIO_S8: {
			Sint8* out = (Sint8*) audiobuf;

			for(int i=0;i<numSamples;i++) {
				for(int j=0;j<self->m_channels;j++,out++) {
					*out = ((tmpBuf[i]/2) >> 8);
				}
			}
		} break;

		case AUDIO_U16LSB: {
			Uint16* out = (Uint16*) audiobuf;

			for(int i=0;i<numSamples;i++) {
				for(int j=0;j<self->m_channels;j++,out++) {
					*out = SDL_SwapLE16((tmpBuf[i]/2) + 0x8000);
				}
			}
		} break;

		case AUDIO_S16LSB: {
			Sint16* out = (Sint16*) audiobuf;

			for(int i=0;i<numSamples;i++) {
				for(int j=0;j<self->m_channels;j++,out++) {
					*out = SDL_SwapLE16(tmpBuf[i]/2);
				}
			}
		} break;

		case AUDIO_U16MSB: {
			Uint16* out = (Uint16*) audiobuf;

			for(int i=0;i<numSamples;i++) {
				for(int j=0;j<self->m_channels;j++,out++) {
					*out = SDL_SwapBE16((tmpBuf[i]/2) + 0x8000);
				}
			}
		} break;

		case AUDIO_S16MSB: {
			Sint16* out = (Sint16*) audiobuf;

			for(int i=0;i<numSamples;i++) {
				for(int j=0;j<self->m_channels;j++,out++) {
					*out = SDL_SwapBE16(tmpBuf[i]/2);
				}
			}
		} break;
#endif

		case AUDIO_S16LSB: {
			int16_t* out = (int16_t*) audiobuf;

			for (int i = 0; i < numSamples; i++) {
				for (int j = 0; j < self->m_channels; j++, out++) {
					*out = tmpBuf[i];
				}
			}
		} break;

		default: {
			warning("Unsupported audio format");
			return;
		}
	}

	free(tmpBuf);

	self->bJustStartedPlaying = false;
}

void SoundAdLibPC::play(uint8 track) {
	uint8 soundId = _trackEntries[track];

	if ((int8)soundId == -1 || !_soundDataPtr)
		return;

	if (_sfxPlayingSound != -1) {
		// Restore the sounds's normal values.
		_driver->callback(10, _sfxPlayingSound, int(1), int(_sfxPriority));
		_driver->callback(10, _sfxPlayingSound, int(3), int(_sfxFourthByteOfSong));
		_sfxPlayingSound = -1;
	}

	int chan = _driver->callback(9, soundId, int(0));

	if (chan >= 0 && chan != 9) {
		_sfxPlayingSound = soundId;
		_sfxPriority = _driver->callback(9, soundId, int(1));
		_sfxFourthByteOfSong = _driver->callback(9, soundId, int(3));

		// In the cases I've seen, the mysterious fourth byte has been
		// the parameter for the update_setExtraLevel3() callback.
		//
		// The extra level is part of the channels "total level", which
		// is a six-bit value where larger values means softer volume.
		//
		// So what seems to be happening here is that sounds which are
		// started by this function are given a slightly lower priority
		// and a slightly higher (i.e. softer) extra level 3 than they
		// would have if they were started from anywhere else. Strange.

		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);

	bJustStartedPlaying = true;
}

void SoundAdLibPC::beginFadeOut() {
	playSoundEffect(1);
}

void SoundAdLibPC::internalLoadFile(ALLEGRO_FILE* rwop) {
  if(rwop == NULL) {
	return;
  }

  uint8 *file_data = 0;
  size_t file_size = 0;

#ifdef __USE_SDL__
  if((file_size = SDL_RWseek(rwop,0,SEEK_END)) <= 0) {
	fprintf(stderr,"SoundAdLibPC::internalLoadFile(): Cannot seek in SDL_RWop!\n");
	return;
  }
#else
  if((file_size = al_fsize(rwop)) <= 0) {
	return;
  }
#endif


  playSoundEffect(0);
  playSoundEffect(0);

#ifdef __USE_SDL__
  if(SDL_RWseek(rwop,0,SEEK_SET) != 0) {
	fprintf(stderr,"SoundAdLibPC::internalLoadFile(): Cannot seek in SDL_RWop!\n");
	return;
  }
#endif

  file_data = new uint8[file_size];
#ifdef __USE_SDL__
  if(SDL_RWread(rwop,file_data,1,file_size) != file_size) {
	fprintf(stderr,"SoundAdLibPC::internalLoadFile(): Cannot read from SDL_RWop!\n");
	return;
  }
#else
  if(al_fread(rwop,file_data,file_size) != file_size) {
	fprintf(stderr,"SoundAdLibPC::internalLoadFile(): Cannot read from SDL_RWop!\n");
	return;
  }
#endif


  _driver->callback(8, int(-1));
  _soundDataPtr = 0;

  uint8 *p = file_data;
  memcpy(_trackEntries, p, 120*sizeof(uint8));
  p += 120;

  int soundDataSize = file_size - 120;

  _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);
}

#ifdef __USE_SDL__
Mix_Chunk* SoundAdLibPC::getSubsong(int Num) {
	Uint8*	buf = NULL;
	int		bufSize = 0;

	playTrack(Num);

	do {
		bufSize += 1024;
		if((buf = (Uint8*) realloc(buf, bufSize)) == NULL) {
			perror("SoundAdLibPC::getSubsong(): Cannot allocate memory!\n");
			exit(EXIT_FAILURE);
		}

		SoundAdLibPC::callback(this, buf + bufSize - 1024, 1024);

		if(bufSize > 1024*1024*16) {
			fprintf(stderr,"SoundAdLibPC::getSubsong(): Decoding aborted after 16MB have been decoded.\n");
			break;
		}

	} while(isPlaying());

	Mix_Chunk* myChunk;
	if((myChunk = (Mix_Chunk*) calloc(sizeof(Mix_Chunk),1)) == NULL) {
		return NULL;
	}

	myChunk->volume = 128;
	myChunk->allocated = 1;
	myChunk->abuf = (Uint8*) buf;
	myChunk->alen = bufSize;
	return myChunk;
}
#endif
