/* 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.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-1-1/engines/sci/sound/drivers/midi.cpp $
 * $Id: midi.cpp 48086 2010-02-17 23:38:43Z fingolfin $
 *
 */

#include "sci/sci.h"

#include "common/config-manager.h"
#include "common/file.h"

#include "sound/fmopl.h"
#include "sound/softsynth/emumidi.h"

#include "sci/resource.h"
#include "sci/sound/drivers/mididriver.h"
#include "sci/sound/drivers/map-mt32-to-gm.h"

namespace Sci {

class MidiPlayer_Midi : public MidiPlayer {
public:
	enum {
		kVoices = 32,
		kReverbConfigNr = 11,
		kMaxSysExSize = 264
	};

	MidiPlayer_Midi(SciVersion version);
	virtual ~MidiPlayer_Midi();

	int open(ResourceManager *resMan);
	void close();
	void send(uint32 b);
	void sysEx(const byte *msg, uint16 length);
	bool hasRhythmChannel() const { return true; }
	byte getPlayId();
	int getPolyphony() const { return kVoices; }
	void setVolume(byte volume);
	int getVolume();
	void setReverb(byte reverb);
	void playSwitch(bool play);

private:
	bool isMt32GmPatch(const byte *data, int size);
	void readMt32GmPatch(const byte *data, int size);
	void readMt32Patch(const byte *data, int size);
	void readMt32DrvData();

	void mapMt32ToGm(byte *data, size_t size);
	uint8 lookupGmInstrument(const char *iname);
	uint8 lookupGmRhythmKey(const char *iname);
	uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins);

	void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay);
	void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay);
	void setMt32Volume(byte volume);
	void resetMt32();

	void noteOn(int channel, int note, int velocity);
	void setPatch(int channel, int patch);
	void controlChange(int channel, int control, int value);

	struct Channel {
		byte mappedPatch;
		byte patch;
		int velocityMapIdx;
		bool playing;
		int8 keyShift;
		int8 volAdjust;
		uint8 pan;
		uint8 hold;
		uint8 volume;

		Channel() : mappedPatch(MIDI_UNMAPPED), patch(MIDI_UNMAPPED), velocityMapIdx(0), playing(false),
			keyShift(0), volAdjust(0), pan(0x80), hold(0), volume(0x7f) { }
	};

	bool _isMt32;
	bool _isOldPatchFormat;
	bool _hasReverb;
	bool _playSwitch;
	int _masterVolume;

	byte _reverbConfig[kReverbConfigNr][3];
	Channel _channels[16];
	uint8 _percussionMap[128];
	int8 _keyShift[128];
	int8 _volAdjust[128];
	uint8 _patchMap[128];
	uint8 _velocityMapIdx[128];
	uint8 _velocityMap[4][128];

	// These are extensions used for our own MT-32 to GM mapping
	uint8 _pitchBendRange[128];
	uint8 _percussionVelocityScale[128];

	byte _goodbyeMsg[20];
	byte _sysExBuf[kMaxSysExSize];
};

MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
	MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
	_driver = createMidi(midiType);

	if (midiType == MD_MT32 || ConfMan.getBool("native_mt32"))
		_isMt32 = true;

	_sysExBuf[0] = 0x41;
	_sysExBuf[1] = 0x10;
	_sysExBuf[2] = 0x16;
	_sysExBuf[3] = 0x12;
}

MidiPlayer_Midi::~MidiPlayer_Midi() {
	delete _driver;
}

