diff options
-rw-r--r-- | engines/igor/igor.cpp | 37 | ||||
-rw-r--r-- | engines/igor/igor.h | 12 | ||||
-rw-r--r-- | engines/igor/menu.cpp | 5 | ||||
-rw-r--r-- | engines/igor/midi.cpp | 362 | ||||
-rw-r--r-- | engines/igor/midi.h | 167 | ||||
-rw-r--r-- | engines/igor/module.mk | 1 | ||||
-rw-r--r-- | engines/igor/parts/part_12.cpp | 9 | ||||
-rw-r--r-- | engines/igor/parts/part_13.cpp | 1 | ||||
-rw-r--r-- | engines/igor/parts/part_14.cpp | 1 | ||||
-rw-r--r-- | engines/igor/parts/part_16.cpp | 1 | ||||
-rw-r--r-- | engines/igor/parts/part_17.cpp | 1 | ||||
-rw-r--r-- | engines/igor/parts/part_95.cpp | 102 | ||||
-rw-r--r-- | engines/igor/parts/part_main.cpp | 16 |
13 files changed, 682 insertions, 33 deletions
diff --git a/engines/igor/igor.cpp b/engines/igor/igor.cpp index 6a75be08b8..e0d98b1dbc 100644 --- a/engines/igor/igor.cpp +++ b/engines/igor/igor.cpp @@ -32,12 +32,15 @@ #include "sound/voc.h" #include "igor/igor.h" +#include "igor/midi.h" namespace Igor { IgorEngine::IgorEngine(OSystem *system, int gameVersion) : Engine(system), _gameVersion(gameVersion) { + _midiPlayer = new MidiPlayer(this); + _screenVGA = (uint8 *)malloc(320 * 200); for (int i = 0; i < 4; ++i) { _facingIgorFrames[i] = (uint8 *)malloc(13500); @@ -60,6 +63,7 @@ IgorEngine::IgorEngine(OSystem *system, int gameVersion) } IgorEngine::~IgorEngine() { + delete _midiPlayer; Common::clearAllSpecialDebugLevels(); free(_screenVGA); for (int i = 0; i < 4; ++i) { @@ -332,14 +336,15 @@ void IgorEngine::playMusic(int num) { } if (seq) { for (int i = 0; i < 5; ++i) { - free(_musicSequenceTable[i]); + free(_musicSequenceTable[i].data); } for (int i = 0; seq[i]; ++i) { - _musicSequenceTable[i] = loadData(seq[i]); + _musicSequenceTable[i].data = loadData(seq[i], 0, &_musicSequenceTable[i].dataSize); } } _gameState.musicNum = num; _gameState.musicSequenceIndex = 1; + _midiPlayer->playMusic(_musicSequenceTable[0].data, _musicSequenceTable[0].dataSize); } void IgorEngine::updateMusic() { @@ -825,6 +830,8 @@ void IgorEngine::loadDialogueData(int dlg) { decodeRoomString(src, _dialogueQuestions[i][n], len); _dialogueQuestions[i][n][len] = '\0'; debugC(9, kDebugResource, "loadDialogueData() _dialogueQuestions[%d][%d] '%s'", i, n, _dialogueQuestions[i][n]); + } else { + _dialogueQuestions[i][n][0] = '\0'; } } } @@ -836,6 +843,8 @@ void IgorEngine::loadDialogueData(int dlg) { decodeRoomString(src, _dialogueReplies[i], len); _dialogueReplies[i][len] = '\0'; debugC(9, kDebugResource, "loadDialogueData() _dialogueReplies[%d] '%s'", i, _dialogueReplies[i]); + } else { + _dialogueReplies[i][0] = '\0'; } } free(p); @@ -1588,6 +1597,9 @@ void IgorEngine::handleRoomInput() { } void IgorEngine::animateIgorTalking(int frame) { + if (getPart() == 4) { + return; + } if (getPart() == 85) { PART_85_HELPER_6(frame); return; @@ -2792,13 +2804,14 @@ void IgorEngine::handleDialogue(int x, int y, int r, int g, int b) { _gameState.dialogueChoiceStart = 1; _gameState.dialogueChoiceCount = 1; _dialogueEnded = false; + if (getPart() == 12 && _objectsState[44] == 0) { + _gameState.dialogueData[6] = 1; + dialogueReplyToQuestion(x, y, r, g, b, 40); + } do { if (getPart() == 15 && _objectsState[48] == 0) { _gameState.dialogueData[6] = 0; } - if (getPart() == 12 && _objectsState[44] == 0) { - _gameState.dialogueData[6] = 1; - } drawDialogueChoices(); (this->*_updateDialogue)(kUpdateDialogueAnimStanding); _dialogueChoiceSelected = selectDialogue(); @@ -2922,14 +2935,16 @@ void IgorEngine::dialogueAskQuestion() { waitForEndOfIgorDialogue(); } -void IgorEngine::dialogueReplyToQuestion(int x, int y, int r, int g, int b) { - int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize; - int reply = _gameState.dialogueData[offset + 4]; - debugC(9, kDebugEngine, "dialogueReplyToQuestion() dialogue choice %d reply %d", _dialogueChoiceSelected, reply); +void IgorEngine::dialogueReplyToQuestion(int x, int y, int r, int g, int b, int reply) { if (reply == 0) { - return; + int offset = (_dialogueInfo[_dialogueChoiceSelected] - 1) * 6 + (_gameState.dialogueChoiceCount - 1) * 30 + (_gameState.dialogueChoiceStart - 1) * _roomDataOffsets.dlg.matSize; + reply = _gameState.dialogueData[offset + 4]; + debugC(9, kDebugEngine, "dialogueReplyToQuestion() dialogue choice %d reply %d", _dialogueChoiceSelected, reply); + if (reply == 0) { + return; + } } - offset = _roomDataOffsets.dlg.matSize * 3 + reply; + int offset = 30 + _roomDataOffsets.dlg.matSize + reply; int count = _gameState.dialogueData[offset - 1]; int dialogueIndex = 250; for (int i = 0; i < count; ++i) { diff --git a/engines/igor/igor.h b/engines/igor/igor.h index 6a8b79d1af..a71c741c89 100644 --- a/engines/igor/igor.h +++ b/engines/igor/igor.h @@ -265,6 +265,7 @@ struct Action { }; class TypeSerializer; +class MidiPlayer; class IgorEngine: public Engine { public: @@ -390,13 +391,15 @@ protected: void drawDialogueChoices(); int selectDialogue(); void dialogueAskQuestion(); - void dialogueReplyToQuestion(int x, int y, int r, int g, int b); + void dialogueReplyToQuestion(int x, int y, int r, int g, int b, int reply = 0); void saveOrLoadGameState(TypeSerializer &typeSerializer); void loadGameState(int slot); void saveGameState(int slot); void generateGameStateFileName(int num, char *dst, int len) const; + MidiPlayer *_midiPlayer; + Common::File _ovlFile; Common::File _sndFile; Common::File _tblFile; @@ -465,7 +468,10 @@ protected: ExecuteActionProc _executeMainAction; ExecuteActionProc _executeRoomAction; uint8 _previousMusic; - uint8 *_musicSequenceTable[5]; + struct { + uint8 *data; + int dataSize; + } _musicSequenceTable[5]; Action _currentAction; uint8 _actionCode; uint8 _actionWalkPoint; @@ -571,7 +577,7 @@ protected: void PART_12_ACTION_105(); void PART_12_ACTION_108(); void PART_12_UPDATE_ROOM_BACKGROUND(); - void PART_12_UPDATE_DIALOGUE_TOBIAS(int action); + void PART_12_UPDATE_DIALOGUE_CHURCHMAN(int action); void PART_12_HANDLE_DIALOGUE_CHURCHMAN(); void PART_12_HELPER_1(int num); void PART_12_HELPER_2(); diff --git a/engines/igor/menu.cpp b/engines/igor/menu.cpp index cb977bcdac..d991c47fa9 100644 --- a/engines/igor/menu.cpp +++ b/engines/igor/menu.cpp @@ -333,11 +333,6 @@ void IgorEngine::handleOptionsMenu() { _system->updateScreen(); _system->delayMillis(1000 / 60); } - if (!_eventQuitGame && _currentPart == kInvalidPart) { - if (_gameVersion == kIdEngDemo100 || _gameVersion == kIdEngDemo110) { - _currentPart = kSharewarePart; - } - } showCursor(); } diff --git a/engines/igor/midi.cpp b/engines/igor/midi.cpp new file mode 100644 index 0000000000..28bf434293 --- /dev/null +++ b/engines/igor/midi.cpp @@ -0,0 +1,362 @@ +/* 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$ + * $Id$ + * + */ + +#include "igor/igor.h" +#include "igor/midi.h" + +namespace Igor { + +MidiParser_CTMF::MidiParser_CTMF() + : _instrumentsCount(0) { + memset(_instruments, 0, sizeof(_instruments)); +} + +void MidiParser_CTMF::decodeHeader(const uint8 *p) { + _instrumentsDataOffset = READ_LE_UINT16(p); p += 2; + _midiDataOffset = READ_LE_UINT16(p); p += 2; + _ticksPerQuarter = READ_LE_UINT16(p); p += 2; + _ticksPerSecond = READ_LE_UINT16(p); p += 2; + p += 22; + _instrumentsCount = READ_LE_UINT16(p); p += 2; + _basicTempo = READ_LE_UINT16(p); p += 2; +} + +void MidiParser_CTMF::decodeAdlibInstrument(struct AdlibInstrument *ins, const uint8 *p) { + ins->chr[kAdlibCarrier] = p[0]; + ins->chr[kAdlibModulator] = p[1]; + ins->scale[kAdlibCarrier] = p[2]; + ins->scale[kAdlibModulator] = p[3]; + ins->attack[kAdlibCarrier] = p[4]; + ins->attack[kAdlibModulator] = p[5]; + ins->sustain[kAdlibCarrier] = p[6]; + ins->sustain[kAdlibModulator] = p[7]; + ins->waveSel[kAdlibCarrier] = p[8]; + ins->waveSel[kAdlibModulator] = p[9]; + ins->feedback = p[10]; +} + +bool MidiParser_CTMF::loadMusic(byte *data, uint32 size) { + if (memcmp(data, "CTMF", 4) == 0 && READ_LE_UINT16(data + 4) == 0x101) { + decodeHeader(data + 6); + assert(_instrumentsCount <= kMaxInstruments); + for (int i = 0; i < _instrumentsCount; ++i) { + decodeAdlibInstrument(&_instruments[i], data + _instrumentsDataOffset + i * 16); + } + // reset parser + _num_tracks = 1; + _tracks[0] = data + _midiDataOffset; + _ppqn = _ticksPerQuarter; + setTempo(500000); + setTrack(0); + return true; + } + return false; +} + +void MidiParser_CTMF::parseNextEvent(EventInfo &info) { + info.start = _position._play_pos; + info.delta = readVLQ(_position._play_pos); + + if ((_position._play_pos[0] & 0xF0) >= 0x80) { + info.event = *_position._play_pos++; + } else { + info.event = _position._running_status; + } + + if ((info.event & 0x80) == 0) { + return; + } + + _position._running_status = info.event; + switch (info.command()) { + case 0x8: // Note Off + case 0x9: // Note On + case 0xB: // Control Mode Change + info.basic.param1 = *_position._play_pos++; + info.basic.param2 = *_position._play_pos++; + if (info.command() == 0x9 && info.basic.param2 == 0) { + info.event = info.channel() | 0x80; // Note Off + } + return; + case 0xC: // Program Change + info.basic.param1 = *(_position._play_pos++); + info.basic.param2 = 0; + return; + case 0xF: + switch (info.event & 15) { + case 0xF: + info.ext.type = *(_position._play_pos++); + info.length = readVLQ(_position._play_pos); + info.ext.data = _position._play_pos; + _position._play_pos += info.length; + return; + } + } + warning("MidiParser_CTMF::parseNextEvent: Unhandled event code %x", info.event); +} + +int AdlibMidiDriver::open() { + MidiDriver_Emulated::open(); + _opl = makeAdlibOPL(getRate()); + memset(_adlibData, 0, sizeof(_adlibData)); + _adlibRhythmMode = false; + for (int i = 0; i < kAdlibChannelsCount; ++i) { + _adlibChannels[i].ch = -1; + _adlibChannels[i].lt = _adlibChannels[i].note = 0; + } + memset(_adlibInstrumentsMappingTable, 0, sizeof(_adlibInstrumentsMappingTable)); + adlibSetupCard(); + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); + return 0; +} + +void AdlibMidiDriver::close() { + _mixer->stopHandle(_mixerSoundHandle); + OPLDestroy(_opl); +} + +void AdlibMidiDriver::send(uint32 b) { + int channel = b & 15; + int cmd = (b >> 4) & 7; + int param1 = (b >> 8) & 255; + int param2 = (b >> 16) & 255; + switch (cmd) { + case 0: + adlibTurnNoteOff(channel, param1); + break; + case 1: + adlibTurnNoteOn(channel, param1, param2); + break; + case 3: + adlibControlChange(channel, param1, param2); + break; + case 4: + adlibProgramChange(channel, param1); + break; + default: + warning("Unhandled cmd %d channel %d (0x%X)", cmd, channel, b); + break; + } +} + +void AdlibMidiDriver::generateSamples(int16 *data, int len) { + memset(data, 0, sizeof(int16) * len); + YM3812UpdateOne(_opl, data, len); +} + +void AdlibMidiDriver::adlibWrite(int port, int value) { + OPLWriteReg(_opl, port, value); + _adlibData[port & 255] = value & 255; +} + +void AdlibMidiDriver::adlibSetupCard() { + for (int i = 0; i < 256; ++i) { + adlibWrite(i, 0); + } + adlibWrite(1, 0x20); + adlibWrite(0xBD, 0xC0); +} + +void AdlibMidiDriver::adlibTurnNoteOff(int channel, int note) { + for (int i = 0; i < kAdlibChannelsCount; ++i) { + if (_adlibChannels[i].ch == channel && _adlibChannels[i].note == note) { + adlibEndNote(i); + _adlibChannels[i].ch = -1; + } + } +} + +void AdlibMidiDriver::adlibTurnNoteOn(int channel, int note, int velocity) { + assert(velocity != 0); + + for (int i = 0; i < kAdlibChannelsCount; ++i) { + if (_adlibChannels[i].ch != -1) { + ++_adlibChannels[i].lt; + } + } + + int ch = -1; + if (!_adlibRhythmMode || channel < 11) { + int maxLt = -1; + int maxCh = -1; + for (int i = 0; i < (_adlibRhythmMode ? 6 : 9); ++i) { + if (_adlibChannels[i].ch == -1) { + ch = i; + break; + } + if (_adlibChannels[i].lt > maxLt) { + maxLt = _adlibChannels[i].lt; + maxCh = i; + } + } + if (ch == -1) { + assert(maxCh != -1); + ch = maxCh; + adlibEndNote(ch); + } + } else { + ch = _adlibPercussionsMappingTable[channel - 11]; + } + + const AdlibInstrument &ins = _adlibInstruments[_adlibInstrumentsMappingTable[channel]]; + if (!_adlibRhythmMode || channel < 12) { + adlibSetupInstrument(ch, ins); + } else { + adlibSetupPercussion(channel, ins); + } + adlibSetupNote(ch, note - 13, velocity); + _adlibChannels[ch].ch = channel; + _adlibChannels[ch].note = note; + _adlibChannels[ch].lt = 0; +} + +void AdlibMidiDriver::adlibSetupInstrument(int channel, const AdlibInstrument &ins) { + adlibWrite(0x20 + _adlibOperatorsTable[channel], ins.chr[kAdlibCarrier]); + adlibWrite(0x23 + _adlibOperatorsTable[channel], ins.chr[kAdlibModulator]); + adlibWrite(0x40 + _adlibOperatorsTable[channel], ins.scale[kAdlibCarrier]); + if ((ins.feedback & 1) == 0) { + adlibWrite(0x43 + _adlibOperatorsTable[channel], ins.scale[kAdlibModulator]); + } else { + adlibWrite(0x43 + _adlibOperatorsTable[channel], 0); + } + adlibWrite(0x60 + _adlibOperatorsTable[channel], ins.attack[kAdlibCarrier]); + adlibWrite(0x63 + _adlibOperatorsTable[channel], ins.attack[kAdlibModulator]); + adlibWrite(0x80 + _adlibOperatorsTable[channel], ins.sustain[kAdlibCarrier]); + adlibWrite(0x83 + _adlibOperatorsTable[channel], ins.sustain[kAdlibModulator]); + adlibWrite(0xE0 + _adlibOperatorsTable[channel], ins.waveSel[kAdlibCarrier]); + adlibWrite(0xE3 + _adlibOperatorsTable[channel], ins.waveSel[kAdlibModulator]); + adlibWrite(0xC0 + channel, ins.feedback); +} + +void AdlibMidiDriver::adlibSetupPercussion(int channel, const AdlibInstrument &ins) { + channel = _adlibChannelsMappingTable[channel - 12]; + adlibWrite(0x20 + channel, ins.chr[kAdlibCarrier]); + adlibWrite(0x40 + channel, ins.scale[kAdlibCarrier]); + adlibWrite(0x60 + channel, ins.attack[kAdlibCarrier]); + adlibWrite(0x80 + channel, ins.sustain[kAdlibCarrier]); + adlibWrite(0xE0 + channel, ins.waveSel[kAdlibCarrier]); + adlibWrite(0xC0 + channel, ins.feedback); +} + +void AdlibMidiDriver::adlibSetupNote(int channel, int note, int velocity) { + adlibSetVolume(channel, velocity); + int f = _adlibNoteFreqTable[note % 12]; + adlibWrite(0xA0 + channel, f); + int oct = note / 12; + int c = ((f & 0x300) >> 8) + (oct << 2); + if (!_adlibRhythmMode || channel < 6) { + c |= 0x20; + } + adlibWrite(0xB0 + channel, c); +} + +void AdlibMidiDriver::adlibEndNote(int channel) { + adlibWrite(0xB0 + channel, _adlibData[0xB0 + channel] & ~0x20); +} + +void AdlibMidiDriver::adlibSetVolume(int channel, int volume) { + volume = 63 - (volume >> 1); + if ((_adlibData[0xC0 + channel] & 1) == 1) { + adlibWrite(0x40 + _adlibOperatorsTable[channel], volume | (_adlibData[0x40 + _adlibOperatorsTable[channel]] & 0xC0)); + } + adlibWrite(0x43 + _adlibOperatorsTable[channel], volume | (_adlibData[0x43 + _adlibOperatorsTable[channel]] & 0xC0)); +} + +void AdlibMidiDriver::adlibControlChange(int channel, int control, int param) { + switch (control) { + case 0x67: + _adlibRhythmMode = param != 0; + if (_adlibRhythmMode) { + adlibWrite(0xBD, _adlibData[0xBD] | 0x20); + } else { + adlibWrite(0xBD, _adlibData[0xBD] & ~0x20); + } + break; + case 0x7B: + adlibTurnNoteOff(channel, -1); + break; + default: + warning("Unhandled adlibControlChange 0x%X %d", control, param); + break; + } +} + +void AdlibMidiDriver::adlibProgramChange(int channel, int num) { + _adlibInstrumentsMappingTable[channel] = num; +} + +const uint8 AdlibMidiDriver::_adlibOperatorsTable[] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; + +const uint8 AdlibMidiDriver::_adlibChannelsMappingTable[] = { 20, 18, 21, 17 }; + +const int16 AdlibMidiDriver::_adlibNoteFreqTable[] = { 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, 686 }; + +const uint8 AdlibMidiDriver::_adlibPercussionsMappingTable[] = { 6, 7, 8, 8, 7 }; + +MidiPlayer::MidiPlayer(IgorEngine *vm) : _isPlaying(false) { + _driver = new AdlibMidiDriver(vm->_mixer); + _driver->open(); + _parser = new MidiParser_CTMF; + _parser->setMidiDriver(_driver); + _parser->setTimerRate(_driver->getBaseTempo()); + _driver->setTimerCallback(this, &MidiPlayer::updateTimerCallback); +} + +MidiPlayer::~MidiPlayer() { + stopMusic(); + _driver->setTimerCallback(0, 0); + _driver->close(); + delete _parser; + delete _driver; +} + +void MidiPlayer::playMusic(uint8 *data, uint32 size) { + stopMusic(); + _mutex.lock(); + _isPlaying = true; + _parser->loadMusic(data, size); + _parser->setTrack(0); + _driver->setInstruments(&_parser->_instruments[0]); + _mutex.unlock(); +} + +void MidiPlayer::stopMusic() { + _mutex.lock(); + if (_isPlaying) { + _isPlaying = false; + _parser->unloadMusic(); + } + _mutex.unlock(); +} + +void MidiPlayer::updateTimer() { + _mutex.lock(); + if (_isPlaying) { + _parser->onTimer(); + } + _mutex.unlock(); +} + +} // namespace Igor diff --git a/engines/igor/midi.h b/engines/igor/midi.h new file mode 100644 index 0000000000..c61ddd9eed --- /dev/null +++ b/engines/igor/midi.h @@ -0,0 +1,167 @@ +/* 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$ + * $Id$ + * + */ + +#ifndef IGOR_MIDI_H +#define IGOR_MIDI_H + +#include "common/util.h" +#include "common/mutex.h" + +#include "sound/fmopl.h" +#include "sound/mididrv.h" +#include "sound/midiparser.h" +#include "sound/softsynth/emumidi.h" + +namespace Igor { + +enum { + kAdlibCarrier = 0, + kAdlibModulator +}; + +struct AdlibInstrument { + uint8 chr[2]; + uint8 scale[2]; + uint8 attack[2]; + uint8 sustain[2]; + uint8 waveSel[2]; + uint8 feedback; +}; + +class MidiParser_CTMF : public MidiParser { +public: + + enum { + kMaxInstruments = 16 + }; + + MidiParser_CTMF(); + ~MidiParser_CTMF() {} + + bool loadMusic(byte *data, uint32 size); + + AdlibInstrument _instruments[kMaxInstruments]; + int _instrumentsCount; + +protected: + + void decodeHeader(const uint8 *p); + void decodeAdlibInstrument(struct AdlibInstrument *ins, const uint8 *p); + + void parseNextEvent(EventInfo &info); + +private: + + int _instrumentsDataOffset; + int _midiDataOffset; + int _ticksPerQuarter; + int _ticksPerSecond; + int _basicTempo; +}; + +class AdlibMidiDriver : public MidiDriver_Emulated { +public: + + enum { + kAdlibChannelsCount = 18 + }; + + AdlibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) {} + ~AdlibMidiDriver() {} + + // MidiDriver + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length) {} + void sysEx(const byte *msg, uint16 length) {} + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + + // AudioStream + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + + // MidiDriver_Emulated + void generateSamples(int16 *buf, int len); + + void setInstruments(AdlibInstrument *i) { _adlibInstruments = i; } + +private: + + void adlibWrite(int port, int value); + void adlibSetupCard(); + void adlibTurnNoteOff(int channel, int note); + void adlibTurnNoteOn(int channel, int note, int velocity); + void adlibSetupInstrument(int channel, const AdlibInstrument &ins); + void adlibSetupPercussion(int channel, const AdlibInstrument &ins); + void adlibSetupNote(int channel, int note, int velocity); + void adlibEndNote(int channel); + void adlibSetVolume(int channel, int volume); + void adlibControlChange(int channel, int control, int param); + void adlibProgramChange(int channel, int num); + + FM_OPL *_opl; + uint8 _adlibData[256]; + bool _adlibRhythmMode; + struct { + int ch; + int note; + int lt; + } _adlibChannels[kAdlibChannelsCount]; + int _adlibInstrumentsMappingTable[kAdlibChannelsCount]; + AdlibInstrument *_adlibInstruments; + + static const uint8 _adlibOperatorsTable[]; + static const uint8 _adlibChannelsMappingTable[]; + static const int16 _adlibNoteFreqTable[]; + static const uint8 _adlibPercussionsMappingTable[]; +}; + +class IgorEngine; + +class MidiPlayer { +public: + + MidiPlayer(IgorEngine *vm); + ~MidiPlayer(); + + void playMusic(uint8 *data, uint32 size); + void stopMusic(); + +private: + + void updateTimer(); + static void updateTimerCallback(void *p) { ((MidiPlayer *)p)->updateTimer(); } + + MidiParser_CTMF *_parser; + AdlibMidiDriver *_driver; + Common::Mutex _mutex; + bool _isPlaying; +}; + +} // namespace Igor + +#endif diff --git a/engines/igor/module.mk b/engines/igor/module.mk index a5d4f0e979..ab75085782 100644 --- a/engines/igor/module.mk +++ b/engines/igor/module.mk @@ -4,6 +4,7 @@ MODULE_OBJS := \ detection.o \ igor.o \ menu.o \ + midi.o \ saveload.o \ staticres.o \ parts/part_04.o \ diff --git a/engines/igor/parts/part_12.cpp b/engines/igor/parts/part_12.cpp index 055ce1eb1e..3b12480b60 100644 --- a/engines/igor/parts/part_12.cpp +++ b/engines/igor/parts/part_12.cpp @@ -32,6 +32,7 @@ static const uint8 PART_12_DATA_ANIM_1[10] = { 0, 1, 2, 1, 2, 1, 2, 1, 2, 3 }; static const uint8 PART_12_DATA_ANIM_2[16] = { 0, 1, 2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 6, 7 }; void IgorEngine::PART_12_EXEC_ACTION(int action) { + debugC(9, kDebugGame, "PART_12_EXEC_ACTION %d", action); switch (action) { case 101: PART_12_ACTION_101(); @@ -141,7 +142,7 @@ void IgorEngine::PART_12_ACTION_104() { _walkData[0].dxPos = 10; _walkData[0].yPosChanged = 1; _walkData[0].dyPos = 3; - _walkData[0].scaleHeight = 1; + _walkData[0].scaleHeight = 50; _walkDataCurrentIndex = 0; _walkDataLastIndex = 1; src = _animFramesBuffer + 0x395B + READ_LE_UINT16(_animFramesBuffer + 0xA448) - 1; @@ -310,7 +311,7 @@ void IgorEngine::PART_12_UPDATE_ROOM_BACKGROUND() { } } -void IgorEngine::PART_12_UPDATE_DIALOGUE_TOBIAS(int action) { +void IgorEngine::PART_12_UPDATE_DIALOGUE_CHURCHMAN(int action) { switch (action) { case kUpdateDialogueAnimEndOfSentence: PART_12_HELPER_8(); @@ -326,7 +327,7 @@ void IgorEngine::PART_12_UPDATE_DIALOGUE_TOBIAS(int action) { void IgorEngine::PART_12_HANDLE_DIALOGUE_CHURCHMAN() { loadDialogueData(DLG_OutsideChurch); - _updateDialogue = &IgorEngine::PART_12_UPDATE_DIALOGUE_TOBIAS; + _updateDialogue = &IgorEngine::PART_12_UPDATE_DIALOGUE_CHURCHMAN; handleDialogue(95, 55, 51, 28, 63); _updateDialogue = 0; } @@ -403,7 +404,7 @@ void IgorEngine::PART_12_HELPER_4() { ADD_DIALOGUE_TEXT(214, 1); ADD_DIALOGUE_TEXT(215, 1); SET_DIALOGUE_TEXT(1, 3); - _updateDialogue = &IgorEngine::PART_12_UPDATE_DIALOGUE_TOBIAS; + _updateDialogue = &IgorEngine::PART_12_UPDATE_DIALOGUE_CHURCHMAN; startCutsceneDialogue(95, 55, 51, 28, 63); waitForEndOfCutsceneDialogue(95, 55, 51, 28, 63); _updateDialogue = 0; diff --git a/engines/igor/parts/part_13.cpp b/engines/igor/parts/part_13.cpp index 0aafdcbbec..24195a5a63 100644 --- a/engines/igor/parts/part_13.cpp +++ b/engines/igor/parts/part_13.cpp @@ -28,6 +28,7 @@ namespace Igor { void IgorEngine::PART_13_EXEC_ACTION(int action) { + debugC(9, kDebugGame, "PART_13_EXEC_ACTION %d", action); switch (action) { case 101: PART_13_ACTION_101_103(); diff --git a/engines/igor/parts/part_14.cpp b/engines/igor/parts/part_14.cpp index 0ff74cbdb5..22cbc7aed5 100644 --- a/engines/igor/parts/part_14.cpp +++ b/engines/igor/parts/part_14.cpp @@ -33,6 +33,7 @@ static int VAR_NEW_CHURCH_MOSAIC_STONE; static int VAR_CURRENT_CHURCH_MOSAIC_STONE; void IgorEngine::PART_14_EXEC_ACTION(int action) { + debugC(9, kDebugGame, "PART_14_EXEC_ACTION %d", action); switch (action) { case 101: PART_14_ACTION_101(); diff --git a/engines/igor/parts/part_16.cpp b/engines/igor/parts/part_16.cpp index d5f2e05ccd..aeff170bc5 100644 --- a/engines/igor/parts/part_16.cpp +++ b/engines/igor/parts/part_16.cpp @@ -30,6 +30,7 @@ namespace Igor { static int VAR_CURRENT_TALKING_ACTOR; void IgorEngine::PART_16_EXEC_ACTION(int action) { + debugC(9, kDebugGame, "PART_16_EXEC_ACTION %d", action); switch (action) { case 101: PART_16_ACTION_101(); diff --git a/engines/igor/parts/part_17.cpp b/engines/igor/parts/part_17.cpp index 45b77b0966..6134c734c9 100644 --- a/engines/igor/parts/part_17.cpp +++ b/engines/igor/parts/part_17.cpp @@ -30,6 +30,7 @@ namespace Igor { static int VAR_CURRENT_TALKING_ACTOR; void IgorEngine::PART_17_EXEC_ACTION(int action) { + debugC(9, kDebugGame, "PART_17_EXEC_ACTION %d", action); switch (action) { case 101: PART_17_ACTION_101(); diff --git a/engines/igor/parts/part_95.cpp b/engines/igor/parts/part_95.cpp index 0abe2d7149..b32751ec23 100644 --- a/engines/igor/parts/part_95.cpp +++ b/engines/igor/parts/part_95.cpp @@ -27,40 +27,142 @@ namespace Igor { +struct SharewareScreenString { + uint8 color; + int y; + const char *str; +}; + +static const SharewareScreenString STR_SHAREWARE[] = { + // 950 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 228, 70, "This is SHAREWARE!" }, + { 228, 81, "You can copy this version!" }, + { 228, 96, "Pass it around, give it to your friends, family," }, + { 228, 107, "colleagues and upload it to your favorite BBS." }, + { 228, 122, "Let everyone enjoy IGOR!" }, + { 228, 161, "To place an order: 1-800-OPTIK-99" }, + // 951 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 228, 42, "Shareware version" }, + { 228, 85, "Order the full IGOR game for only $34.99 US." }, + { 228, 96, "$5.00 for shipping and handling (US & CANADA)." }, + { 228, 107, "Please add $3.00 for international shipping." }, + { 228, 161, "To place an order: 1-800-OPTIK-99" }, + // 952 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 228, 42, "Shareware version" }, + { 228, 70, "90 day limited warranty." }, + { 228, 85, "Please allow 2-4 days for delivery (US only)." }, + { 228, 96, "Elsewhere, up to a week or two..." }, + { 228, 111, "Overnight/second day shipping available an" }, + { 228, 122, "aditional change. Please call for exact pricing." }, + { 228, 161, "To place an order: 1-800-OPTIK-99" }, + // 953 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 228, 42, "Shareware version" }, + { 228, 70, "Three easy ways to order:" }, + { 228, 85, "- Call 1-800-678-4599 (orders only) and use" }, + { 228, 96, "Your Visa, Mastercard or Discover card." }, + { 228, 110, "- Fax your order (please include credit card" }, + { 228, 121, "information) to (412) 381-1031" }, + { 228, 161, "To place an order: 1-800-OPTIK-99" }, + // 954 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 228, 42, "Shareware version" }, + { 228, 74, "- Mail a check or money order to:" }, + { 228, 85, "Optik Software Inc." }, + { 228, 96, "1000 Napor Boulevard" }, + { 228, 107, "Pittsburgh, PA. 15205" }, + { 228, 118, "USA" }, + { 228, 161, "To place an order: 1-800-OPTIK-99" }, + // 955 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 240, 42, "Shareware version" }, + { 240, 79, "Optik Software Inc." }, + { 240, 90, "Orders only: 1-800-OPTIK-99 (67845-99)" }, + { 240, 101, "Fax: (412) 381-1031" }, + { 240, 112, "E-mail: optiksoft\xFA""aol.com" }, + { 240, 161, "To place an order: 1-800-OPTIK-99" }, + // 956 + { 255, 30, "\"Igor. Objective Uikokahonia\"" }, + { 228, 42, "Shareware version" }, + { 228, 64, "A game by" }, + { 228, 80, "PENDULO STUDIOS" }, + { 228, 91, "P.O. Box 21091" }, + { 228, 102, "28009 Madrid" }, + { 228, 113, "Spain" }, + { 228, 128, "E-mail: 100641.1737\xFA""compuserve.com" }, + { 228, 161, "To place an order: 1-800-OPTIK-99" } +}; + void IgorEngine::PART_95() { memset(_currentPalette, 0, 768); setPaletteRange(0, 255); memset(_screenVGA, 0, 64000); + int startStr = -1, endStr = -1; switch (_currentPart) { case 950: loadData(PAL_Shareware1, _paletteBuffer); loadData(IMG_Shareware1, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 0; + endStr = 6; + } break; case 951: loadData(PAL_Shareware2, _paletteBuffer); loadData(IMG_Shareware2, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 7; + endStr = 12; + } break; case 952: loadData(PAL_Shareware3, _paletteBuffer); loadData(IMG_Shareware3, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 13; + endStr = 19; + } break; case 953: loadData(PAL_Shareware4, _paletteBuffer); loadData(IMG_Shareware4, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 20; + endStr = 27; + } break; case 954: loadData(PAL_Shareware5, _paletteBuffer); loadData(IMG_Shareware5, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 28; + endStr = 35; + } break; case 955: loadData(PAL_Shareware6, _paletteBuffer); loadData(IMG_Shareware6, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 36; + endStr = 42; + } break; case 956: loadData(PAL_Shareware7, _paletteBuffer); loadData(IMG_Shareware7, _screenVGA); + if (_gameVersion == kIdEngDemo110) { + startStr = 43; + endStr = 51; + } break; } + for (int i = startStr; i <= endStr; ++i) { + const SharewareScreenString *s = &STR_SHAREWARE[i]; + drawString(_screenVGA, s->str, (320 - getStringWidth(s->str)) / 2, s->y, s->color, 0, 0); + } fadeInPalette(768); for (int i = 0; !_inputVars[kInputEscape] && i < 3000; ++i) { waitForTimer(); diff --git a/engines/igor/parts/part_main.cpp b/engines/igor/parts/part_main.cpp index 09b2454f08..44731b72b8 100644 --- a/engines/igor/parts/part_main.cpp +++ b/engines/igor/parts/part_main.cpp @@ -929,15 +929,6 @@ void IgorEngine::PART_MAIN() { case 904: PART_90(); break; - case 950: - case 951: - case 952: - case 953: - case 954: - case 955: - case 956: - PART_95(); - break; default: warning("PART_MAIN() Unhandled part %d", _currentPart); _currentPart = kInvalidPart; @@ -974,7 +965,12 @@ void IgorEngine::PART_MAIN() { } _gameState.nextMusicCounter = 0; } - } while (_currentPart != 255 && !_eventQuitGame); + } while (_currentPart != kInvalidPart && !_eventQuitGame); + if (_gameVersion == kIdEngDemo100 || _gameVersion == kIdEngDemo110) { + for (_currentPart = kSharewarePart; !_eventQuitGame && _currentPart <= kSharewarePart + 6; ++_currentPart) { + PART_95(); + } + } } } // namespace Igor |