diff --git a/audio/softsynth/adlib.cpp b/audio/adlib.cpp similarity index 99% rename from audio/softsynth/adlib.cpp rename to audio/adlib.cpp index 98519343b42..f6091644955 100644 --- a/audio/softsynth/adlib.cpp +++ b/audio/adlib.cpp @@ -927,18 +927,20 @@ static void createLookupTable() { // //////////////////////////////////////// -class MidiDriver_ADLIB : public MidiDriver_Emulated { +class MidiDriver_ADLIB : public MidiDriver { friend class AdLibPart; friend class AdLibPercussionChannel; public: - MidiDriver_ADLIB(Audio::Mixer *mixer); + MidiDriver_ADLIB(); int open(); void close(); void send(uint32 b); void send(byte channel, uint32 b); // Supports higher than channel 15 uint32 property(int prop, uint32 param); + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } void setPitchBendRange(byte channel, uint range); void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); @@ -946,10 +948,7 @@ public: MidiChannel *allocateChannel(); MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported - - // AudioStream API - bool isStereo() const { return _opl->isStereo(); } - int getRate() const { return _mixer->getOutputRate(); } + virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); private: bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games @@ -963,6 +962,9 @@ private: byte *_regCacheSecondary; #endif + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + int _timerCounter; uint16 _channelTable2[9]; @@ -974,7 +976,8 @@ private: AdLibPart _parts[32]; AdLibPercussionChannel _percussion; - void generateSamples(int16 *buf, int len); + bool _isOpen; + void onTimer(); void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); void partKeyOff(AdLibPart *part, byte note); @@ -1376,8 +1379,7 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins // MidiDriver method implementations -MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer) { +MidiDriver_ADLIB::MidiDriver_ADLIB() { uint i; _scummSmallHeader = false; @@ -1403,13 +1405,16 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer) _timerIncrease = 0xD69; _timerThreshold = 0x411B; _opl = 0; + _adlibTimerProc = 0; + _adlibTimerParam = 0; + _isOpen = false; } int MidiDriver_ADLIB::open() { if (_isOpen) return MERR_ALREADY_OPEN; - MidiDriver_Emulated::open(); + _isOpen = true; int i; AdLibVoice *voice; @@ -1434,7 +1439,7 @@ int MidiDriver_ADLIB::open() { _opl3Mode = false; } #endif - _opl->init(getRate()); + _opl->init(); _regCache = (byte *)calloc(256, 1); @@ -1452,8 +1457,7 @@ int MidiDriver_ADLIB::open() { } #endif - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - + _opl->start(new Common::Functor0Mem(this, &MidiDriver_ADLIB::onTimer)); return 0; } @@ -1462,7 +1466,8 @@ void MidiDriver_ADLIB::close() { return; _isOpen = false; - _mixer->stopHandle(_mixerSoundHandle); + // Stop the OPL timer + _opl->stop(); uint i; for (i = 0; i < ARRAYSIZE(_voices); ++i) { @@ -1616,14 +1621,10 @@ void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) { } #endif -void MidiDriver_ADLIB::generateSamples(int16 *data, int len) { - if (_opl->isStereo()) { - len *= 2; - } - _opl->readBuffer(data, len); -} - void MidiDriver_ADLIB::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); + _timerCounter += _timerIncrease; while (_timerCounter >= _timerThreshold) { _timerCounter -= _timerThreshold; @@ -1655,6 +1656,11 @@ void MidiDriver_ADLIB::onTimer() { } } +void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) { AdLibVoice *tmp; @@ -2300,7 +2306,7 @@ MusicDevices AdLibEmuMusicPlugin::getDevices() const { } Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { - *mididriver = new MidiDriver_ADLIB(g_system->getMixer()); + *mididriver = new MidiDriver_ADLIB(); return Common::kNoError; } diff --git a/audio/alsa_opl.cpp b/audio/alsa_opl.cpp new file mode 100644 index 00000000000..6b9e48e987f --- /dev/null +++ b/audio/alsa_opl.cpp @@ -0,0 +1,349 @@ +/* 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. + * + */ + +/* OPL implementation for hardware OPL using ALSA Direct FM API. + * + * Caveats and limitations: + * - Pretends to be a softsynth (emitting silence). + * - Dual OPL2 mode requires OPL3 hardware. + * - Every register write leads to a series of register writes on the hardware, + * due to the lack of direct register access in the ALSA Direct FM API. + * - No timers + */ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#include "common/debug.h" +#include "common/str.h" +#include "audio/fmopl.h" + +#include +#include +#include + +namespace OPL { +namespace ALSA { + +class OPL : public ::OPL::RealOPL { +private: + enum { + kOpl2Voices = 9, + kVoices = 18, + kOpl2Operators = 18, + kOperators = 36 + }; + + Config::OplType _type; + int _iface; + snd_hwdep_t *_opl; + snd_dm_fm_voice _oper[kOperators]; + snd_dm_fm_note _voice[kVoices]; + snd_dm_fm_params _params; + int index[2]; + static const int voiceToOper0[kVoices]; + static const int regOffsetToOper[0x20]; + + void writeOplReg(int c, int r, int v); + void clear(); + +public: + OPL(Config::OplType type); + ~OPL(); + + bool init(); + void reset(); + + void write(int a, int v); + byte read(int a); + + void writeReg(int r, int v); +}; + +const int OPL::voiceToOper0[OPL::kVoices] = + { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; + +const int OPL::regOffsetToOper[0x20] = + { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) { +} + +OPL::~OPL() { + stop(); + + if (_opl) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + snd_hwdep_close(_opl); + } +} + +void OPL::clear() { + index[0] = index[1] = 0; + + memset(_oper, 0, sizeof(_oper)); + memset(_voice, 0, sizeof(_voice)); + memset(&_params, 0, sizeof(_params)); + + for (int i = 0; i < kOperators; ++i) { + _oper[i].op = (i / 3) % 2; + _oper[i].voice = (i / 6) * 3 + (i % 3); + } + + for (int i = 0; i < kVoices; ++i) + _voice[i].voice = i; + + // For OPL3 hardware we need to set up the panning in OPL2 modes + if (_iface == SND_HWDEP_IFACE_OPL3) { + if (_type == Config::kDualOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) + _oper[i].left = 1; // FIXME below + for (int i = kOpl2Operators; i < kOperators; ++i) + _oper[i].right = 1; + } else if (_type == Config::kOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) { + _oper[i].left = 1; + _oper[i].right = 1; + } + } + } +} + +bool OPL::init() { + clear(); + + int card = -1; + snd_ctl_t *ctl; + snd_hwdep_info_t *info; + snd_hwdep_info_alloca(&info); + + int iface = SND_HWDEP_IFACE_OPL3; + if (_type == Config::kOpl2) + iface = SND_HWDEP_IFACE_OPL2; + + // Look for OPL hwdep interface + while (!snd_card_next(&card) && card >= 0) { + int dev = -1; + Common::String name = Common::String::format("hw:%d", card); + + if (snd_ctl_open(&ctl, name.c_str(), 0) < 0) + continue; + + while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) { + name = Common::String::format("hw:%d,%d", card, dev); + + if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) + continue; + + if (!snd_hwdep_info(_opl, info)) { + int found = snd_hwdep_info_get_iface(info); + // OPL3 can be used for (Dual) OPL2 mode + if (found == iface || found == SND_HWDEP_IFACE_OPL3) { + snd_ctl_close(ctl); + _iface = found; + reset(); + return true; + } + } + + // Wrong interface, try next device + snd_hwdep_close(_opl); + _opl = nullptr; + } + + snd_ctl_close(ctl); + } + + return false; +} + +void OPL::reset() { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + if (_iface == SND_HWDEP_IFACE_OPL3) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3); + + clear(); + + // Sync up with the hardware + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]); +} + +void OPL::write(int port, int val) { + val &= 0xff; + int chip = (port & 2) >> 1; + + if (port & 1) { + switch(_type) { + case Config::kOpl2: + writeOplReg(0, index[0], val); + break; + case Config::kDualOpl2: + if (port & 8) { + writeOplReg(0, index[0], val); + writeOplReg(1, index[1], val); + } else + writeOplReg(chip, index[chip], val); + break; + case Config::kOpl3: + writeOplReg(chip, index[chip], val); + } + } else { + switch(_type) { + case Config::kOpl2: + index[0] = val; + break; + case Config::kDualOpl2: + if (port & 8) { + index[0] = val; + index[1] = val; + } else + index[chip] = val; + break; + case Config::kOpl3: + index[chip] = val; + } + } +} + +byte OPL::read(int port) { + return 0; +} + +void OPL::writeReg(int r, int v) { + switch (_type) { + case Config::kOpl2: + writeOplReg(0, r, v); + break; + case Config::kDualOpl2: + writeOplReg(0, r, v); + writeOplReg(1, r, v); + break; + case Config::kOpl3: + writeOplReg(r >= 0x100, r & 0xff, v); + } +} + +void OPL::writeOplReg(int c, int r, int v) { + if (r == 0x04 && c == 1 && _type == Config::kOpl3) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast(v & 0x3f)); + } else if (r == 0x08 && c == 0) { + _params.kbd_split = (v >> 6) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r == 0xbd && c == 0) { + _params.hihat = v & 0x1; + _params.cymbal = (v >> 1) & 0x1; + _params.tomtom = (v >> 2) & 0x1; + _params.snare = (v >> 3) & 0x1; + _params.bass = (v >> 4) & 0x1; + _params.rhythm = (v >> 5) & 0x1; + _params.vib_depth = (v >> 6) & 0x1; + _params.am_depth = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r < 0xa0 || r >= 0xe0) { + // Operator + int idx = regOffsetToOper[r & 0x1f]; + + if (idx == -1) + return; + + if (c == 1) + idx += kOpl2Operators; + + switch (r & 0xf0) { + case 0x20: + case 0x30: + _oper[idx].harmonic = v & 0xf; + _oper[idx].kbd_scale = (v >> 4) & 0x1; + _oper[idx].do_sustain = (v >> 5) & 0x1; + _oper[idx].vibrato = (v >> 6) & 0x1; + _oper[idx].am = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x40: + case 0x50: + _oper[idx].volume = ~v & 0x3f; + _oper[idx].scale_level = (v >> 6) & 0x3; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x60: + case 0x70: + _oper[idx].decay = v & 0xf; + _oper[idx].attack = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x80: + case 0x90: + _oper[idx].release = v & 0xf; + _oper[idx].sustain = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0xe0: + case 0xf0: + _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + } + } else { + // Voice + int idx = r & 0xf; + + if (idx >= kOpl2Voices) + return; + + if (c == 1) + idx += kOpl2Voices; + + int opIdx = voiceToOper0[idx]; + + switch (r & 0xf0) { + case 0xa0: + _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xb0: + _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff); + _voice[idx].octave = (v >> 2) & 0x7; + _voice[idx].key_on = (v >> 5) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xc0: + _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1; + _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7; + if (_type == Config::kOpl3) { + _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1; + _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1; + } + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]); + } + } +} + +OPL *create(Config::OplType type) { + return new OPL(type); +} + +} // End of namespace ALSA +} // End of namespace OPL diff --git a/audio/decoders/3do.cpp b/audio/decoders/3do.cpp new file mode 100644 index 00000000000..6d558d4c8c6 --- /dev/null +++ b/audio/decoders/3do.cpp @@ -0,0 +1,343 @@ +/* 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. + * + */ + +#include "common/textconsole.h" +#include "common/stream.h" +#include "common/util.h" + +#include "audio/decoders/3do.h" +#include "audio/decoders/raw.h" +#include "audio/decoders/adpcm_intern.h" + +namespace Audio { + +// Reuses ADPCM table +#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable +#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable + +RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) { + if (stereo) { + warning("make3DO_ADP4Stream(): stereo currently not supported"); + return 0; + } + + if (audioLengthMSecsPtr) { + // Caller requires the milliseconds of audio + uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample + if (stereo) { + audioLengthMSecs /= 2; + } + *audioLengthMSecsPtr = audioLengthMSecs; + } + + return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace); +} + +Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) + : _sampleRate(sampleRate), _stereo(stereo), + _stream(stream, disposeAfterUse) { + + _callerDecoderData = persistentSpace; + memset(&_initialDecoderData, 0, sizeof(_initialDecoderData)); + _initialRead = true; + + reset(); +} + +void Audio3DO_ADP4_Stream::reset() { + memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData)); + _streamBytesLeft = _stream->size(); + _stream->seek(0); +} + +bool Audio3DO_ADP4_Stream::rewind() { + reset(); + return true; +} + +int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) { + int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex]; + int32 decodedSample = _curDecoderData.lastSample; + int16 delta = currentStep >> 3; + + if (compressedNibble & 1) + delta += currentStep >> 2; + + if (compressedNibble & 2) + delta += currentStep >> 1; + + if (compressedNibble & 4) + delta += currentStep; + + if (compressedNibble & 8) { + decodedSample -= delta; + } else { + decodedSample += delta; + } + + _curDecoderData.lastSample = CLIP(decodedSample, -32768, 32767); + + _curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07]; + _curDecoderData.stepIndex = CLIP(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1); + + return _curDecoderData.lastSample; +} + +// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written +int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) { + int8 byteCache[AUDIO_3DO_CACHE_SIZE]; + int8 *byteCachePtr = NULL; + int byteCacheSize = 0; + int requestedBytesLeft = 0; + int decodedSamplesCount = 0; + + int8 compressedByte = 0; + + if (endOfData()) + return 0; // no more bytes left + + if (_callerDecoderData) { + // copy caller decoder data over + memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData)); + if (_initialRead) { + _initialRead = false; + memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData)); + } + } + + requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample + if (requestedBytesLeft > _streamBytesLeft) + requestedBytesLeft = _streamBytesLeft; // not enough bytes left + + // in case caller requests an uneven amount of samples, we will return an even amount + + // buffering, so that direct decoding of files and such runs way faster + while (requestedBytesLeft) { + if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) { + byteCacheSize = AUDIO_3DO_CACHE_SIZE; + } else { + byteCacheSize = requestedBytesLeft; + } + + requestedBytesLeft -= byteCacheSize; + _streamBytesLeft -= byteCacheSize; + + // Fill our byte cache + _stream->read(byteCache, byteCacheSize); + + byteCachePtr = byteCache; + + // Mono + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + + buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4); + decodedSamplesCount++; + buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f); + decodedSamplesCount++; + } + } + + if (_callerDecoderData) { + // copy caller decoder data back + memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData)); + } + + return decodedSamplesCount; +} + +// ============================================================================ +static int16 audio_3DO_SDX2_SquareTable[256] = { + -32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322, + -27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762, + -23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602, + -19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842, + -15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482, + -12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522, + -9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962, + -6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802, + -4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042, + -2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682, + -1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722, + -648, -578, -512, -450, -392, -338, -288, -242, -200, -162, + -128, -98, -72, -50, -32, -18, -8, -2, 0, 2, + 8, 18, 32, 50, 72, 98, 128, 162, 200, 242, + 288, 338, 392, 450, 512, 578, 648, 722, 800, 882, + 968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922, + 2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362, + 3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202, + 5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442, + 7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082, + 10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122, + 13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562, + 16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402, + 20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642, + 25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282, + 29768, 30258, 30752, 31250, 31752, 32258 +}; + +Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) + : _sampleRate(sampleRate), _stereo(stereo), + _stream(stream, disposeAfterUse) { + + _callerDecoderData = persistentSpace; + memset(&_initialDecoderData, 0, sizeof(_initialDecoderData)); + _initialRead = true; + + reset(); +} + +void Audio3DO_SDX2_Stream::reset() { + memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData)); + _streamBytesLeft = _stream->size(); + _stream->seek(0); +} + +bool Audio3DO_SDX2_Stream::rewind() { + reset(); + return true; +} + +// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written +int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) { + int8 byteCache[AUDIO_3DO_CACHE_SIZE]; + int8 *byteCachePtr = NULL; + int byteCacheSize = 0; + int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample + int decodedSamplesCount = 0; + + int8 compressedByte = 0; + uint8 squareTableOffset = 0; + int16 decodedSample = 0; + + if (endOfData()) + return 0; // no more bytes left + + if (_stereo) { + // We expect numSamples to be even in case of Stereo audio + assert((numSamples & 1) == 0); + } + + if (_callerDecoderData) { + // copy caller decoder data over + memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData)); + if (_initialRead) { + _initialRead = false; + memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData)); + } + } + + requestedBytesLeft = numSamples; + if (requestedBytesLeft > _streamBytesLeft) + requestedBytesLeft = _streamBytesLeft; // not enough bytes left + + // buffering, so that direct decoding of files and such runs way faster + while (requestedBytesLeft) { + if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) { + byteCacheSize = AUDIO_3DO_CACHE_SIZE; + } else { + byteCacheSize = requestedBytesLeft; + } + + requestedBytesLeft -= byteCacheSize; + _streamBytesLeft -= byteCacheSize; + + // Fill our byte cache + _stream->read(byteCache, byteCacheSize); + + byteCachePtr = byteCache; + + if (!_stereo) { + // Mono + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + squareTableOffset = compressedByte + 128; + + if (!(compressedByte & 1)) + _curDecoderData.lastSample1 = 0; + + decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample1 = decodedSample; + + buffer[decodedSamplesCount] = decodedSample; + decodedSamplesCount++; + } + } else { + // Stereo + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + squareTableOffset = compressedByte + 128; + + if (!(decodedSamplesCount & 1)) { + // First channel + if (!(compressedByte & 1)) + _curDecoderData.lastSample1 = 0; + + decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample1 = decodedSample; + } else { + // Second channel + if (!(compressedByte & 1)) + _curDecoderData.lastSample2 = 0; + + decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample2 = decodedSample; + } + + buffer[decodedSamplesCount] = decodedSample; + decodedSamplesCount++; + } + } + } + + if (_callerDecoderData) { + // copy caller decoder data back + memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData)); + } + + return decodedSamplesCount; +} + +RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) { + if (stereo) { + if (stream->size() & 1) { + warning("make3DO_SDX2Stream(): stereo data is uneven size"); + return 0; + } + } + + if (audioLengthMSecsPtr) { + // Caller requires the milliseconds of audio + uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample + if (stereo) { + audioLengthMSecs /= 2; + } + *audioLengthMSecsPtr = audioLengthMSecs; + } + + return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace); +} + +} // End of namespace Audio diff --git a/audio/decoders/3do.h b/audio/decoders/3do.h new file mode 100644 index 00000000000..75243585433 --- /dev/null +++ b/audio/decoders/3do.h @@ -0,0 +1,158 @@ +/* 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. + * + */ + +/** + * @file + * Sound decoder used in engines: + * - sherlock (3DO version of Serrated Scalpel) + */ + +#ifndef AUDIO_3DO_SDX2_H +#define AUDIO_3DO_SDX2_H + +#include "common/scummsys.h" +#include "common/types.h" +#include "common/substream.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Audio { + +class SeekableAudioStream; + +// amount of bytes to be used within the decoder classes as buffers +#define AUDIO_3DO_CACHE_SIZE 1024 + +// persistent spaces +struct audio_3DO_ADP4_PersistentSpace { + int16 lastSample; + int16 stepIndex; +}; + +struct audio_3DO_SDX2_PersistentSpace { + int16 lastSample1; + int16 lastSample2; +}; + +class Audio3DO_ADP4_Stream : public RewindableAudioStream { +public: + Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace); + +protected: + const uint16 _sampleRate; + const bool _stereo; + + Common::DisposablePtr _stream; + int32 _streamBytesLeft; + + void reset(); + bool rewind(); + bool endOfData() const { return (_stream->pos() >= _stream->size()); } + bool isStereo() const { return _stereo; } + int getRate() const { return _sampleRate; } + + int readBuffer(int16 *buffer, const int numSamples); + + bool _initialRead; + audio_3DO_ADP4_PersistentSpace *_callerDecoderData; + audio_3DO_ADP4_PersistentSpace _initialDecoderData; + audio_3DO_ADP4_PersistentSpace _curDecoderData; + +private: + int16 decodeSample(byte compressedNibble); +}; + +class Audio3DO_SDX2_Stream : public RewindableAudioStream { +public: + Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr); + +protected: + const uint16 _sampleRate; + const bool _stereo; + + Common::DisposablePtr _stream; + int32 _streamBytesLeft; + + void reset(); + bool rewind(); + bool endOfData() const { return (_stream->pos() >= _stream->size()); } + bool isStereo() const { return _stereo; } + int getRate() const { return _sampleRate; } + + int readBuffer(int16 *buffer, const int numSamples); + + bool _initialRead; + audio_3DO_SDX2_PersistentSpace *_callerDecoderData; + audio_3DO_SDX2_PersistentSpace _initialDecoderData; + audio_3DO_SDX2_PersistentSpace _curDecoderData; +}; + +/** + * Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream + * from that data. + * + * @param stream the SeekableReadStream from which to read the 3DO SDX2 data + * @sampleRate sample rate + * @stereo if it's stereo or mono + * @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds + * @disposeAfterUse disposeAfterUse whether to delete the stream after use + * @persistentSpacePtr pointer to the persistent space structure + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *make3DO_ADP4AudioStream( + Common::SeekableReadStream *stream, + uint16 sampleRate, + bool stereo, + uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, + audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL +); + +/** + * Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream + * from that data. + * + * @param stream the SeekableReadStream from which to read the 3DO SDX2 data + * @sampleRate sample rate + * @stereo if it's stereo or mono + * @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds + * @disposeAfterUse disposeAfterUse whether to delete the stream after use + * @persistentSpacePtr pointer to the persistent space structure + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *make3DO_SDX2AudioStream( + Common::SeekableReadStream *stream, + uint16 sampleRate, + bool stereo, + uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, + audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL +); + +} // End of namespace Audio + +#endif diff --git a/audio/decoders/aiff.cpp b/audio/decoders/aiff.cpp index b714721c02e..72baf845823 100644 --- a/audio/decoders/aiff.cpp +++ b/audio/decoders/aiff.cpp @@ -24,16 +24,19 @@ * The code in this file is based on information found at * http://www.borg.com/~jglatt/tech/aiff.htm * - * We currently only implement uncompressed AIFF. If we ever need AIFF-C, SoX - * (http://sox.sourceforge.net) may be a good place to start from. + * Also partially based on libav's aiffdec.c */ +#include "common/debug.h" #include "common/endian.h" #include "common/stream.h" +#include "common/substream.h" #include "common/textconsole.h" +#include "audio/audiostream.h" #include "audio/decoders/aiff.h" #include "audio/decoders/raw.h" +#include "audio/decoders/3do.h" namespace Audio { @@ -62,23 +65,34 @@ uint32 readExtended(Common::SeekableReadStream &stream) { return mantissa; } -bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags) { - byte buf[4]; +// AIFF versions +static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F'); +static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C'); - stream.read(buf, 4); - if (memcmp(buf, "FORM", 4) != 0) { - warning("loadAIFFFromStream: No 'FORM' header"); - return false; +// Codecs +static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original + +RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) { + warning("makeAIFFStream: No 'FORM' header"); + + if (disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + return 0; } - stream.readUint32BE(); + stream->readUint32BE(); // file size - // This could be AIFC, but we don't handle that case. + uint32 version = stream->readUint32BE(); - stream.read(buf, 4); - if (memcmp(buf, "AIFF", 4) != 0) { - warning("loadAIFFFromStream: No 'AIFF' header"); - return false; + if (version != kVersionAIFF && version != kVersionAIFC) { + warning("makeAIFFStream: No 'AIFF' or 'AIFC' header"); + + if (disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + return 0; } // From here on, we only care about the COMM and SSND chunks, which are @@ -87,95 +101,131 @@ bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate bool foundCOMM = false; bool foundSSND = false; - uint16 numChannels = 0, bitsPerSample = 0; - uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0; + uint16 channels = 0, bitsPerSample = 0; + uint32 blockAlign = 0, rate = 0; + uint32 codec = kCodecPCM; // AIFF default + Common::SeekableReadStream *dataStream = 0; - while (!(foundCOMM && foundSSND) && !stream.err() && !stream.eos()) { - uint32 length, pos; + while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) { + uint32 tag = stream->readUint32BE(); + uint32 length = stream->readUint32BE(); + uint32 pos = stream->pos(); - stream.read(buf, 4); - length = stream.readUint32BE(); - pos = stream.pos(); + if (stream->eos() || stream->err()) + break; - if (memcmp(buf, "COMM", 4) == 0) { + switch (tag) { + case MKTAG('C', 'O', 'M', 'M'): foundCOMM = true; - numChannels = stream.readUint16BE(); - numSampleFrames = stream.readUint32BE(); - bitsPerSample = stream.readUint16BE(); - rate = readExtended(stream); - size = numSampleFrames * numChannels * (bitsPerSample / 8); - } else if (memcmp(buf, "SSND", 4) == 0) { + channels = stream->readUint16BE(); + /* frameCount = */ stream->readUint32BE(); + bitsPerSample = stream->readUint16BE(); + rate = readExtended(*stream); + + if (version == kVersionAIFC) + codec = stream->readUint32BE(); + break; + case MKTAG('S', 'S', 'N', 'D'): foundSSND = true; - offset = stream.readUint32BE(); - blockSize = stream.readUint32BE(); - soundOffset = stream.pos(); + /* uint32 offset = */ stream->readUint32BE(); + blockAlign = stream->readUint32BE(); + dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse); + break; + case MKTAG('F', 'V', 'E', 'R'): + switch (stream->readUint32BE()) { + case 0: + version = kVersionAIFF; + break; + case 0xA2805140: + version = kVersionAIFC; + break; + default: + warning("Unknown AIFF version chunk version"); + break; + } + break; + case MKTAG('w', 'a', 'v', 'e'): + warning("Found unhandled AIFF-C extra data chunk"); + + if (!dataStream && disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + delete dataStream; + return 0; + default: + debug(1, "Skipping AIFF '%s' chunk", tag2str(tag)); + break; } - stream.seek(pos + length); + stream->seek(pos + length + (length & 1)); // ensure we're also word-aligned } if (!foundCOMM) { - warning("loadAIFFFromStream: Cound not find 'COMM' chunk"); - return false; + warning("makeAIFFStream: Cound not find 'COMM' chunk"); + + if (!dataStream && disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + delete dataStream; + return 0; } if (!foundSSND) { - warning("loadAIFFFromStream: Cound not find 'SSND' chunk"); - return false; + warning("makeAIFFStream: Cound not find 'SSND' chunk"); + + if (disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + return 0; } // We only implement a subset of the AIFF standard. - if (numChannels < 1 || numChannels > 2) { - warning("loadAIFFFromStream: Only 1 or 2 channels are supported, not %d", numChannels); - return false; - } - - if (bitsPerSample != 8 && bitsPerSample != 16) { - warning("loadAIFFFromStream: Only 8 or 16 bits per sample are supported, not %d", bitsPerSample); - return false; - } - - if (offset != 0 || blockSize != 0) { - warning("loadAIFFFromStream: Block-aligned data is not supported"); - return false; - } - - // Samples are always signed, and big endian. - - flags = 0; - if (bitsPerSample == 16) - flags |= Audio::FLAG_16BITS; - if (numChannels == 2) - flags |= Audio::FLAG_STEREO; - - stream.seek(soundOffset); - - // Stream now points at the sample data - - return true; -} - -SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, - DisposeAfterUse::Flag disposeAfterUse) { - int size, rate; - byte *data, flags; - - if (!loadAIFFFromStream(*stream, size, rate, flags)) { - if (disposeAfterUse == DisposeAfterUse::YES) - delete stream; + if (channels < 1 || channels > 2) { + warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", channels); + delete dataStream; return 0; } - data = (byte *)malloc(size); - assert(data); - stream->read(data, size); + // Seek to the start of dataStream, required for at least FileStream + dataStream->seek(0); - if (disposeAfterUse == DisposeAfterUse::YES) - delete stream; + switch (codec) { + case kCodecPCM: + case MKTAG('t', 'w', 'o', 's'): + case MKTAG('s', 'o', 'w', 't'): { + // PCM samples are always signed. + byte rawFlags = 0; + if (bitsPerSample == 16) + rawFlags |= Audio::FLAG_16BITS; + if (channels == 2) + rawFlags |= Audio::FLAG_STEREO; + if (codec == MKTAG('s', 'o', 'w', 't')) + rawFlags |= Audio::FLAG_LITTLE_ENDIAN; - // Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES. - return makeRawStream(data, size, rate, flags); + return makeRawStream(dataStream, rate, rawFlags); + } + case MKTAG('i', 'm', 'a', '4'): + // TODO: Use QT IMA ADPCM + warning("Unhandled AIFF-C QT IMA ADPCM compression"); + break; + case MKTAG('Q', 'D', 'M', '2'): + // TODO: Need to figure out how to integrate this + // (But hopefully never needed) + warning("Unhandled AIFF-C QDM2 compression"); + break; + case MKTAG('A', 'D', 'P', '4'): + // ADP4 on 3DO + return make3DO_ADP4AudioStream(dataStream, rate, channels == 2); + case MKTAG('S', 'D', 'X', '2'): + // SDX2 on 3DO + return make3DO_SDX2AudioStream(dataStream, rate, channels == 2); + default: + warning("Unhandled AIFF-C compression tag '%s'", tag2str(codec)); + } + + delete dataStream; + return 0; } } // End of namespace Audio diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h index afb0342cfdd..3af2efb4c94 100644 --- a/audio/decoders/aiff.h +++ b/audio/decoders/aiff.h @@ -23,6 +23,7 @@ /** * @file * Sound decoder used in engines: + * - bbvs * - pegasus * - saga * - sci @@ -41,28 +42,17 @@ class SeekableReadStream; namespace Audio { -class SeekableAudioStream; - -/** - * Try to load an AIFF from the given seekable stream. Returns true if - * successful. In that case, the stream's seek position will be set to the - * start of the audio data, and size, rate and flags contain information - * necessary for playback. Currently this function only supports uncompressed - * raw PCM. - */ -extern bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags); +class RewindableAudioStream; /** * Try to load an AIFF from the given seekable stream and create an AudioStream * from that data. * - * This function uses loadAIFFFromStream() internally. - * * @param stream the SeekableReadStream from which to read the AIFF data * @param disposeAfterUse whether to delete the stream after use * @return a new SeekableAudioStream, or NULL, if an error occurred */ -SeekableAudioStream *makeAIFFStream( +RewindableAudioStream *makeAIFFStream( Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); diff --git a/audio/decoders/wave.h b/audio/decoders/wave.h index 1dcaefd8458..6bc9f721017 100644 --- a/audio/decoders/wave.h +++ b/audio/decoders/wave.h @@ -23,15 +23,25 @@ /** * @file * Sound decoder used in engines: + * - access * - agos + * - cge + * - cge2 + * - fullpipe * - gob + * - hopkins * - mohawk + * - prince * - saga * - sci * - scumm + * - sherlock * - sword1 * - sword2 + * - tony * - tucker + * - wintermute + * - zvision */ #ifndef AUDIO_WAVE_H diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp index c18e5444103..cc00ace2643 100644 --- a/audio/fmopl.cpp +++ b/audio/fmopl.cpp @@ -22,21 +22,33 @@ #include "audio/fmopl.h" +#include "audio/mixer.h" #include "audio/softsynth/opl/dosbox.h" #include "audio/softsynth/opl/mame.h" #include "common/config-manager.h" +#include "common/system.h" #include "common/textconsole.h" +#include "common/timer.h" #include "common/translation.h" namespace OPL { +// Factory functions + +#ifdef USE_ALSA +namespace ALSA { + OPL *create(Config::OplType type); +} // End of namespace ALSA +#endif // USE_ALSA + // Config implementation enum OplEmulator { kAuto = 0, kMame = 1, - kDOSBox = 2 + kDOSBox = 2, + kALSA = 3 }; OPL::OPL() { @@ -50,6 +62,9 @@ const Config::EmulatorDescription Config::_drivers[] = { { "mame", _s("MAME OPL emulator"), kMame, kFlagOpl2 }, #ifndef DISABLE_DOSBOX_OPL { "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 }, +#endif +#ifdef USE_ALSA + { "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 }, #endif { 0, 0, 0, 0 } }; @@ -63,6 +78,15 @@ Config::DriverId Config::parse(const Common::String &name) { return -1; } +const Config::EmulatorDescription *Config::findDriver(DriverId id) { + for (int i = 0; _drivers[i].name; ++i) { + if (_drivers[i].id == id) + return &_drivers[i]; + } + + return 0; +} + Config::DriverId Config::detect(OplType type) { uint32 flags = 0; switch (type) { @@ -80,12 +104,21 @@ Config::DriverId Config::detect(OplType type) { } DriverId drv = parse(ConfMan.get("opl_driver")); + if (drv == kAuto) { + // Since the "auto" can be explicitly set for a game, and this + // driver shows up in the GUI as "", check if there is + // a global setting for it before resorting to auto-detection. + drv = parse(ConfMan.get("opl_driver", Common::ConfigManager::kApplicationDomain)); + } // When a valid driver is selected, check whether it supports // the requested OPL chip. if (drv != -1 && drv != kAuto) { + const EmulatorDescription *driverDesc = findDriver(drv); // If the chip is supported, just use the driver. - if ((flags & _drivers[drv].flags)) { + if (!driverDesc) { + warning("The selected OPL driver %d could not be found", drv); + } else if ((flags & driverDesc->flags)) { return drv; } else { // Else we will output a warning and just @@ -145,6 +178,11 @@ OPL *Config::create(DriverId driver, OplType type) { return new DOSBox::OPL(type); #endif +#ifdef USE_ALSA + case kALSA: + return ALSA::create(type); +#endif + default: warning("Unsupported OPL emulator %d", driver); // TODO: Maybe we should add some dummy emulator too, which just outputs @@ -153,43 +191,143 @@ OPL *Config::create(DriverId driver, OplType type) { } } +void OPL::start(TimerCallback *callback, int timerFrequency) { + _callback.reset(callback); + startCallbacks(timerFrequency); +} + +void OPL::stop() { + stopCallbacks(); + _callback.reset(); +} + bool OPL::_hasInstance = false; -} // End of namespace OPL - -void OPLDestroy(FM_OPL *OPL) { - delete OPL; +RealOPL::RealOPL() : _baseFreq(0), _remainingTicks(0) { } -void OPLResetChip(FM_OPL *OPL) { - OPL->reset(); +RealOPL::~RealOPL() { + // Stop callbacks, just in case. If it's still playing at this + // point, there's probably a bigger issue, though. The subclass + // needs to call stop() or the pointer can still use be used in + // the mixer thread at the same time. + stop(); } -void OPLWrite(FM_OPL *OPL, int a, int v) { - OPL->write(a, v); +void RealOPL::setCallbackFrequency(int timerFrequency) { + stopCallbacks(); + startCallbacks(timerFrequency); } -unsigned char OPLRead(FM_OPL *OPL, int a) { - return OPL->read(a); +void RealOPL::startCallbacks(int timerFrequency) { + _baseFreq = timerFrequency; + assert(_baseFreq > 0); + + // We can't request more a timer faster than 100Hz. We'll handle this by calling + // the proc multiple times in onTimer() later on. + if (timerFrequency > kMaxFreq) + timerFrequency = kMaxFreq; + + _remainingTicks = 0; + g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealOPL"); } -void OPLWriteReg(FM_OPL *OPL, int r, int v) { - OPL->writeReg(r, v); +void RealOPL::stopCallbacks() { + g_system->getTimerManager()->removeTimerProc(timerProc); + _baseFreq = 0; + _remainingTicks = 0; } -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) { - OPL->readBuffer(buffer, length); +void RealOPL::timerProc(void *refCon) { + static_cast(refCon)->onTimer(); } -FM_OPL *makeAdLibOPL(int rate) { - FM_OPL *opl = OPL::Config::create(); +void RealOPL::onTimer() { + uint callbacks = 1; - if (opl) { - if (!opl->init(rate)) { - delete opl; - opl = 0; - } + if (_baseFreq > kMaxFreq) { + // We run faster than our max, so run the callback multiple + // times to approximate the actual timer callback frequency. + uint totalTicks = _baseFreq + _remainingTicks; + callbacks = totalTicks / kMaxFreq; + _remainingTicks = totalTicks % kMaxFreq; } - return opl; + // Call the callback multiple times. The if is on the inside of the + // loop in case the callback removes itself. + for (uint i = 0; i < callbacks; i++) + if (_callback && _callback->isValid()) + (*_callback)(); } + +EmulatedOPL::EmulatedOPL() : + _nextTick(0), + _samplesPerTick(0), + _baseFreq(0), + _handle(new Audio::SoundHandle()) { +} + +EmulatedOPL::~EmulatedOPL() { + // Stop callbacks, just in case. If it's still playing at this + // point, there's probably a bigger issue, though. The subclass + // needs to call stop() or the pointer can still use be used in + // the mixer thread at the same time. + stop(); + + delete _handle; +} + +int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) { + const int stereoFactor = isStereo() ? 2 : 1; + int len = numSamples / stereoFactor; + int step; + + do { + step = len; + if (step > (_nextTick >> FIXP_SHIFT)) + step = (_nextTick >> FIXP_SHIFT); + + generateSamples(buffer, step * stereoFactor); + + _nextTick -= step << FIXP_SHIFT; + if (!(_nextTick >> FIXP_SHIFT)) { + if (_callback && _callback->isValid()) + (*_callback)(); + + _nextTick += _samplesPerTick; + } + + buffer += step * stereoFactor; + len -= step; + } while (len); + + return numSamples; +} + +int EmulatedOPL::getRate() const { + return g_system->getMixer()->getOutputRate(); +} + +void EmulatedOPL::startCallbacks(int timerFrequency) { + setCallbackFrequency(timerFrequency); + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +void EmulatedOPL::stopCallbacks() { + g_system->getMixer()->stopHandle(*_handle); +} + +void EmulatedOPL::setCallbackFrequency(int timerFrequency) { + _baseFreq = timerFrequency; + assert(_baseFreq != 0); + + int d = getRate() / _baseFreq; + int r = getRate() % _baseFreq; + + // This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ + // but less prone to arithmetic overflow. + + _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq; +} + +} // End of namespace OPL diff --git a/audio/fmopl.h b/audio/fmopl.h index 85ac606c7a5..ba0872d87ba 100644 --- a/audio/fmopl.h +++ b/audio/fmopl.h @@ -23,8 +23,16 @@ #ifndef AUDIO_FMOPL_H #define AUDIO_FMOPL_H +#include "audio/audiostream.h" + +#include "common/func.h" +#include "common/ptr.h" #include "common/scummsys.h" +namespace Audio { +class SoundHandle; +} + namespace Common { class String; } @@ -70,6 +78,12 @@ public: */ static DriverId parse(const Common::String &name); + /** + * @return The driver description for the given id or 0 in case it is not + * available. + */ + static const EmulatorDescription *findDriver(DriverId id); + /** * Detects a driver for the specific type. * @@ -92,6 +106,14 @@ private: static const EmulatorDescription _drivers[]; }; +/** + * The type of the OPL timer callback functor. + */ +typedef Common::Functor0 TimerCallback; + +/** + * A representation of a Yamaha OPL chip. + */ class OPL { private: static bool _hasInstance; @@ -102,10 +124,9 @@ public: /** * Initializes the OPL emulator. * - * @param rate output sample rate * @return true on success, false on failure */ - virtual bool init(int rate) = 0; + virtual bool init() = 0; /** * Reinitializes the OPL emulator @@ -139,6 +160,101 @@ public: */ virtual void writeReg(int r, int v) = 0; + /** + * Start the OPL with callbacks. + */ + void start(TimerCallback *callback, int timerFrequency = kDefaultCallbackFrequency); + + /** + * Stop the OPL + */ + void stop(); + + /** + * Change the callback frequency. This must only be called from a + * timer proc. + */ + virtual void setCallbackFrequency(int timerFrequency) = 0; + + enum { + /** + * The default callback frequency that start() uses + */ + kDefaultCallbackFrequency = 250 + }; + +protected: + /** + * Start the callbacks. + */ + virtual void startCallbacks(int timerFrequency) = 0; + + /** + * Stop the callbacks. + */ + virtual void stopCallbacks() = 0; + + /** + * The functor for callbacks. + */ + Common::ScopedPtr _callback; +}; + +/** + * An OPL that represents a real OPL, as opposed to an emulated one. + * + * This will use an actual timer instead of using one calculated from + * the number of samples in an AudioStream::readBuffer call. + */ +class RealOPL : public OPL { +public: + RealOPL(); + virtual ~RealOPL(); + + // OPL API + void setCallbackFrequency(int timerFrequency); + +protected: + // OPL API + void startCallbacks(int timerFrequency); + void stopCallbacks(); + +private: + static void timerProc(void *refCon); + void onTimer(); + + uint _baseFreq; + uint _remainingTicks; + + enum { + kMaxFreq = 100 + }; +}; + +/** + * An OPL that represents an emulated OPL. + * + * This will send callbacks based on the number of samples + * decoded in readBuffer(). + */ +class EmulatedOPL : public OPL, protected Audio::AudioStream { +public: + EmulatedOPL(); + virtual ~EmulatedOPL(); + + // OPL API + void setCallbackFrequency(int timerFrequency); + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples); + int getRate() const; + bool endOfData() const { return false; } + +protected: + // OPL API + void startCallbacks(int timerFrequency); + void stopCallbacks(); + /** * Read up to 'length' samples. * @@ -149,33 +265,21 @@ public: * So if you request 4 samples from a stereo OPL, you will get * a total of two left channel and two right channel samples. */ - virtual void readBuffer(int16 *buffer, int length) = 0; + virtual void generateSamples(int16 *buffer, int numSamples) = 0; - /** - * Returns whether the setup OPL mode is stereo or not - */ - virtual bool isStereo() const = 0; +private: + int _baseFreq; + + enum { + FIXP_SHIFT = 16 + }; + + int _nextTick; + int _samplesPerTick; + + Audio::SoundHandle *_handle; }; } // End of namespace OPL -// Legacy API -// !You should not write any new code using the legacy API! -typedef OPL::OPL FM_OPL; - -void OPLDestroy(FM_OPL *OPL); - -void OPLResetChip(FM_OPL *OPL); -void OPLWrite(FM_OPL *OPL, int a, int v); -unsigned char OPLRead(FM_OPL *OPL, int a); -void OPLWriteReg(FM_OPL *OPL, int r, int v); -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length); - -/** - * Legacy factory to create an AdLib (OPL2) chip. - * - * !You should not write any new code using the legacy API! - */ -FM_OPL *makeAdLibOPL(int rate); - #endif diff --git a/audio/midiparser.h b/audio/midiparser.h index 9c10462cd76..2cca56b14c9 100644 --- a/audio/midiparser.h +++ b/audio/midiparser.h @@ -370,6 +370,7 @@ public: public: typedef void (*XMidiCallbackProc)(byte eventData, void *refCon); + typedef void (*XMidiNewTimbreListProc)(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize); MidiParser(); virtual ~MidiParser() { allNotesOff(); } @@ -395,7 +396,7 @@ public: static void defaultXMidiCallback(byte eventData, void *refCon); static MidiParser *createParser_SMF(); - static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0); + static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0, XMidiNewTimbreListProc newTimbreListProc = NULL, MidiDriver_BASE *newTimbreListDriver = NULL); static MidiParser *createParser_QT(); static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); } }; diff --git a/audio/miles.h b/audio/miles.h new file mode 100644 index 00000000000..23d5998fba5 --- /dev/null +++ b/audio/miles.h @@ -0,0 +1,83 @@ +/* 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. + * + */ + +#ifndef AUDIO_MILES_MIDIDRIVER_H +#define AUDIO_MILES_MIDIDRIVER_H + +#include "audio/mididrv.h" +#include "common/error.h" +#include "common/stream.h" + +namespace Audio { + +#define MILES_MIDI_CHANNEL_COUNT 16 + +// Miles Audio supported controllers for control change messages +#define MILES_CONTROLLER_SELECT_PATCH_BANK 114 +#define MILES_CONTROLLER_PROTECT_VOICE 112 +#define MILES_CONTROLLER_PROTECT_TIMBRE 113 +#define MILES_CONTROLLER_MODULATION 1 +#define MILES_CONTROLLER_VOLUME 7 +#define MILES_CONTROLLER_EXPRESSION 11 +#define MILES_CONTROLLER_PANNING 10 +#define MILES_CONTROLLER_SUSTAIN 64 +#define MILES_CONTROLLER_PITCH_RANGE 6 +#define MILES_CONTROLLER_RESET_ALL 121 +#define MILES_CONTROLLER_ALL_NOTES_OFF 123 +#define MILES_CONTROLLER_PATCH_REVERB 59 +#define MILES_CONTROLLER_PATCH_BENDER 60 +#define MILES_CONTROLLER_REVERB_MODE 61 +#define MILES_CONTROLLER_REVERB_TIME 62 +#define MILES_CONTROLLER_REVERB_LEVEL 63 +#define MILES_CONTROLLER_RHYTHM_KEY_TIMBRE 58 + +// 3 SysEx controllers, each range 5 +// 32-36 for 1st queue +// 37-41 for 2nd queue +// 42-46 for 3rd queue +#define MILES_CONTROLLER_SYSEX_RANGE_BEGIN 32 +#define MILES_CONTROLLER_SYSEX_RANGE_END 46 + +#define MILES_CONTROLLER_SYSEX_QUEUE_COUNT 3 +#define MILES_CONTROLLER_SYSEX_QUEUE_SIZE 32 + +#define MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS1 0 +#define MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS2 1 +#define MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS3 2 +#define MILES_CONTROLLER_SYSEX_COMMAND_DATA 3 +#define MILES_CONTROLLER_SYSEX_COMMAND_SEND 4 + +#define MILES_CONTROLLER_XMIDI_RANGE_BEGIN 110 +#define MILES_CONTROLLER_XMIDI_RANGE_END 120 + +// Miles Audio actually used 0x4000, because they didn't shift the 2 bytes properly +#define MILES_PITCHBENDER_DEFAULT 0x2000 + +extern MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, const Common::String &filenameOPL3, Common::SeekableReadStream *streamAdLib = nullptr, Common::SeekableReadStream *streamOPL3 = nullptr); + +extern MidiDriver *MidiDriver_Miles_MT32_create(const Common::String &instrumentDataFilename); + +extern void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize); + +} // End of namespace Audio + +#endif // AUDIO_MILES_MIDIDRIVER_H diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp new file mode 100644 index 00000000000..bf5c9d4a731 --- /dev/null +++ b/audio/miles_adlib.cpp @@ -0,0 +1,1274 @@ +/* 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. + * + */ + +#include "audio/miles.h" + +#include "common/file.h" +#include "common/system.h" +#include "common/textconsole.h" + +#include "audio/fmopl.h" +#include "audio/softsynth/emumidi.h" + +namespace Audio { + +// Miles Audio AdLib/OPL3 driver +// +// TODO: currently missing: OPL3 4-op voices +// +// Special cases (great for testing): +// - sustain feature is used by Return To Zork (demo) right at the start +// - sherlock holmes 2 does lots of priority sorts right at the start of the intro + +#define MILES_ADLIB_VIRTUAL_FMVOICES_COUNT_MAX 20 +#define MILES_ADLIB_PHYSICAL_FMVOICES_COUNT_MAX 18 + +#define MILES_ADLIB_PERCUSSION_BANK 127 + +#define MILES_ADLIB_STEREO_PANNING_THRESHOLD_LEFT 27 +#define MILES_ADLIB_STEREO_PANNING_THRESHOLD_RIGHT 100 + +enum kMilesAdLibUpdateFlags { + kMilesAdLibUpdateFlags_None = 0, + kMilesAdLibUpdateFlags_Reg_20 = 1 << 0, + kMilesAdLibUpdateFlags_Reg_40 = 1 << 1, + kMilesAdLibUpdateFlags_Reg_60 = 1 << 2, // register 0x6x + 0x8x + kMilesAdLibUpdateFlags_Reg_C0 = 1 << 3, + kMilesAdLibUpdateFlags_Reg_E0 = 1 << 4, + kMilesAdLibUpdateFlags_Reg_A0 = 1 << 5, // register 0xAx + 0xBx + kMilesAdLibUpdateFlags_Reg_All = 0x3F +}; + +uint16 milesAdLibOperator1Register[MILES_ADLIB_PHYSICAL_FMVOICES_COUNT_MAX] = { + 0x0000, 0x0001, 0x0002, 0x0008, 0x0009, 0x000A, 0x0010, 0x0011, 0x0012, + 0x0100, 0x0101, 0x0102, 0x0108, 0x0109, 0x010A, 0x0110, 0x0111, 0x0112 +}; + +uint16 milesAdLibOperator2Register[MILES_ADLIB_PHYSICAL_FMVOICES_COUNT_MAX] = { + 0x0003, 0x0004, 0x0005, 0x000B, 0x000C, 0x000D, 0x0013, 0x0014, 0x0015, + 0x0103, 0x0104, 0x0105, 0x010B, 0x010C, 0x010D, 0x0113, 0x0114, 0x0115 +}; + +uint16 milesAdLibChannelRegister[MILES_ADLIB_PHYSICAL_FMVOICES_COUNT_MAX] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108 +}; + +struct InstrumentEntry { + byte bankId; + byte patchId; + int16 transposition; + byte reg20op1; + byte reg40op1; + byte reg60op1; + byte reg80op1; + byte regE0op1; + byte reg20op2; + byte reg40op2; + byte reg60op2; + byte reg80op2; + byte regE0op2; + byte regC0; +}; + +// hardcoded, dumped from ADLIB.MDI +uint16 milesAdLibFrequencyLookUpTable[] = { + 0x02B2, 0x02B4, 0x02B7, 0x02B9, 0x02BC, 0x02BE, 0x02C1, 0x02C3, 0x02C6, 0x02C9, 0x02CB, 0x02CE, + 0x02D0, 0x02D3, 0x02D6, 0x02D8, 0x02DB, 0x02DD, 0x02E0, 0x02E3, 0x02E5, 0x02E8, 0x02EB, 0x02ED, + 0x02F0, 0x02F3, 0x02F6, 0x02F8, 0x02FB, 0x02FE, 0x0301, 0x0303, 0x0306, 0x0309, 0x030C, 0x030F, + 0x0311, 0x0314, 0x0317, 0x031A, 0x031D, 0x0320, 0x0323, 0x0326, 0x0329, 0x032B, 0x032E, 0x0331, + 0x0334, 0x0337, 0x033A, 0x033D, 0x0340, 0x0343, 0x0346, 0x0349, 0x034C, 0x034F, 0x0352, 0x0356, + 0x0359, 0x035C, 0x035F, 0x0362, 0x0365, 0x0368, 0x036B, 0x036F, 0x0372, 0x0375, 0x0378, 0x037B, + 0x037F, 0x0382, 0x0385, 0x0388, 0x038C, 0x038F, 0x0392, 0x0395, 0x0399, 0x039C, 0x039F, 0x03A3, + 0x03A6, 0x03A9, 0x03AD, 0x03B0, 0x03B4, 0x03B7, 0x03BB, 0x03BE, 0x03C1, 0x03C5, 0x03C8, 0x03CC, + 0x03CF, 0x03D3, 0x03D7, 0x03DA, 0x03DE, 0x03E1, 0x03E5, 0x03E8, 0x03EC, 0x03F0, 0x03F3, 0x03F7, + 0x03FB, 0x03FE, 0xFE01, 0xFE03, 0xFE05, 0xFE07, 0xFE08, 0xFE0A, 0xFE0C, 0xFE0E, 0xFE10, 0xFE12, + 0xFE14, 0xFE16, 0xFE18, 0xFE1A, 0xFE1C, 0xFE1E, 0xFE20, 0xFE21, 0xFE23, 0xFE25, 0xFE27, 0xFE29, + 0xFE2B, 0xFE2D, 0xFE2F, 0xFE31, 0xFE34, 0xFE36, 0xFE38, 0xFE3A, 0xFE3C, 0xFE3E, 0xFE40, 0xFE42, + 0xFE44, 0xFE46, 0xFE48, 0xFE4A, 0xFE4C, 0xFE4F, 0xFE51, 0xFE53, 0xFE55, 0xFE57, 0xFE59, 0xFE5C, + 0xFE5E, 0xFE60, 0xFE62, 0xFE64, 0xFE67, 0xFE69, 0xFE6B, 0xFE6D, 0xFE6F, 0xFE72, 0xFE74, 0xFE76, + 0xFE79, 0xFE7B, 0xFE7D, 0xFE7F, 0xFE82, 0xFE84, 0xFE86, 0xFE89, 0xFE8B, 0xFE8D, 0xFE90, 0xFE92, + 0xFE95, 0xFE97, 0xFE99, 0xFE9C, 0xFE9E, 0xFEA1, 0xFEA3, 0xFEA5, 0xFEA8, 0xFEAA, 0xFEAD, 0xFEAF +}; + +// hardcoded, dumped from ADLIB.MDI +uint16 milesAdLibVolumeSensitivityTable[] = { + 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127 +}; + + +class MidiDriver_Miles_AdLib : public MidiDriver { +public: + MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount); + virtual ~MidiDriver_Miles_AdLib(); + + // MidiDriver + int open(); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel() { return NULL; } + MidiChannel *getPercussionChannel() { return NULL; } + + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } + + void setVolume(byte volume); + virtual uint32 property(int prop, uint32 param); + + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); + +private: + bool _modeOPL3; + byte _modePhysicalFmVoicesCount; + byte _modeVirtualFmVoicesCount; + bool _modeStereo; + + // Structure to hold information about current status of MIDI Channels + struct MidiChannelEntry { + byte currentPatchBank; + const InstrumentEntry *currentInstrumentPtr; + uint16 currentPitchBender; + byte currentPitchRange; + byte currentVoiceProtection; + + byte currentVolume; + byte currentVolumeExpression; + + byte currentPanning; + + byte currentModulation; + byte currentSustain; + + byte currentActiveVoicesCount; + + MidiChannelEntry() : currentPatchBank(0), + currentInstrumentPtr(NULL), + currentPitchBender(MILES_PITCHBENDER_DEFAULT), + currentPitchRange(0), + currentVoiceProtection(0), + currentVolume(0), currentVolumeExpression(0), + currentPanning(0), + currentModulation(0), + currentSustain(0), + currentActiveVoicesCount(0) { } + }; + + // Structure to hold information about current status of virtual FM Voices + struct VirtualFmVoiceEntry { + bool inUse; + byte actualMidiChannel; + + const InstrumentEntry *currentInstrumentPtr; + + bool isPhysical; + byte physicalFmVoice; + + uint16 currentPriority; + + byte currentOriginalMidiNote; + byte currentNote; + int16 currentTransposition; + byte currentVelocity; + + bool sustained; + + VirtualFmVoiceEntry(): inUse(false), + actualMidiChannel(0), + currentInstrumentPtr(NULL), + isPhysical(false), physicalFmVoice(0), + currentPriority(0), + currentOriginalMidiNote(0), + currentNote(0), + currentTransposition(0), + currentVelocity(0), + sustained(false) { } + }; + + // Structure to hold information about current status of physical FM Voices + struct PhysicalFmVoiceEntry { + bool inUse; + byte virtualFmVoice; + + byte currentB0hReg; + + PhysicalFmVoiceEntry(): inUse(false), + virtualFmVoice(0), + currentB0hReg(0) { } + }; + + OPL::OPL *_opl; + int _masterVolume; + + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + + bool _isOpen; + + // stores information about all MIDI channels (not the actual OPL FM voice channels!) + MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT]; + + // stores information about all virtual OPL FM voices + VirtualFmVoiceEntry _virtualFmVoices[MILES_ADLIB_VIRTUAL_FMVOICES_COUNT_MAX]; + + // stores information about all physical OPL FM voices + PhysicalFmVoiceEntry _physicalFmVoices[MILES_ADLIB_PHYSICAL_FMVOICES_COUNT_MAX]; + + // holds all instruments + InstrumentEntry *_instrumentTablePtr; + uint16 _instrumentTableCount; + + bool circularPhysicalAssignment; + byte circularPhysicalAssignmentFmVoice; + + void onTimer(); + + void resetData(); + void resetAdLib(); + void resetAdLibOperatorRegisters(byte baseRegister, byte value); + void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value); + + void setRegister(int reg, int value); + + int16 searchFreeVirtualFmVoiceChannel(); + int16 searchFreePhysicalFmVoiceChannel(); + + void noteOn(byte midiChannel, byte note, byte velocity); + void noteOff(byte midiChannel, byte note); + + void prioritySort(); + + void releaseFmVoice(byte virtualFmVoice); + + void releaseSustain(byte midiChannel); + + void updatePhysicalFmVoice(byte virtualFmVoice, bool keyOn, uint16 registerUpdateFlags); + + void controlChange(byte midiChannel, byte controllerNumber, byte controllerValue); + void programChange(byte midiChannel, byte patchId); + + const InstrumentEntry *searchInstrument(byte bankId, byte patchId); + + void pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2); +}; + +MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount) + : _masterVolume(15), _opl(0), + _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) { + + _instrumentTablePtr = instrumentTablePtr; + _instrumentTableCount = instrumentTableCount; + + // Set up for OPL3, we will downgrade in case we can't create OPL3 emulator + // regular AdLib (OPL2) card + _modeOPL3 = true; + _modeVirtualFmVoicesCount = 20; + _modePhysicalFmVoicesCount = 18; + _modeStereo = true; + + // Older Miles Audio drivers did not do a circular assign for physical FM-voices + // Sherlock Holmes 2 used the circular assign + circularPhysicalAssignment = true; + // this way the first circular physical FM-voice search will start at FM-voice 0 + circularPhysicalAssignmentFmVoice = MILES_ADLIB_PHYSICAL_FMVOICES_COUNT_MAX; + + resetData(); +} + +MidiDriver_Miles_AdLib::~MidiDriver_Miles_AdLib() { + delete[] _instrumentTablePtr; // is created in factory MidiDriver_Miles_AdLib_create() +} + +int MidiDriver_Miles_AdLib::open() { + if (_modeOPL3) { + // Try to create OPL3 first + _opl = OPL::Config::create(OPL::Config::kOpl3); + } + if (!_opl) { + // not created yet, downgrade to OPL2 + _modeOPL3 = false; + _modeVirtualFmVoicesCount = 16; + _modePhysicalFmVoicesCount = 9; + _modeStereo = false; + + _opl = OPL::Config::create(OPL::Config::kOpl2); + } + + if (!_opl) { + // We still got nothing -> can't do anything anymore + return -1; + } + + _opl->init(); + + _isOpen = true; + + _opl->start(new Common::Functor0Mem(this, &MidiDriver_Miles_AdLib::onTimer)); + + resetAdLib(); + + return 0; +} + +void MidiDriver_Miles_AdLib::close() { + delete _opl; + _isOpen = false; +} + +void MidiDriver_Miles_AdLib::setVolume(byte volume) { + _masterVolume = volume; + //renewNotes(-1, true); +} + +void MidiDriver_Miles_AdLib::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); +} + +void MidiDriver_Miles_AdLib::resetData() { + memset(_midiChannels, 0, sizeof(_midiChannels)); + memset(_virtualFmVoices, 0, sizeof(_virtualFmVoices)); + memset(_physicalFmVoices, 0, sizeof(_physicalFmVoices)); + + for (byte midiChannel = 0; midiChannel < MILES_MIDI_CHANNEL_COUNT; midiChannel++) { + // defaults, were sent to driver during driver initialization + _midiChannels[midiChannel].currentVolume = 0x7F; + _midiChannels[midiChannel].currentPanning = 0x40; // center + _midiChannels[midiChannel].currentVolumeExpression = 127; + + // Miles Audio 2: hardcoded pitch range as a global (not channel specific), set to 12 + // Miles Audio 3: pitch range per MIDI channel + _midiChannels[midiChannel].currentPitchBender = MILES_PITCHBENDER_DEFAULT; + _midiChannels[midiChannel].currentPitchRange = 12; + } + +} + +void MidiDriver_Miles_AdLib::resetAdLib() { + if (_modeOPL3) { + setRegister(0x105, 1); // enable OPL3 + setRegister(0x104, 0); // activate 18 2-operator FM-voices + } + + setRegister(0x01, 0x20); // enable waveform control on both operators + setRegister(0x04, 0xE0); // Timer control + + setRegister(0x08, 0); // select FM music mode + setRegister(0xBD, 0); // disable Rhythm + + // reset FM voice instrument data + resetAdLibOperatorRegisters(0x20, 0); + resetAdLibOperatorRegisters(0x60, 0); + resetAdLibOperatorRegisters(0x80, 0); + resetAdLibFMVoiceChannelRegisters(0xA0, 0); + resetAdLibFMVoiceChannelRegisters(0xB0, 0); + resetAdLibFMVoiceChannelRegisters(0xC0, 0); + resetAdLibOperatorRegisters(0xE0, 0); + resetAdLibOperatorRegisters(0x40, 0x3F); +} + +void MidiDriver_Miles_AdLib::resetAdLibOperatorRegisters(byte baseRegister, byte value) { + byte physicalFmVoice = 0; + + for (physicalFmVoice = 0; physicalFmVoice < _modePhysicalFmVoicesCount; physicalFmVoice++) { + setRegister(baseRegister + milesAdLibOperator1Register[physicalFmVoice], value); + setRegister(baseRegister + milesAdLibOperator2Register[physicalFmVoice], value); + } +} + +void MidiDriver_Miles_AdLib::resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value) { + byte physicalFmVoice = 0; + + for (physicalFmVoice = 0; physicalFmVoice < _modePhysicalFmVoicesCount; physicalFmVoice++) { + setRegister(baseRegister + milesAdLibChannelRegister[physicalFmVoice], value); + } +} + +// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php +void MidiDriver_Miles_AdLib::send(uint32 b) { + byte command = b & 0xf0; + byte channel = b & 0xf; + byte op1 = (b >> 8) & 0xff; + byte op2 = (b >> 16) & 0xff; + + switch (command) { + case 0x80: + noteOff(channel, op1); + break; + case 0x90: + noteOn(channel, op1, op2); + break; + case 0xb0: // Control change + controlChange(channel, op1, op2); + break; + case 0xc0: // Program Change + programChange(channel, op1); + break; + case 0xa0: // Polyphonic key pressure (aftertouch) + case 0xd0: // Channel pressure (aftertouch) + // Aftertouch doesn't seem to be implemented in the Miles Audio AdLib driver + break; + case 0xe0: + pitchBendChange(channel, op1, op2); + break; + case 0xf0: // SysEx + warning("MILES-ADLIB: SysEx: %x", b); + break; + default: + warning("MILES-ADLIB: Unknown event %02x", command); + } +} + +void MidiDriver_Miles_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + +int16 MidiDriver_Miles_AdLib::searchFreeVirtualFmVoiceChannel() { + for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (!_virtualFmVoices[virtualFmVoice].inUse) + return virtualFmVoice; + } + return -1; +} + +int16 MidiDriver_Miles_AdLib::searchFreePhysicalFmVoiceChannel() { + if (!circularPhysicalAssignment) { + // Older assign logic + for (byte physicalFmVoice = 0; physicalFmVoice < _modePhysicalFmVoicesCount; physicalFmVoice++) { + if (!_physicalFmVoices[physicalFmVoice].inUse) + return physicalFmVoice; + } + } else { + // Newer one + // Remembers last physical FM-voice and searches from that spot + byte physicalFmVoice = circularPhysicalAssignmentFmVoice; + for (byte physicalFmVoiceCount = 0; physicalFmVoiceCount < _modePhysicalFmVoicesCount; physicalFmVoiceCount++) { + physicalFmVoice++; + if (physicalFmVoice >= _modePhysicalFmVoicesCount) + physicalFmVoice = 0; + if (!_physicalFmVoices[physicalFmVoice].inUse) { + circularPhysicalAssignmentFmVoice = physicalFmVoice; + return physicalFmVoice; + } + } + } + return -1; +} + +void MidiDriver_Miles_AdLib::noteOn(byte midiChannel, byte note, byte velocity) { + const InstrumentEntry *instrumentPtr = NULL; + + if (velocity == 0) { + noteOff(midiChannel, note); + return; + } + + if (midiChannel == 9) { + // percussion channel + // search for instrument according to given note + instrumentPtr = searchInstrument(MILES_ADLIB_PERCUSSION_BANK, note); + } else { + // directly get instrument of channel + instrumentPtr = _midiChannels[midiChannel].currentInstrumentPtr; + } + if (!instrumentPtr) { + warning("MILES-ADLIB: noteOn: invalid instrument"); + return; + } + + //warning("Note On: channel %d, note %d, velocity %d, instrument %d/%d", midiChannel, note, velocity, instrumentPtr->bankId, instrumentPtr->patchId); + + // look for free virtual FM voice + int16 virtualFmVoice = searchFreeVirtualFmVoiceChannel(); + + if (virtualFmVoice == -1) { + // Out of virtual voices, can't do anything about it + return; + } + + // Scale back velocity + velocity = (velocity & 0x7F) >> 3; + velocity = milesAdLibVolumeSensitivityTable[velocity]; + + if (midiChannel != 9) { + _virtualFmVoices[virtualFmVoice].currentNote = note; + _virtualFmVoices[virtualFmVoice].currentTransposition = instrumentPtr->transposition; + } else { + // Percussion channel + _virtualFmVoices[virtualFmVoice].currentNote = instrumentPtr->transposition; + _virtualFmVoices[virtualFmVoice].currentTransposition = 0; + } + + _virtualFmVoices[virtualFmVoice].inUse = true; + _virtualFmVoices[virtualFmVoice].actualMidiChannel = midiChannel; + _virtualFmVoices[virtualFmVoice].currentOriginalMidiNote = note; + _virtualFmVoices[virtualFmVoice].currentInstrumentPtr = instrumentPtr; + _virtualFmVoices[virtualFmVoice].currentVelocity = velocity; + _virtualFmVoices[virtualFmVoice].isPhysical = false; + _virtualFmVoices[virtualFmVoice].sustained = false; + _virtualFmVoices[virtualFmVoice].currentPriority = 32767; + + int16 physicalFmVoice = searchFreePhysicalFmVoiceChannel(); + if (physicalFmVoice == -1) { + // None found + // go through priorities and reshuffle voices + prioritySort(); + return; + } + + // Another voice active on this MIDI channel + _midiChannels[midiChannel].currentActiveVoicesCount++; + + // Mark virtual FM-Voice as being connected to physical FM-Voice + _virtualFmVoices[virtualFmVoice].isPhysical = true; + _virtualFmVoices[virtualFmVoice].physicalFmVoice = physicalFmVoice; + + // Mark physical FM-Voice as being connected to virtual FM-Voice + _physicalFmVoices[physicalFmVoice].inUse = true; + _physicalFmVoices[physicalFmVoice].virtualFmVoice = virtualFmVoice; + + // Update the physical FM-Voice + updatePhysicalFmVoice(virtualFmVoice, true, kMilesAdLibUpdateFlags_Reg_All); +} + +void MidiDriver_Miles_AdLib::noteOff(byte midiChannel, byte note) { + //warning("Note Off: channel %d, note %d", midiChannel, note); + + // Search through all virtual FM-Voices for current midiChannel + note + for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + if ((_virtualFmVoices[virtualFmVoice].actualMidiChannel == midiChannel) && (_virtualFmVoices[virtualFmVoice].currentOriginalMidiNote == note)) { + // found one + if (_midiChannels[midiChannel].currentSustain >= 64) { + _virtualFmVoices[virtualFmVoice].sustained = true; + continue; + } + // + releaseFmVoice(virtualFmVoice); + } + } + } +} + +void MidiDriver_Miles_AdLib::prioritySort() { + byte virtualFmVoice = 0; + uint16 virtualPriority = 0; + uint16 virtualPriorities[MILES_ADLIB_VIRTUAL_FMVOICES_COUNT_MAX]; + uint16 virtualFmVoicesCount = 0; + byte midiChannel = 0; + + memset(&virtualPriorities, 0, sizeof(virtualPriorities)); + + //warning("prioritysort"); + + // First calculate priorities for all virtual FM voices, that are in use + for (virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + virtualFmVoicesCount++; + + midiChannel = _virtualFmVoices[virtualFmVoice].actualMidiChannel; + if (_midiChannels[midiChannel].currentVoiceProtection >= 64) { + // Voice protection enabled + virtualPriority = 0xFFFF; + } else { + virtualPriority = _virtualFmVoices[virtualFmVoice].currentPriority; + } + byte currentActiveVoicesCount = _midiChannels[midiChannel].currentActiveVoicesCount; + if (virtualPriority >= currentActiveVoicesCount) { + virtualPriority -= _midiChannels[midiChannel].currentActiveVoicesCount; + } else { + virtualPriority = 0; // overflow, should never happen + } + virtualPriorities[virtualFmVoice] = virtualPriority; + } + } + + // + while (virtualFmVoicesCount) { + uint16 unvoicedHighestPriority = 0; + byte unvoicedHighestFmVoice = 0; + uint16 voicedLowestPriority = 65535; + byte voicedLowestFmVoice = 0; + + for (virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + virtualPriority = virtualPriorities[virtualFmVoice]; + if (!_virtualFmVoices[virtualFmVoice].isPhysical) { + // currently not physical, so unvoiced + if (virtualPriority >= unvoicedHighestPriority) { + unvoicedHighestPriority = virtualPriority; + unvoicedHighestFmVoice = virtualFmVoice; + } + } else { + // currently physical, so voiced + if (virtualPriority <= voicedLowestPriority) { + voicedLowestPriority = virtualPriority; + voicedLowestFmVoice = virtualFmVoice; + } + } + } + } + + if (unvoicedHighestPriority < voicedLowestPriority) + break; // We are done + + if (unvoicedHighestPriority == 0) + break; + + // Safety checks + assert(_virtualFmVoices[voicedLowestFmVoice].isPhysical); + assert(!_virtualFmVoices[unvoicedHighestFmVoice].isPhysical); + + // Steal this physical voice + byte physicalFmVoice = _virtualFmVoices[voicedLowestFmVoice].physicalFmVoice; + + //warning("MILES-ADLIB: stealing physical FM-Voice %d from virtual FM-Voice %d for virtual FM-Voice %d", physicalFmVoice, voicedLowestFmVoice, unvoicedHighestFmVoice); + //warning("priority old %d, priority new %d", unvoicedHighestPriority, voicedLowestPriority); + + releaseFmVoice(voicedLowestFmVoice); + + // Get some data of the unvoiced highest priority virtual FM Voice + midiChannel = _virtualFmVoices[unvoicedHighestFmVoice].actualMidiChannel; + + // Another voice active on this MIDI channel + _midiChannels[midiChannel].currentActiveVoicesCount++; + + // Mark virtual FM-Voice as being connected to physical FM-Voice + _virtualFmVoices[unvoicedHighestFmVoice].isPhysical = true; + _virtualFmVoices[unvoicedHighestFmVoice].physicalFmVoice = physicalFmVoice; + + // Mark physical FM-Voice as being connected to virtual FM-Voice + _physicalFmVoices[physicalFmVoice].inUse = true; + _physicalFmVoices[physicalFmVoice].virtualFmVoice = unvoicedHighestFmVoice; + + // Update the physical FM-Voice + updatePhysicalFmVoice(unvoicedHighestFmVoice, true, kMilesAdLibUpdateFlags_Reg_All); + + virtualFmVoicesCount--; + } +} + +void MidiDriver_Miles_AdLib::releaseFmVoice(byte virtualFmVoice) { + // virtual Voice not actually played? -> exit + if (!_virtualFmVoices[virtualFmVoice].isPhysical) { + _virtualFmVoices[virtualFmVoice].inUse = false; + return; + } + + byte midiChannel = _virtualFmVoices[virtualFmVoice].actualMidiChannel; + byte physicalFmVoice = _virtualFmVoices[virtualFmVoice].physicalFmVoice; + + // stop note from playing + updatePhysicalFmVoice(virtualFmVoice, false, kMilesAdLibUpdateFlags_Reg_A0); + + // this virtual FM voice isn't physical anymore + _virtualFmVoices[virtualFmVoice].isPhysical = false; + _virtualFmVoices[virtualFmVoice].inUse = false; + + // Remove physical FM-Voice from being active + _physicalFmVoices[physicalFmVoice].inUse = false; + + // One less voice active on this MIDI channel + assert(_midiChannels[midiChannel].currentActiveVoicesCount); + _midiChannels[midiChannel].currentActiveVoicesCount--; +} + +void MidiDriver_Miles_AdLib::releaseSustain(byte midiChannel) { + // Search through all virtual FM-Voices for currently sustained notes and call noteOff on them + for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + if ((_virtualFmVoices[virtualFmVoice].actualMidiChannel == midiChannel) && (_virtualFmVoices[virtualFmVoice].sustained)) { + // is currently sustained + // so do a noteOff (which will check current sustain controller) + noteOff(midiChannel, _virtualFmVoices[virtualFmVoice].currentOriginalMidiNote); + } + } + } +} + +void MidiDriver_Miles_AdLib::updatePhysicalFmVoice(byte virtualFmVoice, bool keyOn, uint16 registerUpdateFlags) { + byte midiChannel = _virtualFmVoices[virtualFmVoice].actualMidiChannel; + + if (!_virtualFmVoices[virtualFmVoice].isPhysical) { + // virtual FM-Voice has no physical FM-Voice assigned? -> ignore + return; + } + + byte physicalFmVoice = _virtualFmVoices[virtualFmVoice].physicalFmVoice; + const InstrumentEntry *instrumentPtr = _virtualFmVoices[virtualFmVoice].currentInstrumentPtr; + + uint16 op1Reg = milesAdLibOperator1Register[physicalFmVoice]; + uint16 op2Reg = milesAdLibOperator2Register[physicalFmVoice]; + uint16 channelReg = milesAdLibChannelRegister[physicalFmVoice]; + + uint16 compositeVolume = 0; + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_40) { + // Calculate new volume + byte midiVolume = _midiChannels[midiChannel].currentVolume; + byte midiVolumeExpression = _midiChannels[midiChannel].currentVolumeExpression; + compositeVolume = midiVolume * midiVolumeExpression * 2; + + compositeVolume = compositeVolume >> 8; // get upmost 8 bits + if (compositeVolume) + compositeVolume++; // round up in case result wasn't 0 + + compositeVolume = compositeVolume * _virtualFmVoices[virtualFmVoice].currentVelocity * 2; + compositeVolume = compositeVolume >> 8; // get upmost 8 bits + if (compositeVolume) + compositeVolume++; // round up in case result wasn't 0 + } + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_20) { + // Amplitude Modulation / Vibrato / Envelope Generator Type / Keyboard Scaling Rate / Modulator Frequency Multiple + byte reg20op1 = instrumentPtr->reg20op1; + byte reg20op2 = instrumentPtr->reg20op2; + + if (_midiChannels[midiChannel].currentModulation >= 64) { + // set bit 6 (Vibrato) + reg20op1 |= 0x40; + reg20op2 |= 0x40; + } + setRegister(0x20 + op1Reg, reg20op1); + setRegister(0x20 + op2Reg, reg20op2); + } + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_40) { + // Volume (Level Key Scaling / Total Level) + byte reg40op1 = instrumentPtr->reg40op1; + byte reg40op2 = instrumentPtr->reg40op2; + + uint16 volumeOp1 = (~reg40op1) & 0x3F; + uint16 volumeOp2 = (~reg40op2) & 0x3F; + + if (instrumentPtr->regC0 & 1) { + // operator 2 enabled + // scale volume factor + volumeOp1 = (volumeOp1 * compositeVolume) / 127; + // 2nd operator always scaled + } + + volumeOp2 = (volumeOp2 * compositeVolume) / 127; + + volumeOp1 = (~volumeOp1) & 0x3F; // negate it, so we get the proper value for the register + volumeOp2 = (~volumeOp2) & 0x3F; // ditto + reg40op1 = (reg40op1 & 0xC0) | volumeOp1; // keep "scaling level" and merge in our volume + reg40op2 = (reg40op2 & 0xC0) | volumeOp2; + + setRegister(0x40 + op1Reg, reg40op1); + setRegister(0x40 + op2Reg, reg40op2); + } + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_60) { + // Attack Rate / Decay Rate + // Sustain Level / Release Rate + byte reg60op1 = instrumentPtr->reg60op1; + byte reg60op2 = instrumentPtr->reg60op2; + byte reg80op1 = instrumentPtr->reg80op1; + byte reg80op2 = instrumentPtr->reg80op2; + + setRegister(0x60 + op1Reg, reg60op1); + setRegister(0x60 + op2Reg, reg60op2); + setRegister(0x80 + op1Reg, reg80op1); + setRegister(0x80 + op2Reg, reg80op2); + } + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_E0) { + // Waveform Select + byte regE0op1 = instrumentPtr->regE0op1; + byte regE0op2 = instrumentPtr->regE0op2; + + setRegister(0xE0 + op1Reg, regE0op1); + setRegister(0xE0 + op2Reg, regE0op2); + } + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_C0) { + // Feedback / Algorithm + byte regC0 = instrumentPtr->regC0; + + if (_modeOPL3) { + // Panning for OPL3 + byte panning = _midiChannels[midiChannel].currentPanning; + + if (panning <= MILES_ADLIB_STEREO_PANNING_THRESHOLD_LEFT) { + regC0 |= 0x20; // left speaker only + } else if (panning >= MILES_ADLIB_STEREO_PANNING_THRESHOLD_RIGHT) { + regC0 |= 0x10; // right speaker only + } else { + regC0 |= 0x30; // center + } + } + + setRegister(0xC0 + channelReg, regC0); + } + + if (registerUpdateFlags & kMilesAdLibUpdateFlags_Reg_A0) { + // Frequency / Key-On + // Octave / F-Number / Key-On + if (!keyOn) { + // turn off note + byte regB0 = _physicalFmVoices[physicalFmVoice].currentB0hReg & 0x1F; // remove bit 5 "key on" + setRegister(0xB0 + channelReg, regB0); + + } else { + // turn on note, calculate frequency, octave... + int16 pitchBender = _midiChannels[midiChannel].currentPitchBender; + byte pitchRange = _midiChannels[midiChannel].currentPitchRange; + int16 currentNote = _virtualFmVoices[virtualFmVoice].currentNote; + int16 physicalNote = 0; + int16 halfTone = 0; + uint16 frequency = 0; + uint16 frequencyIdx = 0; + byte octave = 0; + + pitchBender -= 0x2000; + pitchBender = pitchBender >> 5; // divide by 32 + pitchBender = pitchBender * pitchRange; // pitchrange 12: now +0x0C00 to -0xC00 + // difference between Miles Audio 2 + 3 + // Miles Audio 2 used a pitch range of 12, which was basically hardcoded + // Miles Audio 3 used an array, which got set by control change events + + currentNote += _virtualFmVoices->currentTransposition; + + // Normalize note + currentNote -= 24; + do { + currentNote += 12; + } while (currentNote < 0); + currentNote += 12; + + do { + currentNote -= 12; + } while (currentNote > 95); + + // combine note + pitchbender, also adjust by 8 for rounding + currentNote = (currentNote << 8) + pitchBender + 8; + + currentNote = currentNote >> 4; // get actual note + + // Normalize + currentNote -= (12 * 16); + do { + currentNote += (12 * 16); + } while (currentNote < 0); + + currentNote += (12 * 16); + do { + currentNote -= (12 * 16); + } while (currentNote > ((96 * 16) - 1)); + + physicalNote = currentNote >> 4; + + halfTone = physicalNote % 12; // remainder of physicalNote / 12 + + frequencyIdx = (halfTone << 4) + (currentNote & 0x0F); + assert(frequencyIdx < sizeof(milesAdLibFrequencyLookUpTable)); + frequency = milesAdLibFrequencyLookUpTable[frequencyIdx]; + + octave = (physicalNote / 12) - 1; + + if (frequency & 0x8000) + octave++; + + if (octave & 0x80) { + octave++; + frequency = frequency >> 1; + } + + byte regA0 = frequency & 0xFF; + byte regB0 = ((frequency >> 8) & 0x03) | (octave << 2) | 0x20; + + setRegister(0xA0 + channelReg, regA0); + setRegister(0xB0 + channelReg, regB0); + + _physicalFmVoices[physicalFmVoice].currentB0hReg = regB0; + } + } + + //warning("end of update voice"); +} + +void MidiDriver_Miles_AdLib::controlChange(byte midiChannel, byte controllerNumber, byte controllerValue) { + uint16 registerUpdateFlags = kMilesAdLibUpdateFlags_None; + + switch (controllerNumber) { + case MILES_CONTROLLER_SELECT_PATCH_BANK: + //warning("patch bank channel %d, bank %x", midiChannel, controllerValue); + _midiChannels[midiChannel].currentPatchBank = controllerValue; + break; + + case MILES_CONTROLLER_PROTECT_VOICE: + _midiChannels[midiChannel].currentVoiceProtection = controllerValue; + break; + + case MILES_CONTROLLER_PROTECT_TIMBRE: + // It seems that this can get ignored, because we don't cache timbres at all + break; + + case MILES_CONTROLLER_MODULATION: + _midiChannels[midiChannel].currentModulation = controllerValue; + registerUpdateFlags = kMilesAdLibUpdateFlags_Reg_20; + break; + + case MILES_CONTROLLER_VOLUME: + _midiChannels[midiChannel].currentVolume = controllerValue; + registerUpdateFlags = kMilesAdLibUpdateFlags_Reg_40; + break; + + case MILES_CONTROLLER_EXPRESSION: + _midiChannels[midiChannel].currentVolumeExpression = controllerValue; + registerUpdateFlags = kMilesAdLibUpdateFlags_Reg_40; + break; + + case MILES_CONTROLLER_PANNING: + _midiChannels[midiChannel].currentPanning = controllerValue; + if (_modeStereo) { + // Update register only in case we are in stereo mode + registerUpdateFlags = kMilesAdLibUpdateFlags_Reg_C0; + } + break; + + case MILES_CONTROLLER_SUSTAIN: + _midiChannels[midiChannel].currentSustain = controllerValue; + if (controllerValue < 64) { + releaseSustain(midiChannel); + } + break; + + case MILES_CONTROLLER_PITCH_RANGE: + // Miles Audio 3 feature + _midiChannels[midiChannel].currentPitchRange = controllerValue; + break; + + case MILES_CONTROLLER_RESET_ALL: + _midiChannels[midiChannel].currentSustain = 0; + releaseSustain(midiChannel); + _midiChannels[midiChannel].currentModulation = 0; + _midiChannels[midiChannel].currentVolumeExpression = 127; + _midiChannels[midiChannel].currentPitchBender = MILES_PITCHBENDER_DEFAULT; + registerUpdateFlags = kMilesAdLibUpdateFlags_Reg_20 | kMilesAdLibUpdateFlags_Reg_40 | kMilesAdLibUpdateFlags_Reg_A0; + break; + + case MILES_CONTROLLER_ALL_NOTES_OFF: + for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + // used + if (_virtualFmVoices[virtualFmVoice].actualMidiChannel == midiChannel) { + // by our current MIDI channel -> noteOff + noteOff(midiChannel, _virtualFmVoices[virtualFmVoice].currentNote); + } + } + } + break; + + default: + //warning("MILES-ADLIB: Unsupported control change %d", controllerNumber); + break; + } + + if (registerUpdateFlags) { + for (byte virtualFmVoice = 0; virtualFmVoice < _modeVirtualFmVoicesCount; virtualFmVoice++) { + if (_virtualFmVoices[virtualFmVoice].inUse) { + // used + if (_virtualFmVoices[virtualFmVoice].actualMidiChannel == midiChannel) { + // by our current MIDI channel -> update + updatePhysicalFmVoice(virtualFmVoice, true, registerUpdateFlags); + } + } + } + } +} + +void MidiDriver_Miles_AdLib::programChange(byte midiChannel, byte patchId) { + const InstrumentEntry *instrumentPtr = NULL; + byte patchBank = _midiChannels[midiChannel].currentPatchBank; + + //warning("patch channel %d, patch %x, bank %x", midiChannel, patchId, patchBank); + + // we check, if we actually have data for the requested instrument... + instrumentPtr = searchInstrument(patchBank, patchId); + if (!instrumentPtr) { + warning("MILES-ADLIB: unknown instrument requested (%d, %d)", patchBank, patchId); + return; + } + + // and remember it in that case for the current MIDI-channel + _midiChannels[midiChannel].currentInstrumentPtr = instrumentPtr; +} + +const InstrumentEntry *MidiDriver_Miles_AdLib::searchInstrument(byte bankId, byte patchId) { + const InstrumentEntry *instrumentPtr = _instrumentTablePtr; + + for (uint16 instrumentNr = 0; instrumentNr < _instrumentTableCount; instrumentNr++) { + if ((instrumentPtr->bankId == bankId) && (instrumentPtr->patchId == patchId)) { + return instrumentPtr; + } + instrumentPtr++; + } + + return NULL; +} + +void MidiDriver_Miles_AdLib::pitchBendChange(byte midiChannel, byte parameter1, byte parameter2) { + // Miles Audio actually didn't shift parameter 2 1 down in here + // which means in memory it used a 15-bit pitch bender, which also means the default was 0x4000 + if ((parameter1 & 0x80) || (parameter2 & 0x80)) { + warning("MILES-ADLIB: invalid pitch bend change"); + return; + } + _midiChannels[midiChannel].currentPitchBender = parameter1 | (parameter2 << 7); +} + +void MidiDriver_Miles_AdLib::setRegister(int reg, int value) { + if (!(reg & 0x100)) { + _opl->write(0x220, reg); + _opl->write(0x221, value); + //warning("OPL write %x %x (%d)", reg, value, value); + } else { + _opl->write(0x222, reg & 0xFF); + _opl->write(0x223, value); + //warning("OPL3 write %x %x (%d)", reg & 0xFF, value, value); + } +} + +uint32 MidiDriver_Miles_AdLib::property(int prop, uint32 param) { + return 0; +} + +MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, const Common::String &filenameOPL3, Common::SeekableReadStream *streamAdLib, Common::SeekableReadStream *streamOPL3) { + // Load adlib instrument data from file SAMPLE.AD (OPL3: SAMPLE.OPL) + Common::String timbreFilename; + Common::SeekableReadStream *timbreStream = nullptr; + + bool preferOPL3 = false; + + Common::File *fileStream = new Common::File(); + uint32 fileSize = 0; + uint32 fileDataOffset = 0; + uint32 fileDataLeft = 0; + + + uint32 streamSize = 0; + byte *streamDataPtr = nullptr; + + byte curBankId = 0; + byte curPatchId = 0; + + InstrumentEntry *instrumentTablePtr = nullptr; + uint16 instrumentTableCount = 0; + InstrumentEntry *instrumentPtr = nullptr; + uint32 instrumentOffset = 0; + uint16 instrumentDataSize = 0; + + // Logic: + // We prefer OPL3 timbre data in case OPL3 is available in ScummVM + // If it's not or OPL3 timbre data is not available, we go for AdLib timbre data + // And if OPL3 is not available in ScummVM and also AdLib timbre data is not available, + // we then still go for OPL3 timbre data. + // + // Note: for most games OPL3 timbre data + AdLib timbre data is the same. + // And at least in theory we should still be able to use OPL3 timbre data even for AdLib. + // However there is a special OPL3-specific timbre format, which is currently not supported. + // In this case the error message "unsupported instrument size" should appear. I haven't found + // a game that uses it, which is why I haven't implemented it yet. + + if (OPL::Config::detect(OPL::Config::kOpl3) >= 0) { + // OPL3 available, prefer OPL3 timbre data because of this + preferOPL3 = true; + } + + // Check if streams were passed to us and select one of them + if ((streamAdLib) || (streamOPL3)) { + // At least one stream was passed by caller + if (preferOPL3) { + // Prefer OPL3 timbre stream in case OPL3 is available + timbreStream = streamOPL3; + } + if (!timbreStream) { + // Otherwise prefer AdLib timbre stream first + if (streamAdLib) { + timbreStream = streamAdLib; + } else { + // If not available, use OPL3 timbre stream + if (streamOPL3) { + timbreStream = streamOPL3; + } + } + } + } + + // Now check if any filename was passed to us + if ((!filenameAdLib.empty()) || (!filenameOPL3.empty())) { + // If that's the case, check if one of those exists + if (preferOPL3) { + // OPL3 available + if (!filenameOPL3.empty()) { + if (fileStream->exists(filenameOPL3)) { + // If OPL3 available, prefer OPL3 timbre file in case file exists + timbreFilename = filenameOPL3; + } + } + if (timbreFilename.empty()) { + if (!filenameAdLib.empty()) { + if (fileStream->exists(filenameAdLib)) { + // otherwise use AdLib timbre file, if it exists + timbreFilename = filenameAdLib; + } + } + } + } else { + // OPL3 not available + // Prefer the AdLib one for now + if (!filenameAdLib.empty()) { + if (fileStream->exists(filenameAdLib)) { + // if AdLib file exists, use it + timbreFilename = filenameAdLib; + } + } + if (timbreFilename.empty()) { + if (!filenameOPL3.empty()) { + if (fileStream->exists(filenameOPL3)) { + // if OPL3 file exists, use it + timbreFilename = filenameOPL3; + } + } + } + } + if (timbreFilename.empty() && (!timbreStream)) { + // If none of them exists and also no stream was passed, we can't do anything about it + if (!filenameAdLib.empty()) { + if (!filenameOPL3.empty()) { + error("MILES-ADLIB: could not open timbre file (%s or %s)", filenameAdLib.c_str(), filenameOPL3.c_str()); + } else { + error("MILES-ADLIB: could not open timbre file (%s)", filenameAdLib.c_str()); + } + } else { + error("MILES-ADLIB: could not open timbre file (%s)", filenameOPL3.c_str()); + } + } + } + + if (!timbreFilename.empty()) { + // Filename was passed to us and file exists (this is the common case for most games) + // We prefer this situation + + if (!fileStream->open(timbreFilename)) + error("MILES-ADLIB: could not open timbre file (%s)", timbreFilename.c_str()); + + streamSize = fileStream->size(); + + streamDataPtr = new byte[streamSize]; + + if (fileStream->read(streamDataPtr, streamSize) != streamSize) + error("MILES-ADLIB: error while reading timbre file (%s)", timbreFilename.c_str()); + fileStream->close(); + + } else if (timbreStream) { + // Timbre data was passed directly (possibly read from resource file by caller) + // Currently used by "Amazon Guardians of Eden", "Simon 2" and "Return To Zork" + streamSize = timbreStream->size(); + + streamDataPtr = new byte[streamSize]; + + if (timbreStream->read(streamDataPtr, streamSize) != streamSize) + error("MILES-ADLIB: error while reading timbre stream"); + + } else { + error("MILES-ADLIB: timbre filenames nor timbre stream were passed"); + } + + delete fileStream; + + // File is like this: + // [patch:BYTE] [bank:BYTE] [patchoffset:UINT32] + // ... + // until patch + bank are both 0xFF, which signals end of header + + // First we check how many entries there are + fileDataOffset = 0; + fileDataLeft = streamSize; + while (1) { + if (fileDataLeft < 6) + error("MILES-ADLIB: unexpected EOF in instrument file"); + + curPatchId = streamDataPtr[fileDataOffset++]; + curBankId = streamDataPtr[fileDataOffset++]; + + if ((curBankId == 0xFF) && (curPatchId == 0xFF)) + break; + + fileDataOffset += 4; // skip over offset + instrumentTableCount++; + } + + if (instrumentTableCount == 0) + error("MILES-ADLIB: no instruments in instrument file"); + + // Allocate space for instruments + instrumentTablePtr = new InstrumentEntry[instrumentTableCount]; + + // Now actually read all entries + instrumentPtr = instrumentTablePtr; + + fileDataOffset = 0; + fileDataLeft = fileSize; + while (1) { + curPatchId = streamDataPtr[fileDataOffset++]; + curBankId = streamDataPtr[fileDataOffset++]; + + if ((curBankId == 0xFF) && (curPatchId == 0xFF)) + break; + + instrumentOffset = READ_LE_UINT32(streamDataPtr + fileDataOffset); + fileDataOffset += 4; + + instrumentPtr->bankId = curBankId; + instrumentPtr->patchId = curPatchId; + + instrumentDataSize = READ_LE_UINT16(streamDataPtr + instrumentOffset); + if (instrumentDataSize != 14) + error("MILES-ADLIB: unsupported instrument size"); + + instrumentPtr->transposition = (signed char)streamDataPtr[instrumentOffset + 2]; + instrumentPtr->reg20op1 = streamDataPtr[instrumentOffset + 3]; + instrumentPtr->reg40op1 = streamDataPtr[instrumentOffset + 4]; + instrumentPtr->reg60op1 = streamDataPtr[instrumentOffset + 5]; + instrumentPtr->reg80op1 = streamDataPtr[instrumentOffset + 6]; + instrumentPtr->regE0op1 = streamDataPtr[instrumentOffset + 7]; + instrumentPtr->regC0 = streamDataPtr[instrumentOffset + 8]; + instrumentPtr->reg20op2 = streamDataPtr[instrumentOffset + 9]; + instrumentPtr->reg40op2 = streamDataPtr[instrumentOffset + 10]; + instrumentPtr->reg60op2 = streamDataPtr[instrumentOffset + 11]; + instrumentPtr->reg80op2 = streamDataPtr[instrumentOffset + 12]; + instrumentPtr->regE0op2 = streamDataPtr[instrumentOffset + 13]; + + // Instrument read, next instrument please + instrumentPtr++; + } + + // Free instrument file/stream data + delete[] streamDataPtr; + + return new MidiDriver_Miles_AdLib(instrumentTablePtr, instrumentTableCount); +} + +} // End of namespace Audio diff --git a/audio/miles_mt32.cpp b/audio/miles_mt32.cpp new file mode 100644 index 00000000000..dff863f1192 --- /dev/null +++ b/audio/miles_mt32.cpp @@ -0,0 +1,912 @@ +/* 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. + * + */ + +#include "audio/miles.h" + +#include "common/config-manager.h" +#include "common/file.h" +#include "common/mutex.h" +#include "common/system.h" +#include "common/textconsole.h" + +namespace Audio { + +// Miles Audio MT32 driver +// + +#define MILES_MT32_PATCHES_COUNT 128 +#define MILES_MT32_CUSTOMTIMBRE_COUNT 64 + +#define MILES_MT32_TIMBREBANK_STANDARD_ROLAND 0 +#define MILES_MT32_TIMBREBANK_MELODIC_MODULE 127 + +#define MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE 14 +#define MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE 58 +#define MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT 4 +#define MILES_MT32_PATCHDATA_TOTAL_SIZE (MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE + (MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE * MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT)) + +#define MILES_MT32_SYSEX_TERMINATOR 0xFF + +struct MilesMT32InstrumentEntry { + byte bankId; + byte patchId; + byte commonParameter[MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE + 1]; + byte partialParameters[MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT][MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE + 1]; +}; + +const byte milesMT32SysExResetParameters[] = { + 0x01, MILES_MT32_SYSEX_TERMINATOR +}; + +const byte milesMT32SysExChansSetup[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, MILES_MT32_SYSEX_TERMINATOR +}; + +const byte milesMT32SysExPartialReserveTable[] = { + 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x04, MILES_MT32_SYSEX_TERMINATOR +}; + +const byte milesMT32SysExInitReverb[] = { + 0x00, 0x03, 0x02, MILES_MT32_SYSEX_TERMINATOR // Reverb mode 0, reverb time 3, reverb level 2 +}; + +class MidiDriver_Miles_MT32 : public MidiDriver { +public: + MidiDriver_Miles_MT32(MilesMT32InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount); + virtual ~MidiDriver_Miles_MT32(); + + // MidiDriver + int open(); + void close(); + bool isOpen() const { return _isOpen; } + + void send(uint32 b); + + MidiChannel *allocateChannel() { + if (_driver) + return _driver->allocateChannel(); + return NULL; + } + MidiChannel *getPercussionChannel() { + if (_driver) + return _driver->getPercussionChannel(); + return NULL; + } + + void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { + if (_driver) + _driver->setTimerCallback(timer_param, timer_proc); + } + + uint32 getBaseTempo() { + if (_driver) { + return _driver->getBaseTempo(); + } + return 1000000 / _baseFreq; + } + +protected: + Common::Mutex _mutex; + MidiDriver *_driver; + bool _MT32; + bool _nativeMT32; + + bool _isOpen; + int _baseFreq; + +public: + void processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize); + +private: + void resetMT32(); + + void MT32SysEx(const uint32 targetAddress, const byte *dataPtr); + + uint32 calculateSysExTargetAddress(uint32 baseAddress, uint32 index); + + void writeRhythmSetup(byte note, byte customTimbreId); + void writePatchTimbre(byte patchId, byte timbreGroup, byte timbreId); + void writePatchByte(byte patchId, byte index, byte patchValue); + void writeToSystemArea(byte index, byte value); + + void controlChange(byte midiChannel, byte controllerNumber, byte controllerValue); + void programChange(byte midiChannel, byte patchId); + + const MilesMT32InstrumentEntry *searchCustomInstrument(byte patchBank, byte patchId); + int16 searchCustomTimbre(byte patchBank, byte patchId); + + void setupPatch(byte patchBank, byte patchId); + int16 installCustomTimbre(byte patchBank, byte patchId); + +private: + struct MidiChannelEntry { + byte currentPatchBank; + byte currentPatchId; + + bool usingCustomTimbre; + byte currentCustomTimbreId; + + MidiChannelEntry() : currentPatchBank(0), + currentPatchId(0), + usingCustomTimbre(false), + currentCustomTimbreId(0) { } + }; + + struct MidiCustomTimbreEntry { + bool used; + bool protectionEnabled; + byte currentPatchBank; + byte currentPatchId; + + uint32 lastUsedNoteCounter; + + MidiCustomTimbreEntry() : used(false), + protectionEnabled(false), + currentPatchBank(0), + currentPatchId(0), + lastUsedNoteCounter(0) {} + }; + + struct MilesMT32SysExQueueEntry { + uint32 targetAddress; + byte dataPos; + byte data[MILES_CONTROLLER_SYSEX_QUEUE_SIZE + 1]; // 1 extra byte for terminator + + MilesMT32SysExQueueEntry() : targetAddress(0), + dataPos(0) { + memset(data, 0, sizeof(data)); + } + }; + + // stores information about all MIDI channels + MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT]; + + // stores information about all custom timbres + MidiCustomTimbreEntry _customTimbres[MILES_MT32_CUSTOMTIMBRE_COUNT]; + + byte _patchesBank[MILES_MT32_PATCHES_COUNT]; + + // holds all instruments + MilesMT32InstrumentEntry *_instrumentTablePtr; + uint16 _instrumentTableCount; + + uint32 _noteCounter; // used to figure out, which timbres are outdated + + // SysEx Queues + MilesMT32SysExQueueEntry _sysExQueues[MILES_CONTROLLER_SYSEX_QUEUE_COUNT]; +}; + +MidiDriver_Miles_MT32::MidiDriver_Miles_MT32(MilesMT32InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount) { + _instrumentTablePtr = instrumentTablePtr; + _instrumentTableCount = instrumentTableCount; + + _driver = NULL; + _isOpen = false; + _MT32 = false; + _nativeMT32 = false; + _baseFreq = 250; + + _noteCounter = 0; + + memset(_patchesBank, 0, sizeof(_patchesBank)); +} + +MidiDriver_Miles_MT32::~MidiDriver_Miles_MT32() { + Common::StackLock lock(_mutex); + if (_driver) { + _driver->setTimerCallback(0, 0); + _driver->close(); + delete _driver; + } + _driver = NULL; +} + +int MidiDriver_Miles_MT32::open() { + assert(!_driver); + + // Setup midi driver + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32); + MusicType musicType = MidiDriver::getMusicType(dev); + + switch (musicType) { + case MT_MT32: + _nativeMT32 = true; + break; + case MT_GM: + if (ConfMan.getBool("native_mt32")) { + _nativeMT32 = true; + } + break; + default: + break; + } + + if (!_nativeMT32) { + error("MILES-MT32: non-mt32 currently not supported!"); + } + + _driver = MidiDriver::createMidi(dev); + if (!_driver) + return 255; + + if (_nativeMT32) + _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + + int ret = _driver->open(); + if (ret) + return ret; + + if (_nativeMT32) { + _driver->sendMT32Reset(); + + resetMT32(); + } + + return 0; +} + +void MidiDriver_Miles_MT32::close() { + if (_driver) { + _driver->close(); + } +} + +void MidiDriver_Miles_MT32::resetMT32() { + // reset all internal parameters / patches + MT32SysEx(0x7F0000, milesMT32SysExResetParameters); + + // init part/channel assignments + MT32SysEx(0x10000D, milesMT32SysExChansSetup); + + // partial reserve table + MT32SysEx(0x100004, milesMT32SysExPartialReserveTable); + + // init reverb + MT32SysEx(0x100001, milesMT32SysExInitReverb); +} + +void MidiDriver_Miles_MT32::MT32SysEx(const uint32 targetAddress, const byte *dataPtr) { + byte sysExMessage[270]; + uint16 sysExPos = 0; + byte sysExByte = 0; + uint16 sysExChecksum = 0; + + memset(&sysExMessage, 0, sizeof(sysExMessage)); + + sysExMessage[0] = 0x41; // Roland + sysExMessage[1] = 0x10; + sysExMessage[2] = 0x16; // Model MT32 + sysExMessage[3] = 0x12; // Command DT1 + + sysExChecksum = 0; + + sysExMessage[4] = (targetAddress >> 16) & 0xFF; + sysExMessage[5] = (targetAddress >> 8) & 0xFF; + sysExMessage[6] = targetAddress & 0xFF; + + for (byte targetAddressByte = 4; targetAddressByte < 7; targetAddressByte++) { + assert(sysExMessage[targetAddressByte] < 0x80); // security check + sysExChecksum -= sysExMessage[targetAddressByte]; + } + + sysExPos = 7; + while (1) { + sysExByte = *dataPtr++; + if (sysExByte == MILES_MT32_SYSEX_TERMINATOR) + break; // Message done + + assert(sysExPos < sizeof(sysExMessage)); + assert(sysExByte < 0x80); // security check + sysExMessage[sysExPos++] = sysExByte; + sysExChecksum -= sysExByte; + } + + // Calculate checksum + assert(sysExPos < sizeof(sysExMessage)); + sysExMessage[sysExPos++] = sysExChecksum & 0x7f; + + // Send SysEx + _driver->sysEx(sysExMessage, sysExPos); + + // Wait the time it takes to send the SysEx data + uint32 delay = (sysExPos + 2) * 1000 / 3125; + + // Plus an additional delay for the MT-32 rev00 + if (_nativeMT32) + delay += 40; + + g_system->delayMillis(delay); +} + +// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php +void MidiDriver_Miles_MT32::send(uint32 b) { + byte command = b & 0xf0; + byte midiChannel = b & 0xf; + byte op1 = (b >> 8) & 0xff; + byte op2 = (b >> 16) & 0xff; + + switch (command) { + case 0x80: // note off + case 0x90: // note on + case 0xa0: // Polyphonic key pressure (aftertouch) + case 0xd0: // Channel pressure (aftertouch) + case 0xe0: // pitch bend change + _noteCounter++; + if (_midiChannels[midiChannel].usingCustomTimbre) { + // Remember that this timbre got used now + _customTimbres[_midiChannels[midiChannel].currentCustomTimbreId].lastUsedNoteCounter = _noteCounter; + } + _driver->send(b); + break; + case 0xb0: // Control change + controlChange(midiChannel, op1, op2); + break; + case 0xc0: // Program Change + programChange(midiChannel, op1); + break; + case 0xf0: // SysEx + warning("MILES-MT32: SysEx: %x", b); + break; + default: + warning("MILES-MT32: Unknown event %02x", command); + } +} + +void MidiDriver_Miles_MT32::controlChange(byte midiChannel, byte controllerNumber, byte controllerValue) { + byte channelPatchId = 0; + byte channelCustomTimbreId = 0; + + switch (controllerNumber) { + case MILES_CONTROLLER_SELECT_PATCH_BANK: + _midiChannels[midiChannel].currentPatchBank = controllerValue; + return; + + case MILES_CONTROLLER_PATCH_REVERB: + channelPatchId = _midiChannels[midiChannel].currentPatchId; + + writePatchByte(channelPatchId, 6, controllerValue); + _driver->send(0xC0 | midiChannel | (channelPatchId << 8)); // execute program change + return; + + case MILES_CONTROLLER_PATCH_BENDER: + channelPatchId = _midiChannels[midiChannel].currentPatchId; + + writePatchByte(channelPatchId, 4, controllerValue); + _driver->send(0xC0 | midiChannel | (channelPatchId << 8)); // execute program change + return; + + case MILES_CONTROLLER_REVERB_MODE: + writeToSystemArea(1, controllerValue); + return; + + case MILES_CONTROLLER_REVERB_TIME: + writeToSystemArea(2, controllerValue); + return; + + case MILES_CONTROLLER_REVERB_LEVEL: + writeToSystemArea(3, controllerValue); + return; + + case MILES_CONTROLLER_RHYTHM_KEY_TIMBRE: + if (_midiChannels[midiChannel].usingCustomTimbre) { + // custom timbre is set on current channel + writeRhythmSetup(controllerValue, _midiChannels[midiChannel].currentCustomTimbreId); + } + return; + + case MILES_CONTROLLER_PROTECT_TIMBRE: + if (_midiChannels[midiChannel].usingCustomTimbre) { + // custom timbre set on current channel + channelCustomTimbreId = _midiChannels[midiChannel].currentCustomTimbreId; + if (controllerValue >= 64) { + // enable protection + _customTimbres[channelCustomTimbreId].protectionEnabled = true; + } else { + // disable protection + _customTimbres[channelCustomTimbreId].protectionEnabled = false; + } + } + return; + + default: + break; + } + + if ((controllerNumber >= MILES_CONTROLLER_SYSEX_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_SYSEX_RANGE_END)) { + // send SysEx + byte sysExQueueNr = 0; + + // figure out which queue is accessed + controllerNumber -= MILES_CONTROLLER_SYSEX_RANGE_BEGIN; + while (controllerNumber > MILES_CONTROLLER_SYSEX_COMMAND_SEND) { + sysExQueueNr++; + controllerNumber -= (MILES_CONTROLLER_SYSEX_COMMAND_SEND + 1); + } + assert(sysExQueueNr < MILES_CONTROLLER_SYSEX_QUEUE_COUNT); + + byte sysExPos = _sysExQueues[sysExQueueNr].dataPos; + bool sysExSend = false; + + switch(controllerNumber) { + case MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS1: + _sysExQueues[sysExQueueNr].targetAddress &= 0x00FFFF; + _sysExQueues[sysExQueueNr].targetAddress |= (controllerValue << 16); + break; + case MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS2: + _sysExQueues[sysExQueueNr].targetAddress &= 0xFF00FF; + _sysExQueues[sysExQueueNr].targetAddress |= (controllerValue << 8); + break; + case MILES_CONTROLLER_SYSEX_COMMAND_ADDRESS3: + _sysExQueues[sysExQueueNr].targetAddress &= 0xFFFF00; + _sysExQueues[sysExQueueNr].targetAddress |= controllerValue; + break; + case MILES_CONTROLLER_SYSEX_COMMAND_DATA: + if (sysExPos < MILES_CONTROLLER_SYSEX_QUEUE_SIZE) { + // Space left? put current byte into queue + _sysExQueues[sysExQueueNr].data[sysExPos] = controllerValue; + sysExPos++; + _sysExQueues[sysExQueueNr].dataPos = sysExPos; + if (sysExPos >= MILES_CONTROLLER_SYSEX_QUEUE_SIZE) { + // overflow? -> send it now + sysExSend = true; + } + } + break; + case MILES_CONTROLLER_SYSEX_COMMAND_SEND: + sysExSend = true; + break; + default: + assert(0); + } + + if (sysExSend) { + if (sysExPos > 0) { + // data actually available? -> send it + _sysExQueues[sysExQueueNr].data[sysExPos] = MILES_MT32_SYSEX_TERMINATOR; // put terminator + + // Execute SysEx + MT32SysEx(_sysExQueues[sysExQueueNr].targetAddress, _sysExQueues[sysExQueueNr].data); + + // adjust target address to point at the end of the current data + _sysExQueues[sysExQueueNr].targetAddress += sysExPos; + // reset queue data buffer + _sysExQueues[sysExQueueNr].dataPos = 0; + } + } + return; + } + + if ((controllerNumber >= MILES_CONTROLLER_XMIDI_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_XMIDI_RANGE_END)) { + // XMIDI controllers? ignore those + return; + } + + _driver->send(0xB0 | midiChannel | (controllerNumber << 8) | (controllerValue << 16)); +} + +void MidiDriver_Miles_MT32::programChange(byte midiChannel, byte patchId) { + byte channelPatchBank = _midiChannels[midiChannel].currentPatchBank; + byte activePatchBank = _patchesBank[patchId]; + + //warning("patch channel %d, patch %x, bank %x", midiChannel, patchId, channelPatchBank); + + // remember patch id for the current MIDI-channel + _midiChannels[midiChannel].currentPatchId = patchId; + + if (channelPatchBank != activePatchBank) { + // associate patch with timbre + setupPatch(channelPatchBank, patchId); + } + + // If this is a custom patch, remember customTimbreId + int16 customTimbre = searchCustomTimbre(channelPatchBank, patchId); + if (customTimbre >= 0) { + _midiChannels[midiChannel].usingCustomTimbre = true; + _midiChannels[midiChannel].currentCustomTimbreId = customTimbre; + } else { + _midiChannels[midiChannel].usingCustomTimbre = false; + } + + // Finally send program change to MT32 + _driver->send(0xC0 | midiChannel | (patchId << 8)); +} + +int16 MidiDriver_Miles_MT32::searchCustomTimbre(byte patchBank, byte patchId) { + byte customTimbreId = 0; + + for (customTimbreId = 0; customTimbreId < MILES_MT32_CUSTOMTIMBRE_COUNT; customTimbreId++) { + if (_customTimbres[customTimbreId].used) { + if ((_customTimbres[customTimbreId].currentPatchBank == patchBank) && (_customTimbres[customTimbreId].currentPatchId == patchId)) { + return customTimbreId; + } + } + } + return -1; +} + +const MilesMT32InstrumentEntry *MidiDriver_Miles_MT32::searchCustomInstrument(byte patchBank, byte patchId) { + const MilesMT32InstrumentEntry *instrumentPtr = _instrumentTablePtr; + + for (uint16 instrumentNr = 0; instrumentNr < _instrumentTableCount; instrumentNr++) { + if ((instrumentPtr->bankId == patchBank) && (instrumentPtr->patchId == patchId)) + return instrumentPtr; + instrumentPtr++; + } + return NULL; +} + +void MidiDriver_Miles_MT32::setupPatch(byte patchBank, byte patchId) { + _patchesBank[patchId] = patchBank; + + if (patchBank) { + // non-built-in bank + int16 customTimbreId = searchCustomTimbre(patchBank, patchId); + if (customTimbreId >= 0) { + // now available? -> use this timbre + writePatchTimbre(patchId, 2, customTimbreId); // Group MEMORY + return; + } + } + + // for built-in bank (or timbres, that are not available) use default MT32 timbres + byte timbreId = patchId & 0x3F; + if (!(patchId & 0x40)) { + writePatchTimbre(patchId, 0, timbreId); // Group A + } else { + writePatchTimbre(patchId, 1, timbreId); // Group B + } +} + +void MidiDriver_Miles_MT32::processXMIDITimbreChunk(const byte *timbreListPtr, uint32 timbreListSize) { + uint16 timbreCount = 0; + uint32 expectedSize = 0; + const byte *timbreListSeeker = timbreListPtr; + + if (timbreListSize < 2) { + warning("MILES-MT32: XMIDI-TIMB chunk - not enough bytes in chunk"); + return; + } + + timbreCount = READ_LE_UINT16(timbreListPtr); + expectedSize = timbreCount * 2; + if (expectedSize > timbreListSize) { + warning("MILES-MT32: XMIDI-TIMB chunk - size mismatch"); + return; + } + + timbreListSeeker += 2; + + while (timbreCount) { + const byte patchId = *timbreListSeeker++; + const byte patchBank = *timbreListSeeker++; + int16 customTimbreId = 0; + + switch (patchBank) { + case MILES_MT32_TIMBREBANK_STANDARD_ROLAND: + case MILES_MT32_TIMBREBANK_MELODIC_MODULE: + // ignore those 2 banks + break; + + default: + // Check, if this timbre was already loaded + customTimbreId = searchCustomTimbre(patchBank, patchId); + + if (customTimbreId < 0) { + // currently not loaded, try to install it + installCustomTimbre(patchBank, patchId); + } + } + timbreCount--; + } +} + +// +int16 MidiDriver_Miles_MT32::installCustomTimbre(byte patchBank, byte patchId) { + switch(patchBank) { + case MILES_MT32_TIMBREBANK_STANDARD_ROLAND: // Standard Roland MT32 bank + case MILES_MT32_TIMBREBANK_MELODIC_MODULE: // Reserved for melodic mode + return -1; + default: + break; + } + + // Original driver did a search for custom timbre here + // and in case it was found, it would call setup_patch() + // we are called from within setup_patch(), so this isn't needed + + int16 customTimbreId = -1; + int16 leastUsedTimbreId = -1; + uint32 leastUsedTimbreNoteCounter = _noteCounter; + const MilesMT32InstrumentEntry *instrumentPtr = NULL; + + // Check, if requested instrument is actually available + instrumentPtr = searchCustomInstrument(patchBank, patchId); + if (!instrumentPtr) { + warning("MILES-MT32: instrument not found during installCustomTimbre()"); + return -1; // not found -> bail out + } + + // Look for an empty timbre slot + // or get the least used non-protected slot + for (byte customTimbreNr = 0; customTimbreNr < MILES_MT32_CUSTOMTIMBRE_COUNT; customTimbreNr++) { + if (!_customTimbres[customTimbreNr].used) { + // found an empty slot -> use this one + customTimbreId = customTimbreNr; + break; + } else { + // used slot + if (!_customTimbres[customTimbreNr].protectionEnabled) { + // not protected + uint32 customTimbreNoteCounter = _customTimbres[customTimbreNr].lastUsedNoteCounter; + if (customTimbreNoteCounter < leastUsedTimbreNoteCounter) { + leastUsedTimbreId = customTimbreNr; + leastUsedTimbreNoteCounter = customTimbreNoteCounter; + } + } + } + } + + if (customTimbreId < 0) { + // no empty slot found, check if we got a least used non-protected slot + if (leastUsedTimbreId < 0) { + // everything is protected, bail out + warning("MILES-MT32: no non-protected timbre slots available during installCustomTimbre()"); + return -1; + } + customTimbreId = leastUsedTimbreId; + } + + // setup timbre slot + _customTimbres[customTimbreId].used = true; + _customTimbres[customTimbreId].currentPatchBank = patchBank; + _customTimbres[customTimbreId].currentPatchId = patchId; + _customTimbres[customTimbreId].lastUsedNoteCounter = _noteCounter; + _customTimbres[customTimbreId].protectionEnabled = false; + + uint32 targetAddress = 0x080000 | (customTimbreId << 9); + uint32 targetAddressCommon = targetAddress + 0x000000; + uint32 targetAddressPartial1 = targetAddress + 0x00000E; + uint32 targetAddressPartial2 = targetAddress + 0x000048; + uint32 targetAddressPartial3 = targetAddress + 0x000102; + uint32 targetAddressPartial4 = targetAddress + 0x00013C; + +#if 0 + byte parameterData[MILES_MT32_PATCHDATA_TOTAL_SIZE + 1]; + uint16 parameterDataPos = 0; + + memcpy(parameterData, instrumentPtr->commonParameter, MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE); + parameterDataPos += MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE; + memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[0], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE); + parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE; + memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[1], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE); + parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE; + memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[2], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE); + parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE; + memcpy(parameterData + parameterDataPos, instrumentPtr->partialParameters[3], MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE); + parameterDataPos += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE; + parameterData[parameterDataPos] = MILES_MT32_SYSEX_TERMINATOR; + + MT32SysEx(targetAddressCommon, parameterData); +#endif + + // upload common parameter data + MT32SysEx(targetAddressCommon, instrumentPtr->commonParameter); + // upload partial parameter data + MT32SysEx(targetAddressPartial1, instrumentPtr->partialParameters[0]); + MT32SysEx(targetAddressPartial2, instrumentPtr->partialParameters[1]); + MT32SysEx(targetAddressPartial3, instrumentPtr->partialParameters[2]); + MT32SysEx(targetAddressPartial4, instrumentPtr->partialParameters[3]); + + setupPatch(patchBank, patchId); + + return customTimbreId; +} + +uint32 MidiDriver_Miles_MT32::calculateSysExTargetAddress(uint32 baseAddress, uint32 index) { + uint16 targetAddressLSB = baseAddress & 0xFF; + uint16 targetAddressKSB = (baseAddress >> 8) & 0xFF; + uint16 targetAddressMSB = (baseAddress >> 16) & 0xFF; + + // add index to it, but use 7-bit of the index for each byte + targetAddressLSB += (index & 0x7F); + targetAddressKSB += ((index >> 7) & 0x7F); + targetAddressMSB += ((index >> 14) & 0x7F); + + // adjust bytes, so that none of them is above or equal 0x80 + while (targetAddressLSB >= 0x80) { + targetAddressLSB -= 0x80; + targetAddressKSB++; + } + while (targetAddressKSB >= 0x80) { + targetAddressKSB -= 0x80; + targetAddressMSB++; + } + assert(targetAddressMSB < 0x80); + + // put everything together + return targetAddressLSB | (targetAddressKSB << 8) | (targetAddressMSB << 16); +} + +void MidiDriver_Miles_MT32::writeRhythmSetup(byte note, byte customTimbreId) { + byte sysExData[2]; + uint32 targetAddress = 0; + + targetAddress = calculateSysExTargetAddress(0x030110, ((note - 24) << 2)); + + sysExData[0] = customTimbreId; + sysExData[1] = MILES_MT32_SYSEX_TERMINATOR; // terminator + + MT32SysEx(targetAddress, sysExData); +} + +void MidiDriver_Miles_MT32::writePatchTimbre(byte patchId, byte timbreGroup, byte timbreId) { + byte sysExData[3]; + uint32 targetAddress = 0; + + // write to patch memory (starts at 0x050000, each entry is 8 bytes) + targetAddress = calculateSysExTargetAddress(0x050000, patchId << 3); + + sysExData[0] = timbreGroup; // 0 - group A, 1 - group B, 2 - memory, 3 - rhythm + sysExData[1] = timbreId; // timbre number (0-63) + sysExData[2] = MILES_MT32_SYSEX_TERMINATOR; // terminator + + MT32SysEx(targetAddress, sysExData); +} + +void MidiDriver_Miles_MT32::writePatchByte(byte patchId, byte index, byte patchValue) { + byte sysExData[2]; + uint32 targetAddress = 0; + + targetAddress = calculateSysExTargetAddress(0x050000, (patchId << 3) + index); + + sysExData[0] = patchValue; + sysExData[1] = MILES_MT32_SYSEX_TERMINATOR; // terminator + + MT32SysEx(targetAddress, sysExData); +} + +void MidiDriver_Miles_MT32::writeToSystemArea(byte index, byte value) { + byte sysExData[2]; + uint32 targetAddress = 0; + + targetAddress = calculateSysExTargetAddress(0x100000, index); + + sysExData[0] = value; + sysExData[1] = MILES_MT32_SYSEX_TERMINATOR; // terminator + + MT32SysEx(targetAddress, sysExData); +} + +MidiDriver *MidiDriver_Miles_MT32_create(const Common::String &instrumentDataFilename) { + MilesMT32InstrumentEntry *instrumentTablePtr = NULL; + uint16 instrumentTableCount = 0; + + if (!instrumentDataFilename.empty()) { + // Load MT32 instrument data from file SAMPLE.MT + Common::File *fileStream = new Common::File(); + uint32 fileSize = 0; + byte *fileDataPtr = NULL; + uint32 fileDataOffset = 0; + uint32 fileDataLeft = 0; + + byte curBankId = 0; + byte curPatchId = 0; + + MilesMT32InstrumentEntry *instrumentPtr = NULL; + uint32 instrumentOffset = 0; + uint16 instrumentDataSize = 0; + + if (!fileStream->open(instrumentDataFilename)) + error("MILES-MT32: could not open instrument file '%s'", instrumentDataFilename.c_str()); + + fileSize = fileStream->size(); + + fileDataPtr = new byte[fileSize]; + + if (fileStream->read(fileDataPtr, fileSize) != fileSize) + error("MILES-MT32: error while reading instrument file"); + fileStream->close(); + delete fileStream; + + // File is like this: + // [patch:BYTE] [bank:BYTE] [patchoffset:UINT32] + // ... + // until patch + bank are both 0xFF, which signals end of header + + // First we check how many entries there are + fileDataOffset = 0; + fileDataLeft = fileSize; + while (1) { + if (fileDataLeft < 6) + error("MILES-MT32: unexpected EOF in instrument file"); + + curPatchId = fileDataPtr[fileDataOffset++]; + curBankId = fileDataPtr[fileDataOffset++]; + + if ((curBankId == 0xFF) && (curPatchId == 0xFF)) + break; + + fileDataOffset += 4; // skip over offset + instrumentTableCount++; + } + + if (instrumentTableCount == 0) + error("MILES-MT32: no instruments in instrument file"); + + // Allocate space for instruments + instrumentTablePtr = new MilesMT32InstrumentEntry[instrumentTableCount]; + + // Now actually read all entries + instrumentPtr = instrumentTablePtr; + + fileDataOffset = 0; + fileDataLeft = fileSize; + while (1) { + curPatchId = fileDataPtr[fileDataOffset++]; + curBankId = fileDataPtr[fileDataOffset++]; + + if ((curBankId == 0xFF) && (curPatchId == 0xFF)) + break; + + instrumentOffset = READ_LE_UINT32(fileDataPtr + fileDataOffset); + fileDataOffset += 4; + + instrumentPtr->bankId = curBankId; + instrumentPtr->patchId = curPatchId; + + instrumentDataSize = READ_LE_UINT16(fileDataPtr + instrumentOffset); + if (instrumentDataSize != (MILES_MT32_PATCHDATA_TOTAL_SIZE + 2)) + error("MILES-MT32: unsupported instrument size"); + + instrumentOffset += 2; + // Copy common parameter data + memcpy(instrumentPtr->commonParameter, fileDataPtr + instrumentOffset, MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE); + instrumentPtr->commonParameter[MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE] = MILES_MT32_SYSEX_TERMINATOR; // Terminator + instrumentOffset += MILES_MT32_PATCHDATA_COMMONPARAMETER_SIZE; + + // Copy partial parameter data + for (byte partialNr = 0; partialNr < MILES_MT32_PATCHDATA_PARTIALPARAMETERS_COUNT; partialNr++) { + memcpy(&instrumentPtr->partialParameters[partialNr], fileDataPtr + instrumentOffset, MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE); + instrumentPtr->partialParameters[partialNr][MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE] = MILES_MT32_SYSEX_TERMINATOR; // Terminator + instrumentOffset += MILES_MT32_PATCHDATA_PARTIALPARAMETER_SIZE; + } + + // Instrument read, next instrument please + instrumentPtr++; + } + + // Free instrument file data + delete[] fileDataPtr; + } + + return new MidiDriver_Miles_MT32(instrumentTablePtr, instrumentTableCount); +} + +void MidiDriver_Miles_MT32_processXMIDITimbreChunk(MidiDriver_BASE *driver, const byte *timbreListPtr, uint32 timbreListSize) { + MidiDriver_Miles_MT32 *driverMT32 = dynamic_cast(driver); + + if (driverMT32) { + driverMT32->processXMIDITimbreChunk(timbreListPtr, timbreListSize); + } +} + +} // End of namespace Audio diff --git a/audio/module.mk b/audio/module.mk index d40793b1fe7..11458e6fc09 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -1,16 +1,20 @@ MODULE := audio MODULE_OBJS := \ + adlib.o \ audiostream.o \ fmopl.o \ mididrv.o \ - midiparser.o \ midiparser_qt.o \ + midiparser.o \ + miles_adlib.o \ + miles_mt32.o \ mixer.o \ mpu401.o \ musicplugin.o \ null.o \ timestamp.o \ + decoders/3do.o \ decoders/aac.o \ decoders/adpcm.o \ decoders/aiff.o \ @@ -27,7 +31,6 @@ MODULE_OBJS := \ decoders/wave.o \ decoders/wma.o \ decoders/xa.o \ - softsynth/adlib.o \ softsynth/cms.o \ softsynth/opl/dbopl.o \ softsynth/opl/dosbox.o \ @@ -35,6 +38,11 @@ MODULE_OBJS := \ softsynth/fluidsynth.o \ softsynth/mt32.o +ifdef USE_ALSA +MODULE_OBJS += \ + alsa_opl.o +endif + ifndef USE_ARM_SOUND_ASM MODULE_OBJS += \ rate.o diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index 5c3d833f54d..3d90ec93d0b 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -32,6 +32,7 @@ #include "dosbox.h" #include "dbopl.h" +#include "audio/mixer.h" #include "common/system.h" #include "common/scummsys.h" #include "common/util.h" @@ -148,6 +149,7 @@ OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) { } OPL::~OPL() { + stop(); free(); } @@ -156,7 +158,7 @@ void OPL::free() { _emulator = 0; } -bool OPL::init(int rate) { +bool OPL::init() { free(); memset(&_reg, 0, sizeof(_reg)); @@ -167,19 +169,19 @@ bool OPL::init(int rate) { return false; DBOPL::InitTables(); - _emulator->Setup(rate); + _rate = g_system->getMixer()->getOutputRate(); + _emulator->Setup(_rate); if (_type == Config::kDualOpl2) { // Setup opl3 mode in the hander _emulator->WriteReg(0x105, 1); } - _rate = rate; return true; } void OPL::reset() { - init(_rate); + init(); } void OPL::write(int port, int val) { @@ -307,7 +309,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) { _emulator->WriteReg(fullReg, val); } -void OPL::readBuffer(int16 *buffer, int length) { +void OPL::generateSamples(int16 *buffer, int length) { // For stereo OPL cards, we divide the sample count by 2, // to match stereo AudioStream behavior. if (_type != Config::kOpl2) diff --git a/audio/softsynth/opl/dosbox.h b/audio/softsynth/opl/dosbox.h index 513a49f6b87..c52f06761a6 100644 --- a/audio/softsynth/opl/dosbox.h +++ b/audio/softsynth/opl/dosbox.h @@ -69,7 +69,7 @@ namespace DBOPL { struct Chip; } // end of namespace DBOPL -class OPL : public ::OPL::OPL { +class OPL : public ::OPL::EmulatedOPL { private: Config::OplType _type; uint _rate; @@ -87,7 +87,7 @@ public: OPL(Config::OplType type); ~OPL(); - bool init(int rate); + bool init(); void reset(); void write(int a, int v); @@ -95,8 +95,10 @@ public: void writeReg(int r, int v); - void readBuffer(int16 *buffer, int length); bool isStereo() const { return _type != Config::kOpl2; } + +protected: + void generateSamples(int16 *buffer, int length); }; } // End of namespace DOSBox diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index da75ba76baf..696169be09b 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -31,6 +31,8 @@ #include "mame.h" +#include "audio/mixer.h" +#include "common/system.h" #include "common/textconsole.h" #include "common/util.h" @@ -46,15 +48,19 @@ namespace OPL { namespace MAME { OPL::~OPL() { + stop(); MAME::OPLDestroy(_opl); _opl = 0; } -bool OPL::init(int rate) { - if (_opl) +bool OPL::init() { + if (_opl) { + stopCallbacks(); MAME::OPLDestroy(_opl); + } + + _opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate()); - _opl = MAME::makeAdLibOPL(rate); return (_opl != 0); } @@ -74,7 +80,7 @@ void OPL::writeReg(int r, int v) { MAME::OPLWriteReg(_opl, r, v); } -void OPL::readBuffer(int16 *buffer, int length) { +void OPL::generateSamples(int16 *buffer, int length) { MAME::YM3812UpdateOne(_opl, buffer, length); } diff --git a/audio/softsynth/opl/mame.h b/audio/softsynth/opl/mame.h index bd479d9e455..67d80bb193e 100644 --- a/audio/softsynth/opl/mame.h +++ b/audio/softsynth/opl/mame.h @@ -174,14 +174,14 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length); FM_OPL *makeAdLibOPL(int rate); // OPL API implementation -class OPL : public ::OPL::OPL { +class OPL : public ::OPL::EmulatedOPL { private: FM_OPL *_opl; public: OPL() : _opl(0) {} ~OPL(); - bool init(int rate); + bool init(); void reset(); void write(int a, int v); @@ -189,8 +189,10 @@ public: void writeReg(int r, int v); - void readBuffer(int16 *buffer, int length); bool isStereo() const { return false; } + +protected: + void generateSamples(int16 *buffer, int length); }; } // End of namespace MAME diff --git a/b/audio/adlib.cpp b/b/audio/adlib.cpp new file mode 100644 index 00000000000..f6091644955 --- /dev/null +++ b/b/audio/adlib.cpp @@ -0,0 +1,2318 @@ +/* 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. + * + */ + +#include "audio/softsynth/emumidi.h" +#include "common/debug.h" +#include "common/error.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/textconsole.h" +#include "common/types.h" +#include "common/util.h" +#include "audio/fmopl.h" +#include "audio/musicplugin.h" +#include "common/translation.h" + +#ifdef DEBUG_ADLIB +static int g_tick; +#endif + +// Only include OPL3 when we actually have an AdLib emulator builtin, which +// supports OPL3. +#ifndef DISABLE_DOSBOX_OPL +#define ENABLE_OPL3 +#endif + +class MidiDriver_ADLIB; +struct AdLibVoice; + +// We use packing for the following two structs, because the code +// does simply copy them over from byte streams, without any +// serialization. Check AdLibPart::sysEx_customInstrument for an +// example of this. +// +// It might be very well possible, that none of the compilers we support +// add any padding bytes at all, since the structs contain only variables +// of the type 'byte'. But better safe than sorry. +#include "common/pack-start.h" +struct InstrumentExtra { + byte a, b, c, d, e, f, g, h; +} PACKED_STRUCT; + +struct AdLibInstrument { + byte modCharacteristic; + byte modScalingOutputLevel; + byte modAttackDecay; + byte modSustainRelease; + byte modWaveformSelect; + byte carCharacteristic; + byte carScalingOutputLevel; + byte carAttackDecay; + byte carSustainRelease; + byte carWaveformSelect; + byte feedback; + byte flagsA; + InstrumentExtra extraA; + byte flagsB; + InstrumentExtra extraB; + byte duration; +} PACKED_STRUCT; +#include "common/pack-end.h" + +class AdLibPart : public MidiChannel { + friend class MidiDriver_ADLIB; + +protected: +// AdLibPart *_prev, *_next; + AdLibVoice *_voice; + int16 _pitchBend; + byte _pitchBendFactor; + //int8 _transposeEff; + byte _volEff; + int8 _detuneEff; + byte _modWheel; + bool _pedal; + byte _program; + byte _priEff; + byte _pan; + AdLibInstrument _partInstr; +#ifdef ENABLE_OPL3 + AdLibInstrument _partInstrSecondary; +#endif + +protected: + MidiDriver_ADLIB *_owner; + bool _allocated; + byte _channel; + + void init(MidiDriver_ADLIB *owner, byte channel); + void allocate() { _allocated = true; } + +public: + AdLibPart() { + _voice = 0; + _pitchBend = 0; + _pitchBendFactor = 2; + //_transposeEff = 0; + _volEff = 0; + _detuneEff = 0; + _modWheel = 0; + _pedal = 0; + _program = 0; + _priEff = 0; + _pan = 64; + + _owner = 0; + _allocated = false; + _channel = 0; + + memset(&_partInstr, 0, sizeof(_partInstr)); +#ifdef ENABLE_OPL3 + memset(&_partInstrSecondary, 0, sizeof(_partInstrSecondary)); +#endif + } + + MidiDriver *device(); + byte getNumber() { return _channel; } + void release() { _allocated = false; } + + void send(uint32 b); + + // Regular messages + void noteOff(byte note); + void noteOn(byte note, byte velocity); + void programChange(byte program); + void pitchBend(int16 bend); + + // Control Change messages + void controlChange(byte control, byte value); + void modulationWheel(byte value); + void volume(byte value); + void panPosition(byte value); + void pitchBendFactor(byte value); + void detune(byte value); + void priority(byte value); + void sustain(bool value); + void effectLevel(byte value) { return; } // Not supported + void chorusLevel(byte value) { return; } // Not supported + void allNotesOff(); + + // SysEx messages + void sysEx_customInstrument(uint32 type, const byte *instr); +}; + +// FYI (Jamieson630) +// It is assumed that any invocation to AdLibPercussionChannel +// will be done through the MidiChannel base class as opposed to the +// AdLibPart base class. If this were NOT the case, all the functions +// listed below would need to be virtual in AdLibPart as well as MidiChannel. +class AdLibPercussionChannel : public AdLibPart { + friend class MidiDriver_ADLIB; + +protected: + void init(MidiDriver_ADLIB *owner, byte channel); + +public: + ~AdLibPercussionChannel(); + + void noteOff(byte note); + void noteOn(byte note, byte velocity); + void programChange(byte program) { } + + // Control Change messages + void modulationWheel(byte value) { } + void pitchBendFactor(byte value) { } + void detune(byte value) { } + void priority(byte value) { } + void sustain(bool value) { } + + // SysEx messages + void sysEx_customInstrument(uint32 type, const byte *instr); + +private: + byte _notes[256]; + AdLibInstrument *_customInstruments[256]; +}; + +struct Struct10 { + byte active; + int16 curVal; + int16 count; + uint16 maxValue; + int16 startValue; + byte loop; + byte tableA[4]; + byte tableB[4]; + int8 unk3; + int8 modWheel; + int8 modWheelLast; + uint16 speedLoMax; + uint16 numSteps; + int16 speedHi; + int8 direction; + uint16 speedLo; + uint16 speedLoCounter; +}; + +struct Struct11 { + int16 modifyVal; + byte param, flag0x40, flag0x10; + Struct10 *s10; +}; + +struct AdLibVoice { + AdLibPart *_part; + AdLibVoice *_next, *_prev; + byte _waitForPedal; + byte _note; + byte _channel; + byte _twoChan; + byte _vol1, _vol2; + int16 _duration; + + Struct10 _s10a; + Struct11 _s11a; + Struct10 _s10b; + Struct11 _s11b; + +#ifdef ENABLE_OPL3 + byte _secTwoChan; + byte _secVol1, _secVol2; +#endif + + AdLibVoice() { memset(this, 0, sizeof(AdLibVoice)); } +}; + +struct AdLibSetParams { + byte registerBase; + byte shift; + byte mask; + byte inversion; +}; + +static const byte g_operator1Offsets[9] = { + 0, 1, 2, 8, + 9, 10, 16, 17, + 18 +}; + +static const byte g_operator2Offsets[9] = { + 3, 4, 5, 11, + 12, 13, 19, 20, + 21 +}; + +static const AdLibSetParams g_setParamTable[] = { + {0x40, 0, 63, 63}, // level + {0xE0, 2, 0, 0}, // unused + {0x40, 6, 192, 0}, // level key scaling + {0x20, 0, 15, 0}, // modulator frequency multiple + {0x60, 4, 240, 15}, // attack rate + {0x60, 0, 15, 15}, // decay rate + {0x80, 4, 240, 15}, // sustain level + {0x80, 0, 15, 15}, // release rate + {0xE0, 0, 3, 0}, // waveformSelect select + {0x20, 7, 128, 0}, // amp mod + {0x20, 6, 64, 0}, // vib + {0x20, 5, 32, 0}, // eg typ + {0x20, 4, 16, 0}, // ksr + {0xC0, 0, 1, 0}, // decay alg + {0xC0, 1, 14, 0} // feedback +}; + +static const byte g_paramTable1[16] = { + 29, 28, 27, 0, + 3, 4, 7, 8, + 13, 16, 17, 20, + 21, 30, 31, 0 +}; + +static const uint16 g_maxValTable[16] = { + 0x2FF, 0x1F, 0x7, 0x3F, + 0x0F, 0x0F, 0x0F, 0x3, + 0x3F, 0x0F, 0x0F, 0x0F, + 0x3, 0x3E, 0x1F, 0 +}; + +static const uint16 g_numStepsTable[] = { + 1, 2, 4, 5, + 6, 7, 8, 9, + 10, 12, 14, 16, + 18, 21, 24, 30, + 36, 50, 64, 82, + 100, 136, 160, 192, + 240, 276, 340, 460, + 600, 860, 1200, 1600 +}; + +static const byte g_noteFrequencies[] = { + 90, 91, 92, 92, 93, 94, 94, 95, + 96, 96, 97, 98, 98, 99, 100, 101, + 101, 102, 103, 104, 104, 105, 106, 107, + 107, 108, 109, 110, 111, 111, 112, 113, + 114, 115, 115, 116, 117, 118, 119, 120, + 121, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, + 143, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 157, 158, 159, 160, + 161, 162, 163, 165, 166, 167, 168, 169, + 171, 172, 173, 174, 176, 177, 178, 180, + 181, 182, 184, 185, 186, 188, 189, 190, + 192, 193, 194, 196, 197, 199, 200, 202, + 203, 205, 206, 208, 209, 211, 212, 214, + 215, 217, 218, 220, 222, 223, 225, 226, + 228, 230, 231, 233, 235, 236, 238, 240, + 242, 243, 245, 247, 249, 251, 252, 254 +}; + +static const AdLibInstrument g_gmInstruments[128] = { + // 0x00 + { 0xC2, 0xC5, 0x2B, 0x99, 0x58, 0xC2, 0x1F, 0x1E, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x22, 0x53, 0x0E, 0x8A, 0x30, 0x14, 0x06, 0x1D, 0x7A, 0x5C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x00, 0x1C, 0x79, 0x40, 0x02, 0x00, 0x4B, 0x79, 0x58, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x89, 0x2A, 0x89, 0x49, 0xC2, 0x16, 0x1C, 0xB8, 0x7C, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x20, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0x6F, 0x81, 0x0E, 0x3B, 0x5A, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8C, 0x80, 0x05, 0xEA, 0x59, 0x82, 0x0A, 0x3C, 0xAA, 0x64, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x85, 0x40, 0x0D, 0xEC, 0x71, 0x84, 0x58, 0x3E, 0xCB, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8A, 0xC0, 0x0C, 0xDC, 0x50, 0x88, 0x58, 0x3D, 0xDA, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC9, 0x40, 0x2B, 0x78, 0x42, 0xC2, 0x04, 0x4C, 0x8A, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A }, + { 0x2A, 0x0E, 0x17, 0x89, 0x28, 0x22, 0x0C, 0x1B, 0x09, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x9B, 0x08, 0x08, 0x26, 0xE2, 0x06, 0x0A, 0x08, 0x70, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC5, 0x05, 0x00, 0xFC, 0x40, 0x84, 0x00, 0x00, 0xDC, 0x50, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x86, 0x40, 0x5D, 0x5A, 0x41, 0x81, 0x00, 0x0B, 0x5A, 0x7F, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + // 0x10 + { 0xED, 0x00, 0x7B, 0xC8, 0x40, 0xE1, 0x99, 0x4A, 0xE9, 0x7E, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x4F, 0x3A, 0xD7, 0x7C, 0xE2, 0x97, 0x49, 0xF9, 0x7D, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x10, 0x2F, 0xF7, 0x7D, 0xF3, 0x45, 0x8F, 0xC7, 0x62, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x01, 0x8C, 0x9F, 0xDA, 0x70, 0xE4, 0x50, 0x9F, 0xDA, 0x6A, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x08, 0xD5, 0x9D, 0xA5, 0x45, 0xE2, 0x3F, 0x9F, 0xD6, 0x49, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE5, 0x0F, 0x7D, 0xB8, 0x2E, 0xA2, 0x0F, 0x7C, 0xC7, 0x61, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0x62, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x88, 0x9C, 0x50, 0x64, 0xE2, 0x18, 0x70, 0xC4, 0x7C, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x42, 0x55, 0x3E, 0xEB, 0x24, 0xD4, 0x08, 0x0D, 0xA9, 0x71, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0xC2, 0x00, 0x2B, 0x17, 0x51, 0xC2, 0x1E, 0x4D, 0x97, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 }, + { 0xC6, 0x01, 0x2D, 0xA7, 0x44, 0xC2, 0x06, 0x0E, 0xA7, 0x79, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x0C, 0x06, 0x06, 0x55, 0xC2, 0x3F, 0x09, 0x86, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A }, + { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0x59, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0x7F, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B }, + // 0x20 + { 0xC2, 0x40, 0x3C, 0x96, 0x58, 0xC4, 0xDE, 0x0E, 0xC7, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 }, + { 0x31, 0x13, 0x2D, 0xD7, 0x3C, 0xE2, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x22, 0x86, 0x0D, 0xD7, 0x50, 0xE4, 0x18, 0x5E, 0xB8, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0xF2, 0x0A, 0x0D, 0xD7, 0x40, 0xE4, 0x1F, 0x5E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x09, 0x4B, 0xD6, 0x48, 0xE4, 0x1F, 0x1C, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0x62, 0x11, 0x0C, 0xE6, 0x3C, 0xE4, 0x1F, 0x0C, 0xC8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x12, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x7D, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x13, 0x3D, 0xE6, 0x34, 0xE4, 0x1F, 0x5D, 0xB8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xA2, 0x40, 0x5D, 0xBA, 0x3F, 0xE2, 0x00, 0x8F, 0xD8, 0x79, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x3D, 0xDA, 0x3B, 0xE1, 0x00, 0x7E, 0xD8, 0x7A, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x6D, 0xFA, 0x5D, 0xE2, 0x00, 0x8F, 0xC8, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0x4A, 0xE3, 0x18, 0x6F, 0xE9, 0x7E, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0x66, 0xE2, 0x00, 0x7F, 0xE9, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x0F, 0x66, 0xAA, 0x51, 0x02, 0x64, 0x29, 0xF9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x16, 0x4A, 0x04, 0xBA, 0x39, 0xC2, 0x58, 0x2D, 0xCA, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x02, 0x00, 0x01, 0x7A, 0x79, 0x02, 0x3F, 0x28, 0xEA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + // 0x30 + { 0x62, 0x53, 0x9C, 0xBA, 0x31, 0x62, 0x5B, 0xAD, 0xC9, 0x55, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x6E, 0xDA, 0x49, 0xE2, 0x13, 0x8F, 0xF9, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x8F, 0xFA, 0x50, 0xF2, 0x04, 0x7F, 0xFA, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x3D, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x80, 0x9C, 0x99, 0x42, 0xE2, 0x04, 0x7D, 0x78, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC3, 0x3F, 0x4B, 0xE9, 0x7E, 0xC1, 0x3F, 0x9B, 0xF9, 0x7F, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0x8F, 0xFB, 0x50, 0xF6, 0x47, 0x8F, 0xE9, 0x68, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0xAF, 0x88, 0x58, 0xF2, 0x54, 0x6E, 0xC9, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x84, 0x4E, 0x78, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0x7D, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + // 0x40 + { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0x7D, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x0F, 0x90, 0xF8, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x8E, 0xC9, 0x3D, 0xE6, 0x00, 0x7E, 0xD8, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x5F, 0xF9, 0x48, 0xE6, 0x98, 0x8F, 0xF8, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x0C, 0x6E, 0xD8, 0x3D, 0x2A, 0x06, 0x7D, 0xD8, 0x58, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x7E, 0x89, 0x38, 0xE6, 0x84, 0x80, 0xF8, 0x68, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x6C, 0xD9, 0x30, 0xE2, 0x00, 0x8D, 0xC8, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x88, 0x48, 0x40, 0xE2, 0x0A, 0x7D, 0xA8, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x77, 0xC5, 0x54, 0xE2, 0x00, 0x9E, 0xD7, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x86, 0xB9, 0x64, 0xE2, 0x05, 0x9F, 0xD7, 0x78, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x68, 0x68, 0x56, 0xE2, 0x08, 0x9B, 0xB3, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0xA6, 0x87, 0x41, 0xE2, 0x0A, 0x7E, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x9A, 0xB8, 0x48, 0xE2, 0x00, 0x9E, 0xF9, 0x60, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0x64, 0x68, 0xE2, 0x28, 0x6F, 0x73, 0x7C, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + // 0x50 + { 0xE8, 0x00, 0x7D, 0x99, 0x54, 0xE6, 0x80, 0x80, 0xF8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9F, 0xB9, 0x6D, 0xE1, 0x00, 0x8F, 0xC8, 0x7D, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x09, 0x68, 0x4A, 0xE2, 0x2B, 0x9E, 0xF3, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x99, 0xE8, 0x3B, 0xE2, 0x25, 0x6F, 0x93, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x6F, 0xDA, 0x69, 0xE2, 0x05, 0x2F, 0xD8, 0x6A, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x0F, 0xF7, 0x7D, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x3C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x40, 0x0D, 0x89, 0x7D, 0xE2, 0x17, 0x7E, 0xD9, 0x7C, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0xDF, 0x8A, 0x56, 0xE2, 0x5E, 0xCF, 0xBA, 0x7E, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0B, 0x68, 0x60, 0xE2, 0x01, 0x9E, 0xB8, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0xAE, 0xAB, 0x49, 0xE2, 0x00, 0xAE, 0xBA, 0x6C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEB, 0x80, 0x8C, 0xCB, 0x3A, 0xE2, 0x86, 0xAF, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE5, 0x40, 0xDB, 0x3B, 0x3C, 0xE2, 0x80, 0xBE, 0xCA, 0x71, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x9E, 0xAA, 0x3D, 0xE1, 0x43, 0x0F, 0xBA, 0x7E, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x40, 0xEC, 0xCA, 0x44, 0xE2, 0x03, 0xBF, 0xBA, 0x66, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + // 0x60 + { 0xEA, 0x00, 0x68, 0xB8, 0x48, 0xE2, 0x0A, 0x8E, 0xB8, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x61, 0x00, 0xBE, 0x99, 0x7E, 0xE3, 0x40, 0xCF, 0xCA, 0x7D, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xCD, 0x00, 0x0B, 0x00, 0x48, 0xC2, 0x58, 0x0C, 0x00, 0x7C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C }, + { 0xE2, 0x00, 0x0E, 0x00, 0x52, 0xE2, 0x58, 0x5F, 0xD0, 0x7D, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xCC, 0x00, 0x7D, 0xDA, 0x40, 0xC2, 0x00, 0x5E, 0x9B, 0x58, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE9, 0xC0, 0xEE, 0xD8, 0x43, 0xE2, 0x05, 0xDD, 0xAA, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xDA, 0x00, 0x8F, 0xAC, 0x4A, 0x22, 0x05, 0x8D, 0x8A, 0x75, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x8A, 0xCB, 0x7A, 0x74, 0xE6, 0x56, 0xAF, 0xDB, 0x70, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x41, 0xAC, 0x5B, 0x5B, 0xC2, 0x80, 0x0D, 0xCB, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 }, + { 0x75, 0x00, 0x0E, 0xCB, 0x5A, 0xE2, 0x1E, 0x0A, 0xC9, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x41, 0x00, 0x0E, 0xEA, 0x53, 0xC2, 0x00, 0x08, 0xCA, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0xC1, 0x40, 0x0C, 0x59, 0x6A, 0xC2, 0x80, 0x3C, 0xAB, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0x4B, 0x00, 0x0A, 0xF5, 0x61, 0xC2, 0x19, 0x0C, 0xE9, 0x7C, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x62, 0x00, 0x7F, 0xD8, 0x54, 0xEA, 0x00, 0x8F, 0xD8, 0x7D, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0x56, 0xE1, 0x00, 0x8F, 0xD8, 0x7E, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + // 0x70 + { 0xCF, 0x40, 0x09, 0xEA, 0x54, 0xC4, 0x00, 0x0C, 0xDB, 0x64, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x0C, 0xAA, 0x54, 0xC4, 0x00, 0x18, 0xF9, 0x64, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xC9, 0x0E, 0x88, 0xD9, 0x3E, 0xC2, 0x08, 0x1A, 0xEA, 0x6C, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x03, 0x00, 0x15, 0x00, 0x64, 0x02, 0x00, 0x08, 0x00, 0x7C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x01, 0x00, 0x47, 0xD7, 0x6C, 0x01, 0x3F, 0x0C, 0xFB, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x00, 0x00, 0x36, 0x67, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x02, 0x00, 0x36, 0x68, 0x7C, 0x01, 0x3F, 0x0E, 0xFA, 0x7C, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0xCB, 0x00, 0xAF, 0x00, 0x7E, 0xC0, 0x00, 0xC0, 0x06, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F }, + { 0x05, 0x0D, 0x80, 0xA6, 0x7F, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x0F, 0x00, 0x90, 0xFA, 0x68, 0x06, 0x00, 0xA7, 0x39, 0x54, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xC9, 0x15, 0xDD, 0xFF, 0x7C, 0x00, 0x00, 0xE7, 0xFC, 0x6C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 }, + { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x07, 0x80, 0x0B, 0xC8, 0x65, 0x02, 0x3F, 0x0C, 0xEA, 0x7C, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x08, 0x00, 0x0B, 0x3C, 0x7C, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } +}; + +static AdLibInstrument g_gmPercussionInstruments[39] = { + { 0x1A, 0x3F, 0x15, 0x05, 0x7C, 0x02, 0x21, 0x2B, 0xE4, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x11, 0x12, 0x04, 0x07, 0x7C, 0x02, 0x23, 0x0B, 0xE5, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x0A, 0x3F, 0x0B, 0x01, 0x7C, 0x1F, 0x1C, 0x46, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 }, + { 0x00, 0x3F, 0x0F, 0x00, 0x7C, 0x10, 0x12, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0F, 0x3F, 0x0B, 0x00, 0x7C, 0x1F, 0x0F, 0x19, 0xD0, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x3F, 0x1F, 0x00, 0x7E, 0x1F, 0x16, 0x07, 0x00, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x1F, 0x4A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xFF, 0x7E, 0x00, 0xC7, 0x2D, 0xF7, 0x73, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x12, 0x3F, 0x05, 0x06, 0x7C, 0x43, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xCF, 0x7E, 0x00, 0x45, 0x2A, 0xF8, 0x4B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0x12, 0x3F, 0x06, 0x17, 0x7C, 0x03, 0x27, 0x0B, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x7F, 0x08, 0xCD, 0x7E, 0x00, 0x40, 0x1A, 0x69, 0x63, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0x13, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x17, 0x0A, 0xD9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x15, 0x3F, 0x05, 0x06, 0x7C, 0x03, 0x21, 0x0C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x2B, 0xFB, 0x7E, 0xC0, 0x1E, 0x1A, 0xCA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x17, 0x3F, 0x04, 0x09, 0x7C, 0x03, 0x22, 0x0D, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x0F, 0x5E, 0x7C, 0xC6, 0x13, 0x00, 0xCA, 0x7F, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x7E, 0x9D, 0x7C, 0xC8, 0xC0, 0x0A, 0xBA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xCF, 0x3F, 0x4D, 0x9F, 0x7C, 0xC6, 0x00, 0x08, 0xDA, 0x5B, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0xCF, 0x3F, 0x5D, 0xAA, 0x7A, 0xC0, 0xA4, 0x67, 0x99, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x4A, 0xFD, 0x7C, 0xCF, 0x00, 0x59, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0F, 0x18, 0x0A, 0xFA, 0x57, 0x06, 0x07, 0x06, 0x39, 0x7C, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x2B, 0xFC, 0x7C, 0xCC, 0xC6, 0x0B, 0xEA, 0x7F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x05, 0x1A, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x0C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x04, 0x19, 0x04, 0x00, 0x7C, 0x12, 0x10, 0x2C, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x04, 0x0A, 0x04, 0x00, 0x6C, 0x01, 0x07, 0x0D, 0xFA, 0x74, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x15, 0x14, 0x05, 0x00, 0x7D, 0x01, 0x07, 0x5C, 0xE9, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x10, 0x10, 0x05, 0x08, 0x7C, 0x01, 0x08, 0x0D, 0xEA, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x11, 0x00, 0x06, 0x87, 0x7F, 0x02, 0x40, 0x09, 0x59, 0x68, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x13, 0x26, 0x04, 0x6A, 0x7F, 0x01, 0x00, 0x08, 0x5A, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC4, 0x00, 0x18, 0xF9, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x0C, 0xAA, 0x50, 0xC3, 0x00, 0x18, 0xF8, 0x54, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCB, 0x3F, 0x8F, 0x00, 0x7E, 0xC5, 0x00, 0x98, 0xD6, 0x5F, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0x0C, 0x18, 0x87, 0xB3, 0x7F, 0x19, 0x10, 0x55, 0x75, 0x7C, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x05, 0x11, 0x15, 0x00, 0x64, 0x02, 0x08, 0x08, 0x00, 0x5C, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x04, 0x08, 0x15, 0x00, 0x48, 0x01, 0x08, 0x08, 0x00, 0x60, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xDA, 0x00, 0x53, 0x30, 0x68, 0x07, 0x1E, 0x49, 0xC4, 0x7E, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x1C, 0x00, 0x07, 0xBC, 0x6C, 0x0C, 0x14, 0x0B, 0x6A, 0x7E, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x0A, 0x0E, 0x7F, 0x00, 0x7D, 0x13, 0x20, 0x28, 0x03, 0x7C, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } +}; + +#ifdef ENABLE_OPL3 +static const AdLibInstrument g_gmInstrumentsOPL3[128][2] = { + { { 0xC2, 0xC2, 0x0A, 0x6B, 0xA0, 0xC2, 0x08, 0x0D, 0x88, 0xC8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x02, 0x00, 0x0C, 0x78, 0x61, 0x04, 0x4C, 0x0B, 0x9A, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 } }, + { { 0x22, 0x53, 0x0E, 0x8A, 0x60, 0x14, 0x06, 0x1D, 0x7A, 0xB8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x22, 0x5A, 0x0E, 0x8A, 0x40, 0x14, 0x2F, 0x0E, 0x7A, 0x88, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x06, 0x00, 0x1C, 0x79, 0x70, 0x02, 0x00, 0x4B, 0x79, 0xA8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x00, 0x1A, 0x79, 0x60, 0x02, 0x00, 0x4C, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x80, 0x0B, 0x89, 0x90, 0xC2, 0x06, 0x1B, 0xA8, 0xB0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x23 }, + { 0x04, 0x28, 0x5D, 0xB8, 0x01, 0x02, 0x00, 0x3C, 0x70, 0x88, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x17, 0x3D, 0x6A, 0x00, 0xC4, 0x2E, 0x2D, 0xC9, 0x40, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x1E, 0x1C, 0x99, 0x00, 0x02, 0x3A, 0x4C, 0x79, 0x00, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0xD3, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x87, 0x40, 0x3A, 0x5A, 0x94, 0x82, 0x04, 0x3D, 0x59, 0xAC, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x3B, 0x5A, 0xC3, 0x81, 0x00, 0x3B, 0x5A, 0xFB, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x8C, 0x80, 0x05, 0xEA, 0xA9, 0x82, 0x04, 0x3D, 0xAA, 0xB0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8C, 0x80, 0x06, 0x98, 0xA9, 0x86, 0x10, 0x36, 0x7A, 0xFD, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x85, 0x40, 0x0D, 0xEC, 0xE1, 0x84, 0x58, 0x3E, 0xCB, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x84, 0x40, 0x0D, 0xEB, 0xE0, 0x84, 0x48, 0x3E, 0xCA, 0xC0, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x8A, 0xC0, 0x0C, 0xDC, 0xA0, 0x88, 0x58, 0x3D, 0xDA, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC9, 0x40, 0x2B, 0x78, 0x8A, 0xC2, 0x0A, 0x4C, 0x8A, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A }, + { 0xCA, 0x40, 0x47, 0xCA, 0xB4, 0xC2, 0x00, 0x57, 0x8A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1A } }, + { { 0x2A, 0x0E, 0x17, 0x89, 0x50, 0x22, 0x0C, 0x1B, 0x09, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x2A, 0x1A, 0x19, 0x8A, 0x00, 0x22, 0x38, 0x0B, 0x0A, 0x00, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x06, 0x0A, 0x08, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x9B, 0x08, 0x08, 0x4A, 0xE2, 0x2F, 0x0A, 0x08, 0x68, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC5, 0x0A, 0x05, 0xDC, 0xB8, 0x84, 0x06, 0x00, 0xEC, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x09, 0x10, 0x04, 0x5B, 0xA5, 0x02, 0x08, 0x00, 0xEC, 0x70, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x86, 0x40, 0x5D, 0x5A, 0x81, 0x81, 0x00, 0x0B, 0x5A, 0xFB, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xED, 0x0F, 0x5B, 0xC8, 0xC8, 0xE2, 0x9F, 0x4A, 0xE9, 0xF9, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x40, 0x0A, 0xA7, 0x64, 0xE2, 0x8B, 0x6A, 0x79, 0xB1, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x4F, 0x3A, 0xD7, 0xF8, 0xE2, 0x97, 0x49, 0xF9, 0xF9, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC9, 0x02, 0x16, 0x9A, 0xAB, 0xC4, 0x15, 0x46, 0xBA, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x08, 0x2F, 0xF7, 0xE1, 0xF3, 0x42, 0x8F, 0xC7, 0xC2, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x2D, 0xF7, 0xC1, 0xE4, 0x40, 0x7F, 0xC7, 0xD2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x01, 0x8C, 0x9F, 0xDA, 0xE8, 0xE4, 0x50, 0x9F, 0xDA, 0xF2, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x80, 0x9F, 0xDA, 0x00, 0xE3, 0x50, 0x9F, 0xD9, 0xFA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x08, 0xD5, 0x9D, 0xA5, 0x89, 0xE2, 0x3F, 0x9F, 0xD6, 0x91, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE5, 0x0F, 0x7D, 0xB8, 0x5A, 0xA2, 0x0C, 0x7C, 0xC7, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x06, 0x4C, 0xAC, 0x56, 0x31, 0x02, 0x08, 0x8D, 0x46, 0xDC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x2A, 0x9F, 0xDB, 0x01, 0xE1, 0x04, 0x8F, 0xD7, 0xC2, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x00, 0x9F, 0xDB, 0xA9, 0xE1, 0x00, 0x8F, 0xD7, 0xBA, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x88, 0x9C, 0x50, 0xC8, 0xE2, 0x18, 0x70, 0xC4, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9C, 0x50, 0xB0, 0xE4, 0x00, 0x70, 0xC4, 0xA0, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x02, 0xA3, 0x0D, 0xDA, 0x01, 0xC2, 0x35, 0x5D, 0x58, 0x00, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } }, + { { 0x42, 0x53, 0x3E, 0xEB, 0x48, 0xD4, 0x05, 0x1D, 0xA9, 0xC9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 }, + { 0x42, 0x54, 0x6F, 0xEB, 0x61, 0xD4, 0x02, 0x2E, 0xA9, 0xC8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x18 } }, + { { 0xC2, 0x00, 0x59, 0x17, 0xB1, 0xC2, 0x1E, 0x6D, 0x98, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 }, + { 0xC2, 0x00, 0x08, 0xB3, 0x99, 0xC2, 0x06, 0x2B, 0x58, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x19 } }, + { { 0xC6, 0x01, 0x2D, 0xA7, 0x88, 0xC2, 0x08, 0x0E, 0xA7, 0xC1, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x2D, 0xA7, 0x91, 0xC2, 0x02, 0x0E, 0xA7, 0xD1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x0C, 0x06, 0x06, 0xA9, 0xC2, 0x3F, 0x08, 0xB8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A }, + { 0xC1, 0x00, 0x68, 0x50, 0xB8, 0xC2, 0x00, 0x48, 0x84, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0A } }, + { { 0xC2, 0x2E, 0x4F, 0x77, 0x00, 0xC4, 0x08, 0x0E, 0x98, 0xB1, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x2F, 0x6F, 0x79, 0x00, 0xC8, 0x0F, 0x5E, 0x98, 0xB9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x30, 0x4F, 0xCA, 0x01, 0xC4, 0x0D, 0x0E, 0xB8, 0xFB, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B }, + { 0xC4, 0x29, 0x4F, 0xCA, 0x03, 0xC8, 0x0D, 0x0C, 0xB7, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0B } }, + { { 0xC2, 0x41, 0x3D, 0x96, 0x88, 0xC4, 0xCA, 0x0E, 0xC7, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 }, + { 0xC2, 0x04, 0x58, 0xC9, 0x90, 0xC2, 0x94, 0x2C, 0xB9, 0xF0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x20 } }, + { { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x31, 0x13, 0x2D, 0xD7, 0x78, 0xE2, 0x18, 0x2E, 0xB8, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0x22, 0x86, 0x0D, 0xD7, 0xA0, 0xE4, 0x18, 0x5E, 0xB8, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } }, + { { 0xF2, 0x0A, 0x0D, 0xD7, 0x80, 0xE4, 0x1F, 0x5E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x06, 0x9A, 0xD7, 0xA0, 0xC2, 0x1F, 0x59, 0xB8, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 }, + { 0xF2, 0x09, 0x4B, 0xD6, 0x90, 0xE4, 0x1F, 0x1C, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x28 } }, + { { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x11, 0x0C, 0xE6, 0x78, 0xE4, 0x1F, 0x0C, 0xC8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x12, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x7D, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x13, 0x3D, 0xE6, 0x68, 0xE4, 0x1F, 0x5D, 0xB8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xA2, 0x40, 0x5D, 0xBA, 0x7B, 0xE2, 0x00, 0x8F, 0xD8, 0xF1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x3D, 0xDA, 0x73, 0xE1, 0x00, 0x7E, 0xD8, 0xF2, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x6D, 0xFA, 0xB9, 0xE2, 0x00, 0x8F, 0xC8, 0xF1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x4E, 0xDB, 0x92, 0xE3, 0x18, 0x6F, 0xE9, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x6F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x4E, 0xDB, 0xCA, 0xE2, 0x00, 0x7F, 0xE9, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x02, 0x0F, 0x66, 0xAA, 0xA1, 0x02, 0x64, 0x29, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x02, 0x00, 0x65, 0xAA, 0xF1, 0x02, 0x4A, 0x28, 0xF9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x16, 0x4A, 0x04, 0xBA, 0x71, 0xC2, 0x48, 0x2E, 0xCA, 0xF0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x14, 0xC0, 0x66, 0x08, 0x90, 0xC2, 0x48, 0x2C, 0x0A, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x02, 0x0A, 0x01, 0x7A, 0xB1, 0x02, 0x12, 0x2A, 0xEA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x06, 0x75, 0x05, 0xB1, 0x01, 0x3F, 0x28, 0xEA, 0xF9, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x16 } }, + { { 0x62, 0x53, 0x9C, 0xBA, 0x61, 0x62, 0x5A, 0xAD, 0xCA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x9F, 0x8A, 0x98, 0xE2, 0x11, 0x7F, 0xB8, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0x6E, 0xDA, 0x91, 0xE2, 0x13, 0x8F, 0xF9, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x40, 0x8F, 0xFA, 0xA0, 0xF2, 0x04, 0x7F, 0xFA, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA0, 0xCE, 0x5B, 0x02, 0xE2, 0x32, 0x7F, 0xFB, 0x79, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x80, 0x9C, 0x99, 0x82, 0xE2, 0x04, 0x8D, 0x78, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE0, 0x44, 0x8A, 0xA9, 0x5B, 0xE1, 0x06, 0x8D, 0x79, 0xBA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x06, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0xA0, 0xAC, 0x67, 0x02, 0xE2, 0x00, 0x7C, 0x7A, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE7, 0x94, 0xAD, 0xB7, 0x03, 0xE2, 0x00, 0x7C, 0xBA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xC3, 0x3F, 0x4B, 0xE9, 0xFA, 0xC1, 0x3F, 0x9B, 0xF9, 0xFB, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xB2, 0x20, 0xAD, 0xE9, 0x00, 0x62, 0x05, 0x8F, 0xC8, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x02, 0xAF, 0xFB, 0x90, 0xF6, 0x54, 0x8F, 0xE9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x9F, 0xFA, 0xB0, 0xF2, 0x58, 0x7F, 0xEA, 0xF8, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x00, 0xAF, 0x88, 0xA8, 0xF2, 0x46, 0x6E, 0xC9, 0xE0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x00, 0x7B, 0x88, 0xA8, 0xD2, 0x4C, 0x69, 0xE9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xF2, 0x2A, 0x9F, 0x98, 0x01, 0xE2, 0x8F, 0x4E, 0x78, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x02, 0x85, 0x89, 0xC8, 0xD2, 0x94, 0x77, 0x49, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x02, 0x9F, 0xB8, 0x90, 0x22, 0x8A, 0x9F, 0xE8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x86, 0xB8, 0x98, 0x02, 0x8F, 0x89, 0xE8, 0xF9, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x2A, 0x7F, 0xB8, 0x01, 0xE4, 0x00, 0x0D, 0xC5, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x8E, 0xE8, 0x01, 0xF2, 0x00, 0x4D, 0xD6, 0xF9, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x23, 0x8F, 0xEA, 0x00, 0xF2, 0x00, 0x5E, 0xD9, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB4, 0x26, 0x6E, 0x98, 0x01, 0x62, 0x00, 0x7D, 0xC8, 0xF9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x2E, 0x20, 0xD9, 0x01, 0xF2, 0x1A, 0x90, 0xF8, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x10, 0x69, 0x18, 0xCF, 0xD4, 0x14, 0x5B, 0x04, 0xFD, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xB8, 0x28, 0x9E, 0x98, 0x01, 0x62, 0x00, 0x3D, 0xC8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x8E, 0xC9, 0x79, 0xE6, 0x00, 0x7E, 0xD8, 0xD0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x5F, 0xF9, 0x88, 0xE4, 0x9E, 0x8F, 0xF8, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x97, 0xF9, 0x90, 0xC9, 0x80, 0x69, 0x98, 0xA0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x0C, 0x6E, 0xD8, 0x79, 0x2A, 0x09, 0x7D, 0xD8, 0xC0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x02, 0x04, 0x8A, 0xD8, 0x80, 0x0C, 0x12, 0x85, 0xD8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x7E, 0x89, 0x70, 0xE6, 0x8F, 0x80, 0xF8, 0xF0, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x67, 0x59, 0x70, 0xC6, 0x8A, 0x77, 0xA8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x80, 0x6C, 0xD9, 0x60, 0xE2, 0x00, 0x8D, 0xC8, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x80, 0x88, 0x48, 0x98, 0xE2, 0x1E, 0x8E, 0xC9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xF2, 0x40, 0xA8, 0xB9, 0x80, 0xE2, 0x0C, 0x89, 0x09, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x77, 0xC5, 0xA8, 0xE2, 0x00, 0x9E, 0xD7, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0x86, 0xB9, 0xA8, 0xE2, 0x14, 0x9F, 0xD7, 0xB0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x80, 0x94, 0x09, 0x78, 0xC2, 0x00, 0x97, 0x97, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x68, 0x68, 0xAA, 0xE2, 0x0A, 0x9B, 0xB3, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC2, 0x00, 0x86, 0x68, 0xA0, 0xC2, 0x00, 0x77, 0x47, 0xE0, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0xA6, 0x87, 0x81, 0xE2, 0x0A, 0x7E, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x89, 0x40, 0x79, 0xE2, 0x00, 0x7E, 0xC9, 0x90, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x80, 0xAA, 0xB8, 0x90, 0xE2, 0x00, 0x9E, 0xF9, 0xC0, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x80, 0x9D, 0xB8, 0x51, 0xE2, 0x00, 0x9E, 0xF9, 0xA0, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0x64, 0xD0, 0xE2, 0x28, 0x6F, 0x73, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x00, 0x7D, 0x99, 0xA8, 0xE6, 0x80, 0x80, 0xF8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x00, 0x9F, 0xB9, 0xD9, 0xE1, 0x00, 0x8F, 0xC8, 0xF9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x09, 0x68, 0x92, 0xE2, 0x2B, 0x9E, 0xF3, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xC4, 0x00, 0x99, 0xE8, 0x73, 0xE2, 0x25, 0x6F, 0x93, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE6, 0x00, 0x6F, 0xDA, 0xC9, 0xE2, 0x05, 0x2F, 0xD8, 0xAA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x4F, 0xDA, 0xC8, 0xE2, 0x00, 0x0F, 0xD8, 0xD0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEC, 0x60, 0x9D, 0xC7, 0x00, 0xE2, 0x21, 0x7F, 0xC9, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x00, 0x0F, 0xF7, 0xF9, 0xE1, 0x3F, 0x0F, 0xA7, 0x01, 0x0D, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0xA9, 0x0F, 0xA8, 0x02, 0xE2, 0x3C, 0x5F, 0xDA, 0x78, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE8, 0x40, 0x0D, 0x89, 0xF9, 0xE2, 0x17, 0x7E, 0xD9, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0xDF, 0x8A, 0xAA, 0xE2, 0x5E, 0xCF, 0xBA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0B, 0x68, 0xC0, 0xE2, 0x01, 0x9E, 0xB8, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0xAE, 0xAB, 0x91, 0xE2, 0x00, 0xAE, 0xBA, 0xD8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEB, 0x80, 0x8C, 0xCB, 0x72, 0xE2, 0x86, 0xAF, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEB, 0xC3, 0x9C, 0xCB, 0xA2, 0xE2, 0x4C, 0xAE, 0xCA, 0xFA, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE5, 0x40, 0xDB, 0x3B, 0x78, 0xE2, 0x80, 0xBE, 0xCA, 0xE1, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x80, 0x8E, 0xCB, 0xC0, 0xE2, 0x90, 0xAE, 0xCA, 0xFB, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE4, 0x00, 0x9E, 0xAA, 0x79, 0xE1, 0x43, 0x0F, 0xBA, 0xFA, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE7, 0x40, 0xEB, 0xCA, 0x80, 0xE2, 0x03, 0xBF, 0xBA, 0xC2, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE3, 0x80, 0xDB, 0xCA, 0x40, 0xE2, 0x08, 0xDF, 0xBA, 0xC1, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEA, 0x00, 0x68, 0xB8, 0x90, 0xE2, 0x0A, 0x8E, 0xB8, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x61, 0x00, 0xBE, 0x99, 0xFA, 0xE3, 0x40, 0xCF, 0xCA, 0xF9, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0xCE, 0x9A, 0xA8, 0xE2, 0x45, 0xCF, 0xCA, 0xA0, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C }, + { 0xCD, 0x00, 0x0B, 0x00, 0x90, 0xC2, 0x58, 0x0C, 0x00, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x1C } }, + { { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE2, 0x00, 0x0E, 0x00, 0xA2, 0xE2, 0x58, 0x5F, 0xD0, 0xF9, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xEC, 0x00, 0x7D, 0xDA, 0x80, 0xE2, 0x00, 0x5E, 0x9B, 0xA8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE6, 0x0A, 0x4C, 0xC9, 0x60, 0xE2, 0x07, 0x0C, 0x7A, 0xB8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE9, 0xC0, 0xEE, 0xD8, 0x83, 0xE2, 0x05, 0xDD, 0xAA, 0xE0, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xED, 0x48, 0xDE, 0xD8, 0xB4, 0xE1, 0x00, 0xDD, 0xAA, 0xA9, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xDA, 0x00, 0x8F, 0xAC, 0x92, 0x22, 0x05, 0x8D, 0x8A, 0xE9, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xEF, 0x00, 0x8C, 0xAA, 0x67, 0x25, 0x00, 0x9D, 0xAB, 0xC1, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x62, 0x82, 0xCB, 0x7A, 0xD8, 0xE6, 0x56, 0xAF, 0xDB, 0xE0, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x84, 0xBB, 0xAA, 0xCA, 0xCF, 0x41, 0xAC, 0xDA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xC2, 0x41, 0xAC, 0xBB, 0xBB, 0xC2, 0x85, 0x0E, 0xCB, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 }, + { 0xC2, 0x03, 0x6A, 0x5B, 0xA4, 0xC2, 0x0D, 0x2A, 0xBB, 0xFC, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x12 } }, + { { 0x75, 0x00, 0x0E, 0xBB, 0xB2, 0xE2, 0x1E, 0x0A, 0xA9, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0x62, 0x00, 0x04, 0x9A, 0xE8, 0xE2, 0x00, 0x0A, 0x48, 0xFD, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x41, 0x00, 0x0E, 0xEA, 0xA3, 0xC2, 0x00, 0x08, 0xCA, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0xC1, 0x40, 0x0C, 0x59, 0xD2, 0xC2, 0x80, 0x3C, 0xAB, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } }, + { { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x4B, 0x00, 0x0A, 0xF5, 0xC1, 0xC2, 0x19, 0x0C, 0xE9, 0xF8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x62, 0x00, 0x7F, 0xD8, 0xA8, 0xEA, 0x00, 0x8F, 0xD8, 0xF9, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xE1, 0x00, 0x7F, 0xD9, 0xAA, 0xE1, 0x00, 0x8F, 0xD8, 0xFA, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x09, 0xEA, 0xA8, 0xC4, 0x00, 0x0C, 0xDB, 0xC8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x40, 0x0C, 0xAA, 0xA8, 0xC4, 0x00, 0x18, 0xF9, 0xC8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xC9, 0x0C, 0x88, 0xD9, 0x6A, 0xC2, 0x14, 0x3A, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0xC5, 0x00, 0x98, 0xD9, 0x92, 0xC1, 0x16, 0x6E, 0xF9, 0xE8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x00, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xF8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x01, 0x0C, 0x44, 0xE6, 0xE8, 0x01, 0x3F, 0x0C, 0xEA, 0xF8, 0x0C, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x02, 0x3F, 0x05, 0x08, 0xF8, 0x03, 0x3F, 0x3C, 0xF9, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x00, 0x36, 0x67, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x02, 0x00, 0x36, 0x68, 0xF8, 0x01, 0x3F, 0x0E, 0xFA, 0xF8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F }, + { 0xCB, 0x00, 0xAF, 0x00, 0xFA, 0xC0, 0x00, 0xC0, 0x06, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0F } }, + { { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x05, 0x0D, 0x80, 0xA6, 0xFB, 0x0B, 0x38, 0xA9, 0xD8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x0F, 0x00, 0x90, 0xFA, 0xD0, 0x06, 0x00, 0xA7, 0x39, 0xA8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 }, + { 0xC9, 0x15, 0xDD, 0xFF, 0xF8, 0x00, 0x00, 0xE7, 0xFC, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x38 } }, + { { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x48, 0x3C, 0x30, 0xF6, 0x03, 0x0A, 0x38, 0x97, 0xE8, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x07, 0x80, 0x0B, 0xC8, 0xC9, 0x02, 0x3F, 0x0C, 0xEA, 0xF8, 0x0F, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x00, 0x21, 0x66, 0x40, 0x03, 0x00, 0x3F, 0x47, 0x00, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x08, 0x00, 0x0B, 0x3C, 0xF8, 0x08, 0x3F, 0x06, 0xF3, 0x00, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x00, 0x3F, 0x4C, 0xFB, 0x00, 0x00, 0x3F, 0x0A, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } } +}; + +static const AdLibInstrument g_gmPercussionInstrumentsOPL3[39][2] = { + { { 0x1A, 0x3F, 0x15, 0x05, 0xF8, 0x02, 0x21, 0x2B, 0xE4, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0x11, 0x18, 0x15, 0x00, 0xF8, 0x12, 0x00, 0x2B, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0x11, 0x12, 0x04, 0x07, 0xF8, 0x02, 0x18, 0x0B, 0xE5, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x11, 0x28, 0x06, 0x04, 0xF8, 0x02, 0x1E, 0x1B, 0x02, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x0A, 0x3F, 0x0B, 0x01, 0xF8, 0x1F, 0x13, 0x46, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 }, + { 0x04, 0x18, 0x06, 0x01, 0xB0, 0x10, 0x00, 0x07, 0x00, 0x90, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x01 } }, + { { 0x00, 0x3F, 0x0F, 0x00, 0xF8, 0x10, 0x0A, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x02, 0x14, 0x04, 0x00, 0xC0, 0x11, 0x08, 0x07, 0x00, 0xC6, 0x02, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x0F, 0x3F, 0x0B, 0x00, 0xF8, 0x1F, 0x07, 0x19, 0xD0, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0E, 0x32, 0x76, 0x03, 0xF8, 0x1F, 0x0F, 0x77, 0xD4, 0xFC, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x00, 0x3F, 0x1F, 0x00, 0xFA, 0x1F, 0x0C, 0x07, 0x00, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x07, 0x11, 0x13, 0x00, 0xA0, 0x13, 0x00, 0x07, 0x00, 0xC8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x4A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x02, 0x22, 0x05, 0xB6, 0xF8, 0x04, 0x0A, 0x59, 0x03, 0xF8, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xFF, 0xFA, 0x00, 0xC0, 0x2D, 0xF7, 0xE3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xD2, 0x7F, 0x04, 0x0F, 0xFA, 0x10, 0xCD, 0x24, 0x07, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x12, 0x3F, 0x05, 0x06, 0xF8, 0x43, 0x17, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x13, 0x09, 0x96, 0xF8, 0x44, 0x0A, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xCF, 0xFA, 0x00, 0x40, 0x2A, 0xF8, 0x8B, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0xCF, 0x7F, 0x05, 0x07, 0xFA, 0x00, 0x40, 0x25, 0x08, 0xC3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } }, + { { 0x12, 0x3F, 0x06, 0x17, 0xF8, 0x03, 0x1D, 0x0B, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x1A, 0x08, 0x96, 0xF8, 0x44, 0x00, 0x08, 0x03, 0xF8, 0x05, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x7F, 0x08, 0xCD, 0xFA, 0x00, 0x40, 0x1A, 0x69, 0xB3, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C }, + { 0xCD, 0x3F, 0x36, 0x05, 0xFC, 0x0F, 0x47, 0x46, 0x06, 0xDF, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0C } }, + { { 0x13, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x0D, 0x0A, 0xD9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x14, 0x09, 0x96, 0xF8, 0x44, 0x02, 0x07, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x15, 0x3F, 0x05, 0x06, 0xF8, 0x03, 0x16, 0x0C, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x00, 0x07, 0x96, 0xE8, 0x44, 0x02, 0x08, 0x03, 0xF8, 0x07, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x16, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0xCF, 0x3F, 0x2B, 0xFB, 0xFA, 0xC0, 0x1E, 0x1A, 0xCA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x17, 0x3F, 0x04, 0x09, 0xF8, 0x03, 0x18, 0x0D, 0xE9, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x12, 0x00, 0x07, 0x96, 0xF8, 0x44, 0x02, 0x08, 0xF9, 0xF8, 0x01, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x0F, 0x5E, 0xF8, 0xC6, 0x0C, 0x00, 0xCA, 0xFB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0xCF, 0x3F, 0x04, 0x57, 0xF8, 0xC5, 0x13, 0x06, 0x05, 0xFF, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0xCF, 0x3F, 0x7E, 0x9D, 0xF8, 0xC8, 0xC0, 0x0A, 0xBA, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 }, + { 0xCF, 0x3F, 0x77, 0x09, 0xF8, 0xC2, 0xC0, 0x08, 0xB5, 0xEA, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x06 } }, + { { 0xCF, 0x3F, 0x4D, 0x9F, 0xF8, 0xC6, 0x00, 0x08, 0xDA, 0xAB, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0xCF, 0x3F, 0x47, 0x06, 0xF8, 0xCD, 0x00, 0x07, 0x05, 0xB3, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0xCF, 0x3F, 0x5D, 0xAA, 0xF2, 0xC0, 0x8A, 0x67, 0x99, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x9A, 0x69, 0xF8, 0xCF, 0x88, 0x88, 0x48, 0xFA, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x3F, 0x4A, 0xFD, 0xF8, 0xCF, 0x00, 0x59, 0xEA, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x3F, 0x48, 0x06, 0xF8, 0xCF, 0x00, 0x54, 0x04, 0xF9, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x0F, 0x18, 0x0A, 0xFA, 0xAB, 0x06, 0x06, 0x06, 0x39, 0xF8, 0x0A, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x18, 0x04, 0x09, 0xAC, 0x05, 0x07, 0x08, 0x07, 0xF8, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x3F, 0x2B, 0xFC, 0xF8, 0xCC, 0xC4, 0x0B, 0xEA, 0xFB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 }, + { 0xCF, 0x3F, 0x25, 0x06, 0xF8, 0xCC, 0xD7, 0x05, 0x02, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x10 } }, + { { 0x05, 0x1A, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x0C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x01, 0x00, 0x09, 0x08, 0x40, 0x13, 0x00, 0x2A, 0x0A, 0xD8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x04, 0x19, 0x04, 0x00, 0xF8, 0x12, 0x08, 0x2C, 0xEA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 }, + { 0x04, 0x00, 0x07, 0x08, 0x40, 0x12, 0x00, 0x29, 0x08, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x04 } }, + { { 0x04, 0x0A, 0x04, 0x00, 0xD8, 0x01, 0x02, 0x0D, 0xFA, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 }, + { 0x04, 0x00, 0x03, 0x09, 0x93, 0x02, 0x00, 0x28, 0x09, 0xE8, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x07 } }, + { { 0x15, 0x14, 0x05, 0x00, 0xF9, 0x01, 0x03, 0x5C, 0xE9, 0xD8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x05, 0x00, 0x03, 0x03, 0x49, 0x02, 0x00, 0x58, 0x08, 0xE0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x10, 0x10, 0x05, 0x08, 0xF8, 0x01, 0x03, 0x0D, 0xEA, 0xE8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 }, + { 0x10, 0x00, 0x0C, 0x0C, 0x48, 0x02, 0x00, 0x08, 0xB9, 0xE0, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x05 } }, + { { 0x11, 0x00, 0x06, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x15, 0x00, 0x04, 0x87, 0xFB, 0x02, 0x40, 0x09, 0x59, 0xD0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } }, + { { 0x13, 0x26, 0x04, 0x6A, 0xFB, 0x01, 0x00, 0x08, 0x5A, 0xE0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 }, + { 0x12, 0x26, 0x03, 0x6A, 0xFB, 0x02, 0x00, 0x06, 0x5A, 0xC0, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x08 } }, + { { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC4, 0x00, 0x18, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x05, 0xA6, 0xA0, 0xC6, 0x00, 0x16, 0xF8, 0x60, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCF, 0x4D, 0x0C, 0xAA, 0xA0, 0xC3, 0x00, 0x18, 0xF8, 0x98, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0xCF, 0x4E, 0x06, 0xAA, 0xA0, 0xC5, 0x00, 0x19, 0xF9, 0x90, 0x04, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xCB, 0x3F, 0x8F, 0x00, 0xFA, 0xC5, 0x06, 0x98, 0xD6, 0xBB, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D }, + { 0xC0, 0x00, 0xF0, 0x00, 0x00, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x0D } }, + { { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x19, 0x0B, 0x55, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x0C, 0x18, 0x87, 0xB3, 0xFB, 0x1B, 0x10, 0x57, 0x75, 0xF8, 0x0E, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x05, 0x11, 0x15, 0x00, 0xC8, 0x02, 0x00, 0x08, 0x00, 0xA8, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x02, 0x11, 0x13, 0x00, 0xC8, 0x02, 0x00, 0x05, 0x00, 0x80, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0x04, 0x08, 0x15, 0x00, 0x90, 0x01, 0x00, 0x08, 0x00, 0xC0, 0x08, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 }, + { 0x03, 0x08, 0x14, 0x00, 0x90, 0x02, 0x00, 0x07, 0x00, 0xA8, 0x00, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x02 } }, + { { 0xDA, 0x00, 0x53, 0x30, 0xC0, 0x07, 0x10, 0x49, 0xC4, 0xDA, 0x03, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0xD2, 0x00, 0x56, 0x30, 0x90, 0x06, 0x00, 0x46, 0x56, 0x62, 0x09, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } }, + { { 0x1C, 0x00, 0x07, 0xBC, 0xC8, 0x0C, 0x0A, 0x0B, 0x6A, 0xF2, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 }, + { 0x18, 0x00, 0x07, 0xBC, 0x88, 0x09, 0x00, 0x0B, 0x6A, 0xBA, 0x0B, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x03 } }, + { { 0x0A, 0x0E, 0x7F, 0x00, 0xF9, 0x13, 0x16, 0x28, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 }, + { 0x01, 0x0E, 0x54, 0x00, 0xF9, 0x15, 0x03, 0x27, 0x03, 0xF8, 0x06, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0x00 } } +}; +#endif + +static const byte g_gmPercussionInstrumentMap[128] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0xFF, 0xFF, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x21, 0x22, 0x23, 0xFF, 0xFF, + 0x24, 0x25, 0xFF, 0xFF, 0xFF, 0x26, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static byte g_volumeLookupTable[64][32]; + +static const byte g_volumeTable[] = { + 0, 4, 7, 11, + 13, 16, 18, 20, + 22, 24, 26, 27, + 29, 30, 31, 33, + 34, 35, 36, 37, + 38, 39, 40, 41, + 42, 43, 44, 44, + 45, 46, 47, 47, + 48, 49, 49, 50, + 51, 51, 52, 53, + 53, 54, 54, 55, + 55, 56, 56, 57, + 57, 58, 58, 59, + 59, 60, 60, 60, + 61, 61, 62, 62, + 62, 63, 63, 63 +}; + +static int lookupVolume(int a, int b) { + if (b == 0) + return 0; + + if (b == 31) + return a; + + if (a < -63 || a > 63) { + return b * (a + 1) >> 5; + } + + if (b < 0) { + if (a < 0) { + return g_volumeLookupTable[-a][-b]; + } else { + return -g_volumeLookupTable[a][-b]; + } + } else { + if (a < 0) { + return -g_volumeLookupTable[-a][b]; + } else { + return g_volumeLookupTable[a][b]; + } + } +} + +static void createLookupTable() { + int i, j; + int sum; + + for (i = 0; i < 64; i++) { + sum = i; + for (j = 0; j < 32; j++) { + g_volumeLookupTable[i][j] = sum >> 5; + sum += i; + } + } + for (i = 0; i < 64; i++) + g_volumeLookupTable[i][0] = 0; +} + +//////////////////////////////////////// +// +// AdLib MIDI driver +// +//////////////////////////////////////// + +class MidiDriver_ADLIB : public MidiDriver { + friend class AdLibPart; + friend class AdLibPercussionChannel; + +public: + MidiDriver_ADLIB(); + + int open(); + void close(); + void send(uint32 b); + void send(byte channel, uint32 b); // Supports higher than channel 15 + uint32 property(int prop, uint32 param); + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } + + void setPitchBendRange(byte channel, uint range); + void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); + + MidiChannel *allocateChannel(); + MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported + + virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); + +private: + bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games +#ifdef ENABLE_OPL3 + bool _opl3Mode; +#endif + + OPL::OPL *_opl; + byte *_regCache; +#ifdef ENABLE_OPL3 + byte *_regCacheSecondary; +#endif + + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + + int _timerCounter; + + uint16 _channelTable2[9]; + int _voiceIndex; + int _timerIncrease; + int _timerThreshold; + uint16 _curNotTable[9]; + AdLibVoice _voices[9]; + AdLibPart _parts[32]; + AdLibPercussionChannel _percussion; + + bool _isOpen; + + void onTimer(); + void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); + void partKeyOff(AdLibPart *part, byte note); + + void adlibKeyOff(int chan); + void adlibNoteOn(int chan, byte note, int mod); + void adlibNoteOnEx(int chan, byte note, int mod); + int adlibGetRegValueParam(int chan, byte data); + void adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2); +#ifdef ENABLE_OPL3 + void adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan); +#endif + byte adlibGetRegValue(byte reg) { + return _regCache[reg]; + } +#ifdef ENABLE_OPL3 + byte adlibGetRegValueSecondary(byte reg) { + return _regCacheSecondary[reg]; + } +#endif + void adlibSetParam(int channel, byte param, int value, bool primary = true); + void adlibKeyOnOff(int channel); + void adlibWrite(byte reg, byte value); +#ifdef ENABLE_OPL3 + void adlibWriteSecondary(byte reg, byte value); +#endif + void adlibPlayNote(int channel, int note); + + AdLibVoice *allocateVoice(byte pri); + + void mcOff(AdLibVoice *voice); + + static void linkMc(AdLibPart *part, AdLibVoice *voice); + void mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11); + void mcInitStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11, byte flags, + const InstrumentExtra *ie); + + void struct10Init(Struct10 *s10, const InstrumentExtra *ie); + static byte struct10OnTimer(Struct10 *s10, Struct11 *s11); + static void struct10Setup(Struct10 *s10); + static int randomNr(int a); + void mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); +}; + +// MidiChannel method implementations + +void AdLibPart::init(MidiDriver_ADLIB *owner, byte channel) { + _owner = owner; + _channel = channel; + _priEff = 127; + programChange(0); +} + +MidiDriver *AdLibPart::device() { + return _owner; +} + +void AdLibPart::send(uint32 b) { + _owner->send(_channel, b); +} + +void AdLibPart::noteOff(byte note) { +#ifdef DEBUG_ADLIB + debug(6, "%10d: noteOff(%d)", g_tick, note); +#endif + _owner->partKeyOff(this, note); +} + +void AdLibPart::noteOn(byte note, byte velocity) { +#ifdef DEBUG_ADLIB + debug(6, "%10d: noteOn(%d,%d)", g_tick, note, velocity); +#endif + _owner->partKeyOn(this, &_partInstr, note, velocity, +#ifdef ENABLE_OPL3 + &_partInstrSecondary, +#else + NULL, +#endif + _pan); +} + +void AdLibPart::programChange(byte program) { + if (program > 127) + return; + + /* + uint i; + uint count = 0; + for (i = 0; i < ARRAYSIZE(g_gmInstruments[0]); ++i) + count += g_gmInstruments[program][i]; + if (!count) + warning("No AdLib instrument defined for GM program %d", (int)program); + */ + _program = program; +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + memcpy(&_partInstr, &g_gmInstruments[program], sizeof(AdLibInstrument)); +#ifdef ENABLE_OPL3 + } else { + memcpy(&_partInstr, &g_gmInstrumentsOPL3[program][0], sizeof(AdLibInstrument)); + memcpy(&_partInstrSecondary, &g_gmInstrumentsOPL3[program][1], sizeof(AdLibInstrument)); + } +#endif +} + +void AdLibPart::pitchBend(int16 bend) { + AdLibVoice *voice; + + _pitchBend = bend; + for (voice = _voice; voice; voice = voice->_next) { +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); +#ifdef ENABLE_OPL3 + } else { + _owner->adlibNoteOn(voice->_channel, voice->_note, _pitchBend >> 1); + } +#endif + } +} + +void AdLibPart::controlChange(byte control, byte value) { + switch (control) { + case 0: + case 32: + // Bank select. Not supported + break; + case 1: + modulationWheel(value); + break; + case 7: + volume(value); + break; + case 10: + panPosition(value); + break; + case 16: + pitchBendFactor(value); + break; + case 17: + detune(value); + break; + case 18: + priority(value); + break; + case 64: + sustain(value > 0); + break; + case 91: + // Effects level. Not supported. + break; + case 93: + // Chorus level. Not supported. + break; + case 119: + // Unknown, used in Simon the Sorcerer 2 + break; + case 121: + // reset all controllers + modulationWheel(0); + pitchBendFactor(0); + detune(0); + sustain(0); + break; + case 123: + allNotesOff(); + break; + default: + warning("AdLib: Unknown control change message %d (%d)", (int)control, (int)value); + } +} + +void AdLibPart::modulationWheel(byte value) { + AdLibVoice *voice; + + _modWheel = value; + for (voice = _voice; voice; voice = voice->_next) { + if (voice->_s10a.active && voice->_s11a.flag0x40) + voice->_s10a.modWheel = _modWheel >> 2; + if (voice->_s10b.active && voice->_s11b.flag0x40) + voice->_s10b.modWheel = _modWheel >> 2; + } +} + +void AdLibPart::volume(byte value) { + AdLibVoice *voice; + + _volEff = value; + for (voice = _voice; voice; voice = voice->_next) { +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[g_volumeLookupTable[voice->_vol2][_volEff >> 2]]); + if (voice->_twoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[g_volumeLookupTable[voice->_vol1][_volEff >> 2]]); + } +#ifdef ENABLE_OPL3 + } else { + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_vol2 + 1) * _volEff) >> 7], true); + _owner->adlibSetParam(voice->_channel, 0, g_volumeTable[((voice->_secVol2 + 1) * _volEff) >> 7], false); + if (voice->_twoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_vol1 + 1) * _volEff) >> 7], true); + } + if (voice->_secTwoChan) { + _owner->adlibSetParam(voice->_channel, 13, g_volumeTable[((voice->_secVol1 + 1) * _volEff) >> 7], false); + } + } +#endif + } +} + +void AdLibPart::panPosition(byte value) { + _pan = value; +} + +void AdLibPart::pitchBendFactor(byte value) { +#ifdef ENABLE_OPL3 + // Not supported in OPL3 mode. + if (_owner->_opl3Mode) { + return; + } +#endif + + AdLibVoice *voice; + + _pitchBendFactor = value; + for (voice = _voice; voice; voice = voice->_next) { + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); + } +} + +void AdLibPart::detune(byte value) { + // Sam&Max's OPL3 driver uses this for a completly different purpose. It + // is related to voice allocation. We ignore this for now. + // TODO: We probably need to look how the interpreter side of Sam&Max's + // iMuse version handles all this too. Implementing the driver side here + // would be not that hard. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + //_maxNotes = value; + return; + } +#endif + + AdLibVoice *voice; + + _detuneEff = value; + for (voice = _voice; voice; voice = voice->_next) { + _owner->adlibNoteOn(voice->_channel, voice->_note/* + _transposeEff*/, + (_pitchBend * _pitchBendFactor >> 6) + _detuneEff); + } +} + +void AdLibPart::priority(byte value) { + _priEff = value; +} + +void AdLibPart::sustain(bool value) { + AdLibVoice *voice; + + _pedal = value; + if (!value) { + for (voice = _voice; voice; voice = voice->_next) { + if (voice->_waitForPedal) + _owner->mcOff(voice); + } + } +} + +void AdLibPart::allNotesOff() { + while (_voice) + _owner->mcOff(_voice); +} + +void AdLibPart::sysEx_customInstrument(uint32 type, const byte *instr) { + // Sam&Max allows for instrument overwrites, but we will not support it + // until we can find any track actually using it. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + warning("AdLibPart::sysEx_customInstrument: Used in OPL3 mode"); + return; + } +#endif + + if (type == 'ADL ') { + memcpy(&_partInstr, instr, sizeof(AdLibInstrument)); + } +} + +// MidiChannel method implementations for percussion + +AdLibPercussionChannel::~AdLibPercussionChannel() { + for (int i = 0; i < ARRAYSIZE(_customInstruments); ++i) { + delete _customInstruments[i]; + } +} + +void AdLibPercussionChannel::init(MidiDriver_ADLIB *owner, byte channel) { + AdLibPart::init(owner, channel); + _priEff = 0; + _volEff = 127; + + // Initialize the custom instruments data + memset(_notes, 0, sizeof(_notes)); + memset(_customInstruments, 0, sizeof(_customInstruments)); +} + +void AdLibPercussionChannel::noteOff(byte note) { + if (_customInstruments[note]) { + note = _notes[note]; + } + + // This used to ignore note off events, since the builtin percussion + // instrument data has a duration value, which causes the percussion notes + // to stop automatically. This is not the case for (Groovie's) custom + // percussion instruments though. Also the OPL3 driver of Sam&Max actually + // does not handle the duration value, so we need it there too. + _owner->partKeyOff(this, note); +} + +void AdLibPercussionChannel::noteOn(byte note, byte velocity) { + const AdLibInstrument *inst = NULL; + const AdLibInstrument *sec = NULL; + + // The custom instruments have priority over the default mapping + // We do not support custom instruments in OPL3 mode though. +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + inst = _customInstruments[note]; + if (inst) + note = _notes[note]; +#ifdef ENABLE_OPL3 + } +#endif + + if (!inst) { + // Use the default GM to FM mapping as a fallback + byte key = g_gmPercussionInstrumentMap[note]; + if (key != 0xFF) { +#ifdef ENABLE_OPL3 + if (!_owner->_opl3Mode) { +#endif + inst = &g_gmPercussionInstruments[key]; +#ifdef ENABLE_OPL3 + } else { + inst = &g_gmPercussionInstrumentsOPL3[key][0]; + sec = &g_gmPercussionInstrumentsOPL3[key][1]; + } +#endif + } + } + + if (!inst) { + debug(2, "No instrument FM definition for GM percussion key %d", (int)note); + return; + } + + _owner->partKeyOn(this, inst, note, velocity, sec, _pan); +} + +void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *instr) { + // We do not allow custom instruments in OPL3 mode right now. +#ifdef ENABLE_OPL3 + if (_owner->_opl3Mode) { + warning("AdLibPercussionChannel::sysEx_customInstrument: Used in OPL3 mode"); + return; + } +#endif + + if (type == 'ADLP') { + byte note = instr[0]; + _notes[note] = instr[1]; + + // Allocate memory for the new instruments + if (!_customInstruments[note]) { + _customInstruments[note] = new AdLibInstrument; + memset(_customInstruments[note], 0, sizeof(AdLibInstrument)); + } + + // Save the new instrument data + _customInstruments[note]->modCharacteristic = instr[2]; + _customInstruments[note]->modScalingOutputLevel = instr[3]; + _customInstruments[note]->modAttackDecay = instr[4]; + _customInstruments[note]->modSustainRelease = instr[5]; + _customInstruments[note]->modWaveformSelect = instr[6]; + _customInstruments[note]->carCharacteristic = instr[7]; + _customInstruments[note]->carScalingOutputLevel = instr[8]; + _customInstruments[note]->carAttackDecay = instr[9]; + _customInstruments[note]->carSustainRelease = instr[10]; + _customInstruments[note]->carWaveformSelect = instr[11]; + _customInstruments[note]->feedback = instr[12]; + } +} + +// MidiDriver method implementations + +MidiDriver_ADLIB::MidiDriver_ADLIB() { + uint i; + + _scummSmallHeader = false; +#ifdef ENABLE_OPL3 + _opl3Mode = false; +#endif + + _regCache = 0; +#ifdef ENABLE_OPL3 + _regCacheSecondary = 0; +#endif + + _timerCounter = 0; + _voiceIndex = -1; + for (i = 0; i < ARRAYSIZE(_curNotTable); ++i) { + _curNotTable[i] = 0; + } + + for (i = 0; i < ARRAYSIZE(_parts); ++i) { + _parts[i].init(this, i + ((i >= 9) ? 1 : 0)); + } + _percussion.init(this, 9); + _timerIncrease = 0xD69; + _timerThreshold = 0x411B; + _opl = 0; + _adlibTimerProc = 0; + _adlibTimerParam = 0; + _isOpen = false; +} + +int MidiDriver_ADLIB::open() { + if (_isOpen) + return MERR_ALREADY_OPEN; + + _isOpen = true; + + int i; + AdLibVoice *voice; + + for (i = 0, voice = _voices; i != ARRAYSIZE(_voices); i++, voice++) { + voice->_channel = i; + voice->_s11a.s10 = &voice->_s10b; + voice->_s11b.s10 = &voice->_s10a; + } + + // Try to use OPL3 when requested. +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + _opl = OPL::Config::create(OPL::Config::kOpl3); + } + + // Initialize plain OPL2 when no OPL3 is intiailized already. + if (!_opl) { +#endif + _opl = OPL::Config::create(); +#ifdef ENABLE_OPL3 + _opl3Mode = false; + } +#endif + _opl->init(); + + _regCache = (byte *)calloc(256, 1); + + adlibWrite(8, 0x40); + adlibWrite(0xBD, 0x00); +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + adlibWrite(1, 0x20); + createLookupTable(); +#ifdef ENABLE_OPL3 + } else { + _regCacheSecondary = (byte *)calloc(256, 1); + adlibWriteSecondary(5, 1); + } +#endif + + _opl->start(new Common::Functor0Mem(this, &MidiDriver_ADLIB::onTimer)); + return 0; +} + +void MidiDriver_ADLIB::close() { + if (!_isOpen) + return; + _isOpen = false; + + // Stop the OPL timer + _opl->stop(); + + uint i; + for (i = 0; i < ARRAYSIZE(_voices); ++i) { + if (_voices[i]._part) + mcOff(&_voices[i]); + } + + // Turn off the OPL emulation + delete _opl; + _opl = 0; + + free(_regCache); +#ifdef ENABLE_OPL3 + free(_regCacheSecondary); +#endif +} + +void MidiDriver_ADLIB::send(uint32 b) { + send(b & 0xF, b & 0xFFFFFFF0); +} + +void MidiDriver_ADLIB::send(byte chan, uint32 b) { + //byte param3 = (byte) ((b >> 24) & 0xFF); + byte param2 = (byte)((b >> 16) & 0xFF); + byte param1 = (byte)((b >> 8) & 0xFF); + byte cmd = (byte)(b & 0xF0); + + AdLibPart *part; + if (chan == 9) + part = &_percussion; + else + part = &_parts[chan]; + + switch (cmd) { + case 0x80:// Note Off + part->noteOff(param1); + break; + case 0x90: // Note On + part->noteOn(param1, param2); + break; + case 0xA0: // Aftertouch + break; // Not supported. + case 0xB0: // Control Change + part->controlChange(param1, param2); + break; + case 0xC0: // Program Change + part->programChange(param1); + break; + case 0xD0: // Channel Pressure + break; // Not supported. + case 0xE0: // Pitch Bend + part->pitchBend((param1 | (param2 << 7)) - 0x2000); + break; + case 0xF0: // SysEx + // We should never get here! SysEx information has to be + // sent via high-level semantic methods. + warning("MidiDriver_ADLIB: Receiving SysEx command on a send() call"); + break; + + default: + warning("MidiDriver_ADLIB: Unknown send() command 0x%02X", cmd); + } +} + +uint32 MidiDriver_ADLIB::property(int prop, uint32 param) { + switch (prop) { + case PROP_OLD_ADLIB: // Older games used a different operator volume algorithm + _scummSmallHeader = (param > 0); + if (_scummSmallHeader) { + _timerIncrease = 473; + _timerThreshold = 1000; + } else { + _timerIncrease = 0xD69; + _timerThreshold = 0x411B; + } + return 1; + + case PROP_SCUMM_OPL3: // Sam&Max OPL3 support. +#ifdef ENABLE_OPL3 + _opl3Mode = (param > 0); +#endif + return 1; + } + + return 0; +} + +void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) { +#ifdef ENABLE_OPL3 + // Not supported in OPL3 mode. + if (_opl3Mode) { + return; + } +#endif + + AdLibVoice *voice; + AdLibPart *part = &_parts[channel]; + + part->_pitchBendFactor = range; + for (voice = part->_voice; voice; voice = voice->_next) { + adlibNoteOn(voice->_channel, voice->_note/* + part->_transposeEff*/, + (part->_pitchBend * part->_pitchBendFactor >> 6) + part->_detuneEff); + } +} + +void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { + _parts[channel].sysEx_customInstrument(type, instr); +} + +MidiChannel *MidiDriver_ADLIB::allocateChannel() { + AdLibPart *part; + uint i; + + for (i = 0; i < ARRAYSIZE(_parts); ++i) { + part = &_parts[i]; + if (!part->_allocated) { + part->allocate(); + return part; + } + } + return NULL; +} + +// All the code brought over from IMuseAdLib + +void MidiDriver_ADLIB::adlibWrite(byte reg, byte value) { + if (_regCache[reg] == value) { + return; + } +#ifdef DEBUG_ADLIB + debug(6, "%10d: adlibWrite[%x] = %x", g_tick, reg, value); +#endif + _regCache[reg] = value; + + _opl->writeReg(reg, value); +} + +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) { + assert(_opl3Mode); + + if (_regCacheSecondary[reg] == value) { + return; + } +#ifdef DEBUG_ADLIB + debug(6, "%10d: adlibWriteSecondary[%x] = %x", g_tick, reg, value); +#endif + _regCacheSecondary[reg] = value; + + _opl->writeReg(reg | 0x100, value); +} +#endif + +void MidiDriver_ADLIB::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); + + _timerCounter += _timerIncrease; + while (_timerCounter >= _timerThreshold) { + _timerCounter -= _timerThreshold; +#ifdef DEBUG_ADLIB + g_tick++; +#endif + // Sam&Max's OPL3 driver does not have any timer handling like this. +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + AdLibVoice *voice = _voices; + for (int i = 0; i != ARRAYSIZE(_voices); i++, voice++) { + if (!voice->_part) + continue; + if (voice->_duration && (voice->_duration -= 0x11) <= 0) { + mcOff(voice); + return; + } + if (voice->_s10a.active) { + mcIncStuff(voice, &voice->_s10a, &voice->_s11a); + } + if (voice->_s10b.active) { + mcIncStuff(voice, &voice->_s10b, &voice->_s11b); + } + } +#ifdef ENABLE_OPL3 + } +#endif + } +} + +void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + +void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) { + AdLibVoice *tmp; + + adlibKeyOff(voice->_channel); + + tmp = voice->_prev; + + if (voice->_next) + voice->_next->_prev = tmp; + if (tmp) + tmp->_next = voice->_next; + else + voice->_part->_voice = voice->_next; + voice->_part = NULL; +} + +void MidiDriver_ADLIB::mcIncStuff(AdLibVoice *voice, Struct10 *s10, Struct11 *s11) { + byte code; + AdLibPart *part = voice->_part; + + code = struct10OnTimer(s10, s11); + + if (code & 1) { + switch (s11->param) { + case 0: + voice->_vol2 = s10->startValue + s11->modifyVal; + if (!_scummSmallHeader) { + adlibSetParam(voice->_channel, 0, + g_volumeTable[g_volumeLookupTable[voice->_vol2] + [part->_volEff >> 2]]); + } else { + adlibSetParam(voice->_channel, 0, voice->_vol2); + } + break; + case 13: + voice->_vol1 = s10->startValue + s11->modifyVal; + if (voice->_twoChan && !_scummSmallHeader) { + adlibSetParam(voice->_channel, 13, + g_volumeTable[g_volumeLookupTable[voice->_vol1] + [part->_volEff >> 2]]); + } else { + adlibSetParam(voice->_channel, 13, voice->_vol1); + } + break; + case 30: + s11->s10->modWheel = (char)s11->modifyVal; + break; + case 31: + s11->s10->unk3 = (char)s11->modifyVal; + break; + default: + adlibSetParam(voice->_channel, s11->param, + s10->startValue + s11->modifyVal); + break; + } + } + + if (code & 2 && s11->flag0x10) + adlibKeyOnOff(voice->_channel); +} + +void MidiDriver_ADLIB::adlibKeyOff(int chan) { + byte reg = chan + 0xB0; + adlibWrite(reg, adlibGetRegValue(reg) & ~0x20); +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + adlibWriteSecondary(reg, adlibGetRegValueSecondary(reg) & ~0x20); + } +#endif +} + +byte MidiDriver_ADLIB::struct10OnTimer(Struct10 *s10, Struct11 *s11) { + byte result = 0; + int i; + + if (s10->count && (s10->count -= 17) <= 0) { + s10->active = 0; + return 0; + } + + i = s10->curVal + s10->speedHi; + s10->speedLoCounter += s10->speedLo; + if (s10->speedLoCounter >= s10->speedLoMax) { + s10->speedLoCounter -= s10->speedLoMax; + i += s10->direction; + } + if (s10->curVal != i || s10->modWheel != s10->modWheelLast) { + s10->curVal = i; + s10->modWheelLast = s10->modWheel; + i = lookupVolume(i, s10->modWheelLast); + if (i != s11->modifyVal) { + s11->modifyVal = i; + result = 1; + } + } + + if (!--s10->numSteps) { + s10->active++; + if (s10->active > 4) { + if (s10->loop) { + s10->active = 1; + result |= 2; + struct10Setup(s10); + } else { + s10->active = 0; + } + } else { + struct10Setup(s10); + } + } + + return result; +} + +void MidiDriver_ADLIB::adlibSetParam(int channel, byte param, int value, bool primary) { + const AdLibSetParams *as; + byte reg; + + assert(channel >= 0 && channel < 9); +#ifdef ENABLE_OPL3 + assert(!_opl3Mode || (param == 0 || param == 13)); +#endif + + if (param <= 12) { + reg = g_operator2Offsets[channel]; + } else if (param <= 25) { + param -= 13; + reg = g_operator1Offsets[channel]; + } else if (param <= 27) { + param -= 13; + reg = channel; + } else if (param == 28 || param == 29) { + if (param == 28) + value -= 15; + else + value -= 383; + value <<= 4; + _channelTable2[channel] = value; + adlibPlayNote(channel, _curNotTable[channel] + value); + return; + } else { + return; + } + + as = &g_setParamTable[param]; + if (as->inversion) + value = as->inversion - value; + reg += as->registerBase; +#ifdef ENABLE_OPL3 + if (primary) { +#endif + adlibWrite(reg, (adlibGetRegValue(reg) & ~as->mask) | (((byte)value) << as->shift)); +#ifdef ENABLE_OPL3 + } else { + adlibWriteSecondary(reg, (adlibGetRegValueSecondary(reg) & ~as->mask) | (((byte)value) << as->shift)); + } +#endif +} + +void MidiDriver_ADLIB::adlibKeyOnOff(int channel) { +#ifdef ENABLE_OPL3 + assert(!_opl3Mode); +#endif + + byte val; + byte reg = channel + 0xB0; + assert(channel >= 0 && channel < 9); + + val = adlibGetRegValue(reg); + adlibWrite(reg, val & ~0x20); + adlibWrite(reg, val | 0x20); +} + +void MidiDriver_ADLIB::struct10Setup(Struct10 *s10) { + int b, c, d, e, f, g, h; + byte t; + + b = s10->unk3; + f = s10->active - 1; + + t = s10->tableA[f]; + e = g_numStepsTable[g_volumeLookupTable[t & 0x7F][b]]; + if (t & 0x80) { + e = randomNr(e); + } + if (e == 0) + e++; + + s10->numSteps = s10->speedLoMax = e; + + if (f != 2) { + c = s10->maxValue; + g = s10->startValue; + t = s10->tableB[f]; + d = lookupVolume(c, (t & 0x7F) - 31); + if (t & 0x80) { + d = randomNr(d); + } + if (d + g > c) { + h = c - g; + } else { + h = d; + if (d + g < 0) + h = -g; + } + h -= s10->curVal; + } else { + h = 0; + } + + s10->speedHi = h / e; + if (h < 0) { + h = -h; + s10->direction = -1; + } else { + s10->direction = 1; + } + + s10->speedLo = h % e; + s10->speedLoCounter = 0; +} + +void MidiDriver_ADLIB::adlibPlayNote(int channel, int note) { + byte old, oct, notex; + int note2; + int i; + + note2 = (note >> 7) - 4; + note2 = (note2 < 128) ? note2 : 0; + + oct = (note2 / 12); + if (oct > 7) + oct = 7 << 2; + else + oct <<= 2; + notex = note2 % 12 + 3; + + old = adlibGetRegValue(channel + 0xB0); + if (old & 0x20) { + old &= ~0x20; + if (oct > old) { + if (notex < 6) { + notex += 12; + oct -= 4; + } + } else if (oct < old) { + if (notex > 11) { + notex -= 12; + oct += 4; + } + } + } + + i = (notex << 3) + ((note >> 4) & 0x7); + adlibWrite(channel + 0xA0, g_noteFrequencies[i]); + adlibWrite(channel + 0xB0, oct | 0x20); +} + +int MidiDriver_ADLIB::randomNr(int a) { + static byte _randSeed = 1; + if (_randSeed & 1) { + _randSeed >>= 1; + _randSeed ^= 0xB8; + } else { + _randSeed >>= 1; + } + return _randSeed * a >> 8; +} + +void MidiDriver_ADLIB::partKeyOff(AdLibPart *part, byte note) { + AdLibVoice *voice; + + for (voice = part->_voice; voice; voice = voice->_next) { + if (voice->_note == note) { + if (part->_pedal) + voice->_waitForPedal = true; + else + mcOff(voice); + } + } +} + +void MidiDriver_ADLIB::partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) { + AdLibVoice *voice; + + voice = allocateVoice(part->_priEff); + if (!voice) + return; + + linkMc(part, voice); + mcKeyOn(voice, instr, note, velocity, second, pan); +} + +AdLibVoice *MidiDriver_ADLIB::allocateVoice(byte pri) { + AdLibVoice *ac, *best = NULL; + int i; + + for (i = 0; i < 9; i++) { + if (++_voiceIndex >= 9) + _voiceIndex = 0; + ac = &_voices[_voiceIndex]; + if (!ac->_part) + return ac; + if (!ac->_next) { + if (ac->_part->_priEff <= pri) { + pri = ac->_part->_priEff; + best = ac; + } + } + } + + /* SCUMM V3 games don't have note priorities, first comes wins. */ + if (_scummSmallHeader) + return NULL; + + if (best) + mcOff(best); + return best; +} + +void MidiDriver_ADLIB::linkMc(AdLibPart *part, AdLibVoice *voice) { + voice->_part = part; + voice->_next = (AdLibVoice *)part->_voice; + part->_voice = voice; + voice->_prev = NULL; + + if (voice->_next) + voice->_next->_prev = voice; +} + +void MidiDriver_ADLIB::mcKeyOn(AdLibVoice *voice, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan) { + AdLibPart *part = voice->_part; + byte vol1, vol2; +#ifdef ENABLE_OPL3 + byte secVol1 = 0, secVol2 = 0; +#endif + + voice->_twoChan = instr->feedback & 1; + voice->_note = note; + voice->_waitForPedal = false; + voice->_duration = instr->duration; + if (voice->_duration != 0) + voice->_duration *= 63; + + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) + vol1 = (instr->modScalingOutputLevel & 0x3F) + (velocity * ((instr->modWaveformSelect >> 3) + 1)) / 64; + else +#endif + vol1 = (instr->modScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->modWaveformSelect >> 2]; + } else { + vol1 = 0x3f - (instr->modScalingOutputLevel & 0x3F); + } + if (vol1 > 0x3F) + vol1 = 0x3F; + voice->_vol1 = vol1; + + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) + vol2 = (instr->carScalingOutputLevel & 0x3F) + (velocity * ((instr->carWaveformSelect >> 3) + 1)) / 64; + else +#endif + vol2 = (instr->carScalingOutputLevel & 0x3F) + g_volumeLookupTable[velocity >> 1][instr->carWaveformSelect >> 2]; + } else { + vol2 = 0x3f - (instr->carScalingOutputLevel & 0x3F); + } + if (vol2 > 0x3F) + vol2 = 0x3F; + voice->_vol2 = vol2; + +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + voice->_secTwoChan = second->feedback & 1; + secVol1 = (second->modScalingOutputLevel & 0x3F) + (velocity * ((second->modWaveformSelect >> 3) + 1)) / 64; + if (secVol1 > 0x3F) { + secVol1 = 0x3F; + } + voice->_secVol1 = secVol1; + secVol2 = (second->carScalingOutputLevel & 0x3F) + (velocity * ((second->carWaveformSelect >> 3) + 1)) / 64; + if (secVol2 > 0x3F) { + secVol2 = 0x3F; + } + voice->_secVol2 = secVol2; + } +#endif + + if (!_scummSmallHeader) { +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + int c = part->_volEff >> 2; + vol2 = g_volumeTable[g_volumeLookupTable[vol2][c]]; + if (voice->_twoChan) + vol1 = g_volumeTable[g_volumeLookupTable[vol1][c]]; +#ifdef ENABLE_OPL3 + } else { + vol2 = g_volumeTable[((vol2 + 1) * part->_volEff) >> 7]; + secVol2 = g_volumeTable[((secVol2 + 1) * part->_volEff) >> 7]; + if (voice->_twoChan) + vol1 = g_volumeTable[((vol1 + 1) * part->_volEff) >> 7]; + if (voice->_secTwoChan) + secVol1 = g_volumeTable[((secVol1 + 1) * part->_volEff) >> 7]; + } +#endif + } + + adlibSetupChannel(voice->_channel, instr, vol1, vol2); +#ifdef ENABLE_OPL3 + if (!_opl3Mode) { +#endif + adlibNoteOnEx(voice->_channel, /*part->_transposeEff + */note, part->_detuneEff + (part->_pitchBend * part->_pitchBendFactor >> 6)); + + if (instr->flagsA & 0x80) { + mcInitStuff(voice, &voice->_s10a, &voice->_s11a, instr->flagsA, &instr->extraA); + } else { + voice->_s10a.active = 0; + } + + if (instr->flagsB & 0x80) { + mcInitStuff(voice, &voice->_s10b, &voice->_s11b, instr->flagsB, &instr->extraB); + } else { + voice->_s10b.active = 0; + } +#ifdef ENABLE_OPL3 + } else { + adlibSetupChannelSecondary(voice->_channel, second, secVol1, secVol2, pan); + adlibNoteOnEx(voice->_channel, note, part->_pitchBend >> 1); + } +#endif +} + +void MidiDriver_ADLIB::adlibSetupChannel(int chan, const AdLibInstrument *instr, byte vol1, byte vol2) { + assert(chan >= 0 && chan < 9); + + byte channel = g_operator1Offsets[chan]; + adlibWrite(channel + 0x20, instr->modCharacteristic); + adlibWrite(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1); + adlibWrite(channel + 0x60, 0xff & (~instr->modAttackDecay)); + adlibWrite(channel + 0x80, 0xff & (~instr->modSustainRelease)); + adlibWrite(channel + 0xE0, instr->modWaveformSelect); + + channel = g_operator2Offsets[chan]; + adlibWrite(channel + 0x20, instr->carCharacteristic); + adlibWrite(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2); + adlibWrite(channel + 0x60, 0xff & (~instr->carAttackDecay)); + adlibWrite(channel + 0x80, 0xff & (~instr->carSustainRelease)); + adlibWrite(channel + 0xE0, instr->carWaveformSelect); + + adlibWrite((byte)chan + 0xC0, instr->feedback +#ifdef ENABLE_OPL3 + | (_opl3Mode ? 0x30 : 0) +#endif + ); +} + +#ifdef ENABLE_OPL3 +void MidiDriver_ADLIB::adlibSetupChannelSecondary(int chan, const AdLibInstrument *instr, byte vol1, byte vol2, byte pan) { + assert(chan >= 0 && chan < 9); + assert(_opl3Mode); + + byte channel = g_operator1Offsets[chan]; + adlibWriteSecondary(channel + 0x20, instr->modCharacteristic); + adlibWriteSecondary(channel + 0x40, (instr->modScalingOutputLevel | 0x3F) - vol1); + adlibWriteSecondary(channel + 0x60, 0xff & (~instr->modAttackDecay)); + adlibWriteSecondary(channel + 0x80, 0xff & (~instr->modSustainRelease)); + adlibWriteSecondary(channel + 0xE0, instr->modWaveformSelect); + + channel = g_operator2Offsets[chan]; + adlibWriteSecondary(channel + 0x20, instr->carCharacteristic); + adlibWriteSecondary(channel + 0x40, (instr->carScalingOutputLevel | 0x3F) - vol2); + adlibWriteSecondary(channel + 0x60, 0xff & (~instr->carAttackDecay)); + adlibWriteSecondary(channel + 0x80, 0xff & (~instr->carSustainRelease)); + adlibWriteSecondary(channel + 0xE0, instr->carWaveformSelect); + + // The original uses the following (strange) behavior: +#if 0 + if (instr->feedback | (pan > 64)) { + adlibWriteSecondary((byte)chan + 0xC0, 0x20); + } else { + adlibWriteSecondary((byte)chan + 0xC0, 0x10); + } +#else + adlibWriteSecondary((byte)chan + 0xC0, instr->feedback | ((pan > 64) ? 0x20 : 0x10)); +#endif +} +#endif + +void MidiDriver_ADLIB::mcInitStuff(AdLibVoice *voice, Struct10 *s10, + Struct11 *s11, byte flags, const InstrumentExtra *ie) { + AdLibPart *part = voice->_part; + s11->modifyVal = 0; + s11->flag0x40 = flags & 0x40; + s10->loop = flags & 0x20; + s11->flag0x10 = flags & 0x10; + s11->param = g_paramTable1[flags & 0xF]; + s10->maxValue = g_maxValTable[flags & 0xF]; + s10->unk3 = 31; + if (s11->flag0x40) { + s10->modWheel = part->_modWheel >> 2; + } else { + s10->modWheel = 31; + } + + switch (s11->param) { + case 0: + s10->startValue = voice->_vol2; + break; + case 13: + s10->startValue = voice->_vol1; + break; + case 30: + s10->startValue = 31; + s11->s10->modWheel = 0; + break; + case 31: + s10->startValue = 0; + s11->s10->unk3 = 0; + break; + default: + s10->startValue = adlibGetRegValueParam(voice->_channel, s11->param); + } + + struct10Init(s10, ie); +} + +void MidiDriver_ADLIB::struct10Init(Struct10 *s10, const InstrumentExtra *ie) { + s10->active = 1; + if (!_scummSmallHeader) { + s10->curVal = 0; + } else { + s10->curVal = s10->startValue; + s10->startValue = 0; + } + s10->modWheelLast = 31; + s10->count = ie->a; + if (s10->count) + s10->count *= 63; + s10->tableA[0] = ie->b; + s10->tableA[1] = ie->d; + s10->tableA[2] = ie->f; + s10->tableA[3] = ie->g; + + s10->tableB[0] = ie->c; + s10->tableB[1] = ie->e; + s10->tableB[2] = 0; + s10->tableB[3] = ie->h; + + struct10Setup(s10); +} + +int MidiDriver_ADLIB::adlibGetRegValueParam(int chan, byte param) { + const AdLibSetParams *as; + byte val; + byte channel; + + assert(chan >= 0 && chan < 9); + + if (param <= 12) { + channel = g_operator2Offsets[chan]; + } else if (param <= 25) { + param -= 13; + channel = g_operator1Offsets[chan]; + } else if (param <= 27) { + param -= 13; + channel = chan; + } else if (param == 28) { + return 0xF; + } else if (param == 29) { + return 0x17F; + } else { + return 0; + } + + as = &g_setParamTable[param]; + val = adlibGetRegValue(channel + as->registerBase); + val &= as->mask; + val >>= as->shift; + if (as->inversion) + val = as->inversion - val; + + return val; +} + +void MidiDriver_ADLIB::adlibNoteOn(int chan, byte note, int mod) { +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + adlibNoteOnEx(chan, note, mod); + return; + } +#endif + + assert(chan >= 0 && chan < 9); + int code = (note << 7) + mod; + _curNotTable[chan] = code; + adlibPlayNote(chan, (int16)_channelTable2[chan] + code); +} + +void MidiDriver_ADLIB::adlibNoteOnEx(int chan, byte note, int mod) { + assert(chan >= 0 && chan < 9); + +#ifdef ENABLE_OPL3 + if (_opl3Mode) { + const int noteAdjusted = note + (mod >> 8) - 7; + const int pitchAdjust = (mod >> 5) & 7; + + adlibWrite(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]); + adlibWriteSecondary(0xA0 + chan, g_noteFrequencies[(noteAdjusted % 12) * 8 + pitchAdjust + 6 * 8]); + adlibWrite(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20); + adlibWriteSecondary(0xB0 + chan, (CLIP(noteAdjusted / 12, 0, 7) << 2) | 0x20); + } else { +#endif + int code = (note << 7) + mod; + _curNotTable[chan] = code; + _channelTable2[chan] = 0; + adlibPlayNote(chan, code); +#ifdef ENABLE_OPL3 + } +#endif +} + +// Plugin interface + +class AdLibEmuMusicPlugin : public MusicPluginObject { +public: + const char *getName() const { + return _s("AdLib Emulator"); + } + + const char *getId() const { + return "adlib"; + } + + MusicDevices getDevices() const; + Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const; +}; + +MusicDevices AdLibEmuMusicPlugin::getDevices() const { + MusicDevices devices; + devices.push_back(MusicDevice(this, "", MT_ADLIB)); + return devices; +} + +Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { + *mididriver = new MidiDriver_ADLIB(); + + return Common::kNoError; +} + +//#if PLUGIN_ENABLED_DYNAMIC(ADLIB) + //REGISTER_PLUGIN_DYNAMIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin); +//#else + REGISTER_PLUGIN_STATIC(ADLIB, PLUGIN_TYPE_MUSIC, AdLibEmuMusicPlugin); +//#endif diff --git a/b/audio/alsa_opl.cpp b/b/audio/alsa_opl.cpp new file mode 100644 index 00000000000..6b9e48e987f --- /dev/null +++ b/b/audio/alsa_opl.cpp @@ -0,0 +1,349 @@ +/* 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. + * + */ + +/* OPL implementation for hardware OPL using ALSA Direct FM API. + * + * Caveats and limitations: + * - Pretends to be a softsynth (emitting silence). + * - Dual OPL2 mode requires OPL3 hardware. + * - Every register write leads to a series of register writes on the hardware, + * due to the lack of direct register access in the ALSA Direct FM API. + * - No timers + */ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#include "common/debug.h" +#include "common/str.h" +#include "audio/fmopl.h" + +#include +#include +#include + +namespace OPL { +namespace ALSA { + +class OPL : public ::OPL::RealOPL { +private: + enum { + kOpl2Voices = 9, + kVoices = 18, + kOpl2Operators = 18, + kOperators = 36 + }; + + Config::OplType _type; + int _iface; + snd_hwdep_t *_opl; + snd_dm_fm_voice _oper[kOperators]; + snd_dm_fm_note _voice[kVoices]; + snd_dm_fm_params _params; + int index[2]; + static const int voiceToOper0[kVoices]; + static const int regOffsetToOper[0x20]; + + void writeOplReg(int c, int r, int v); + void clear(); + +public: + OPL(Config::OplType type); + ~OPL(); + + bool init(); + void reset(); + + void write(int a, int v); + byte read(int a); + + void writeReg(int r, int v); +}; + +const int OPL::voiceToOper0[OPL::kVoices] = + { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; + +const int OPL::regOffsetToOper[0x20] = + { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) { +} + +OPL::~OPL() { + stop(); + + if (_opl) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + snd_hwdep_close(_opl); + } +} + +void OPL::clear() { + index[0] = index[1] = 0; + + memset(_oper, 0, sizeof(_oper)); + memset(_voice, 0, sizeof(_voice)); + memset(&_params, 0, sizeof(_params)); + + for (int i = 0; i < kOperators; ++i) { + _oper[i].op = (i / 3) % 2; + _oper[i].voice = (i / 6) * 3 + (i % 3); + } + + for (int i = 0; i < kVoices; ++i) + _voice[i].voice = i; + + // For OPL3 hardware we need to set up the panning in OPL2 modes + if (_iface == SND_HWDEP_IFACE_OPL3) { + if (_type == Config::kDualOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) + _oper[i].left = 1; // FIXME below + for (int i = kOpl2Operators; i < kOperators; ++i) + _oper[i].right = 1; + } else if (_type == Config::kOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) { + _oper[i].left = 1; + _oper[i].right = 1; + } + } + } +} + +bool OPL::init() { + clear(); + + int card = -1; + snd_ctl_t *ctl; + snd_hwdep_info_t *info; + snd_hwdep_info_alloca(&info); + + int iface = SND_HWDEP_IFACE_OPL3; + if (_type == Config::kOpl2) + iface = SND_HWDEP_IFACE_OPL2; + + // Look for OPL hwdep interface + while (!snd_card_next(&card) && card >= 0) { + int dev = -1; + Common::String name = Common::String::format("hw:%d", card); + + if (snd_ctl_open(&ctl, name.c_str(), 0) < 0) + continue; + + while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) { + name = Common::String::format("hw:%d,%d", card, dev); + + if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) + continue; + + if (!snd_hwdep_info(_opl, info)) { + int found = snd_hwdep_info_get_iface(info); + // OPL3 can be used for (Dual) OPL2 mode + if (found == iface || found == SND_HWDEP_IFACE_OPL3) { + snd_ctl_close(ctl); + _iface = found; + reset(); + return true; + } + } + + // Wrong interface, try next device + snd_hwdep_close(_opl); + _opl = nullptr; + } + + snd_ctl_close(ctl); + } + + return false; +} + +void OPL::reset() { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + if (_iface == SND_HWDEP_IFACE_OPL3) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3); + + clear(); + + // Sync up with the hardware + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]); +} + +void OPL::write(int port, int val) { + val &= 0xff; + int chip = (port & 2) >> 1; + + if (port & 1) { + switch(_type) { + case Config::kOpl2: + writeOplReg(0, index[0], val); + break; + case Config::kDualOpl2: + if (port & 8) { + writeOplReg(0, index[0], val); + writeOplReg(1, index[1], val); + } else + writeOplReg(chip, index[chip], val); + break; + case Config::kOpl3: + writeOplReg(chip, index[chip], val); + } + } else { + switch(_type) { + case Config::kOpl2: + index[0] = val; + break; + case Config::kDualOpl2: + if (port & 8) { + index[0] = val; + index[1] = val; + } else + index[chip] = val; + break; + case Config::kOpl3: + index[chip] = val; + } + } +} + +byte OPL::read(int port) { + return 0; +} + +void OPL::writeReg(int r, int v) { + switch (_type) { + case Config::kOpl2: + writeOplReg(0, r, v); + break; + case Config::kDualOpl2: + writeOplReg(0, r, v); + writeOplReg(1, r, v); + break; + case Config::kOpl3: + writeOplReg(r >= 0x100, r & 0xff, v); + } +} + +void OPL::writeOplReg(int c, int r, int v) { + if (r == 0x04 && c == 1 && _type == Config::kOpl3) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast(v & 0x3f)); + } else if (r == 0x08 && c == 0) { + _params.kbd_split = (v >> 6) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r == 0xbd && c == 0) { + _params.hihat = v & 0x1; + _params.cymbal = (v >> 1) & 0x1; + _params.tomtom = (v >> 2) & 0x1; + _params.snare = (v >> 3) & 0x1; + _params.bass = (v >> 4) & 0x1; + _params.rhythm = (v >> 5) & 0x1; + _params.vib_depth = (v >> 6) & 0x1; + _params.am_depth = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r < 0xa0 || r >= 0xe0) { + // Operator + int idx = regOffsetToOper[r & 0x1f]; + + if (idx == -1) + return; + + if (c == 1) + idx += kOpl2Operators; + + switch (r & 0xf0) { + case 0x20: + case 0x30: + _oper[idx].harmonic = v & 0xf; + _oper[idx].kbd_scale = (v >> 4) & 0x1; + _oper[idx].do_sustain = (v >> 5) & 0x1; + _oper[idx].vibrato = (v >> 6) & 0x1; + _oper[idx].am = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x40: + case 0x50: + _oper[idx].volume = ~v & 0x3f; + _oper[idx].scale_level = (v >> 6) & 0x3; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x60: + case 0x70: + _oper[idx].decay = v & 0xf; + _oper[idx].attack = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x80: + case 0x90: + _oper[idx].release = v & 0xf; + _oper[idx].sustain = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0xe0: + case 0xf0: + _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + } + } else { + // Voice + int idx = r & 0xf; + + if (idx >= kOpl2Voices) + return; + + if (c == 1) + idx += kOpl2Voices; + + int opIdx = voiceToOper0[idx]; + + switch (r & 0xf0) { + case 0xa0: + _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xb0: + _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff); + _voice[idx].octave = (v >> 2) & 0x7; + _voice[idx].key_on = (v >> 5) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xc0: + _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1; + _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7; + if (_type == Config::kOpl3) { + _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1; + _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1; + } + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]); + } + } +} + +OPL *create(Config::OplType type) { + return new OPL(type); +} + +} // End of namespace ALSA +} // End of namespace OPL diff --git a/b/audio/decoders/3do.cpp b/b/audio/decoders/3do.cpp new file mode 100644 index 00000000000..6d558d4c8c6 --- /dev/null +++ b/b/audio/decoders/3do.cpp @@ -0,0 +1,343 @@ +/* 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. + * + */ + +#include "common/textconsole.h" +#include "common/stream.h" +#include "common/util.h" + +#include "audio/decoders/3do.h" +#include "audio/decoders/raw.h" +#include "audio/decoders/adpcm_intern.h" + +namespace Audio { + +// Reuses ADPCM table +#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable +#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable + +RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) { + if (stereo) { + warning("make3DO_ADP4Stream(): stereo currently not supported"); + return 0; + } + + if (audioLengthMSecsPtr) { + // Caller requires the milliseconds of audio + uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample + if (stereo) { + audioLengthMSecs /= 2; + } + *audioLengthMSecsPtr = audioLengthMSecs; + } + + return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace); +} + +Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) + : _sampleRate(sampleRate), _stereo(stereo), + _stream(stream, disposeAfterUse) { + + _callerDecoderData = persistentSpace; + memset(&_initialDecoderData, 0, sizeof(_initialDecoderData)); + _initialRead = true; + + reset(); +} + +void Audio3DO_ADP4_Stream::reset() { + memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData)); + _streamBytesLeft = _stream->size(); + _stream->seek(0); +} + +bool Audio3DO_ADP4_Stream::rewind() { + reset(); + return true; +} + +int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) { + int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex]; + int32 decodedSample = _curDecoderData.lastSample; + int16 delta = currentStep >> 3; + + if (compressedNibble & 1) + delta += currentStep >> 2; + + if (compressedNibble & 2) + delta += currentStep >> 1; + + if (compressedNibble & 4) + delta += currentStep; + + if (compressedNibble & 8) { + decodedSample -= delta; + } else { + decodedSample += delta; + } + + _curDecoderData.lastSample = CLIP(decodedSample, -32768, 32767); + + _curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07]; + _curDecoderData.stepIndex = CLIP(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1); + + return _curDecoderData.lastSample; +} + +// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written +int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) { + int8 byteCache[AUDIO_3DO_CACHE_SIZE]; + int8 *byteCachePtr = NULL; + int byteCacheSize = 0; + int requestedBytesLeft = 0; + int decodedSamplesCount = 0; + + int8 compressedByte = 0; + + if (endOfData()) + return 0; // no more bytes left + + if (_callerDecoderData) { + // copy caller decoder data over + memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData)); + if (_initialRead) { + _initialRead = false; + memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData)); + } + } + + requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample + if (requestedBytesLeft > _streamBytesLeft) + requestedBytesLeft = _streamBytesLeft; // not enough bytes left + + // in case caller requests an uneven amount of samples, we will return an even amount + + // buffering, so that direct decoding of files and such runs way faster + while (requestedBytesLeft) { + if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) { + byteCacheSize = AUDIO_3DO_CACHE_SIZE; + } else { + byteCacheSize = requestedBytesLeft; + } + + requestedBytesLeft -= byteCacheSize; + _streamBytesLeft -= byteCacheSize; + + // Fill our byte cache + _stream->read(byteCache, byteCacheSize); + + byteCachePtr = byteCache; + + // Mono + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + + buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4); + decodedSamplesCount++; + buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f); + decodedSamplesCount++; + } + } + + if (_callerDecoderData) { + // copy caller decoder data back + memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData)); + } + + return decodedSamplesCount; +} + +// ============================================================================ +static int16 audio_3DO_SDX2_SquareTable[256] = { + -32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322, + -27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762, + -23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602, + -19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842, + -15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482, + -12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522, + -9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962, + -6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802, + -4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042, + -2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682, + -1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722, + -648, -578, -512, -450, -392, -338, -288, -242, -200, -162, + -128, -98, -72, -50, -32, -18, -8, -2, 0, 2, + 8, 18, 32, 50, 72, 98, 128, 162, 200, 242, + 288, 338, 392, 450, 512, 578, 648, 722, 800, 882, + 968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922, + 2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362, + 3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202, + 5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442, + 7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082, + 10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122, + 13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562, + 16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402, + 20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642, + 25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282, + 29768, 30258, 30752, 31250, 31752, 32258 +}; + +Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) + : _sampleRate(sampleRate), _stereo(stereo), + _stream(stream, disposeAfterUse) { + + _callerDecoderData = persistentSpace; + memset(&_initialDecoderData, 0, sizeof(_initialDecoderData)); + _initialRead = true; + + reset(); +} + +void Audio3DO_SDX2_Stream::reset() { + memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData)); + _streamBytesLeft = _stream->size(); + _stream->seek(0); +} + +bool Audio3DO_SDX2_Stream::rewind() { + reset(); + return true; +} + +// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written +int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) { + int8 byteCache[AUDIO_3DO_CACHE_SIZE]; + int8 *byteCachePtr = NULL; + int byteCacheSize = 0; + int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample + int decodedSamplesCount = 0; + + int8 compressedByte = 0; + uint8 squareTableOffset = 0; + int16 decodedSample = 0; + + if (endOfData()) + return 0; // no more bytes left + + if (_stereo) { + // We expect numSamples to be even in case of Stereo audio + assert((numSamples & 1) == 0); + } + + if (_callerDecoderData) { + // copy caller decoder data over + memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData)); + if (_initialRead) { + _initialRead = false; + memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData)); + } + } + + requestedBytesLeft = numSamples; + if (requestedBytesLeft > _streamBytesLeft) + requestedBytesLeft = _streamBytesLeft; // not enough bytes left + + // buffering, so that direct decoding of files and such runs way faster + while (requestedBytesLeft) { + if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) { + byteCacheSize = AUDIO_3DO_CACHE_SIZE; + } else { + byteCacheSize = requestedBytesLeft; + } + + requestedBytesLeft -= byteCacheSize; + _streamBytesLeft -= byteCacheSize; + + // Fill our byte cache + _stream->read(byteCache, byteCacheSize); + + byteCachePtr = byteCache; + + if (!_stereo) { + // Mono + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + squareTableOffset = compressedByte + 128; + + if (!(compressedByte & 1)) + _curDecoderData.lastSample1 = 0; + + decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample1 = decodedSample; + + buffer[decodedSamplesCount] = decodedSample; + decodedSamplesCount++; + } + } else { + // Stereo + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + squareTableOffset = compressedByte + 128; + + if (!(decodedSamplesCount & 1)) { + // First channel + if (!(compressedByte & 1)) + _curDecoderData.lastSample1 = 0; + + decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample1 = decodedSample; + } else { + // Second channel + if (!(compressedByte & 1)) + _curDecoderData.lastSample2 = 0; + + decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample2 = decodedSample; + } + + buffer[decodedSamplesCount] = decodedSample; + decodedSamplesCount++; + } + } + } + + if (_callerDecoderData) { + // copy caller decoder data back + memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData)); + } + + return decodedSamplesCount; +} + +RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) { + if (stereo) { + if (stream->size() & 1) { + warning("make3DO_SDX2Stream(): stereo data is uneven size"); + return 0; + } + } + + if (audioLengthMSecsPtr) { + // Caller requires the milliseconds of audio + uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample + if (stereo) { + audioLengthMSecs /= 2; + } + *audioLengthMSecsPtr = audioLengthMSecs; + } + + return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace); +} + +} // End of namespace Audio diff --git a/b/audio/decoders/3do.h b/b/audio/decoders/3do.h new file mode 100644 index 00000000000..75243585433 --- /dev/null +++ b/b/audio/decoders/3do.h @@ -0,0 +1,158 @@ +/* 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. + * + */ + +/** + * @file + * Sound decoder used in engines: + * - sherlock (3DO version of Serrated Scalpel) + */ + +#ifndef AUDIO_3DO_SDX2_H +#define AUDIO_3DO_SDX2_H + +#include "common/scummsys.h" +#include "common/types.h" +#include "common/substream.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Audio { + +class SeekableAudioStream; + +// amount of bytes to be used within the decoder classes as buffers +#define AUDIO_3DO_CACHE_SIZE 1024 + +// persistent spaces +struct audio_3DO_ADP4_PersistentSpace { + int16 lastSample; + int16 stepIndex; +}; + +struct audio_3DO_SDX2_PersistentSpace { + int16 lastSample1; + int16 lastSample2; +}; + +class Audio3DO_ADP4_Stream : public RewindableAudioStream { +public: + Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace); + +protected: + const uint16 _sampleRate; + const bool _stereo; + + Common::DisposablePtr _stream; + int32 _streamBytesLeft; + + void reset(); + bool rewind(); + bool endOfData() const { return (_stream->pos() >= _stream->size()); } + bool isStereo() const { return _stereo; } + int getRate() const { return _sampleRate; } + + int readBuffer(int16 *buffer, const int numSamples); + + bool _initialRead; + audio_3DO_ADP4_PersistentSpace *_callerDecoderData; + audio_3DO_ADP4_PersistentSpace _initialDecoderData; + audio_3DO_ADP4_PersistentSpace _curDecoderData; + +private: + int16 decodeSample(byte compressedNibble); +}; + +class Audio3DO_SDX2_Stream : public RewindableAudioStream { +public: + Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr); + +protected: + const uint16 _sampleRate; + const bool _stereo; + + Common::DisposablePtr _stream; + int32 _streamBytesLeft; + + void reset(); + bool rewind(); + bool endOfData() const { return (_stream->pos() >= _stream->size()); } + bool isStereo() const { return _stereo; } + int getRate() const { return _sampleRate; } + + int readBuffer(int16 *buffer, const int numSamples); + + bool _initialRead; + audio_3DO_SDX2_PersistentSpace *_callerDecoderData; + audio_3DO_SDX2_PersistentSpace _initialDecoderData; + audio_3DO_SDX2_PersistentSpace _curDecoderData; +}; + +/** + * Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream + * from that data. + * + * @param stream the SeekableReadStream from which to read the 3DO SDX2 data + * @sampleRate sample rate + * @stereo if it's stereo or mono + * @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds + * @disposeAfterUse disposeAfterUse whether to delete the stream after use + * @persistentSpacePtr pointer to the persistent space structure + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *make3DO_ADP4AudioStream( + Common::SeekableReadStream *stream, + uint16 sampleRate, + bool stereo, + uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, + audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL +); + +/** + * Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream + * from that data. + * + * @param stream the SeekableReadStream from which to read the 3DO SDX2 data + * @sampleRate sample rate + * @stereo if it's stereo or mono + * @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds + * @disposeAfterUse disposeAfterUse whether to delete the stream after use + * @persistentSpacePtr pointer to the persistent space structure + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *make3DO_SDX2AudioStream( + Common::SeekableReadStream *stream, + uint16 sampleRate, + bool stereo, + uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, + audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL +); + +} // End of namespace Audio + +#endif diff --git a/common/dcl.cpp b/common/dcl.cpp index 2f4cdeda6be..5993c218cb1 100644 --- a/common/dcl.cpp +++ b/common/dcl.cpp @@ -30,17 +30,15 @@ namespace Common { class DecompressorDCL { public: - bool unpack(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked); + bool unpack(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize); protected: /** * Initialize decompressor. - * @param src source stream to read from - * @param dest destination stream to write to - * @param nPacked size of packed data - * @param nUnpacked size of unpacked data + * @param sourceStream source stream to read from + * @param targetStream target memory stream to write to */ - void init(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked); + void init(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize); /** * Get a number of bits from _src stream, starting with the least @@ -66,36 +64,38 @@ protected: int huffman_lookup(const int *tree); - uint32 _dwBits; ///< bits buffer - byte _nBits; ///< number of unread bits in _dwBits - uint32 _szPacked; ///< size of the compressed data - uint32 _szUnpacked; ///< size of the decompressed data - uint32 _dwRead; ///< number of bytes read from _src - uint32 _dwWrote; ///< number of bytes written to _dest - ReadStream *_src; - byte *_dest; + uint32 _dwBits; ///< bits buffer + byte _nBits; ///< number of unread bits in _dwBits + uint32 _sourceSize; ///< size of the source stream + uint32 _targetSize; ///< size of the target stream (if fixed) + bool _targetFixedSize; ///< if target stream is fixed size or dynamic size + uint32 _bytesRead; ///< number of bytes read from _sourceStream + uint32 _bytesWritten; ///< number of bytes written to _targetStream + SeekableReadStream *_sourceStream; + WriteStream *_targetStream; }; -void DecompressorDCL::init(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked) { - _src = src; - _dest = dest; - _szPacked = nPacked; - _szUnpacked = nUnpacked; +void DecompressorDCL::init(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize) { + _sourceStream = sourceStream; + _targetStream = targetStream; + _sourceSize = sourceStream->size(); + _targetSize = targetSize; + _targetFixedSize = targetFixedSize; _nBits = 0; - _dwRead = _dwWrote = 0; + _bytesRead = _bytesWritten = 0; _dwBits = 0; } void DecompressorDCL::fetchBitsLSB() { while (_nBits <= 24) { - _dwBits |= ((uint32)_src->readByte()) << _nBits; + _dwBits |= ((uint32)_sourceStream->readByte()) << _nBits; _nBits += 8; - _dwRead++; + _bytesRead++; } } uint32 DecompressorDCL::getBitsLSB(int n) { - // fetching more data to buffer if needed + // Fetching more data to buffer if needed if (_nBits < n) fetchBitsLSB(); uint32 ret = (_dwBits & ~((~0) << n)); @@ -109,7 +109,8 @@ byte DecompressorDCL::getByteLSB() { } void DecompressorDCL::putByte(byte b) { - _dest[_dwWrote++] = b; + _targetStream->writeByte(b); + _bytesWritten++; } #define HUFFMAN_LEAF 0x40000000 @@ -331,97 +332,189 @@ int DecompressorDCL::huffman_lookup(const int *tree) { #define DCL_BINARY_MODE 0 #define DCL_ASCII_MODE 1 -bool DecompressorDCL::unpack(ReadStream *src, byte *dest, uint32 nPacked, uint32 nUnpacked) { - init(src, dest, nPacked, nUnpacked); +#define MIDI_SETUP_BUNDLE_FILE_MAXIMUM_DICTIONARY_SIZE 4096 +bool DecompressorDCL::unpack(SeekableReadStream *sourceStream, WriteStream *targetStream, uint32 targetSize, bool targetFixedSize) { + byte dictionary[MIDI_SETUP_BUNDLE_FILE_MAXIMUM_DICTIONARY_SIZE]; + uint16 dictionaryPos = 0; + uint16 dictionarySize = 0; + uint16 dictionaryMask = 0; int value; - uint32 val_distance, val_length; + uint16 tokenOffset = 0; + uint16 tokenLength = 0; - int mode = getByteLSB(); - int length_param = getByteLSB(); + init(sourceStream, targetStream, targetSize, targetFixedSize); + + byte mode = getByteLSB(); + byte dictionaryType = getByteLSB(); if (mode != DCL_BINARY_MODE && mode != DCL_ASCII_MODE) { warning("DCL-INFLATE: Error: Encountered mode %02x, expected 00 or 01", mode); return false; } - if (length_param < 3 || length_param > 6) - warning("Unexpected length_param value %d (expected in [3,6])", length_param); + // TODO: original code supported 3 as well??? + // Was this an accident or on purpose? And the original code did just give out a warning + // and didn't error out at all + switch (dictionaryType) { + case 4: + dictionarySize = 1024; + break; + case 5: + dictionarySize = 2048; + break; + case 6: + dictionarySize = 4096; + break; + default: + warning("DCL-INFLATE: Error: unsupported dictionary type %02x", dictionaryType); + return false; + } + dictionaryMask = dictionarySize - 1; - while (_dwWrote < _szUnpacked) { + while ((!targetFixedSize) || (_bytesWritten < _targetSize)) { if (getBitsLSB(1)) { // (length,distance) pair value = huffman_lookup(length_tree); if (value < 8) - val_length = value + 2; + tokenLength = value + 2; else - val_length = 8 + (1 << (value - 7)) + getBitsLSB(value - 7); + tokenLength = 8 + (1 << (value - 7)) + getBitsLSB(value - 7); + + if (tokenLength == 519) + break; // End of stream signal debug(8, " | "); value = huffman_lookup(distance_tree); - if (val_length == 2) - val_distance = (value << 2) | getBitsLSB(2); + if (tokenLength == 2) + tokenOffset = (value << 2) | getBitsLSB(2); else - val_distance = (value << length_param) | getBitsLSB(length_param); - val_distance ++; + tokenOffset = (value << dictionaryType) | getBitsLSB(dictionaryType); + tokenOffset++; - debug(8, "\nCOPY(%d from %d)\n", val_length, val_distance); + debug(8, "\nCOPY(%d from %d)\n", tokenLength, tokenOffset); - if (val_length + _dwWrote > _szUnpacked) { - warning("DCL-INFLATE Error: Write out of bounds while copying %d bytes (declared unpacked size is %d bytes, current is %d + %d bytes)", - val_length, _szUnpacked, _dwWrote, val_length); - return false; + if (_targetFixedSize) { + if (tokenLength + _bytesWritten > _targetSize) { + warning("DCL-INFLATE Error: Write out of bounds while copying %d bytes (declared unpacked size is %d bytes, current is %d + %d bytes)", + tokenLength, _targetSize, _bytesWritten, tokenLength); + return false; + } } - if (_dwWrote < val_distance) { + if (_bytesWritten < tokenOffset) { warning("DCL-INFLATE Error: Attempt to copy from before beginning of input stream (declared unpacked size is %d bytes, current is %d bytes)", - _szUnpacked, _dwWrote); + _targetSize, _bytesWritten); return false; } - while (val_length) { - uint32 copy_length = (val_length > val_distance) ? val_distance : val_length; - assert(val_distance >= copy_length); - uint32 pos = _dwWrote - val_distance; - for (uint32 i = 0; i < copy_length; i++) - putByte(dest[pos + i]); + uint16 dictionaryBaseIndex = (dictionaryPos - tokenOffset) & dictionaryMask; + uint16 dictionaryIndex = dictionaryBaseIndex; + uint16 dictionaryNextIndex = dictionaryPos; - for (uint32 i = 0; i < copy_length; i++) - debug(9, "\33[32;31m%02x\33[37;37m ", dest[pos + i]); - debug(9, "\n"); + while (tokenLength) { + // Write byte from dictionary + putByte(dictionary[dictionaryIndex]); + debug(9, "\33[32;31m%02x\33[37;37m ", dictionary[dictionaryIndex]); - val_length -= copy_length; - val_distance += copy_length; + dictionary[dictionaryNextIndex] = dictionary[dictionaryIndex]; + + dictionaryNextIndex = (dictionaryNextIndex + 1) & dictionaryMask; + dictionaryIndex = (dictionaryIndex + 1) & dictionaryMask; + + if (dictionaryIndex == dictionaryPos) + dictionaryIndex = dictionaryBaseIndex; + if (dictionaryNextIndex == dictionarySize) + dictionaryNextIndex = 0; + + tokenLength--; } + dictionaryPos = dictionaryNextIndex; + debug(9, "\n"); } else { // Copy byte verbatim value = (mode == DCL_ASCII_MODE) ? huffman_lookup(ascii_tree) : getByteLSB(); putByte(value); + + // Also remember it inside dictionary + dictionary[dictionaryPos] = value; + dictionaryPos++; + if (dictionaryPos >= dictionarySize) + dictionaryPos = 0; + debug(9, "\33[32;31m%02x \33[37;37m", value); } } - return _dwWrote == _szUnpacked; + if (_targetFixedSize) { + return _bytesWritten == _targetSize; + } + return true; // For targets featuring dynamic size we always succeed } bool decompressDCL(ReadStream *src, byte *dest, uint32 packedSize, uint32 unpackedSize) { + bool success = false; + DecompressorDCL dcl; + if (!src || !dest) return false; - DecompressorDCL dcl; - return dcl.unpack(src, dest, packedSize, unpackedSize); + byte *sourceBufferPtr = (byte *)malloc(packedSize); + if (!sourceBufferPtr) + return false; + + // Read source into memory + src->read(sourceBufferPtr, packedSize); + + Common::MemoryReadStream *sourceStream = new MemoryReadStream(sourceBufferPtr, packedSize, DisposeAfterUse::NO); + Common::MemoryWriteStream *targetStream = new MemoryWriteStream(dest, unpackedSize); + + success = dcl.unpack(sourceStream, targetStream, unpackedSize, true); + delete sourceStream; + delete targetStream; + return success; } -SeekableReadStream *decompressDCL(ReadStream *src, uint32 packedSize, uint32 unpackedSize) { - byte *data = (byte *)malloc(unpackedSize); +SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream, uint32 packedSize, uint32 unpackedSize) { + bool success = false; + byte *targetPtr = nullptr; + Common::MemoryWriteStream *targetStream; + DecompressorDCL dcl; - if (decompressDCL(src, data, packedSize, unpackedSize)) - return new MemoryReadStream(data, unpackedSize, DisposeAfterUse::YES); + targetPtr = (byte *)malloc(unpackedSize); + if (!targetPtr) + return nullptr; - free(data); - return 0; + targetStream = new MemoryWriteStream(targetPtr, unpackedSize); + + success = dcl.unpack(sourceStream, targetStream, unpackedSize, true); + delete targetStream; + + if (!success) { + free(targetPtr); + return nullptr; + } + return new MemoryReadStream(targetPtr, unpackedSize, DisposeAfterUse::YES); +} + +// This one figures out the unpacked size by itself +// Needed for at least Simon 2, because the unpacked size is not stored anywhere +SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream) { + Common::MemoryWriteStreamDynamic *targetStream; + DecompressorDCL dcl; + + targetStream = new MemoryWriteStreamDynamic(DisposeAfterUse::NO); + + if (dcl.unpack(sourceStream, targetStream, 0, false)) { + byte *targetPtr = targetStream->getData(); + uint32 unpackedSize = targetStream->size(); + delete targetStream; + return new MemoryReadStream(targetPtr, unpackedSize, DisposeAfterUse::YES); + } + delete targetStream; + return nullptr; } } // End of namespace Common diff --git a/common/dcl.h b/common/dcl.h index 0e96f74c079..f90bc23c8de 100644 --- a/common/dcl.h +++ b/common/dcl.h @@ -22,7 +22,8 @@ /** * @file - * PKWARE DCL ("explode") decompressor used in engines: + * PKWARE DCL ("explode") ("PKWARE data compression library") decompressor used in engines: + * - agos (exclusively for Simon 2 setup.shr file) * - mohawk * - sci */ @@ -38,16 +39,22 @@ class ReadStream; class SeekableReadStream; /** - * Try to decompress a PKWARE DCL compressed stream. Returns true if + * Try to decompress a PKWARE DCL (PKWARE data compression library) compressed stream. Returns true if * successful. */ -bool decompressDCL(ReadStream *src, byte *dest, uint32 packedSize, uint32 unpackedSize); +bool decompressDCL(ReadStream *sourceStream, byte *dest, uint32 packedSize, uint32 unpackedSize); /** - * Try to decompress a PKWARE DCL compressed stream. Returns a valid pointer + * Try to decompress a PKWARE DCL (PKWARE data compression library) compressed stream. Returns a valid pointer * if successful and 0 otherwise. */ -SeekableReadStream *decompressDCL(ReadStream *src, uint32 packedSize, uint32 unpackedSize); +SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream, uint32 packedSize, uint32 unpackedSize); + +/** + * Try to decompress a PKWARE DCL (PKWARE data compression library) compressed stream. Returns a valid pointer + * if successful and 0 otherwise. This method is meant for cases, where the unpacked size is not known. + */ +SeekableReadStream *decompressDCL(SeekableReadStream *sourceStream); } // End of namespace Common diff --git a/common/fft.cpp b/common/fft.cpp index 477f7aca627..27a04abb6a9 100644 --- a/common/fft.cpp +++ b/common/fft.cpp @@ -56,6 +56,10 @@ FFT::FFT(int bits, int inverse) : _bits(bits), _inverse(inverse) { } FFT::~FFT() { + for (int i = 0; i < ARRAYSIZE(_cosTables); i++) { + delete _cosTables[i]; + } + delete[] _revTab; delete[] _expTab; delete[] _tmpBuf; diff --git a/configure b/configure index 7ba2c275fa0..0c1e8d1ec52 100755 --- a/configure +++ b/configure @@ -69,6 +69,16 @@ get_var() { eval echo \$${1} } +append_var() { + VAR=${1} + shift + if eval test -z \"\$${VAR}\" ; then + eval ${VAR}='$@' + else + eval ${VAR}=\"\$${VAR} \"'$@' + fi +} + # Add an engine: id name build subengines base-games dependencies add_engine() { _engines="${_engines} ${1}" @@ -1172,7 +1182,7 @@ for ac_option in $@; do _debug_build=no ;; --enable-Werror) - CXXFLAGS="$CXXFLAGS -Werror" + append_var CXXFLAGS "-Werror" ;; --enable-release-mode) _release_build=yes @@ -1309,8 +1319,8 @@ dreamcast) _host_os=dreamcast _host_cpu=sh _host_alias=sh-elf - CXXFLAGS="$CXXFLAGS -ml -m4-single-only" - LDFLAGS="$LDFLAGS -ml -m4-single-only" + append_var CXXFLAGS "-ml -m4-single-only" + append_var LDFLAGS "-ml -m4-single-only" ;; ds) _host_os=ds @@ -1353,13 +1363,13 @@ maemo) # The prefix is always the same on Maemo so we hardcode the default # here. It is still possible to define a custom prefix which is # needed when packaging the app with a user-specific app ID. - test "x$prefix" = xNONE && prefix=/opt/scummvm + test "x$prefix" = xNONE && prefix=/opt/residualvm # Maemo apps are installed into app-specific directories. The # default directory structure of ScummVM makes no sense here so we # hardcode Maemo specific directories here. datarootdir='${prefix}/share' - datadir=/opt/scummvm/share - docdir='${datarootdir}/doc/scummvm' + datadir=/opt/residualvm/share + docdir='${datarootdir}/doc/residualvm' ;; motoezx) _host_os=linux @@ -1424,7 +1434,7 @@ samsungtv) _host_alias=arm-linux-gnueabi ;; tizen) - _host_os=tizen + _host_os=tizen _host_cpu=arm _host_alias=arm-linux-gnueabi ;; @@ -1497,18 +1507,18 @@ if test "$_debug_build" != no; then case $_host_os in amigaos*) # AmigaOS debugger uses plain stabs, with no gdb extensions. - CXXFLAGS="$CXXFLAGS -gstabs" + append_var CXXFLAGS "-gstabs" ;; *) # Use the system default format for debug info. - CXXFLAGS="$CXXFLAGS -g" + append_var CXXFLAGS "-g" esac fi if test "$_release_build" = yes; then # Add a define, which indicates we are doing # an build for a release version. - DEFINES="$DEFINES -DRELEASE_BUILD" + append_var DEFINES "-DRELEASE_BUILD" fi @@ -1680,7 +1690,7 @@ if test "$have_icc" = yes; then # Make ICC error our on unknown command line options instead of printing # a warning. This is for example required to make the -Wglobal-destructors # detection work correctly. - CXXFLAGS="$CXXFLAGS -diag-error 10006,10148" + append_var CXXFLAGS "-diag-error 10006,10148" fi have_gcc=no @@ -1788,20 +1798,20 @@ if test "$have_gcc" = yes ; then amigaos* | android | dreamcast | ds | gamecube | mingw* | n64 | psp | ps2 | ps3 | tizen | wii | wince ) ;; *) - CXXFLAGS="$CXXFLAGS -ansi" + append_var CXXFLAGS "-ansi" ;; esac fi - CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter" + append_var CXXFLAGS "-W -Wno-unused-parameter" add_line_to_config_mk 'HAVE_GCC3 = 1' add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' fi; if test "$_cxx_major" -eq 4 && test "$_cxx_minor" -ge 3 || \ test "$_cxx_major" -gt 4 ; then - CXXFLAGS="$CXXFLAGS -Wno-empty-body" + append_var CXXFLAGS "-Wno-empty-body" else - CXXFLAGS="$CXXFLAGS -Wconversion" + append_var CXXFLAGS "-Wconversion" fi; elif test "$have_icc" = yes ; then add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' @@ -1812,7 +1822,7 @@ fi; # echo_n "Building as C++11... " if test "$_use_cxx11" = "yes" ; then - CXXFLAGS="$CXXFLAGS -std=c++11" + append_var CXXFLAGS "-std=c++11" fi echo $_use_cxx11 @@ -1827,7 +1837,7 @@ android | gamecube | psp | tizen | wii | webos) # ICC does not support pedantic, while GCC and clang do. if test "$have_icc" = no ; then # We *do* want the 'long long' extension. - CXXFLAGS="$CXXFLAGS -pedantic -Wno-long-long" + append_var CXXFLAGS "-pedantic -Wno-long-long" fi ;; esac @@ -1841,7 +1851,7 @@ EOF cc_check -Wglobal-constructors && _global_constructors=yes if test "$_global_constructors" = yes; then - CXXFLAGS="$CXXFLAGS -Wglobal-constructors" + append_var CXXFLAGS "-Wglobal-constructors" fi echo $_global_constructors @@ -2037,7 +2047,7 @@ case $_host_cpu in # (on Pandora and iPhone at least) #define_in_config_if_yes yes 'USE_ARM_COSTUME_ASM' - DEFINES="$DEFINES -DARM_TARGET" + append_var DEFINES "-DARM_TARGET" ;; i[3-6]86) echo "x86" @@ -2046,11 +2056,11 @@ case $_host_cpu in ;; mips*) echo "MIPS" - DEFINES="$DEFINES -DMIPS_TARGET" + append_var DEFINES "-DMIPS_TARGET" ;; powerpc*) echo "PowerPC" - DEFINES="$DEFINES -DPPC_TARGET" + append_var DEFINES "-DPPC_TARGET" ;; amd64 | x86_64) echo "x86_64" @@ -2068,58 +2078,58 @@ echo_n "Checking hosttype... " echo $_host_os case $_host_os in amigaos*) - LDFLAGS="$LDFLAGS -use-dynld -Wl,--export-dynamic" - LDFLAGS="$LDFLAGS -L/sdk/local/newlib/lib" + append_var LDFLAGS "-use-dynld -Wl,--export-dynamic" + append_var LDFLAGS "-L/sdk/local/newlib/lib" # We have to use 'long' for our 4 byte typedef because AmigaOS already typedefs (u)int32 # as (unsigned) long, and consequently we'd get a compiler error otherwise. type_4_byte='long' # Supress format warnings as the long 4 byte causes noisy warnings. - CXXFLAGS="$CXXFLAGS -Wno-format" + append_var CXXFLAGS "-Wno-format" add_line_to_config_mk 'AMIGAOS = 1' _port_mk="backends/platform/sdl/amigaos/amigaos.mk" ;; android) case $_host in android | android-arm) - CXXFLAGS="$CXXFLAGS -march=armv5te" - CXXFLAGS="$CXXFLAGS -mtune=xscale" - CXXFLAGS="$CXXFLAGS -msoft-float" + append_var CXXFLAGS "-march=armv5te" + append_var CXXFLAGS "-mtune=xscale" + append_var CXXFLAGS "-msoft-float" ABI="armeabi" # Platform version 9 is used by ResidualVM ANDROID_PLATFORM=9 ANDROID_PLATFORM_ARCH="arm" ;; android-v7a | android-arm-v7a) - CXXFLAGS="$CXXFLAGS -march=armv7-a" - CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp" - CXXFLAGS="$CXXFLAGS -mfpu=vfp" - LDFLAGS="$LDFLAGS -Wl,--fix-cortex-a8" + append_var CXXFLAGS "-march=armv7-a" + append_var CXXFLAGS "-mfloat-abi=softfp" + append_var CXXFLAGS "-mfpu=vfp" + append_var LDFLAGS "-Wl,--fix-cortex-a8" ABI="armeabi-v7a" # Platform version 9 is used by ResidualVM ANDROID_PLATFORM=9 ANDROID_PLATFORM_ARCH="arm" ;; android-mips) - CXXFLAGS="$CXXFLAGS -march=mips32" - CXXFLAGS="$CXXFLAGS -mtune=mips32" + append_var CXXFLAGS "-march=mips32" + append_var CXXFLAGS "-mtune=mips32" ABI="mips" # Platform version 9 is needed as earlier versions of platform do not support this arch. ANDROID_PLATFORM=9 ANDROID_PLATFORM_ARCH="mips" ;; android-x86) - CXXFLAGS="$CXXFLAGS -march=i686" - CXXFLAGS="$CXXFLAGS -mtune=i686" + append_var CXXFLAGS "-march=i686" + append_var CXXFLAGS "-mtune=i686" ABI="x86" # Platform version 9 is needed as earlier versions of platform do not support this arch. ANDROID_PLATFORM=9 ANDROID_PLATFORM_ARCH="x86" ;; ouya) - CXXFLAGS="$CXXFLAGS -march=armv7-a" - CXXFLAGS="$CXXFLAGS -mtune=cortex-a9" - CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp" - CXXFLAGS="$CXXFLAGS -mfpu=neon" + append_var CXXFLAGS "-march=armv7-a" + append_var CXXFLAGS "-mtune=cortex-a9" + append_var CXXFLAGS "-mfloat-abi=softfp" + append_var CXXFLAGS "-mfpu=neon" ABI="armeabi-v7a" ANDROID_PLATFORM=4 ANDROID_PLATFORM_ARCH="arm" @@ -2127,40 +2137,40 @@ case $_host_os in esac # Setup platform version and arch - CXXFLAGS="$CXXFLAGS --sysroot=$ANDROID_NDK/platforms/android-$ANDROID_PLATFORM/arch-$ANDROID_PLATFORM_ARCH" - LDFLAGS="$LDFLAGS --sysroot=$ANDROID_NDK/platforms/android-$ANDROID_PLATFORM/arch-$ANDROID_PLATFORM_ARCH" + append_var CXXFLAGS "--sysroot=$ANDROID_NDK/platforms/android-$ANDROID_PLATFORM/arch-$ANDROID_PLATFORM_ARCH" + append_var LDFLAGS "--sysroot=$ANDROID_NDK/platforms/android-$ANDROID_PLATFORM/arch-$ANDROID_PLATFORM_ARCH" - CXXFLAGS="$CXXFLAGS -fpic" - CXXFLAGS="$CXXFLAGS -ffunction-sections" - CXXFLAGS="$CXXFLAGS -funwind-tables" + append_var CXXFLAGS "-fpic" + append_var CXXFLAGS "-ffunction-sections" + append_var CXXFLAGS "-funwind-tables" if test "$_debug_build" = yes; then - CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer" - CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" + append_var CXXFLAGS "-fno-omit-frame-pointer" + append_var CXXFLAGS "-fno-strict-aliasing" else - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer" - CXXFLAGS="$CXXFLAGS -fstrict-aliasing" + append_var CXXFLAGS "-fomit-frame-pointer" + append_var CXXFLAGS "-fstrict-aliasing" fi - CXXFLAGS="$CXXFLAGS -finline-limit=300" + append_var CXXFLAGS "-finline-limit=300" _optimization_level=-Os if test "$_host" = android -o "$_host" = android-arm; then - CXXFLAGS="$CXXFLAGS -mthumb-interwork" + append_var CXXFLAGS "-mthumb-interwork" # FIXME: Why is the following in CXXFLAGS and not in DEFINES? Change or document this. - CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5__" - CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5T__" - CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5E__" - CXXFLAGS="$CXXFLAGS -D__ARM_ARCH_5TE__" + append_var CXXFLAGS "-D__ARM_ARCH_5__" + append_var CXXFLAGS "-D__ARM_ARCH_5T__" + append_var CXXFLAGS "-D__ARM_ARCH_5E__" + append_var CXXFLAGS "-D__ARM_ARCH_5TE__" fi # surpress 'mangling of 'va_list' has changed in GCC 4.4' warning - CXXFLAGS="$CXXFLAGS -Wno-psabi" + append_var CXXFLAGS "-Wno-psabi" if test "$_host" = android -o "$_host" = android-arm; then - LDFLAGS="$LDFLAGS -mthumb-interwork" + append_var LDFLAGS "-mthumb-interwork" fi - LDFLAGS="$LDFLAGS -L$ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/`$CXX -dumpversion`/libs/$ABI/" - LIBS="$LIBS -lsupc++" + append_var LDFLAGS "-L$ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/`$CXX -dumpversion`/libs/$ABI/" + append_var LIBS "-lsupc++" add_line_to_config_mk "ANDROID_SDK = $ANDROID_SDK" if test -d "$ANDROID_SDK"/build-tools; then _build_tools_version=`cd "$ANDROID_SDK"/build-tools && ls -1 | sort -rn | head -1` @@ -2171,12 +2181,12 @@ case $_host_os in _seq_midi=no ;; beos*) - DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" # Needs -lbind -lsocket for the timidity MIDI driver LDFLAGS="-L/boot/home/config/lib" CFLAGS="-I/boot/home/config/include" - CXXFLAGS="$CXXFLAGS -fhuge-objects" - LIBS="$LIBS -lbind -lsocket" + append_var CXXFLAGS "-fhuge-objects" + append_var LIBS "-lbind -lsocket" _seq_midi=no ;; cygwin*) @@ -2190,15 +2200,15 @@ case $_host_os in # need to ensure the compiler emits them in the first place. case $_host_cpu in powerpc*) - CFLAGS="$CFLAGS -mlongcall" - CXXFLAGS="$CXXFLAGS -mlongcall" + append_var CFLAGS "-mlongcall" + append_var CXXFLAGS "-mlongcall" ;; esac - DEFINES="$DEFINES -DMACOSX" - LIBS="$LIBS -framework AudioUnit -framework AudioToolbox -framework Carbon -framework CoreMIDI" + append_var DEFINES "-DMACOSX" + append_var LIBS "-framework AudioUnit -framework AudioToolbox -framework Carbon -framework CoreMIDI" # SDL2 doesn't seem to add Cocoa for us. - LIBS="$LIBS -framework Cocoa" + append_var LIBS "-framework Cocoa" add_line_to_config_mk 'MACOSX = 1' # Now we may have MacPorts or Fink installed @@ -2285,115 +2295,115 @@ case $_host_os in fi ;; dreamcast) - DEFINES="$DEFINES -D__DC__" - DEFINES="$DEFINES -DNONSTANDARD_PORT" + append_var DEFINES "-D__DC__" + append_var DEFINES "-DNONSTANDARD_PORT" ;; ds) - DEFINES="$DEFINES -D__DS__" - DEFINES="$DEFINES -DNDS" - DEFINES="$DEFINES -DARM9" - DEFINES="$DEFINES -DARM" - DEFINES="$DEFINES -DNONSTANDARD_PORT" - CXXFLAGS="$CXXFLAGS -isystem $DEVKITPRO/libnds/include" - CXXFLAGS="$CXXFLAGS -isystem $DEVKITPRO/devkitARM/arm-eabi/include" - CXXFLAGS="$CXXFLAGS -mcpu=arm9tdmi" - CXXFLAGS="$CXXFLAGS -mtune=arm9tdmi" - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer" - CXXFLAGS="$CXXFLAGS -mthumb-interwork" - CXXFLAGS="$CXXFLAGS -ffunction-sections" - CXXFLAGS="$CXXFLAGS -fdata-sections" - CXXFLAGS="$CXXFLAGS -fno-strict-aliasing" - CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit" - LDFLAGS="$LDFLAGS -specs=ds_arm9.specs" - LDFLAGS="$LDFLAGS -mthumb-interwork" - LDFLAGS="$LDFLAGS -mno-fpu" - LDFLAGS="$LDFLAGS -Wl,-Map,map.txt" + append_var DEFINES "-D__DS__" + append_var DEFINES "-DNDS" + append_var DEFINES "-DARM9" + append_var DEFINES "-DARM" + append_var DEFINES "-DNONSTANDARD_PORT" + append_var CXXFLAGS "-isystem $DEVKITPRO/libnds/include" + append_var CXXFLAGS "-isystem $DEVKITPRO/devkitARM/arm-eabi/include" + append_var CXXFLAGS "-mcpu=arm9tdmi" + append_var CXXFLAGS "-mtune=arm9tdmi" + append_var CXXFLAGS "-fomit-frame-pointer" + append_var CXXFLAGS "-mthumb-interwork" + append_var CXXFLAGS "-ffunction-sections" + append_var CXXFLAGS "-fdata-sections" + append_var CXXFLAGS "-fno-strict-aliasing" + append_var CXXFLAGS "-fuse-cxa-atexit" + append_var LDFLAGS "-specs=ds_arm9.specs" + append_var LDFLAGS "-mthumb-interwork" + append_var LDFLAGS "-mno-fpu" + append_var LDFLAGS "-Wl,-Map,map.txt" if test "$_dynamic_modules" = no ; then - LDFLAGS="$LDFLAGS -Wl,--gc-sections" + append_var LDFLAGS "-Wl,--gc-sections" else - LDFLAGS="$LDFLAGS -Wl,--no-gc-sections" + append_var LDFLAGS "-Wl,--no-gc-sections" # TODO automate this required 2 step linking phase - # LDFLAGS="$LDFLAGS -Wl,--retain-symbols-file,ds.syms" + # append_var LDFLAGS "-Wl,--retain-symbols-file,ds.syms" fi - LDFLAGS="$LDFLAGS -L$DEVKITPRO/libnds/lib" - LIBS="$LIBS -lnds9" + append_var LDFLAGS "-L$DEVKITPRO/libnds/lib" + append_var LIBS "-lnds9" ;; freebsd*) - LDFLAGS="$LDFLAGS -L/usr/local/lib" - CXXFLAGS="$CXXFLAGS -I/usr/local/include" + append_var LDFLAGS "-L/usr/local/lib" + append_var CXXFLAGS "-I/usr/local/include" ;; gamecube) _optimization_level=-Os - CXXFLAGS="$CXXFLAGS -mogc" - CXXFLAGS="$CXXFLAGS -mcpu=750" - CXXFLAGS="$CXXFLAGS -meabi" - CXXFLAGS="$CXXFLAGS -mhard-float" - CXXFLAGS="$CXXFLAGS -ffunction-sections" - CXXFLAGS="$CXXFLAGS -fdata-sections" - CXXFLAGS="$CXXFLAGS -fmodulo-sched" - CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit" - CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include" + append_var CXXFLAGS "-mogc" + append_var CXXFLAGS "-mcpu=750" + append_var CXXFLAGS "-meabi" + append_var CXXFLAGS "-mhard-float" + append_var CXXFLAGS "-ffunction-sections" + append_var CXXFLAGS "-fdata-sections" + append_var CXXFLAGS "-fmodulo-sched" + append_var CXXFLAGS "-fuse-cxa-atexit" + append_var CXXFLAGS "-I$DEVKITPRO/libogc/include" # libogc is required to link the cc tests (includes _start()) - LDFLAGS="$LDFLAGS -mogc" - LDFLAGS="$LDFLAGS -mcpu=750" - LDFLAGS="$LDFLAGS -L$DEVKITPRO/libogc/lib/cube" - LDFLAGS="$LDFLAGS -logc" + append_var LDFLAGS "-mogc" + append_var LDFLAGS "-mcpu=750" + append_var LDFLAGS "-L$DEVKITPRO/libogc/lib/cube" + append_var LDFLAGS "-logc" if test "$_dynamic_modules" = "yes" ; then # retarded toolchain patch forces --gc-sections, overwrite it - LDFLAGS="$LDFLAGS -Wl,--no-gc-sections" + append_var LDFLAGS "-Wl,--no-gc-sections" fi ;; haiku*) - DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" # Needs -lnetwork for the timidity MIDI driver - LIBS="$LIBS -lnetwork" + append_var LIBS "-lnetwork" _seq_midi=no ;; irix*) - DEFINES="$DEFINES -DIRIX" - DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" - LIBS="$LIBS -lmd -lfastm -lm" + append_var DEFINES "-DIRIX" + append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var LIBS "-lmd -lfastm -lm" _ranlib=: ;; linux* | uclinux*) # When not cross-compiling, enable large file support, but don't # care if getconf doesn't exist or doesn't recognize LFS_CFLAGS. if test -z "$_host"; then - CXXFLAGS="$CXXFLAGS `getconf LFS_CFLAGS 2>/dev/null`" + append_var CXXFLAGS "`getconf LFS_CFLAGS 2>/dev/null`" fi ;; maemo) - DEFINES="$DEFINES -DMAEMO" + append_var DEFINES "-DMAEMO" ;; mingw*) - DEFINES="$DEFINES -DWIN32" - DEFINES="$DEFINES -D__USE_MINGW_ANSI_STDIO=0" - LDFLAGS="$LDFLAGS -static-libgcc -static-libstdc++" - LIBS="$LIBS -lmingw32 -lwinmm -lgdi32" - OBJS="$OBJS residualvmwinres.o" + append_var DEFINES "-DWIN32" + append_var DEFINES "-D__USE_MINGW_ANSI_STDIO=0" + append_var LDFLAGS "-static-libgcc -static-libstdc++" + append_var LIBS "-lmingw32 -lwinmm -lgdi32" + append_var OBJS "residualvmwinres.o" add_line_to_config_mk 'WIN32 = 1' ;; mint*) - DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" ;; n64) - DEFINES="$DEFINES -D__N64__" - DEFINES="$DEFINES -DLIMIT_FPS" - DEFINES="$DEFINES -DNONSTANDARD_PORT" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER" - DEFINES="$DEFINES -DDISABLE_DOSBOX_OPL" - DEFINES="$DEFINES -DDISABLE_FANCY_THEMES" - DEFINES="$DEFINES -DDISABLE_NES_APU" - DEFINES="$DEFINES -DDISABLE_SID" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" + append_var DEFINES "-D__N64__" + append_var DEFINES "-DLIMIT_FPS" + append_var DEFINES "-DNONSTANDARD_PORT" + append_var DEFINES "-DDISABLE_COMMAND_LINE" + append_var DEFINES "-DDISABLE_DEFAULT_SAVEFILEMANAGER" + append_var DEFINES "-DDISABLE_DOSBOX_OPL" + append_var DEFINES "-DDISABLE_FANCY_THEMES" + append_var DEFINES "-DDISABLE_NES_APU" + append_var DEFINES "-DDISABLE_SID" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" ;; ps2) - CXXFLAGS="$CXXFLAGS -G2" - DEFINES="$DEFINES -D_EE" - DEFINES="$DEFINES -D__PLAYSTATION2__" + append_var CXXFLAGS "-G2" + append_var DEFINES "-D_EE" + append_var DEFINES "-D__PLAYSTATION2__" if test -z "$PS2SDK_OLD"; then - DEFINES="$DEFINES -D__NEW_PS2SDK__" + append_var DEFINES "-D__NEW_PS2SDK__" fi ;; ps3) @@ -2401,87 +2411,87 @@ case $_host_os in _sdlpath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin" _freetypepath="$PS3DEV/portlibs/ppu:$PS3DEV/portlibs/ppu/bin" - DEFINES="$DEFINES -DPLAYSTATION3" - CXXFLAGS="$CXXFLAGS -mcpu=cell -mminimal-toc -I$PSL1GHT/ppu/include -I$PS3DEV/portlibs/ppu/include" - LDFLAGS="$LDFLAGS -L$PSL1GHT/ppu/lib -L$PS3DEV/portlibs/ppu/lib" + append_var DEFINES "-DPLAYSTATION3" + append_var CXXFLAGS "-mcpu=cell -mminimal-toc -I$PSL1GHT/ppu/include -I$PS3DEV/portlibs/ppu/include" + append_var LDFLAGS "-L$PSL1GHT/ppu/lib -L$PS3DEV/portlibs/ppu/lib" add_line_to_config_mk 'PLAYSTATION3 = 1' add_line_to_config_h "#define PREFIX \"${prefix}\"" ;; psp) if test -d "$PSPDEV/psp/lib"; then - LDFLAGS="$LDFLAGS -L$PSPDEV/psp/lib" + append_var LDFLAGS "-L$PSPDEV/psp/lib" fi - LDFLAGS="$LDFLAGS -L$PSPSDK/lib" - LDFLAGS="$LDFLAGS -specs=$_srcdir/backends/platform/psp/psp.spec" + append_var LDFLAGS "-L$PSPSDK/lib" + append_var LDFLAGS "-specs=$_srcdir/backends/platform/psp/psp.spec" _optimization_level=-O3 - CXXFLAGS="$CXXFLAGS -I$PSPSDK/include" + append_var CXXFLAGS "-I$PSPSDK/include" # FIXME: Why is the following in CXXFLAGS and not in DEFINES? Change or document this. - CXXFLAGS="$CXXFLAGS -D_PSP_FW_VERSION=150" + append_var CXXFLAGS "-D_PSP_FW_VERSION=150" ;; solaris*) - DEFINES="$DEFINES -DSOLARIS" - DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var DEFINES "-DSOLARIS" + append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" # Needs -lbind -lsocket for the timidity MIDI driver - LIBS="$LIBS -lnsl -lsocket" + append_var LIBS "-lnsl -lsocket" ;; tizen) add_line_to_config_mk "TIZEN_ROOTSTRAP = $TIZEN_ROOTSTRAP" - LDFLAGS="$LDFLAGS --sysroot=${TIZEN_ROOTSTRAP}" - LDFLAGS="$LDFLAGS -L${TIZEN_LIBS}/lib" - CXXFLAGS="$CXXFLAGS -I${TIZEN_LIBS}/include" + append_var LDFLAGS "--sysroot=${TIZEN_ROOTSTRAP}" + append_var LDFLAGS "-L${TIZEN_LIBS}/lib" + append_var CXXFLAGS "-I${TIZEN_LIBS}/include" ;; webos) - CXXFLAGS="$CXXFLAGS --sysroot=$WEBOS_PDK/arm-gcc/sysroot" - CXXFLAGS="$CXXFLAGS -I$WEBOS_PDK/include" - CXXFLAGS="$CXXFLAGS -I$WEBOS_PDK/include/SDL" - CXXFLAGS="$CXXFLAGS -I$WEBOS_PDK/device/usr/include" + append_var CXXFLAGS "--sysroot=$WEBOS_PDK/arm-gcc/sysroot" + append_var CXXFLAGS "-I$WEBOS_PDK/include" + append_var CXXFLAGS "-I$WEBOS_PDK/include/SDL" + append_var CXXFLAGS "-I$WEBOS_PDK/device/usr/include" # These compiler options are needed to support the Palm Pixi - CXXFLAGS="$CXXFLAGS -mcpu=arm1136jf-s" - CXXFLAGS="$CXXFLAGS -mfpu=vfp " - CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp" - LDFLAGS="$LDFLAGS -L$WEBOS_PDK/device/lib" - LDFLAGS="$LDFLAGS -L$WEBOS_PDK/device/usr/lib" - LDFLAGS="$LDFLAGS -Wl,--allow-shlib-undefined" - LDFLAGS="$LDFLAGS --sysroot=$WEBOS_PDK/arm-gcc/sysroot" + append_var CXXFLAGS "-mcpu=arm1136jf-s" + append_var CXXFLAGS "-mfpu=vfp " + append_var CXXFLAGS "-mfloat-abi=softfp" + append_var LDFLAGS "-L$WEBOS_PDK/device/lib" + append_var LDFLAGS "-L$WEBOS_PDK/device/usr/lib" + append_var LDFLAGS "-Wl,--allow-shlib-undefined" + append_var LDFLAGS "--sysroot=$WEBOS_PDK/arm-gcc/sysroot" add_line_to_config_mk "WEBOS_SDK = $WEBOS_SDK" _seq_midi=no ;; wii) _optimization_level=-Os - CXXFLAGS="$CXXFLAGS -mrvl" - CXXFLAGS="$CXXFLAGS -mcpu=750" - CXXFLAGS="$CXXFLAGS -meabi" - CXXFLAGS="$CXXFLAGS -mhard-float" - CXXFLAGS="$CXXFLAGS -ffunction-sections" - CXXFLAGS="$CXXFLAGS -fdata-sections" - CXXFLAGS="$CXXFLAGS -fmodulo-sched" - CXXFLAGS="$CXXFLAGS -fuse-cxa-atexit" - CXXFLAGS="$CXXFLAGS -I$DEVKITPRO/libogc/include" + append_var CXXFLAGS "-mrvl" + append_var CXXFLAGS "-mcpu=750" + append_var CXXFLAGS "-meabi" + append_var CXXFLAGS "-mhard-float" + append_var CXXFLAGS "-ffunction-sections" + append_var CXXFLAGS "-fdata-sections" + append_var CXXFLAGS "-fmodulo-sched" + append_var CXXFLAGS "-fuse-cxa-atexit" + append_var CXXFLAGS "-I$DEVKITPRO/libogc/include" # libogc is required to link the cc tests (includes _start()) - LDFLAGS="$LDFLAGS -mrvl" - LDFLAGS="$LDFLAGS -mcpu=750" - LDFLAGS="$LDFLAGS -L$DEVKITPRO/libogc/lib/wii" - LDFLAGS="$LDFLAGS -logc" + append_var LDFLAGS "-mrvl" + append_var LDFLAGS "-mcpu=750" + append_var LDFLAGS "-L$DEVKITPRO/libogc/lib/wii" + append_var LDFLAGS "-logc" if test "$_dynamic_modules" = "yes" ; then # retarded toolchain patch forces --gc-sections, overwrite it - LDFLAGS="$LDFLAGS -Wl,--no-gc-sections" + append_var LDFLAGS "-Wl,--no-gc-sections" fi ;; wince) _optimization_level=-O3 - CXXFLAGS="$CXXFLAGS -fno-inline-functions" - CXXFLAGS="$CXXFLAGS -march=armv4" - CXXFLAGS="$CXXFLAGS -mtune=xscale" - DEFINES="$DEFINES -D_WIN32_WCE=300" - DEFINES="$DEFINES -D__ARM__" - DEFINES="$DEFINES -D_ARM_" - DEFINES="$DEFINES -DUNICODE" - DEFINES="$DEFINES -DFPM_DEFAULT" - DEFINES="$DEFINES -DNONSTANDARD_PORT" - DEFINES="$DEFINES -DWRAP_MALLOC" - DEFINES="$DEFINES -DWIN32" - DEFINES="$DEFINES -Dcdecl=" - DEFINES="$DEFINES -D__cdecl__=" + append_var CXXFLAGS "-fno-inline-functions" + append_var CXXFLAGS "-march=armv4" + append_var CXXFLAGS "-mtune=xscale" + append_var DEFINES "-D_WIN32_WCE=300" + append_var DEFINES "-D__ARM__" + append_var DEFINES "-D_ARM_" + append_var DEFINES "-DUNICODE" + append_var DEFINES "-DFPM_DEFAULT" + append_var DEFINES "-DNONSTANDARD_PORT" + append_var DEFINES "-DWRAP_MALLOC" + append_var DEFINES "-DWIN32" + append_var DEFINES "-Dcdecl=" + append_var DEFINES "-D__cdecl__=" add_line_to_config_mk "WRAP_MALLOC = 1" ;; esac @@ -2492,8 +2502,8 @@ if test -n "$_host"; then case "$_host" in android | android-arm | android-v7a | android-arm-v7a | android-mips | android-x86 | ouya) # we link a .so as default - LDFLAGS="$LDFLAGS -shared" - LDFLAGS="$LDFLAGS -Wl,-Bsymbolic,--no-undefined" + append_var LDFLAGS "-shared" + append_var LDFLAGS "-Wl,-Bsymbolic,--no-undefined" HOSTEXEPRE=lib HOSTEXEEXT=.so _backend="android" @@ -2506,19 +2516,18 @@ if test -n "$_host"; then arm-linux|arm*-linux-gnueabi|arm-*-linux) ;; arm-riscos|linupy) - DEFINES="$DEFINES -DLINUPY" + append_var DEFINES "-DLINUPY" ;; bfin*) ;; caanoo) - DEFINES="$DEFINES -DCAANOO" + append_var DEFINES "-DCAANOO" if test "$_debug_build" = no; then # Use -O3 on the Caanoo for non-debug builds. _optimization_level=-O3 fi - CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s" - CXXFLAGS="$CXXFLAGS -mtune=arm926ej-s" - ASFLAGS="$ASFLAGS" + append_var CXXFLAGS "-mcpu=arm926ej-s" + append_var CXXFLAGS "-mtune=arm926ej-s" _backend="gph" _build_hq_scalers=no _savegame_timestamp=no @@ -2533,12 +2542,11 @@ if test -n "$_host"; then _strip=$_host-strip ;; dingux) - DEFINES="$DEFINES -DDINGUX" - DEFINES="$DEFINES -DDISABLE_DOSBOX_OPL" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - ASFLAGS="$ASFLAGS" - CXXFLAGS="$CXXFLAGS -msoft-float" - CXXFLAGS="$CXXFLAGS -mips32" + append_var DEFINES "-DDINGUX" + append_var DEFINES "-DDISABLE_DOSBOX_OPL" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" + append_var CXXFLAGS "-msoft-float" + append_var CXXFLAGS "-mips32" _backend="dingux" _mt32emu=no _optimization_level=-O3 @@ -2554,20 +2562,20 @@ if test -n "$_host"; then _port_mk="backends/platform/dingux/dingux.mk" ;; dreamcast) - DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER" - DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" + append_var DEFINES "-DDISABLE_DEFAULT_SAVEFILEMANAGER" + append_var DEFINES "-DDISABLE_TEXT_CONSOLE" + append_var DEFINES "-DDISABLE_COMMAND_LINE" # Enable serial debugging output only when --enable-debug is passed if test "$_release_build" = yes -o "$_debug_build" != yes; then - DEFINES="$DEFINES -DNOSERIAL" + append_var DEFINES "-DNOSERIAL" fi _optimization_level=-O3 - CXXFLAGS="$CXXFLAGS -funroll-loops" - CXXFLAGS="$CXXFLAGS -fschedule-insns2" - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer" - CXXFLAGS="$CXXFLAGS -fdelete-null-pointer-checks" + append_var CXXFLAGS "-funroll-loops" + append_var CXXFLAGS "-fschedule-insns2" + append_var CXXFLAGS "-fomit-frame-pointer" + append_var CXXFLAGS "-fdelete-null-pointer-checks" # no-delayed-branch is a workaround for GCC bug #42841 - "SH: Assembler complains pcrel too far." - CXXFLAGS="$CXXFLAGS -fno-delayed-branch" + append_var CXXFLAGS "-fno-delayed-branch" _backend="dc" _build_scalers=no _mad=yes @@ -2576,16 +2584,16 @@ if test -n "$_host"; then _port_mk="backends/platform/dc/dreamcast.mk" ;; ds) - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER" - DEFINES="$DEFINES -DDISABLE_DOSBOX_OPL" - DEFINES="$DEFINES -DDISABLE_FANCY_THEMES" - DEFINES="$DEFINES -DDISABLE_SID" - DEFINES="$DEFINES -DDISABLE_NES_APU" - DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - DEFINES="$DEFINES -DSTREAM_AUDIO_FROM_DISK" - DEFINES="$DEFINES -DVECTOR_RENDERER_FORMAT=1555" + append_var DEFINES "-DDISABLE_COMMAND_LINE" + append_var DEFINES "-DDISABLE_DEFAULT_SAVEFILEMANAGER" + append_var DEFINES "-DDISABLE_DOSBOX_OPL" + append_var DEFINES "-DDISABLE_FANCY_THEMES" + append_var DEFINES "-DDISABLE_SID" + append_var DEFINES "-DDISABLE_NES_APU" + append_var DEFINES "-DDISABLE_TEXT_CONSOLE" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" + append_var DEFINES "-DSTREAM_AUDIO_FROM_DISK" + append_var DEFINES "-DVECTOR_RENDERER_FORMAT=1555" _backend="ds" _build_scalers=no _mt32emu=no @@ -2606,10 +2614,9 @@ if test -n "$_host"; then add_line_to_config_h "#define USE_WII_DI" ;; gcw0) - DEFINES="$DEFINES -DDINGUX -DGCW0" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - ASFLAGS="$ASFLAGS" - CXXFLAGS="$CXXFLAGS -mips32" + append_var DEFINES "-DDINGUX -DGCW0" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" + append_var CXXFLAGS "-mips32" _backend="dingux" _mt32emu=no _optimization_level=-O3 @@ -2625,10 +2632,10 @@ if test -n "$_host"; then _port_mk="backends/platform/dingux/dingux.mk" ;; gp2x) - DEFINES="$DEFINES -DGP2X" - CXXFLAGS="$CXXFLAGS -march=armv4t" - ASFLAGS="$ASFLAGS -mfloat-abi=soft" - LDFLAGS="$LDFLAGS -static" + append_var DEFINES "-DGP2X" + append_var CXXFLAGS "-march=armv4t" + append_var ASFLAGS "-mfloat-abi=soft" + append_var LDFLAGS "-static" _backend="gph" _build_hq_scalers=no _savegame_timestamp=no @@ -2639,10 +2646,10 @@ if test -n "$_host"; then _port_mk="backends/platform/gph/gp2x-bundle.mk" ;; gp2xwiz) - DEFINES="$DEFINES -DGP2XWIZ" - CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s" - CXXFLAGS="$CXXFLAGS -mtune=arm926ej-s" - ASFLAGS="$ASFLAGS -mfloat-abi=soft" + append_var DEFINES "-DGP2XWIZ" + append_var CXXFLAGS "-mcpu=arm926ej-s" + append_var CXXFLAGS "-mtune=arm926ej-s" + append_var ASFLAGS "-mfloat-abi=soft" _backend="gph" _build_hq_scalers=no _savegame_timestamp=no @@ -2653,8 +2660,8 @@ if test -n "$_host"; then _port_mk="backends/platform/gph/gp2xwiz-bundle.mk" ;; iphone) - DEFINES="$DEFINES -DIPHONE" - ASFLAGS="$ASFLAGS -arch armv6" + append_var DEFINES "-DIPHONE" + append_var ASFLAGS "-arch armv6" _backend="iphone" _build_scalers=no _mt32emu=no @@ -2662,18 +2669,18 @@ if test -n "$_host"; then _timidity=no ;; m68k-atari-mint) - DEFINES="$DEFINES -DSYSTEM_NOT_SUPPORTING_D_TYPE" + append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" _ranlib=m68k-atari-mint-ranlib _ar="m68k-atari-mint-ar cru" _seq_midi=no ;; maemo) _optimization_level=-Os - CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s" - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer" - INCLUDES="$INCLUDES -I/usr/X11R6/include" - LIBS="$LIBS -lX11" - LIBS="$LIBS -L/usr/lib" + append_var CXXFLAGS "-mcpu=arm926ej-s" + append_var CXXFLAGS "-fomit-frame-pointer" + append_var INCLUDES "-I/usr/X11R6/include" + append_var LIBS "-lX11" + append_var LIBS "-L/usr/lib" _backend="maemo" _vkeybd=yes @@ -2692,12 +2699,12 @@ if test -n "$_host"; then _ranlib=$_host-ranlib ;; mips-sgi*) - LDFLAGS="$LDFLAGS -static-libgcc" - LIBS="$LIBS -laudio" + append_var LDFLAGS "-static-libgcc" + append_var LIBS "-laudio" ;; motoezx) - DEFINES="$DEFINES -DMOTOEZX" - ASFLAGS="$ASFLAGS -mfpu=vfp" + append_var DEFINES "-DMOTOEZX" + append_var ASFLAGS "-mfpu=vfp" _backend="linuxmoto" _build_hq_scalers=no _mt32emu=no @@ -2706,8 +2713,8 @@ if test -n "$_host"; then _port_mk="backends/platform/linuxmoto/linuxmoto.mk" ;; motomagx) - DEFINES="$DEFINES -DMOTOMAGX" - ASFLAGS="$ASFLAGS -mfpu=vfp" + append_var DEFINES "-DMOTOMAGX" + append_var ASFLAGS "-mfpu=vfp" _backend="linuxmoto" _build_hq_scalers=no _mt32emu=no @@ -2716,20 +2723,20 @@ if test -n "$_host"; then _port_mk="backends/platform/linuxmoto/linuxmoto.mk" ;; n64) - CXXFLAGS="$CXXFLAGS -mno-extern-sdata" - CXXFLAGS="$CXXFLAGS --param max-inline-insns-auto=20" - CXXFLAGS="$CXXFLAGS -fomit-frame-pointer" - CXXFLAGS="$CXXFLAGS -march=vr4300" - CXXFLAGS="$CXXFLAGS -mtune=vr4300" - CXXFLAGS="$CXXFLAGS -mhard-float" - LDFLAGS="$LDFLAGS -march=vr4300" - LDFLAGS="$LDFLAGS -mtune=vr4300" - LDFLAGS="$LDFLAGS -nodefaultlibs" - LDFLAGS="$LDFLAGS -nostartfiles" - LDFLAGS="$LDFLAGS -mno-crt0" - LDFLAGS="$LDFLAGS -L$N64SDK/hkz-libn64" - LDFLAGS="$LDFLAGS -L$N64SDK/lib" - LDFLAGS="$LDFLAGS -T n64ld_cpp.x -Xlinker -Map -Xlinker residualvm.map" + append_var CXXFLAGS "-mno-extern-sdata" + append_var CXXFLAGS "--param max-inline-insns-auto=20" + append_var CXXFLAGS "-fomit-frame-pointer" + append_var CXXFLAGS "-march=vr4300" + append_var CXXFLAGS "-mtune=vr4300" + append_var CXXFLAGS "-mhard-float" + append_var LDFLAGS "-march=vr4300" + append_var LDFLAGS "-mtune=vr4300" + append_var LDFLAGS "-nodefaultlibs" + append_var LDFLAGS "-nostartfiles" + append_var LDFLAGS "-mno-crt0" + append_var LDFLAGS "-L$N64SDK/hkz-libn64" + append_var LDFLAGS "-L$N64SDK/lib" + append_var LDFLAGS "-T n64ld_cpp.x -Xlinker -Map -Xlinker residualvm.map" _backend="n64" _mt32emu=no _build_scalers=no @@ -2747,16 +2754,16 @@ if test -n "$_host"; then _port_mk="backends/platform/n64/n64.mk" ;; neuros) - DEFINES="$DEFINES -DNEUROS" + append_var DEFINES "-DNEUROS" _backend='null' _build_hq_scalers=no _mt32emu=no ;; openpandora) - DEFINES="$DEFINES -DOPENPANDORA" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" + append_var DEFINES "-DOPENPANDORA" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" if test "$_release_build" = no; then - DEFINES="$DEFINES -DOP_DEBUG" + append_var DEFINES "-DOP_DEBUG" fi # Use -O3 on the OpenPandora for optimized builds. @@ -2765,12 +2772,12 @@ if test -n "$_host"; then fi define_in_config_if_yes yes 'USE_ARM_NEON_ASPECT_CORRECTOR' - CXXFLAGS="$CXXFLAGS -march=armv7-a" - CXXFLAGS="$CXXFLAGS -mtune=cortex-a8" - CXXFLAGS="$CXXFLAGS -mfloat-abi=softfp" - CXXFLAGS="$CXXFLAGS -mfpu=neon" - CXXFLAGS="$CXXFLAGS -fsingle-precision-constant" - ASFLAGS="$ASFLAGS -mfloat-abi=softfp" + append_var CXXFLAGS "-march=armv7-a" + append_var CXXFLAGS "-mtune=cortex-a8" + append_var CXXFLAGS "-mfloat-abi=softfp" + append_var CXXFLAGS "-mfpu=neon" + append_var CXXFLAGS "-fsingle-precision-constant" + append_var ASFLAGS "-mfloat-abi=softfp" _backend="openpandora" _build_hq_scalers=yes _vkeybd=no @@ -2780,11 +2787,11 @@ if test -n "$_host"; then ;; ppc-amigaos) # PPC Linker requires this to fix relocation errors - CXXFLAGS="$CXXFLAGS -mlongcall" + append_var CXXFLAGS "-mlongcall" # Only static builds link successfully on buildbot LDFLAGS=`echo $LDFLAGS | sed 's/-use-dynld//'` - LDFLAGS="$LDFLAGS -static" + append_var LDFLAGS "-static" # toolchain binaries prefixed by host _ranlib=$_host-ranlib @@ -2796,13 +2803,13 @@ if test -n "$_host"; then _port_mk="backends/platform/sdl/amigaos/amigaos.mk" ;; ps2) - DEFINES="$DEFINES -DDISABLE_TEXT_CONSOLE" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - DEFINES="$DEFINES -DDISABLE_DOSBOX_OPL" - DEFINES="$DEFINES -DDISABLE_SID" - DEFINES="$DEFINES -DDISABLE_NES_APU" - CXXFLAGS="$CXXFLAGS -fno-exceptions" - CXXFLAGS="$CXXFLAGS -fno-rtti" + append_var DEFINES "-DDISABLE_TEXT_CONSOLE" + append_var DEFINES "-DDISABLE_COMMAND_LINE" + append_var DEFINES "-DDISABLE_DOSBOX_OPL" + append_var DEFINES "-DDISABLE_SID" + append_var DEFINES "-DDISABLE_NES_APU" + append_var CXXFLAGS "-fno-exceptions" + append_var CXXFLAGS "-fno-rtti" _backend="ps2" _build_scalers=no _mt32emu=no @@ -2824,15 +2831,15 @@ if test -n "$_host"; then if test "$_debug_build" = yes; then # TODO: Setup debug build properly - DEFINES="$DEFINES -D__PS2_DEBUG__" - #INCLUDES="$INCLUDES -I$(PS2GDB)/ee" - #LDFLAGS="$LDFLAGS -L$(PS2GDB)/lib" - LDFLAGS="$LDFLAGS -lps2gdbStub" - LDFLAGS="$LDFLAGS -lps2ip" - LDFLAGS="$LDFLAGS -ldebug" + append_var DEFINES "-D__PS2_DEBUG__" + #append_var INCLUDES "-I$(PS2GDB)/ee" + #append_var LDFLAGS "-L$(PS2GDB)/lib" + append_var LDFLAGS "-lps2gdbStub" + append_var LDFLAGS "-lps2ip" + append_var LDFLAGS "-ldebug" else # If not building for debug mode, strip binaries. - CXXFLAGS="$CXXFLAGS -s" + append_var CXXFLAGS "-s" fi ;; ps3) @@ -2849,9 +2856,9 @@ if test -n "$_host"; then _port_mk="backends/platform/psp/psp.mk" ;; samsungtv) - DEFINES="$DEFINES -DSAMSUNGTV" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - ASFLAGS="$ASFLAGS -mfpu=vfp" + append_var DEFINES "-DSAMSUNGTV" + append_var DEFINES "-DDISABLE_COMMAND_LINE" + append_var ASFLAGS "-mfpu=vfp" HOSTEXEEXT=".so" _backend="samsungtv" _mt32emu=no @@ -2895,7 +2902,7 @@ if test -n "$_host"; then add_line_to_config_h "#define USE_WII_KBD" ;; wince) - LDFLAGS="$LDFLAGS -Wl,--stack,65536" + append_var LDFLAGS "-Wl,--stack,65536" _tremolo=yes _backend="wince" _detectlang=yes @@ -2913,140 +2920,140 @@ fi # case $_backend in android) - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" - CXXFLAGS="$CXXFLAGS -Wa,--noexecstack" - LDFLAGS="$LDFLAGS -Wl,-z,noexecstack" - INCLUDES="$INCLUDES -I$ANDROID_NDK/sources/cxx-stl/system/include" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" + append_var CXXFLAGS "-Wa,--noexecstack" + append_var LDFLAGS "-Wl,-z,noexecstack" + append_var INCLUDES "-I$ANDROID_NDK/sources/cxx-stl/system/include" ;; dc) - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc' - INCLUDES="$INCLUDES "'-isystem $(ronindir)/include' - LDFLAGS="$LDFLAGS -Wl,-Ttext,0x8c010000" - LDFLAGS="$LDFLAGS -nostartfiles" - LDFLAGS="$LDFLAGS "'$(ronindir)/lib/crt0.o' - LDFLAGS="$LDFLAGS "'-L$(ronindir)/lib' + append_var INCLUDES '-I$(srcdir)/backends/platform/dc' + append_var INCLUDES '-isystem $(ronindir)/include' + append_var LDFLAGS "-Wl,-Ttext,0x8c010000" + append_var LDFLAGS "-nostartfiles" + append_var LDFLAGS '$(ronindir)/lib/crt0.o' + append_var LDFLAGS '-L$(ronindir)/lib' # Enable serial debugging output only when --enable-debug is passed if test "$_release_build" = yes -o "$_debug_build" != yes; then - LIBS="$LIBS -lronin-noserial -lm" + append_var LIBS "-lronin-noserial -lm" else - LIBS="$LIBS -lronin -lm" + append_var LIBS "-lronin -lm" fi ;; dingux) - DEFINES="$DEFINES -DDINGUX" + append_var DEFINES "-DDINGUX" ;; ds) - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/ds/arm9/source' - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/ds/commoninclude' - INCLUDES="$INCLUDES "'-Ibackends/platform/ds/arm9/data' + append_var INCLUDES '-I$(srcdir)/backends/platform/ds/arm9/source' + append_var INCLUDES '-I$(srcdir)/backends/platform/ds/commoninclude' + append_var INCLUDES '-Ibackends/platform/ds/arm9/data' ;; gph) # On the GPH devices we want fancy themes but do not want the load/save thumbnail grid. - DEFINES="$DEFINES -DDISABLE_SAVELOADCHOOSER_GRID" - DEFINES="$DEFINES -DGPH_DEVICE" - DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE" + append_var DEFINES "-DDISABLE_SAVELOADCHOOSER_GRID" + append_var DEFINES "-DGPH_DEVICE" + append_var DEFINES "-DREDUCE_MEMORY_USAGE" if test "$_debug_build" = yes; then - DEFINES="$DEFINES -DGPH_DEBUG" + append_var DEFINES "-DGPH_DEBUG" fi ;; iphone) - LIBS="$LIBS -lobjc -framework UIKit -framework CoreGraphics -framework OpenGLES" - LIBS="$LIBS -framework QuartzCore -framework CoreFoundation -framework Foundation" - LIBS="$LIBS -framework AudioToolbox -framework CoreAudio" + append_var LIBS "-lobjc -framework UIKit -framework CoreGraphics -framework OpenGLES" + append_var LIBS "-framework QuartzCore -framework CoreFoundation -framework Foundation" + append_var LIBS "-framework AudioToolbox -framework CoreAudio" ;; linuxmoto) - DEFINES="$DEFINES -DLINUXMOTO" + append_var DEFINES "-DLINUXMOTO" ;; maemo) - DEFINES="$DEFINES -DMAEMO" + append_var DEFINES "-DMAEMO" ;; n64) - INCLUDES="$INCLUDES "'-I$(N64SDK)/include' - INCLUDES="$INCLUDES "'-I$(N64SDK)/mips64/include' - INCLUDES="$INCLUDES "'-I$(N64SDK)/hkz-libn64' - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/n64' - LIBS="$LIBS -lpakfs -lframfs -ln64 -ln64utils -lromfs" - LIBS="$LIBS -lm -lstdc++ -lz" + append_var INCLUDES '-I$(N64SDK)/include' + append_var INCLUDES '-I$(N64SDK)/mips64/include' + append_var INCLUDES '-I$(N64SDK)/hkz-libn64' + append_var INCLUDES '-I$(srcdir)/backends/platform/n64' + append_var LIBS "-lpakfs -lframfs -ln64 -ln64utils -lromfs" + append_var LIBS "-lm -lstdc++ -lz" ;; null) - DEFINES="$DEFINES -DUSE_NULL_DRIVER" + append_var DEFINES "-DUSE_NULL_DRIVER" ;; openpandora) ;; ps2) - DEFINES="$DEFINES -D_EE" - DEFINES="$DEFINES -DFORCE_RTL" - INCLUDES="$INCLUDES -I$PS2SDK/ee/include" - INCLUDES="$INCLUDES -I$PS2SDK/common/include" - INCLUDES="$INCLUDES -I$PS2SDK/ports/include" + append_var DEFINES "-D_EE" + append_var DEFINES "-DFORCE_RTL" + append_var INCLUDES "-I$PS2SDK/ee/include" + append_var INCLUDES "-I$PS2SDK/common/include" + append_var INCLUDES "-I$PS2SDK/ports/include" if test "$_dynamic_modules" = no ; then - LDFLAGS="$LDFLAGS -mno-crt0" - LDFLAGS="$LDFLAGS $PS2SDK/ee/startup/crt0.o" - LDFLAGS="$LDFLAGS -T $PS2SDK/ee/startup/linkfile" + append_var LDFLAGS "-mno-crt0" + append_var LDFLAGS "$PS2SDK/ee/startup/crt0.o" + append_var LDFLAGS "-T $PS2SDK/ee/startup/linkfile" fi - LDFLAGS="$LDFLAGS -L$PS2SDK/ee/lib" - LDFLAGS="$LDFLAGS -L$PS2SDK/ports/lib" - LIBS="$LIBS -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm" - LIBS="$LIBS -lm -lc -lfileXio -lkernel -lstdc++" + append_var LDFLAGS "-L$PS2SDK/ee/lib" + append_var LDFLAGS "-L$PS2SDK/ports/lib" + append_var LIBS "-lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm" + append_var LIBS "-lm -lc -lfileXio -lkernel -lstdc++" ;; psp) - DEFINES="$DEFINES -D__PSP__" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - DEFINES="$DEFINES -DDISABLE_DOSBOX_OPL" - LIBS="$LIBS -lpng" - LIBS="$LIBS -Wl,-Map,mapfile.txt" + append_var DEFINES "-D__PSP__" + append_var DEFINES "-DDISABLE_COMMAND_LINE" + append_var DEFINES "-DDISABLE_DOSBOX_OPL" + append_var LIBS "-lpng" + append_var LIBS "-Wl,-Map,mapfile.txt" ;; samsungtv) - DEFINES="$DEFINES -DSAMSUNGTV" - LDFLAGS="$LDFLAGS -shared" - LDFLAGS="$LDFLAGS -fpic" + append_var DEFINES "-DSAMSUNGTV" + append_var LDFLAGS "-shared" + append_var LDFLAGS "-fpic" ;; tizen) # dirent.h not available. NONSTANDARD_PORT==ensure portdefs.h is included - DEFINES="$DEFINES -DTIZEN -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" - DEFINES="$DEFINES -DNO_STDERR_STDOUT" - DEFINES="$DEFINES -DDISABLE_COMMAND_LINE" - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/tizen' - INCLUDES="$INCLUDES "'-I$(TIZEN_ROOTSTRAP)/usr/include' - INCLUDES="$INCLUDES "'-I$(TIZEN_ROOTSTRAP)/usr/include/osp' + append_var DEFINES "-DTIZEN -DDISABLE_STDIO_FILESTREAM -DNONSTANDARD_PORT" + append_var DEFINES "-DNO_STDERR_STDOUT" + append_var DEFINES "-DDISABLE_COMMAND_LINE" + append_var INCLUDES '-I$(srcdir)/backends/platform/tizen' + append_var INCLUDES '-I$(TIZEN_ROOTSTRAP)/usr/include' + append_var INCLUDES '-I$(TIZEN_ROOTSTRAP)/usr/include/osp' if test "$_debug_build" = yes; then - CXXFLAGS="$CXXFLAGS -D_DEBUG -DBUILD_DLL -O0 -g3" + append_var CXXFLAGS "-D_DEBUG -DBUILD_DLL -O0 -g3" fi # created a shared library for inclusion via the eclipse build - CXXFLAGS="$CXXFLAGS -Wno-psabi" - CXXFLAGS="$CXXFLAGS --sysroot=${TIZEN_ROOTSTRAP}" - CXXFLAGS="$CXXFLAGS -fmessage-length=0" - CXXFLAGS="$CXXFLAGS -fPIC" + append_var CXXFLAGS "-Wno-psabi" + append_var CXXFLAGS "--sysroot=${TIZEN_ROOTSTRAP}" + append_var CXXFLAGS "-fmessage-length=0" + append_var CXXFLAGS "-fPIC" HOSTEXEPRE=lib HOSTEXEEXT=.a ;; webos) # There is no sdl-config in the WebOS PDK so we don't use find_sdlconfig here. # The PDL library acts as the WebOS device toolchain, and is required to control the virtual keyboard among other OS-level events. - LIBS="$LIBS -lSDL -lpdl" - DEFINES="$DEFINES -DWEBOS" - DEFINES="$DEFINES -DSDL_BACKEND" + append_var LIBS "-lSDL -lpdl" + append_var DEFINES "-DWEBOS" + append_var DEFINES "-DSDL_BACKEND" add_line_to_config_mk "SDL_BACKEND = 1" - MODULES="$MODULES backends/platform/sdl" + append_var MODULES "backends/platform/sdl" ;; wii) - DEFINES="$DEFINES -D__WII__" - DEFINES="$DEFINES -DGEKKO" + append_var DEFINES "-D__WII__" + append_var DEFINES "-DGEKKO" case $_host_os in gamecube) - LIBS="$LIBS -lgxflux -liso9660 -lfat -logc -ldb" + append_var LIBS "-lgxflux -liso9660 -lfat -logc -ldb" ;; *) - LIBS="$LIBS -lgxflux -ldi -liso9660 -ltinysmb -lfat -lwiiuse -lbte -logc -lwiikeyboard -ldb" + append_var LIBS "-lgxflux -ldi -liso9660 -ltinysmb -lfat -lwiiuse -lbte -logc -lwiikeyboard -ldb" ;; esac ;; wince) - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/wince' - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/wince/CEgui' - INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/wince/CEkeys' - LIBS="$LIBS -static -lSDL" - DEFINES="$DEFINES -DSDL_BACKEND" + append_var INCLUDES '-I$(srcdir)/backends/platform/wince' + append_var INCLUDES '-I$(srcdir)/backends/platform/wince/CEgui' + append_var INCLUDES '-I$(srcdir)/backends/platform/wince/CEkeys' + append_var LIBS "-static -lSDL" + append_var DEFINES "-DSDL_BACKEND" add_line_to_config_mk "SDL_BACKEND = 1" ;; sdl) @@ -3056,7 +3063,7 @@ case $_backend in exit 1 ;; esac -MODULES="$MODULES backends/platform/$_backend" +append_var MODULES "backends/platform/$_backend" # # Setup SDL specifics for SDL based backends @@ -3064,9 +3071,9 @@ MODULES="$MODULES backends/platform/$_backend" case $_backend in dingux | gph | linuxmoto | maemo | openpandora | samsungtv | sdl) find_sdlconfig - INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`" - LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`" - DEFINES="$DEFINES -DSDL_BACKEND" + append_var INCLUDES "`$_sdlconfig --prefix="$_sdlpath" --cflags`" + append_var LIBS "`$_sdlconfig --prefix="$_sdlpath" --libs`" + append_var DEFINES "-DSDL_BACKEND" add_line_to_config_mk "SDL_BACKEND = 1" _sdlversion=`$_sdlconfig --version` @@ -3166,7 +3173,7 @@ esac echo $_posix if test "$_posix" = yes ; then - DEFINES="$DEFINES -DPOSIX" + append_var DEFINES "-DPOSIX" add_line_to_config_mk 'POSIX = 1' fi @@ -3196,8 +3203,8 @@ fi if test "$_optimizations" = yes ; then # Enable optimizations. This also # makes it possible to use -Wuninitialized, so let's do that. - CXXFLAGS="$CXXFLAGS $_optimization_level" - CXXFLAGS="$CXXFLAGS -Wuninitialized" + append_var CXXFLAGS "$_optimization_level" + append_var CXXFLAGS "-Wuninitialized" fi # @@ -3213,8 +3220,8 @@ if test "$_dynamic_modules" = yes ; then android) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fpic" - LIBS="$LIBS -ldl" + append_var CXXFLAGS "-fpic" + append_var LIBS "-ldl" # Work around an Android 2.0+ run-time linker bug: # The linker doesn't actually look in previously # loaded libraries when trying to resolve symbols - @@ -3233,7 +3240,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive darwin*) _plugin_prefix="" _plugin_suffix=".plugin" - LIBS="$LIBS -ldl" + append_var LIBS "-ldl" _mak_plugins=' PLUGIN_EXTRA_DEPS = $(EXECUTABLE) PLUGIN_LDFLAGS += -bundle -bundle_loader $(EXECUTABLE) -exported_symbols_list "$(srcdir)/plugin.exp" @@ -3253,9 +3260,9 @@ POST_OBJS_FLAGS := -Wl,--no-whole-archive ;; ds) _elf_loader=yes - DEFINES="$DEFINES -DELF_LOADER_CXA_ATEXIT" - DEFINES="$DEFINES -DUNCACHED_PLUGINS" - DEFINES="$DEFINES -DELF_NO_MEM_MANAGER" + append_var DEFINES "-DELF_LOADER_CXA_ATEXIT" + append_var DEFINES "-DUNCACHED_PLUGINS" + append_var DEFINES "-DELF_NO_MEM_MANAGER" _mak_plugins=' PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/ds/plugin.ld -mthumb-interwork -mno-fpu ' @@ -3263,7 +3270,7 @@ PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/ds/plugin.ld -mthumb-interwo freebsd*) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fPIC" + append_var CXXFLAGS "-fPIC" _mak_plugins=' PLUGIN_EXTRA_DEPS = PLUGIN_LDFLAGS += -shared @@ -3273,8 +3280,8 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive ;; gamecube | wii) _elf_loader=yes - DEFINES="$DEFINES -DELF_LOADER_CXA_ATEXIT" - DEFINES="$DEFINES -DUNCACHED_PLUGINS" + append_var DEFINES "-DELF_LOADER_CXA_ATEXIT" + append_var DEFINES "-DUNCACHED_PLUGINS" _mak_plugins=' PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/wii/plugin.ld ' @@ -3282,8 +3289,8 @@ PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/wii/plugin.ld gph*) _plugin_prefix="" _plugin_suffix=".plugin" - CXXFLAGS="$CXXFLAGS -fpic" - LIBS="$LIBS -ldl" + append_var CXXFLAGS "-fpic" + append_var LIBS "-ldl" _mak_plugins=' PLUGIN_EXTRA_DEPS = $(EXECUTABLE) PLUGIN_LDFLAGS += -shared @@ -3294,8 +3301,8 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive irix*) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fpic" - LIBS="$LIBS -ldl" + append_var CXXFLAGS "-fpic" + append_var LIBS "-ldl" _mak_plugins=' PLUGIN_EXTRA_DEPS = PLUGIN_LDFLAGS += -shared -static-libgcc @@ -3307,8 +3314,8 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive linux* | gnu* | k*bsd*-gnu*) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fPIC" - LIBS="$LIBS -ldl" + append_var CXXFLAGS "-fPIC" + append_var LIBS "-ldl" _mak_plugins=' PLUGIN_EXTRA_DEPS = PLUGIN_LDFLAGS += -shared @@ -3327,7 +3334,7 @@ POST_OBJS_FLAGS := -Wl,--export-all-symbols -Wl,--no-whole-archive -Wl,--out-im ' ;; wince) - DEFINES="$DEFINES -DUNCACHED_PLUGINS" + append_var DEFINES "-DUNCACHED_PLUGINS" HOSTEXEEXT=".dll" _plugin_prefix="" _plugin_suffix=".plugin" @@ -3340,7 +3347,7 @@ POST_OBJS_FLAGS := -Wl,--export-all-symbols -Wl,--no-whole-archive -Wl,--out-im ;; ps2) _elf_loader=yes - DEFINES="$DEFINES -DUNCACHED_PLUGINS" + append_var DEFINES "-DUNCACHED_PLUGINS" _mak_plugins=' LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -Wl,-T$(srcdir)/backends/plugins/ps2/main_prog.ld PLUGIN_LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -Wl,-T$(srcdir)/backends/plugins/ps2/plugin.ld -lstdc++ -lc @@ -3348,7 +3355,7 @@ PLUGIN_LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -Wl,-T$(srcdir)/backend ;; psp) _elf_loader=yes - DEFINES="$DEFINES -DUNCACHED_PLUGINS" + append_var DEFINES "-DUNCACHED_PLUGINS" _mak_plugins=' LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/main_prog.ld PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/plugin.ld -lstdc++ -lc @@ -3357,8 +3364,8 @@ PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/plugin.ld -lstdc++ -lc webos) _plugin_prefix="lib" _plugin_suffix=".so" - CXXFLAGS="$CXXFLAGS -fpic" - LIBS="$LIBS -ldl" + append_var CXXFLAGS "-fpic" + append_var LIBS "-ldl" _mak_plugins=' PLUGIN_EXTRA_DEPS = PLUGIN_LDFLAGS += -shared $(LDFLAGS) @@ -3434,7 +3441,7 @@ define_in_config_if_yes "$_build_hq_scalers" 'USE_HQ_SCALERS' cat > $TMPC << EOF int main(void) { return 0; } EOF -cc_check -lm && LIBS="$LIBS -lm" +cc_check -lm && append_var LIBS "-lm" # # Check for Ogg Vorbis @@ -3450,8 +3457,8 @@ EOF -lvorbisfile -lvorbis -logg && _vorbis=yes fi if test "$_vorbis" = yes ; then - LIBS="$LIBS $OGG_LIBS $VORBIS_LIBS -lvorbisfile -lvorbis -logg" - INCLUDES="$INCLUDES $OGG_CFLAGS $VORBIS_CFLAGS" + append_var LIBS "$OGG_LIBS $VORBIS_LIBS -lvorbisfile -lvorbis -logg" + append_var INCLUDES "$OGG_CFLAGS $VORBIS_CFLAGS" fi define_in_config_if_yes "$_vorbis" 'USE_VORBIS' echo "$_vorbis" @@ -3478,13 +3485,13 @@ if test "$_tremor" = yes && test "$_vorbis" = no; then add_line_to_config_h '#define USE_VORBIS' if test "$_tremolo" = yes ; then add_line_to_config_h '#define USE_TREMOLO' - LIBS="$LIBS $TREMOR_LIBS -ltremolo" + append_var LIBS "$TREMOR_LIBS -ltremolo" elif test "$_host" = ps2 ; then LIBS="-ltremor $LIBS" else - LIBS="$LIBS $TREMOR_LIBS -lvorbisidec" + append_var LIBS "$TREMOR_LIBS -lvorbisidec" fi - INCLUDES="$INCLUDES $TREMOR_CFLAGS" + append_var INCLUDES "$TREMOR_CFLAGS" else if test "$_vorbis" = yes; then _tremor="no (Ogg Vorbis/Tremor support is mutually exclusive)" @@ -3514,11 +3521,11 @@ EOF fi if test "$_flac" = yes ; then if test "$_vorbis" = yes ; then - LIBS="$LIBS $FLAC_LIBS $OGG_LIBS -lFLAC -logg" + append_var LIBS "$FLAC_LIBS $OGG_LIBS -lFLAC -logg" else - LIBS="$LIBS $FLAC_LIBS -lFLAC" + append_var LIBS "$FLAC_LIBS -lFLAC" fi - INCLUDES="$INCLUDES $FLAC_CFLAGS" + append_var INCLUDES "$FLAC_CFLAGS" fi define_in_config_if_yes "$_flac" 'USE_FLAC' echo "$_flac" @@ -3536,8 +3543,8 @@ EOF cc_check $MAD_CFLAGS $MAD_LIBS -lmad && _mad=yes fi if test "$_mad" = yes ; then - LIBS="$LIBS $MAD_LIBS -lmad" - INCLUDES="$INCLUDES $MAD_CFLAGS" + append_var LIBS "$MAD_LIBS -lmad" + append_var INCLUDES "$MAD_CFLAGS" fi define_in_config_if_yes "$_mad" 'USE_MAD' echo "$_mad" @@ -3555,10 +3562,10 @@ EOF cc_check $ALSA_CFLAGS $ALSA_LIBS -lasound && _alsa=yes fi if test "$_alsa" = yes ; then - LIBS="$LIBS $ALSA_LIBS -lasound" - INCLUDES="$INCLUDES $ALSA_CFLAGS" + append_var LIBS "$ALSA_LIBS -lasound" + append_var INCLUDES "$ALSA_CFLAGS" fi -define_in_config_h_if_yes "$_alsa" 'USE_ALSA' +define_in_config_if_yes "$_alsa" 'USE_ALSA' echo "$_alsa" # @@ -3581,8 +3588,8 @@ EOF cc_check $JPEG_CFLAGS $JPEG_LIBS -ljpeg && _jpeg=yes fi if test "$_jpeg" = yes ; then - LIBS="$LIBS $JPEG_LIBS -ljpeg" - INCLUDES="$INCLUDES $JPEG_CFLAGS" + append_var LIBS "$JPEG_LIBS -ljpeg" + append_var INCLUDES "$JPEG_CFLAGS" fi define_in_config_if_yes "$_jpeg" 'USE_JPEG' echo "$_jpeg" @@ -3606,8 +3613,8 @@ EOF cc_check $PNG_CFLAGS $PNG_LIBS -lpng -lz && _png=yes fi if test "$_png" = yes ; then - LIBS="$LIBS $PNG_LIBS -lpng -lz" - INCLUDES="$INCLUDES $PNG_CFLAGS" + append_var LIBS "$PNG_LIBS -lpng -lz" + append_var INCLUDES "$PNG_CFLAGS" fi define_in_config_if_yes "$_png" 'USE_PNG' echo "$_png" @@ -3630,8 +3637,8 @@ EOF cc_check $THEORADEC_CFLAGS $THEORADEC_LIBS -ltheoradec && _theoradec=yes fi if test "$_theoradec" = yes ; then - LIBS="$LIBS $THEORADEC_LIBS -ltheoradec" - INCLUDES="$INCLUDES $THEORADEC_CFLAGS" + append_var LIBS "$THEORADEC_LIBS -ltheoradec" + append_var INCLUDES "$THEORADEC_CFLAGS" fi define_in_config_if_yes "$_theoradec" 'USE_THEORADEC' if test ! "$_theoradec" = notsupported ; then @@ -3651,8 +3658,8 @@ EOF cc_check $FAAD_CFLAGS $FAAD_LIBS -lfaad && _faad=yes fi if test "$_faad" = yes ; then - LIBS="$LIBS $FAAD_LIBS -lfaad" - INCLUDES="$INCLUDES $FAAD_CFLAGS" + append_var LIBS "$FAAD_LIBS -lfaad" + append_var INCLUDES "$FAAD_CFLAGS" fi define_in_config_if_yes "$_faad" 'USE_FAAD' echo "$_faad" @@ -3683,8 +3690,8 @@ EOF cc_check $SNDIO_CFLAGS $SNDIO_LIBS -lsndio && _sndio=yes fi if test "$_sndio" = yes ; then - LIBS="$LIBS $SNDIO_LIBS -lsndio" - INCLUDES="$INCLUDES $SNDIO_CFLAGS" + append_var LIBS "$SNDIO_LIBS -lsndio" + append_var INCLUDES "$SNDIO_CFLAGS" fi define_in_config_h_if_yes "$_sndio" 'USE_SNDIO' echo "$_sndio" @@ -3716,8 +3723,8 @@ EOF cc_check $ZLIB_CFLAGS $ZLIB_LIBS -lz && _zlib=yes fi if test "$_zlib" = yes ; then - LIBS="$LIBS $ZLIB_LIBS -lz" - INCLUDES="$INCLUDES $ZLIB_CFLAGS" + append_var LIBS "$ZLIB_LIBS -lz" + append_var INCLUDES "$ZLIB_CFLAGS" fi define_in_config_if_yes "$_zlib" 'USE_ZLIB' echo "$_zlib" @@ -3762,8 +3769,8 @@ EOF fi fi if test "$_mpeg2" = yes ; then - INCLUDES="$INCLUDES $MPEG2_CFLAGS" - LIBS="$LIBS $MPEG2_LIBS -lmpeg2" + append_var INCLUDES "$MPEG2_CFLAGS" + append_var LIBS "$MPEG2_LIBS -lmpeg2" fi define_in_config_if_yes "$_mpeg2" 'USE_MPEG2' echo "$_mpeg2" @@ -3785,8 +3792,8 @@ EOF cc_check $SPARKLE_CFLAGS $SPARKLE_LIBS -framework Sparkle -ObjC++ -lobjc && _sparkle=yes fi if test "$_sparkle" = yes ; then - LIBS="$LIBS $SPARKLE_LIBS -framework Sparkle" - INCLUDES="$INCLUDES $SPARKLE_CFLAGS" + append_var LIBS "$SPARKLE_LIBS -framework Sparkle" + append_var INCLUDES "$SPARKLE_CFLAGS" fi define_in_config_if_yes "$_sparkle" 'USE_SPARKLE' fi @@ -3807,13 +3814,13 @@ fi if test "$_fluidsynth" = yes ; then case $_host_os in mingw*) - LIBS="$LIBS $FLUIDSYNTH_LIBS -lfluidsynth -ldsound -lwinmm" + append_var LIBS "$FLUIDSYNTH_LIBS -lfluidsynth -ldsound -lwinmm" ;; *) - LIBS="$LIBS $FLUIDSYNTH_LIBS -lfluidsynth" + append_var LIBS "$FLUIDSYNTH_LIBS -lfluidsynth" ;; esac - INCLUDES="$INCLUDES $FLUIDSYNTH_CFLAGS" + append_var INCLUDES "$FLUIDSYNTH_CFLAGS" fi define_in_config_if_yes "$_fluidsynth" 'USE_FLUIDSYNTH' echo "$_fluidsynth" @@ -3849,8 +3856,8 @@ else fi if test "$_readline" = yes ; then - LIBS="$LIBS $READLINE_LIBS $_READLINE_LIBS" - INCLUDES="$INCLUDES $READLINE_CFLAGS" + append_var LIBS "$READLINE_LIBS $_READLINE_LIBS" + append_var INCLUDES "$READLINE_CFLAGS" # # Check the type of rl_completion_entry_function. @@ -3909,8 +3916,8 @@ if test "$_libunity" = yes ; then LIBUNITY_LIBS="$LIBUNITY_LIBS `pkg-config --libs 'unity > 3.8.1' 2>> "$TMPLOG"`" LIBUNITY_CFLAGS="$LIBUNITY_CFLAGS `pkg-config --cflags 'unity > 3.8.1' 2>> "$TMPLOG"`" fi - LIBS="$LIBS $LIBUNITY_LIBS" - INCLUDES="$INCLUDES $LIBUNITY_CFLAGS" + append_var LIBS "$LIBUNITY_LIBS" + append_var INCLUDES "$LIBUNITY_CFLAGS" fi define_in_config_h_if_yes "$_libunity" 'USE_UNITY' fi @@ -3948,8 +3955,8 @@ EOF fi if test "$_freetype2" = "yes"; then - LIBS="$LIBS $FREETYPE2_LIBS" - INCLUDES="$INCLUDES $FREETYPE2_CFLAGS" + append_var LIBS "$FREETYPE2_LIBS" + append_var INCLUDES "$FREETYPE2_CFLAGS" fi fi @@ -3983,8 +3990,8 @@ EOF if cc_check $DEFINES $GLEW_LIBS $GLEW_INCLUDES then _glew=yes - LIBS="$LIBS $GLEW_LIBS" - INCLUDES="$INCLUDES $GLEW_INCLUDES" + append_var LIBS "$GLEW_LIBS" + append_var INCLUDES "$GLEW_INCLUDES" fi fi fi @@ -4036,7 +4043,7 @@ if test "$_iconv" = yes ; then echo "does not link at all" else if test "$needs_iconvlib" = yes ; then - ICONV_LIBS="$ICONV_LIBS -liconv" + append_var ICONV_LIBS "-liconv" fi echo "$needs_iconvlib" @@ -4065,8 +4072,8 @@ EOF fi if test "$_iconv" = yes ; then - LIBS="$LIBS $ICONV_LIBS" - INCLUDES="$INCLUDES $ICONV_CFLAGS" + append_var LIBS "$ICONV_LIBS" + append_var INCLUDES "$ICONV_CFLAGS" fi echocheck "iconv" @@ -4086,8 +4093,8 @@ case $_backend in _opengles=yes OPENGL_LIBS="-lGLES_CM -lEGL -lX11" OPENGL_CFLAGS="$OPENGL_LIBS" - LIBS="$LIBS $OPENGL_LIBS" - INCLUDES="$INCLUDES $OPENGL_CFLAGS" + append_var LIBS "$OPENGL_LIBS" + append_var INCLUDES "$OPENGL_CFLAGS" fi ;; esac @@ -4148,7 +4155,7 @@ EOF then _opengl=yes _opengles=yes - OPENGL_LIBS="$OPENGL_LIBS $lib" + append_var OPENGL_LIBS "$lib" break fi done @@ -4158,7 +4165,7 @@ EOF then _opengl=yes _opengles2=yes - OPENGL_LIBS="$OPENGL_LIBS -lGLESv2" + append_var OPENGL_LIBS "-lGLESv2" fi else case $_host_os in @@ -4179,8 +4186,8 @@ EOF fi if test "$_opengl" = yes ; then - LIBS="$LIBS $OPENGL_LIBS" - INCLUDES="$INCLUDES $OPENGL_CFLAGS" + append_var LIBS "$OPENGL_LIBS" + append_var INCLUDES "$OPENGL_CFLAGS" if test "$_opengl_shaders" = yes && test "$_opengles2" = no && test "$_glew" = no; then echo "WARNING: OpenGL Shader support requires GLEW!" _opengl_shaders=no @@ -4261,16 +4268,16 @@ if test "$_have_x86" = yes ; then else case $_host_os in darwin*) - NASMFLAGS="$NASMFLAGS -f macho" + append_var NASMFLAGS "-f macho" ;; mingw*) - NASMFLAGS="$NASMFLAGS -f win32" + append_var NASMFLAGS "-f win32" ;; os2-emx*) - NASMFLAGS="$NASMFLAGS -f aout" + append_var NASMFLAGS "-f aout" ;; *) - NASMFLAGS="$NASMFLAGS -f elf" + append_var NASMFLAGS "-f elf" ;; esac _nasm=yes @@ -4332,7 +4339,7 @@ if test "$_taskbar" = "no"; then else case $_host_os in mingw*) - LIBS="$LIBS -lole32 -luuid" + append_var LIBS "-lole32 -luuid" echo "win32" _taskbar=yes ;; @@ -4340,10 +4347,6 @@ else echo "osx" _taskbar=yes ;; - ps2) - # PS2 bogus dir: it actually depends on launch medium - DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"host:plugins\\\"" - ;; *) if test "$_libunity" = yes; then echo "unity" @@ -4416,22 +4419,26 @@ case $_host_os in # Windows stores all the external data files in executable file. ;; *) - DEFINES="$DEFINES -DDATA_PATH=\\\"$datadir\\\"" + append_var DEFINES "-DDATA_PATH=\\\"$datadir\\\"" ;; esac case $_backend in openpandora) # Add ../plugins as a path so plugins can be found when running from a .PND. - DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"../plugins\\\"" + append_var DEFINES "-DPLUGIN_DIRECTORY=\\\"../plugins\\\"" ;; maemo | webos) # The WebOS and Maemo apps want the plugins in the "lib" directory # without a scummvm sub directory. - DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"$libdir\\\"" + append_var DEFINES "-DPLUGIN_DIRECTORY=\\\"$libdir\\\"" + ;; + ps2) + # PS2 bogus dir: it actually depends on launch medium + append_var DEFINES "-DPLUGIN_DIRECTORY=\\\"host:plugins\\\"" ;; *) - DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"$libdir/residualvm\\\"" + append_var DEFINES "-DPLUGIN_DIRECTORY=\\\"$libdir/residualvm\\\"" ;; esac @@ -4440,9 +4447,9 @@ esac # We need to do it here to prevent mess-ups with the tests e.g. on the PSP # if test "$_enable_prof" = yes ; then - CXXFLAGS="$CXXFLAGS -pg" - LDFLAGS="$LDFLAGS -pg" - DEFINES="$DEFINES -DENABLE_PROFILING" + append_var CXXFLAGS "-pg" + append_var LDFLAGS "-pg" + append_var DEFINES "-DENABLE_PROFILING" fi echo_n "Backend... " @@ -4502,9 +4509,9 @@ case $_backend in android) # ssp at this point so the cxxtests link if test "$_debug_build" = yes; then - CXXFLAGS="$CXXFLAGS -fstack-protector" + append_var CXXFLAGS "-fstack-protector" else - CXXFLAGS="$CXXFLAGS -fno-stack-protector" + append_var CXXFLAGS "-fno-stack-protector" fi static_libs='' @@ -4528,7 +4535,7 @@ case $_backend in n64) # Move some libs down here, otherwise some symbols requires by libvorbis aren't found # during linking stage - LIBS="$LIBS -lc -lgcc -lnosys" + append_var LIBS "-lc -lgcc -lnosys" ;; esac diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 55669fd9f9c..5923642befb 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -20,7 +20,7 @@ * */ -//#define ENABLE_XCODE +#define ENABLE_XCODE // HACK to allow building with the SDL backend on MinGW // see bug #1800764 "TOOLS: MinGW tools building broken" @@ -336,7 +336,12 @@ int main(int argc, char *argv[]) { setup.defines.splice(setup.defines.begin(), featureDefines); // Windows only has support for the SDL backend, so we hardcode it here (along with winmm) - setup.defines.push_back("WIN32"); + if (projectType != kProjectXcode) { + setup.defines.push_back("WIN32"); + } else { + setup.defines.push_back("POSIX"); + setup.defines.push_back("MACOSX"); // This will break iOS, but allows OS X to catch up on browser_osx. + } setup.defines.push_back("SDL_BACKEND"); if (!useSDL2) { cout << "\nLinking to SDL 1.2\n\n"; @@ -410,7 +415,6 @@ int main(int argc, char *argv[]) { globalWarnings.push_back("-Wwrite-strings"); // The following are not warnings at all... We should consider adding them to // a different list of parameters. - globalWarnings.push_back("-fno-rtti"); globalWarnings.push_back("-fno-exceptions"); globalWarnings.push_back("-fcheck-new"); @@ -572,7 +576,7 @@ int main(int argc, char *argv[]) { globalWarnings.push_back("-fno-exceptions"); globalWarnings.push_back("-fcheck-new"); - provider = new CreateProjectTool::XCodeProvider(globalWarnings, projectWarnings); + provider = new CreateProjectTool::XcodeProvider(globalWarnings, projectWarnings); break; } @@ -1039,7 +1043,7 @@ bool producesObjectFile(const std::string &fileName) { std::string n, ext; splitFilename(fileName, n, ext); - if (ext == "cpp" || ext == "c" || ext == "asm") + if (ext == "cpp" || ext == "c" || ext == "asm" || ext == "m" || ext == "mm") return true; else return false; @@ -1279,8 +1283,9 @@ void ProjectProvider::createProject(BuildSetup &setup) { for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { if (i->first == setup.projectName) continue; - + // Retain the files between engines if we're creating a single project in.clear(); ex.clear(); + const std::string moduleDir = setup.srcDir + targetFolder + i->first; createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex); @@ -1290,7 +1295,6 @@ void ProjectProvider::createProject(BuildSetup &setup) { if (setup.tests) { // Create the main project file. in.clear(); ex.clear(); - createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex); createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex); createModuleList(setup.srcDir + "/base", setup.defines, setup.testDirs, in, ex); @@ -1305,7 +1309,6 @@ void ProjectProvider::createProject(BuildSetup &setup) { } else if (!setup.devTools) { // Last but not least create the main project file. in.clear(); ex.clear(); - // File list for the Project file createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex); createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex); diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp index d95bf3e9ee3..babd530ad7d 100644 --- a/devtools/create_project/xcode.cpp +++ b/devtools/create_project/xcode.cpp @@ -30,6 +30,14 @@ namespace CreateProjectTool { #define DEBUG_XCODE_HASH 0 +#ifdef ENABLE_IOS +#define IOS_TARGET 0 +#define OSX_TARGET 1 +#define SIM_TARGET 2 +#else +#define OSX_TARGET 0 +#endif + #define ADD_DEFINE(defines, name) \ defines.push_back(name); @@ -54,39 +62,172 @@ namespace CreateProjectTool { #define REMOVE_SETTING(config, key) \ config.settings.erase(key); -#define ADD_BUILD_FILE(id, name, comment) { \ +#define ADD_BUILD_FILE(id, name, fileRefId, comment) { \ Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); \ - buildFile->addProperty("fileRef", getHash(name), name, SettingsNoValue); \ + buildFile->addProperty("fileRef", fileRefId, name, SettingsNoValue); \ _buildFile.add(buildFile); \ _buildFile.flags = SettingsSingleItem; \ } -#define ADD_FILE_REFERENCE(name, properties) { \ - Object *fileRef = new Object(this, name, name, "PBXFileReference", "PBXFileReference", name); \ +#define ADD_FILE_REFERENCE(id, name, properties) { \ + Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); \ if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); \ - if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue); \ - if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue); \ - if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue); \ + if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable); \ + if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable); \ + if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable); \ if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); \ _fileReference.add(fileRef); \ _fileReference.flags = SettingsSingleItem; \ } -XCodeProvider::XCodeProvider(StringList &global_warnings, std::map &project_warnings, const int version) - : ProjectProvider(global_warnings, project_warnings, version) { +bool producesObjectFileOnOSX(const std::string &fileName) { + std::string n, ext; + splitFilename(fileName, n, ext); + + // Note that the difference between this and the general producesObjectFile is that + // this one adds Objective-C(++), and removes asm-support. + if (ext == "cpp" || ext == "c" || ext == "m" || ext == "mm") + return true; + else + return false; } -void XCodeProvider::createWorkspace(const BuildSetup &setup) { +XcodeProvider::Group::Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path) : Object(objectParent, uniqueName, groupName, "PBXGroup", "", groupName) { + addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable); + addProperty("sourceTree", "", "", SettingsNoValue|SettingsQuoteVariable); + + if (path != "") { + addProperty("path", path, "", SettingsNoValue|SettingsQuoteVariable); + } + _childOrder = 0; + _treeName = uniqueName; +} + +void XcodeProvider::Group::ensureChildExists(const std::string &name) { + std::map::iterator it = _childGroups.find(name); + if (it == _childGroups.end()) { + Group *child = new Group(parent, name, this->_treeName + '/' + name, name); + _childGroups[name] = child; + addChildGroup(child); + parent->_groups.add(child); + } +} + +void XcodeProvider::Group::addChildInternal(const std::string &id, const std::string &comment) { + if (properties.find("children") == properties.end()) { + Property children; + children.hasOrder = true; + children.flags = SettingsAsList; + properties["children"] = children; + } + properties["children"].settings[id] = Setting("", comment + " in Sources", SettingsNoValue, 0, _childOrder++); + if (_childOrder == 1) { + // Force children to use () even when there is only 1 child. + // Also this enforces the use of "," after the single item, instead of ; (see writeProperty) + properties["children"].flags |= SettingsSingleItem; + } else { + properties["children"].flags ^= SettingsSingleItem; + } + +} + +void XcodeProvider::Group::addChildGroup(const Group* group) { + addChildInternal(parent->getHash(group->_treeName), group->_treeName); +} + +void XcodeProvider::Group::addChildFile(const std::string &name) { + std::string id = "FileReference_" + _treeName + "/" + name; + addChildInternal(parent->getHash(id), name); + FileProperty property = FileProperty(name, name, name, "\"\""); + + parent->addFileReference(id, name, property); + if (producesObjectFileOnOSX(name)) { + parent->addBuildFile(_treeName + "/" + name, name, parent->getHash(id), name + " in Sources"); + } +} + +void XcodeProvider::Group::addChildByHash(const std::string &hash, const std::string &name) { + addChildInternal(hash, name); +} + +XcodeProvider::Group *XcodeProvider::Group::getChildGroup(const std::string &name) { + std::map::iterator it = _childGroups.find(name); + assert(it != _childGroups.end()); + return it->second; +} + +XcodeProvider::Group *XcodeProvider::touchGroupsForPath(const std::string &path) { + if (_rootSourceGroup == NULL) { + assert (path == _projectRoot); + _rootSourceGroup = new Group(this, "Sources", path, path); + _groups.add(_rootSourceGroup); + return _rootSourceGroup; + } else { + assert(path.find(_projectRoot) == 0); + std::string subPath = path.substr(_projectRoot.size() + 1); + Group *currentGroup = _rootSourceGroup; + size_t firstPathComponent = subPath.find_first_of('/'); + // We assume here that all paths have trailing '/', otherwise this breaks. + while (firstPathComponent != std::string::npos) { + currentGroup->ensureChildExists(subPath.substr(0, firstPathComponent)); + currentGroup = currentGroup->getChildGroup(subPath.substr(0, firstPathComponent)); + subPath = subPath.substr(firstPathComponent + 1); + firstPathComponent = subPath.find_first_of('/'); + } + return currentGroup; + } +} + +void XcodeProvider::addFileReference(const std::string &id, const std::string &name, FileProperty properties) { + Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); + if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); + if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable); + if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable); + if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable); + if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); + _fileReference.add(fileRef); + _fileReference.flags = SettingsSingleItem; +} + +void XcodeProvider::addProductFileReference(const std::string &id, const std::string &name) { + Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); + fileRef->addProperty("explicitFileType", "compiled.mach-o.executable", "", SettingsNoValue|SettingsQuoteVariable); + fileRef->addProperty("includeInIndex", "0", "", SettingsNoValue); + fileRef->addProperty("path", name, "", SettingsNoValue|SettingsQuoteVariable); + fileRef->addProperty("sourceTree", "BUILT_PRODUCTS_DIR", "", SettingsNoValue); + _fileReference.add(fileRef); + _fileReference.flags = SettingsSingleItem; +} + +void XcodeProvider::addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment) { + + Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); + buildFile->addProperty("fileRef", fileRefId, name, SettingsNoValue); + _buildFile.add(buildFile); + _buildFile.flags = SettingsSingleItem; +} + +XcodeProvider::XcodeProvider(StringList &global_warnings, std::map &project_warnings, const int version) + : ProjectProvider(global_warnings, project_warnings, version) { + _rootSourceGroup = NULL; +} + +void XcodeProvider::createWorkspace(const BuildSetup &setup) { // Create project folder std::string workspace = setup.outputDir + '/' + PROJECT_NAME ".xcodeproj"; createDirectory(workspace); - + _projectRoot = setup.srcDir; + touchGroupsForPath(_projectRoot); + // Setup global objects setupDefines(setup); +#ifdef ENABLE_IOS _targets.push_back(PROJECT_DESCRIPTION "-iPhone"); +#endif _targets.push_back(PROJECT_DESCRIPTION "-OS X"); +#ifdef ENABLE_IOS _targets.push_back(PROJECT_DESCRIPTION "-Simulator"); - +#endif setupCopyFilesBuildPhase(); setupFrameworksBuildPhase(); setupNativeTarget(); @@ -97,7 +238,7 @@ void XCodeProvider::createWorkspace(const BuildSetup &setup) { // We are done with constructing all the object graph and we got through every project, output the main project file // (this is kind of a hack since other providers use separate project files) -void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) { +void XcodeProvider::createOtherBuildFiles(const BuildSetup &setup) { // This needs to be done at the end when all build files have been accounted for setupSourcesBuildPhase(); @@ -105,7 +246,7 @@ void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) { } // Store information about a project here, for use at the end -void XCodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir, +void XcodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir, const StringList &includeList, const StringList &excludeList) { std::string modulePath; if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) { @@ -124,7 +265,7 @@ void XCodeProvider::createProjectFile(const std::string &, const std::string &, ////////////////////////////////////////////////////////////////////////// // Main Project file ////////////////////////////////////////////////////////////////////////// -void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) { +void XcodeProvider::ouputMainProjectFile(const BuildSetup &setup) { std::ofstream project((setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj").c_str()); if (!project) error("Could not open \"" + setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj\" for writing"); @@ -164,92 +305,93 @@ void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) { ////////////////////////////////////////////////////////////////////////// // Files ////////////////////////////////////////////////////////////////////////// -void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, +void XcodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) { - // Add comments for shared lists - _buildFile.comment = "PBXBuildFile"; - _fileReference.comment = "PBXFileReference"; - - // Init root group - _groups.comment = "PBXGroup"; - - // Create group - std::string name = getLastPathComponent(dir.name); - Object *group = new Object(this, "PBXGroup_" + name , "PBXGroup", "PBXGroup", "", name); - - // List of children - Property children; - children.hasOrder = true; - children.flags = SettingsAsList; - - group->addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable); - group->addProperty("sourceTree", "", "", SettingsNoValue|SettingsQuoteVariable); - - int order = 0; + // Ensure that top-level groups are generated for i.e. engines/ + Group *group = touchGroupsForPath(filePrefix); for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) { const FileNode *node = *i; - std::string id = "FileReference_" + node->name; - FileProperty property = FileProperty(node->name, node->name, node->name, ""); - - ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++); - ADD_BUILD_FILE(id, node->name, node->name + " in Sources"); - ADD_FILE_REFERENCE(node->name, property); - + // Iff it is a file, then add (build) file references. Since we're using Groups and not File References + // for folders, we shouldn't add folders as file references, obviously. + if (node->children.empty()) { + group->addChildFile(node->name); + } // Process child nodes if (!node->children.empty()) writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/'); } - - group->properties["children"] = children; - - _groups.add(group); } ////////////////////////////////////////////////////////////////////////// // Setup functions ////////////////////////////////////////////////////////////////////////// -void XCodeProvider::setupCopyFilesBuildPhase() { +void XcodeProvider::setupCopyFilesBuildPhase() { // Nothing to do here } +#define DEF_SYSFRAMEWORK(framework) properties[framework".framework"] = FileProperty("wrapper.framework", framework".framework", "System/Library/Frameworks/" framework ".framework", "SDKROOT"); \ + ADD_SETTING_ORDER_NOVALUE(children, getHash(framework".framework"), framework".framework", fwOrder++); + +#define DEF_LOCALLIB_STATIC(lib) properties[lib".a"] = FileProperty("archive.ar", lib".a", "/opt/local/lib/" lib ".a", "\"\""); \ + ADD_SETTING_ORDER_NOVALUE(children, getHash(lib".a"), lib".a", fwOrder++); + /** * Sets up the frameworks build phase. * * (each native target has different build rules) */ -void XCodeProvider::setupFrameworksBuildPhase() { +void XcodeProvider::setupFrameworksBuildPhase() { _frameworksBuildPhase.comment = "PBXFrameworksBuildPhase"; + // Just use a hardcoded id for the Frameworks-group + Group *frameworksGroup = new Group(this, "Frameworks", "PBXGroup_CustomTemplate_Frameworks_", ""); + + Property children; + children.hasOrder = true; + children.flags = SettingsAsList; + // Setup framework file properties std::map properties; - + int fwOrder = 0; // Frameworks - properties["ApplicationServices.framework"] = FileProperty("wrapper.framework", "ApplicationServices.framework", "System/Library/Frameworks/ApplicationServices.framework", "SDKROOT"); - properties["AudioToolbox.framework"] = FileProperty("wrapper.framework", "AudioToolbox.framework", "System/Library/Frameworks/AudioToolbox.framework", "SDKROOT"); - properties["AudioUnit.framework"] = FileProperty("wrapper.framework", "AudioUnit.framework", "System/Library/Frameworks/AudioUnit.framework", "SDKROOT"); - properties["Carbon.framework"] = FileProperty("wrapper.framework", "Carbon.framework", "System/Library/Frameworks/Carbon.framework", "SDKROOT"); - properties["Cocoa.framework"] = FileProperty("wrapper.framework", "Cocoa.framework", "System/Library/Frameworks/Cocoa.framework", "SDKROOT"); - properties["CoreAudio.framework"] = FileProperty("wrapper.framework", "CoreAudio.framework", "System/Library/Frameworks/CoreAudio.framework", "SDKROOT"); - properties["CoreFoundation.framework"] = FileProperty("wrapper.framework", "CoreFoundation.framework", "System/Library/Frameworks/CoreFoundation.framework", "SDKROOT"); - properties["CoreMIDI.framework"] = FileProperty("wrapper.framework", "CoreMIDI.framework", "System/Library/Frameworks/CoreMIDI.framework", "SDKROOT"); - properties["Foundation.framework"] = FileProperty("wrapper.framework", "Foundation.framework", "System/Library/Frameworks/Foundation.framework", "SDKROOT"); - properties["IOKit.framework"] = FileProperty("wrapper.framework", "IOKit.framework", "System/Library/Frameworks/IOKit.framework", "SDKROOT"); - properties["OpenGLES.framework"] = FileProperty("wrapper.framework", "OpenGLES.framework", "System/Library/Frameworks/OpenGLES.framework", "SDKROOT"); - properties["QuartzCore.framework"] = FileProperty("wrapper.framework", "QuartzCore.framework", "System/Library/Frameworks/QuartzCore.framework", "SDKROOT"); - properties["QuickTime.framework"] = FileProperty("wrapper.framework", "QuickTime.framework", "System/Library/Frameworks/QuickTime.framework", "SDKROOT"); - properties["UIKit.framework"] = FileProperty("wrapper.framework", "UIKit.framework", "System/Library/Frameworks/UIKit.framework", "SDKROOT"); + DEF_SYSFRAMEWORK("ApplicationServices"); + DEF_SYSFRAMEWORK("AudioToolbox"); + DEF_SYSFRAMEWORK("AudioUnit"); + DEF_SYSFRAMEWORK("Carbon"); + DEF_SYSFRAMEWORK("Cocoa"); + DEF_SYSFRAMEWORK("CoreAudio"); + DEF_SYSFRAMEWORK("CoreFoundation"); + DEF_SYSFRAMEWORK("CoreMIDI"); + DEF_SYSFRAMEWORK("Foundation"); + DEF_SYSFRAMEWORK("IOKit"); + DEF_SYSFRAMEWORK("OpenGLES"); + DEF_SYSFRAMEWORK("QuartzCore"); + DEF_SYSFRAMEWORK("QuickTime"); + DEF_SYSFRAMEWORK("UIKit"); + // Optionals: + DEF_SYSFRAMEWORK("OpenGL"); // Local libraries - properties["libFLAC.a"] = FileProperty("archive.ar", "libFLAC.a", "lib/libFLAC.a", "\"\""); - properties["libmad.a"] = FileProperty("archive.ar", "libmad.a", "lib/libmad.a", "\"\""); - //properties["libmpeg2.a"] = FileProperty("archive.ar", "libmpeg2.a", "lib/libmpeg2.a", "\"\""); - properties["libvorbisidec.a"] = FileProperty("archive.ar", "libvorbisidec.a", "lib/libvorbisidec.a", "\"\""); + DEF_LOCALLIB_STATIC("libFLAC"); + DEF_LOCALLIB_STATIC("libmad"); + DEF_LOCALLIB_STATIC("libvorbisidec"); + DEF_LOCALLIB_STATIC("libfreetype"); +// DEF_LOCALLIB_STATIC("libmpeg2"); + frameworksGroup->properties["children"] = children; + _groups.add(frameworksGroup); + // Force this to be added as a sub-group in the root. + _rootSourceGroup->addChildGroup(frameworksGroup); + + + // Declare this here, as it's used across the three targets + int order = 0; +#ifdef ENABLE_IOS ////////////////////////////////////////////////////////////////////////// // iPhone - Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[0], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); + Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[IOS_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_iPhone->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); framework_iPhone->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); @@ -272,23 +414,22 @@ void XCodeProvider::setupFrameworksBuildPhase() { frameworks_iPhone.push_back("libvorbisidec.a"); frameworks_iPhone.push_back("OpenGLES.framework"); - int order = 0; for (ValueList::iterator framework = frameworks_iPhone.begin(); framework != frameworks_iPhone.end(); framework++) { std::string id = "Frameworks_" + *framework + "_iphone"; std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(iPhone_files, getHash(id), comment, order++); - ADD_BUILD_FILE(id, *framework, comment); - ADD_FILE_REFERENCE(*framework, properties[*framework]); + ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); + ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_iPhone->properties["files"] = iPhone_files; _frameworksBuildPhase.add(framework_iPhone); - +#endif ////////////////////////////////////////////////////////////////////////// // ScummVM-OS X - Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[1], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); + Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[OSX_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_OSX->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); framework_OSX->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); @@ -311,6 +452,8 @@ void XCodeProvider::setupFrameworksBuildPhase() { frameworks_osx.push_back("IOKit.framework"); frameworks_osx.push_back("Cocoa.framework"); frameworks_osx.push_back("AudioUnit.framework"); + // Optionals: + frameworks_osx.push_back("OpenGL.framework"); order = 0; for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) { @@ -318,17 +461,17 @@ void XCodeProvider::setupFrameworksBuildPhase() { std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(osx_files, getHash(id), comment, order++); - ADD_BUILD_FILE(id, *framework, comment); - ADD_FILE_REFERENCE(*framework, properties[*framework]); + ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); + ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_OSX->properties["files"] = osx_files; _frameworksBuildPhase.add(framework_OSX); - +#ifdef ENABLE_IOS ////////////////////////////////////////////////////////////////////////// // Simulator - Object *framework_simulator = new Object(this, "PBXFrameworksBuildPhase_" + _targets[2], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); + Object *framework_simulator = new Object(this, "PBXFrameworksBuildPhase_" + _targets[SIM_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_simulator->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); framework_simulator->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); @@ -353,20 +496,28 @@ void XCodeProvider::setupFrameworksBuildPhase() { std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(simulator_files, getHash(id), comment, order++); - ADD_BUILD_FILE(id, *framework, comment); - ADD_FILE_REFERENCE(*framework, properties[*framework]); + ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); + ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_simulator->properties["files"] = simulator_files; _frameworksBuildPhase.add(framework_simulator); +#endif } -void XCodeProvider::setupNativeTarget() { +void XcodeProvider::setupNativeTarget() { _nativeTarget.comment = "PBXNativeTarget"; + // Just use a hardcoded id for the Products-group + Group *productsGroup = new Group(this, "Products", "PBXGroup_CustomTemplate_Products_" , ""); // Output native target section for (unsigned int i = 0; i < _targets.size(); i++) { +#ifndef ENABLE_IOS + if (i != OSX_TARGET) { // TODO: Fix iOS-targets, for now just disable them. + continue; + } +#endif Object *target = new Object(this, "PBXNativeTarget_" + _targets[i], "PBXNativeTarget", "PBXNativeTarget", "", _targets[i]); target->addProperty("buildConfigurationList", getHash("XCConfigurationList_" + _targets[i]), "Build configuration list for PBXNativeTarget \"" + _targets[i] + "\"", SettingsNoValue); @@ -385,14 +536,18 @@ void XCodeProvider::setupNativeTarget() { target->addProperty("name", _targets[i], "", SettingsNoValue|SettingsQuoteVariable); target->addProperty("productName", PROJECT_NAME, "", SettingsNoValue); + addProductFileReference("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i], PROJECT_DESCRIPTION ".app"); + productsGroup->addChildByHash(getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app"); target->addProperty("productReference", getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app", SettingsNoValue); target->addProperty("productType", "com.apple.product-type.application", "", SettingsNoValue|SettingsQuoteVariable); _nativeTarget.add(target); } + _rootSourceGroup->addChildGroup(productsGroup); + _groups.add(productsGroup); } -void XCodeProvider::setupProject() { +void XcodeProvider::setupProject() { _project.comment = "PBXProject"; Object *project = new Object(this, "PBXProject", "PBXProject", "PBXProject", "", "Project object"); @@ -411,22 +566,30 @@ void XCodeProvider::setupProject() { ADD_SETTING_ORDER_NOVALUE(regions, "German", "", 3); project->properties["knownRegions"] = regions; - project->addProperty("mainGroup", getHash("PBXGroup_CustomTemplate"), "CustomTemplate", SettingsNoValue); - project->addProperty("projectDirPath", "", "", SettingsNoValue|SettingsQuoteVariable); + project->addProperty("mainGroup", _rootSourceGroup->getHashRef(), "CustomTemplate", SettingsNoValue); + project->addProperty("projectDirPath", _projectRoot, "", SettingsNoValue|SettingsQuoteVariable); project->addProperty("projectRoot", "", "", SettingsNoValue|SettingsQuoteVariable); // List of targets Property targets; targets.flags = SettingsAsList; - targets.settings[getHash("PBXNativeTarget_" + _targets[0])] = Setting("", _targets[0], SettingsNoValue, 0, 0); - targets.settings[getHash("PBXNativeTarget_" + _targets[1])] = Setting("", _targets[1], SettingsNoValue, 0, 1); - targets.settings[getHash("PBXNativeTarget_" + _targets[2])] = Setting("", _targets[2], SettingsNoValue, 0, 2); +#ifdef ENABLE_IOS + targets.settings[getHash("PBXNativeTarget_" + _targets[IOS_TARGET])] = Setting("", _targets[IOS_TARGET], SettingsNoValue, 0, 0); +#endif + targets.settings[getHash("PBXNativeTarget_" + _targets[OSX_TARGET])] = Setting("", _targets[OSX_TARGET], SettingsNoValue, 0, 1); +#ifdef ENABLE_IOS + targets.settings[getHash("PBXNativeTarget_" + _targets[SIM_TARGET])] = Setting("", _targets[SIM_TARGET], SettingsNoValue, 0, 2); +#endif project->properties["targets"] = targets; +#ifndef ENABLE_IOS + // Force list even when there is only a single target + project->properties["targets"].flags |= SettingsSingleItem; +#endif _project.add(project); } -void XCodeProvider::setupResourcesBuildPhase() { +void XcodeProvider::setupResourcesBuildPhase() { _resourcesBuildPhase.comment = "PBXResourcesBuildPhase"; // Setup resource file properties @@ -483,7 +646,7 @@ void XCodeProvider::setupResourcesBuildPhase() { ADD_SETTING_ORDER_NOVALUE(files, getHash(id), comment, order++); // TODO Fix crash when adding build file for data //ADD_BUILD_FILE(id, *file, comment); - ADD_FILE_REFERENCE(*file, properties[*file]); + ADD_FILE_REFERENCE(*file, *file, properties[*file]); } // Add custom files depending on the target @@ -503,12 +666,41 @@ void XCodeProvider::setupResourcesBuildPhase() { } } -void XCodeProvider::setupSourcesBuildPhase() { - // TODO +void XcodeProvider::setupSourcesBuildPhase() { + _sourcesBuildPhase.comment = "PBXSourcesBuildPhase"; + + // Setup source file properties + std::map properties; + + // Same as for containers: a rule for each native target + for (unsigned int i = 0; i < _targets.size(); i++) { + Object *source = new Object(this, "PBXSourcesBuildPhase_" + _targets[i], "PBXSourcesBuildPhase", "PBXSourcesBuildPhase", "", "Sources"); + + source->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); + + Property files; + files.hasOrder = true; + files.flags = SettingsAsList; + + int order = 0; + for (std::vector::iterator file = _buildFile.objects.begin(); file !=_buildFile.objects.end(); ++file) { + if (!producesObjectFileOnOSX((*file)->name)) { + continue; + } + std::string comment = (*file)->name + " in Sources"; + ADD_SETTING_ORDER_NOVALUE(files, getHash((*file)->id), comment, order++); + } + + source->properties["files"] = files; + + source->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); + + _sourcesBuildPhase.add(source); + } } // Setup all build configurations -void XCodeProvider::setupBuildConfiguration() { +void XcodeProvider::setupBuildConfiguration() { _buildConfiguration.comment = "XCBuildConfiguration"; _buildConfiguration.flags = SettingsAsList; @@ -516,9 +708,9 @@ void XCodeProvider::setupBuildConfiguration() { ///**************************************** // * iPhone // ****************************************/ - +#ifdef ENABLE_IOS // Debug - Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); + Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property iPhone_Debug; ADD_SETTING_QUOTE(iPhone_Debug, "ARCHS", "$(ARCHS_UNIVERSAL_IPHONE_OS)"); ADD_SETTING_QUOTE(iPhone_Debug, "CODE_SIGN_IDENTITY", "iPhone Developer"); @@ -539,10 +731,10 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING(iPhone_Debug, "GCC_THUMB_SUPPORT", "NO"); ADD_SETTING(iPhone_Debug, "GCC_UNROLL_LOOPS", "YES"); ValueList iPhone_HeaderSearchPaths; - iPhone_HeaderSearchPaths.push_back("../../engines/"); - iPhone_HeaderSearchPaths.push_back("../../"); + iPhone_HeaderSearchPaths.push_back("$(SRCROOT)/engines/"); + iPhone_HeaderSearchPaths.push_back("$(SRCROOT)"); iPhone_HeaderSearchPaths.push_back("include/"); - ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, SettingsAsList|SettingsNoQuote, 5); + ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, SettingsAsList|SettingsQuoteVariable, 5); ADD_SETTING(iPhone_Debug, "INFOPLIST_FILE", "Info.plist"); ValueList iPhone_LibPaths; iPhone_LibPaths.push_back("$(inherited)"); @@ -560,7 +752,7 @@ void XCodeProvider::setupBuildConfiguration() { iPhone_Debug_Object->properties["buildSettings"] = iPhone_Debug; // Release - Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); + Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property iPhone_Release(iPhone_Debug); ADD_SETTING(iPhone_Release, "GCC_OPTIMIZATION_LEVEL", "3"); ADD_SETTING(iPhone_Release, "COPY_PHASE_STRIP", "YES"); @@ -572,7 +764,7 @@ void XCodeProvider::setupBuildConfiguration() { _buildConfiguration.add(iPhone_Debug_Object); _buildConfiguration.add(iPhone_Release_Object); - +#endif /**************************************** * scummvm ****************************************/ @@ -581,13 +773,14 @@ void XCodeProvider::setupBuildConfiguration() { Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Debug", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Debug"); Property scummvm_Debug; ADD_SETTING(scummvm_Debug, "ALWAYS_SEARCH_USER_PATHS", "NO"); + ADD_SETTING_QUOTE(scummvm_Debug, "USER_HEADER_SEARCH_PATHS", "$(SRCROOT) $(SRCROOT)/engines"); ADD_SETTING_QUOTE(scummvm_Debug, "ARCHS", "$(ARCHS_STANDARD_32_BIT)"); ADD_SETTING_QUOTE(scummvm_Debug, "CODE_SIGN_IDENTITY", "Don't Code Sign"); ADD_SETTING_QUOTE_VAR(scummvm_Debug, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "Don't Code Sign"); ADD_SETTING_QUOTE(scummvm_Debug, "FRAMEWORK_SEARCH_PATHS", ""); ADD_SETTING(scummvm_Debug, "GCC_C_LANGUAGE_STANDARD", "c99"); ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); - ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "NO"); + ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "YES"); ADD_SETTING(scummvm_Debug, "GCC_INPUT_FILETYPE", "automatic"); ADD_SETTING(scummvm_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); ValueList scummvm_defines(_defines); @@ -601,15 +794,15 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_VARIABLE", "YES"); ValueList scummvm_HeaderPaths; scummvm_HeaderPaths.push_back("include/"); - scummvm_HeaderPaths.push_back("../../engines/"); - scummvm_HeaderPaths.push_back("../../"); - ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsNoQuote|SettingsAsList, 5); + scummvm_HeaderPaths.push_back("$(SRCROOT)/engines/"); + scummvm_HeaderPaths.push_back("$(SRCROOT)"); + ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5); ADD_SETTING_QUOTE(scummvm_Debug, "LIBRARY_SEARCH_PATHS", ""); ADD_SETTING(scummvm_Debug, "ONLY_ACTIVE_ARCH", "YES"); ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_CFLAGS", ""); ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_LDFLAGS", "-lz"); ADD_SETTING(scummvm_Debug, "PREBINDING", "NO"); - ADD_SETTING(scummvm_Debug, "SDKROOT", "macosx10.6"); + ADD_SETTING(scummvm_Debug, "SDKROOT", "macosx"); scummvm_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue); scummvm_Debug_Object->properties["buildSettings"] = scummvm_Debug; @@ -633,7 +826,7 @@ void XCodeProvider::setupBuildConfiguration() { ****************************************/ // Debug - Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); + Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[OSX_TARGET] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property scummvmOSX_Debug; ADD_SETTING_QUOTE(scummvmOSX_Debug, "ARCHS", "$(NATIVE_ARCH)"); ADD_SETTING(scummvmOSX_Debug, "COMPRESS_PNG_FILES", "NO"); @@ -642,7 +835,7 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING_QUOTE(scummvmOSX_Debug, "FRAMEWORK_SEARCH_PATHS", ""); ADD_SETTING(scummvmOSX_Debug, "GCC_C_LANGUAGE_STANDARD", "c99"); ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); - ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "NO"); + ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "YES"); ADD_SETTING(scummvmOSX_Debug, "GCC_DYNAMIC_NO_PIC", "NO"); ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_FIX_AND_CONTINUE", "NO"); ADD_SETTING(scummvmOSX_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); @@ -656,11 +849,12 @@ void XCodeProvider::setupBuildConfiguration() { ValueList scummvmOSX_HeaderPaths; scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL"); scummvmOSX_HeaderPaths.push_back("/opt/local/include"); + scummvmOSX_HeaderPaths.push_back("/opt/local/include/freetype2"); scummvmOSX_HeaderPaths.push_back("include/"); - scummvmOSX_HeaderPaths.push_back("../../engines/"); - scummvmOSX_HeaderPaths.push_back("../../"); - ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsNoQuote|SettingsAsList, 5); - ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/../macosx/Info.plist"); + scummvmOSX_HeaderPaths.push_back("$(SRCROOT)/engines/"); + scummvmOSX_HeaderPaths.push_back("$(SRCROOT)"); + ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5); + ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/dists/macosx/Info.plist"); ValueList scummvmOSX_LibPaths; scummvmOSX_LibPaths.push_back("/sw/lib"); scummvmOSX_LibPaths.push_back("/opt/local/lib"); @@ -671,6 +865,10 @@ void XCodeProvider::setupBuildConfiguration() { ValueList scummvmOSX_LdFlags; scummvmOSX_LdFlags.push_back("-lSDLmain"); scummvmOSX_LdFlags.push_back("-logg"); + scummvmOSX_LdFlags.push_back("-lpng"); + scummvmOSX_LdFlags.push_back("-ljpeg"); + scummvmOSX_LdFlags.push_back("-ltheora"); + scummvmOSX_LdFlags.push_back("-lfreetype"); scummvmOSX_LdFlags.push_back("-lvorbisfile"); scummvmOSX_LdFlags.push_back("-lvorbis"); scummvmOSX_LdFlags.push_back("-lmad"); @@ -685,7 +883,7 @@ void XCodeProvider::setupBuildConfiguration() { scummvmOSX_Debug_Object->properties["buildSettings"] = scummvmOSX_Debug; // Release - Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); + Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[OSX_TARGET] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property scummvmOSX_Release(scummvmOSX_Debug); ADD_SETTING(scummvmOSX_Release, "COPY_PHASE_STRIP", "YES"); REMOVE_SETTING(scummvmOSX_Release, "GCC_DYNAMIC_NO_PIC"); @@ -697,13 +895,13 @@ void XCodeProvider::setupBuildConfiguration() { _buildConfiguration.add(scummvmOSX_Debug_Object); _buildConfiguration.add(scummvmOSX_Release_Object); - +#ifdef ENABLE_IOS /**************************************** * ScummVM-Simulator ****************************************/ // Debug - Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Debug", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); + Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Debug", _targets[SIM_TARGET] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property scummvmSimulator_Debug(iPhone_Debug); ADD_SETTING_QUOTE(scummvmSimulator_Debug, "FRAMEWORK_SEARCH_PATHS", "$(inherited)"); ADD_SETTING_LIST(scummvmSimulator_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvm_defines, SettingsNoQuote|SettingsAsList, 5); @@ -715,7 +913,7 @@ void XCodeProvider::setupBuildConfiguration() { scummvmSimulator_Debug_Object->properties["buildSettings"] = scummvmSimulator_Debug; // Release - Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); + Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[SIM_TARGET] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property scummvmSimulator_Release(scummvmSimulator_Debug); ADD_SETTING(scummvmSimulator_Release, "COPY_PHASE_STRIP", "YES"); ADD_SETTING(scummvmSimulator_Release, "GCC_OPTIMIZATION_LEVEL", "3"); @@ -732,7 +930,7 @@ void XCodeProvider::setupBuildConfiguration() { // Configuration List _configurationList.comment = "XCConfigurationList"; _configurationList.flags = SettingsAsList; - +#endif // Warning: This assumes we have all configurations with a Debug & Release pair for (std::vector::iterator config = _buildConfiguration.objects.begin(); config != _buildConfiguration.objects.end(); config++) { @@ -758,7 +956,7 @@ void XCodeProvider::setupBuildConfiguration() { ////////////////////////////////////////////////////////////////////////// // Setup global defines -void XCodeProvider::setupDefines(const BuildSetup &setup) { +void XcodeProvider::setupDefines(const BuildSetup &setup) { for (StringList::const_iterator i = setup.defines.begin(); i != setup.defines.end(); ++i) { if (*i == "HAVE_NASM") // Not supported on Mac (TODO: change how it's handled in main class or add it only in MSVC/CodeBlocks providers?) @@ -772,7 +970,6 @@ void XCodeProvider::setupDefines(const BuildSetup &setup) { ADD_DEFINE(_defines, "SCUMM_LITTLE_ENDIAN"); ADD_DEFINE(_defines, "UNIX"); ADD_DEFINE(_defines, "SCUMMVM"); - ADD_DEFINE(_defines, "USE_TREMOR"); } ////////////////////////////////////////////////////////////////////////// @@ -780,7 +977,7 @@ void XCodeProvider::setupDefines(const BuildSetup &setup) { ////////////////////////////////////////////////////////////////////////// // TODO use md5 to compute a file hash (and fall back to standard key generation if not passed a file) -std::string XCodeProvider::getHash(std::string key) { +std::string XcodeProvider::getHash(std::string key) { #if DEBUG_XCODE_HASH return key; @@ -800,7 +997,7 @@ std::string XCodeProvider::getHash(std::string key) { bool isSeparator (char s) { return (s == '-'); } -std::string XCodeProvider::newHash() const { +std::string XcodeProvider::newHash() const { std::string hash = createUUID(); // Remove { and - from UUID and resize to 96-bits uppercase hex string @@ -832,7 +1029,7 @@ std::string replace(std::string input, const std::string find, std::string repla return input; } -std::string XCodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const { +std::string XcodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const { std::string output; output += (flags & SettingsSingleItem ? "" : "\t\t\t") + variable + " = "; @@ -847,7 +1044,9 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property & output += writeSetting((*setting).first, (*setting).second); - if ((prop.flags & SettingsAsList) && prop.settings.size() > 1) { + // The combination of SettingsAsList, and SettingsSingleItem should use "," and not ";" (i.e children + // in PBXGroup, so we special case that case here. + if ((prop.flags & SettingsAsList) && (prop.settings.size() > 1 || (prop.flags & SettingsSingleItem))) { output += (prop.settings.size() > 0) ? ",\n" : "\n"; } else { output += ";"; @@ -861,13 +1060,13 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property & return output; } -std::string XCodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const { +std::string XcodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const { return writeSetting(variable, Setting(value, comment, flags, indent)); } // Heavily modified (not in a good way) function, imported from the QMake // XCode project generator pbuilder_pbx.cpp, writeSettings() (under LGPL 2.1) -std::string XCodeProvider::writeSetting(const std::string &variable, const Setting &setting) const { +std::string XcodeProvider::writeSetting(const std::string &variable, const Setting &setting) const { std::string output; const std::string quote = (setting.flags & SettingsNoQuote) ? "" : "\""; const std::string escape_quote = quote.empty() ? "" : "\\" + quote; diff --git a/devtools/create_project/xcode.h b/devtools/create_project/xcode.h index f86e7c555c4..2686d14986c 100644 --- a/devtools/create_project/xcode.h +++ b/devtools/create_project/xcode.h @@ -30,9 +30,9 @@ namespace CreateProjectTool { -class XCodeProvider : public ProjectProvider { +class XcodeProvider : public ProjectProvider { public: - XCodeProvider(StringList &global_warnings, std::map &project_warnings, const int version = 0); + XcodeProvider(StringList &global_warnings, std::map &project_warnings, const int version = 0); protected: @@ -45,7 +45,6 @@ protected: void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix); - private: enum { SettingsAsList = 0x01, @@ -169,7 +168,7 @@ private: PropertyList properties; // List of object properties, including output configuration // Constructs an object and add a default type property - Object(XCodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "") + Object(XcodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "") : id(objectId), name(objectName), refType(objectRefType), comment(objectComment), parent(objectParent) { assert(objectParent); assert(!objectId.empty()); @@ -210,9 +209,10 @@ private: return output; } + // Slight hack, to allow Group access to parent. + protected: + XcodeProvider *parent; private: - XCodeProvider *parent; - // Returns the type property (should always be the first in the properties map) std::string getType() { assert(!properties.empty()); @@ -258,6 +258,36 @@ private: } }; + // A class to maintain a folder-reference group-hierarchy, which together with the functionality below + // allows for breaking up sub-paths into a chain of groups. This helps with merging engines into the + // overall group-layout. + class Group : public Object { + int _childOrder; + std::map _childGroups; + std::string _treeName; + void addChildInternal(const std::string &id, const std::string &comment); + public: + Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path); + void addChildFile(const std::string &name); + void addChildByHash(const std::string &hash, const std::string &name); + // Should be passed the hash for the entry + void addChildGroup(const Group* group); + void ensureChildExists(const std::string &name); + Group *getChildGroup(const std::string &name); + std::string getHashRef() const { return parent->getHash(id); } + }; + + // The path used by the root-source group + std::string _projectRoot; + // The base source group, currently also re-purposed for containing the various support-groups. + Group *_rootSourceGroup; + // Helper function to create the chain of groups for the various subfolders. Necessary as + // create_project likes to start in engines/ + Group *touchGroupsForPath(const std::string &path); + // Functionality for adding file-refs and build-files, as Group-objects need to be able to do this. + void addFileReference(const std::string &id, const std::string &name, FileProperty properties); + void addProductFileReference(const std::string &id, const std::string &name); + void addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment); // All objects std::map _hashDictionnary; ValueList _defines; diff --git a/engines/grim/emi/sound/aifftrack.cpp b/engines/grim/emi/sound/aifftrack.cpp index a1e6661de6b..0ebf2793330 100644 --- a/engines/grim/emi/sound/aifftrack.cpp +++ b/engines/grim/emi/sound/aifftrack.cpp @@ -54,10 +54,11 @@ bool AIFFTrack::openSound(const Common::String &filename, const Common::String & return false; } _soundName = soundName; - Audio::SeekableAudioStream *aiffStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES); + Audio::RewindableAudioStream *aiffStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES); + Audio::SeekableAudioStream *seekStream = dynamic_cast(aiffStream); _stream = aiffStream; if (start) - aiffStream->seek(*start); + seekStream->seek(*start); if (!_stream) return false; _handle = new Audio::SoundHandle(); diff --git a/gui/launcher.cpp b/gui/launcher.cpp index bdc572553b3..ca1b4494a16 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -1160,9 +1160,9 @@ void LauncherDialog::updateButtons() { _loadButton->setEnabled(en); _loadButton->draw(); } - switchButtonsText(_addButton, "~A~dd Game...", "Mass Add..."); + switchButtonsText(_addButton, "~A~dd Game...", _s("Mass Add...")); #ifdef ENABLE_EVENTRECORDER - switchButtonsText(_loadButton, "~L~oad...", "Record..."); + switchButtonsText(_loadButton, "~L~oad...", _s("Record...")); #endif } diff --git a/gui/options.cpp b/gui/options.cpp index 1b56261fd92..0f386d43fa2 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -461,11 +461,9 @@ void OptionsDialog::close() { if (_oplPopUp) { if (_enableAudioSettings) { - const OPL::Config::EmulatorDescription *ed = OPL::Config::getAvailable(); - while (ed->name && ed->id != (int)_oplPopUp->getSelectedTag()) - ++ed; + const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag()); - if (ed->name) + if (ed) ConfMan.set("opl_driver", ed->name, _domain); else ConfMan.removeKey("opl_driver", _domain); diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp index 32f6be2cd54..4e858921ee6 100644 --- a/image/codecs/cinepak.cpp +++ b/image/codecs/cinepak.cpp @@ -433,9 +433,11 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream for (uint16 j = 0; j < 256; j++) { _curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j]; _curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j]; - memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * 4); - memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * 4); } + + // Copy the QuickTime dither tables + memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * 4); + memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * 4); } _curFrame.strips[i].id = stream.readUint16BE(); diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h index dc8172ea0f1..4efb1191ccf 100644 --- a/image/codecs/cinepak.h +++ b/image/codecs/cinepak.h @@ -64,6 +64,9 @@ struct CinepakFrame { * Cinepak decoder. * * Used by BMP/AVI and PICT/QuickTime. + * + * Used in engines: + * - sherlock */ class CinepakDecoder : public Codec { public: @@ -88,7 +91,6 @@ private: byte *_ditherPalette; bool _dirtyPalette; - byte *_rgbLookup; byte *_colorMap; DitherType _ditherType;