void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
	uint8 patch = _channels[channel].mappedPatch;

	if (channel == MIDI_RHYTHM_CHANNEL) {
		if (_percussionMap[note] == MIDI_UNMAPPED) {
			debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note);
			return;
		}

		note = _percussionMap[note];
		// Scale velocity;
		velocity = velocity * _percussionVelocityScale[note] / 127;
	} else if (patch >= 128) {
		if (patch == MIDI_UNMAPPED)
			return;

		// Map to rhythm
		channel = MIDI_RHYTHM_CHANNEL;
		note = patch - 128;

		// Scale velocity;
		velocity = velocity * _percussionVelocityScale[note] / 127;
	} else {
		int8 keyshift = _channels[channel].keyShift;

		int shiftNote = note + keyshift;

		if (keyshift > 0) {
			while (shiftNote > 127)
				shiftNote -= 12;
		} else {
			while (shiftNote < 0)
				shiftNote += 12;
		}

		note = shiftNote;

		// We assume that velocity 0 maps to 0 (for note off)
		int mapIndex = _channels[channel].velocityMapIdx;
		velocity = _velocityMap[mapIndex][velocity];
	}

	_channels[channel].playing = true;
	_driver->send(0x90 | channel, note, velocity);
}

void MidiPlayer_Midi::controlChange(int channel, int control, int value) {
	switch (control) {
	case 0x07:
		_channels[channel].volume = value;

		if (!_playSwitch)
			return;

		value += _channels[channel].volAdjust;

		if (value > 0x7f)
			value = 0x7f;

		if (value < 0)
			value = 1;

		value *= _masterVolume;

		if (value != 0) {
			value /= 15;

			if (value == 0)
				value = 1;
		}
		break;
	case 0x0a:
		if (_channels[channel].pan == value)
			return;

		_channels[channel].pan = value;
		break;
	case 0x40:
		if (_channels[channel].hold == value)
			return;

		_channels[channel].hold = value;
		break;
	case 0x7b:
		if (!_channels[channel].playing)
			return;

		_channels[channel].playing = false;
	}

	_driver->send(0xb0 | channel, control, value);
}

void MidiPlayer_Midi::setPatch(int channel, int patch) {
	bool resetVol = false;

	if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch))
		return;

	_channels[channel].patch = patch;
	_channels[channel].velocityMapIdx = _velocityMapIdx[patch];

	if (_channels[channel].mappedPatch == MIDI_UNMAPPED)
		resetVol = true;

	_channels[channel].mappedPatch = _patchMap[patch];

	if (_patchMap[patch] == MIDI_UNMAPPED) {
		debugC(kDebugLevelSound, "[Midi] Channel %i set to unmapped patch %i", channel, patch);
		_driver->send(0xb0 | channel, 0x7b, 0);
		_driver->send(0xb0 | channel, 0x40, 0);
		return;
	}

	if (_patchMap[patch] >= 128) {
		// Mapped to rhythm, don't send channel commands
		return;
	}

	if (_channels[channel].keyShift != _keyShift[patch]) {
		_channels[channel].keyShift = _keyShift[patch];
		_driver->send(0xb0 | channel, 0x7b, 0);
		_driver->send(0xb0 | channel, 0x40, 0);
		resetVol = true;
	}

	if (resetVol || (_channels[channel].volAdjust != _volAdjust[patch])) {
		_channels[channel].volAdjust = _volAdjust[patch];
		controlChange(channel, 0x07, _channels[channel].volume);
	}

	uint8 bendRange = _pitchBendRange[patch];
	if (bendRange != MIDI_UNMAPPED)
		_driver->setPitchBendRange(channel, bendRange);

	_driver->send(0xc0 | channel, _patchMap[patch], 0);
}

void MidiPlayer_Midi::send(uint32 b) {
	byte command = b & 0xf0;
	byte channel = b & 0xf;
	byte op1 = (b >> 8) & 0x7f;
	byte op2 = (b >> 16) & 0x7f;

	// In early SCI0, we may also get events for AdLib rhythm channels.
	// While an MT-32 would ignore those with the default channel mapping,
	// we filter these out for the benefit of other MIDI devices.
	if (channel < 1 || channel > 9)
		return;

	switch (command) {
	case 0x80:
		noteOn(channel, op1, 0);
		break;
	case 0x90:
		noteOn(channel, op1, op2);
		break;
	case 0xb0:
		controlChange(channel, op1, op2);
		break;
	case 0xc0:
		setPatch(channel, op1);
		break;
	case 0xe0:
		_driver->send(b);
		break;
	default:
		warning("Ignoring MIDI event %02x", command);
	}
}

void MidiPlayer_Midi::setVolume(byte volume) {
	_masterVolume = volume;

	if (!_playSwitch)
		return;

	for (uint i = 1; i < 10; i++) {
		if (_channels[i].volume != 0xff)
			controlChange(i, 0x07, _channels[i].volume & 0x7f);
	}
}

int MidiPlayer_Midi::getVolume() {
	return _masterVolume;
}

void MidiPlayer_Midi::setReverb(byte reverb) {
	_reverb = CLIP<byte>(reverb, 0, kReverbConfigNr - 1);
	if (_hasReverb)
		sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true);
}

void MidiPlayer_Midi::playSwitch(bool play) {
	_playSwitch = play;
	if (play)
		setVolume(_masterVolume);
	else {
		for (uint i = 1; i < 10; i++)
			_driver->send(0xb0 | i, 7, 0);
	}
}

bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size)
{
	if (size < 1155)
		return false;
	if (size > 16889)
		return true;

	bool isMt32 = false;
	bool isMt32Gm = false;

	if (READ_LE_UINT16(data + 1153) + 1155 == size)
		isMt32Gm = true;

	int pos = 492 + 246 * data[491];

	if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd))
		pos += 386;

	if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba))
		pos += 267;

	if (size == pos)
		isMt32 = true;

	if (isMt32 == isMt32Gm)
		error("Failed to detect MT-32 patch format");

	return isMt32Gm;
}

void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay = false) {
	if (len + 8 > kMaxSysExSize) {
		warning("SysEx message exceed maximum size; ignoring");
		return;
	}

	uint16 chk = 0;

	_sysExBuf[4] = (addr >> 16) & 0xff;
	_sysExBuf[5] = (addr >> 8) & 0xff;
	_sysExBuf[6] = addr & 0xff;

	for (int i = 0; i < len; i++)
		_sysExBuf[7 + i] = str->readByte();

	for (int i = 4; i < 7 + len; i++)
		chk += _sysExBuf[i];

	_sysExBuf[7 + len] = 128 - chk % 128;

	if (noDelay)
		_driver->sysEx(_sysExBuf, len + 8);
	else
		sysEx(_sysExBuf, len + 8);
}

void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) {
	Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len);
	sendMt32SysEx(addr, str, len, noDelay);
	delete str;
}

void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) {
	Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size);

	// Send before-SysEx text
	str->seek(0x14);
	sendMt32SysEx(0x200000, str, 20);

	// Save goodbye message
	str->read(_goodbyeMsg, 20);

	byte volume = CLIP<uint16>(str->readUint16LE(), 0, 100);
	setMt32Volume(volume);

	// Reverb default only used in (roughly) SCI0/SCI01
	_reverb = str->readByte();
	_hasReverb = true;

	// Skip reverb SysEx message
	str->seek(11, SEEK_CUR);

	// Read reverb data
	for (int i = 0; i < kReverbConfigNr; i++) {
		_reverbConfig[i][0] = str->readByte();
		_reverbConfig[i][1] = str->readByte();
		_reverbConfig[i][2] = str->readByte();
	}

	// Patches 1-48
	sendMt32SysEx(0x50000, str, 256);
	sendMt32SysEx(0x50200, str, 128);

	// Timbres
	byte timbresNr = str->readByte();
	for (int i = 0; i < timbresNr; i++)
		sendMt32SysEx(0x80000 + (i << 9), str, 246);

	uint16 flag = str->readUint16BE();

	if (!str->eos() && (flag == 0xabcd)) {
		// Patches 49-96
		sendMt32SysEx(0x50300, str, 256);
		sendMt32SysEx(0x50500, str, 128);
		flag = str->readUint16BE();
	}

	if (!str->eos() && (flag == 0xdcba)) {
		// Rhythm key map
		sendMt32SysEx(0x30110, str, 256);
		// Partial reserve
		sendMt32SysEx(0x100004, str, 9);
	}

	// Send after-SysEx text
	str->seek(0);
	sendMt32SysEx(0x200000, str, 20);

	// Send the mystery SysEx
	sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6);

	delete str;
}

void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {
	memcpy(_patchMap, data, 0x80);
	memcpy(_keyShift, data + 0x80, 0x80);
	memcpy(_volAdjust, data + 0x100, 0x80);
	memcpy(_percussionMap, data + 0x180, 0x80);
	_channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[0x200];
	memcpy(_velocityMapIdx, data + 0x201, 0x80);
	memcpy(_velocityMap, data + 0x281, 0x200);

	uint16 midiSize = READ_LE_UINT16(data + 0x481);

	if (midiSize > 0) {
		if (size < midiSize + 1155)
			error("Failed to read MIDI data");

		const byte *midi = data + 1155;
		byte command = 0;
		uint i = 0;

		while (i < midiSize) {
			byte op1, op2;

			if (midi[i] & 0x80)
				command = midi[i++];

			switch (command & 0xf0) {
			case 0xf0: {
				byte *sysExEnd = (byte *)memchr(midi + i, 0xf7, midiSize - i);

				if (!sysExEnd)
					error("Failed to find end of sysEx");

				int len = sysExEnd - (midi + i);
				sysEx(midi + i, len);

				i += len + 1; // One more for the 0x7f
				break;
			}
			case 0x80:
			case 0x90:
			case 0xa0:
			case 0xb0:
			case 0xe0:
				if (i + 1 >= midiSize)
					error("MIDI command exceeds data size");

				op1 = midi[i++];
				op2 = midi[i++];
				_driver->send(command, op1, op2);
				break;
			case 0xc0:
			case 0xd0:
				if (i >= midiSize)
					error("MIDI command exceeds data size");

				op1 = midi[i++];
				_driver->send(command, op1, 0);
				break;
			default:
				error("Failed to find MIDI command byte");
			}
		}
	}
}

void MidiPlayer_Midi::readMt32DrvData() {
	Common::File f;

	if (f.open("MT32.DRV")) {
		int size = f.size();

		assert(size >= 166);

		// Send before-SysEx text
		f.seek(0x59);

		// Skip 2 extra 0 bytes in some drivers
		if (f.readUint16LE() != 0)
			f.seek(-2, SEEK_CUR);

		sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);

		// Send after-SysEx text (SSCI sends this before every song)
		sendMt32SysEx(0x200000, static_cast<Common::SeekableReadStream *>(&f), 20);

		// Save goodbye message
		f.read(_goodbyeMsg, 20);

		// Set volume
		byte volume = CLIP<uint16>(f.readUint16LE(), 0, 100);
		setMt32Volume(volume);

		byte reverbSysEx[13];
		// This old driver should have a full reverb SysEx
		if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7))
			error("Error reading MT32.DRV");

		// Send reverb SysEx
		sysEx(reverbSysEx + 1, 11);
		_hasReverb = false;

		f.seek(0x29);

		// Read AdLib->MT-32 patch map
		for (int i = 0; i < 48; i++) {
			_patchMap[i] = f.readByte();
		}

		f.close();
	} else {
		error("Failed to open MT32.DRV");
	}
}

byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) {
	int i = 0;

	while (Mt32MemoryTimbreMaps[i].name) {
		if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
			return getGmInstrument(Mt32MemoryTimbreMaps[i]);
		i++;
	}
	return MIDI_UNMAPPED;
}

byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) {
	int i = 0;

	while (Mt32MemoryTimbreMaps[i].name) {
		if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
			return Mt32MemoryTimbreMaps[i].gmRhythmKey;
		i++;
	}
	return MIDI_UNMAPPED;
}

uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) {
	if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM)
		return Mt32Ins.gmRhythmKey + 0x80;
	else
		return Mt32Ins.gmInstr;
}

void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) {
	// FIXME: Clean this up
	int memtimbres, patches;
	uint8 group, number, keyshift, finetune, bender_range;
	uint8 *patchpointer;
	uint32 pos;
	int i;

	for (i = 0; i < 128; i++) {
		_patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[i]);
		_pitchBendRange[i] = 12;
	}

	for (i = 0; i < 128; i++)
		_percussionMap[i] = Mt32PresetRhythmKeymap[i];

	memtimbres = *(data + 0x1eb);
	pos = 0x1ec + memtimbres * 0xf6;

	if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) {
		patches = 96;
		pos += 2 + 8 * 48;
	} else {
		patches = 48;
	}

	debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Patches detected", patches);
	debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Memory Timbres", memtimbres);

	debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches..");

	for (i = 0; i < patches; i++) {
		char name[11];

		if (i < 48)
			patchpointer = data + 0x6b + 8 * i;
		else
			patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2;

		group = *patchpointer;
		number = *(patchpointer + 1);
		keyshift = *(patchpointer + 2);
		finetune = *(patchpointer + 3);
		bender_range = *(patchpointer + 4);

		debugCN(kDebugLevelSound, "  [%03d] ", i);

		switch (group) {
		case 1:
			number += 64;
			// Fall through
		case 0:
			_patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[number]);
			debugCN(kDebugLevelSound, "%s -> ", Mt32PresetTimbreMaps[number].name);
			break;
		case 2:
			if (number < memtimbres) {
				strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10);
				name[10] = 0;
				_patchMap[i] = lookupGmInstrument(name);
				debugCN(kDebugLevelSound, "%s -> ", name);
			} else {
				_patchMap[i] = 0xff;
				debugCN(kDebugLevelSound, "[Invalid]  -> ");
			}
			break;
		case 3:
			_patchMap[i] = getGmInstrument(Mt32RhythmTimbreMaps[number]);
			debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number].name);
			break;
		default:
			break;
		}

		if (_patchMap[i] == MIDI_UNMAPPED) {
			debugC(kDebugLevelSound, "[Unmapped]");
		} else {
			if (_patchMap[i] >= 128) {
				debugC(kDebugLevelSound, "%s [Rhythm]", GmPercussionNames[_patchMap[i] - 128]);
			} else {
				debugC(kDebugLevelSound, "%s", GmInstrumentNames[_patchMap[i]]);
			}
		}

		_keyShift[i] = CLIP<uint8>(keyshift, 0, 48) - 24;
		_pitchBendRange[i] = CLIP<uint8>(bender_range, 0, 24);
	}

	if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) {
		debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion..");

		for (i = 0; i < 64 ; i++) {
			number = *(data + pos + 4 * i + 2);
			byte ins = i + 24;

			debugCN(kDebugLevelSound, "  [%03d] ", ins);

			if (number < 64) {
				char name[11];
				strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10);
				name[10] = 0;
				debugCN(kDebugLevelSound, "%s -> ", name);
				_percussionMap[ins] = lookupGmRhythmKey(name);
			} else {
				if (number < 94) {
					debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name);
					_percussionMap[ins] = Mt32RhythmTimbreMaps[number - 64].gmRhythmKey;
				} else {
					debugCN(kDebugLevelSound, "[Key  %03i] -> ", number);
					_percussionMap[ins] = MIDI_UNMAPPED;
				}
			}

			if (_percussionMap[ins] == MIDI_UNMAPPED)
				debugC(kDebugLevelSound, "[Unmapped]");
			else
				debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[ins]]);

			_percussionVelocityScale[ins] = *(data + pos + 4 * i + 3) * 127 / 100;
		}
	}
}

void MidiPlayer_Midi::setMt32Volume(byte volume) {
	sendMt32SysEx(0x100016, &volume, 1);
}

void MidiPlayer_Midi::resetMt32() {
	sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true);

	// This seems to require a longer delay than usual
	g_system->delayMillis(150);
}

int MidiPlayer_Midi::open(ResourceManager *resMan) {
	assert(resMan != NULL);

	int retval = _driver->open();
	if (retval != 0) {
		warning("Failed to open MIDI driver");
		return retval;
	}

	// By default use no mapping
	for (uint i = 0; i < 128; i++) {
		_percussionMap[i] = i;
		_patchMap[i] = i;
		_velocityMap[0][i] = i;
		_keyShift[i] = 0;
		_volAdjust[i] = 0;
		_velocityMapIdx[i] = 0;
		_pitchBendRange[i] = MIDI_UNMAPPED;
		_percussionVelocityScale[i] = 127;
	}

	Resource *res = NULL;

	if (_isMt32) {
		// MT-32
		resetMt32();

		res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);

		if (res) {
			if (isMt32GmPatch(res->data, res->size)) {
				readMt32GmPatch(res->data, res->size);
				strncpy((char *)_goodbyeMsg, "      ScummVM       ", 20);
			} else {
				readMt32Patch(res->data, res->size);
			}
		} else {
			readMt32DrvData();
		}
	} else {
		// General MIDI
		res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0);

		if (res && isMt32GmPatch(res->data, res->size)) {
			// There is a GM patch
			readMt32GmPatch(res->data, res->size);

			// Detect the format of patch 1, so that we know what play mask to use
			res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
			if (!res)
				_isOldPatchFormat = false;
			else
				_isOldPatchFormat = !isMt32GmPatch(res->data, res->size);
		} else {
			// No GM patch found, map instruments using MT-32 patch

			warning("Game has no native support for General MIDI, applying auto-mapping");

			// Modify velocity map to make low velocity notes a little louder
			for (uint i = 1; i < 0x40; i++)
				_velocityMap[0][i] = 0x20 + (i - 1) / 2;

			res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);

			if (res) {
				if (!isMt32GmPatch(res->data, res->size))
					mapMt32ToGm(res->data, res->size);
				else
					error("MT-32 patch has wrong type");
			} else {
				// No MT-32 patch present, try to read from MT32.DRV
				Common::File f;

				if (f.open("MT32.DRV")) {
					int size = f.size();

					assert(size >= 70);

					f.seek(0x29);

					// Read AdLib->MT-32 patch map
					for (int i = 0; i < 48; i++)
						_patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[f.readByte() & 0x7f]);
				}
			}
		}
	}

	return 0;
}

void MidiPlayer_Midi::close() {
	if (_isMt32) {
		// Send goodbye message
		sendMt32SysEx(0x200000, _goodbyeMsg, 20);
	}

	_driver->close();
}

void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) {
	_driver->sysEx(msg, length);

	// Wait the time it takes to send the SysEx data
	uint32 delay = (length + 2) * 1000 / 3125;

	// Plus an additional delay for the MT-32 rev00
	if (_isMt32)
		delay += 40;

	g_system->delayMillis(delay);
	g_system->updateScreen();
}

byte MidiPlayer_Midi::getPlayId() {
	switch (_version) {
	case SCI_VERSION_0_EARLY:
	case SCI_VERSION_0_LATE:
		return 0x01;
	default:
		if (_isMt32)
			return 0x0c;
		else
			return _isOldPatchFormat ? 0x0c : 0x07;
	}
}

MidiPlayer *MidiPlayer_Midi_create(SciVersion version) {
	return new MidiPlayer_Midi(version);
}

} // End of namespace Sci
