aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorGregory Montoir2007-11-18 06:25:23 +0000
committerGregory Montoir2007-11-18 06:25:23 +0000
commit0867e09a8129775de703516ca37108237b5d5c77 (patch)
tree34c092df2fb0db11eed2d478e7c84f880bb75a7a /engines
parentc0cebe93bb189d0aa94ceb9fc9eca26a8b2242e4 (diff)
downloadscummvm-rg350-0867e09a8129775de703516ca37108237b5d5c77.tar.gz
scummvm-rg350-0867e09a8129775de703516ca37108237b5d5c77.tar.bz2
scummvm-rg350-0867e09a8129775de703516ca37108237b5d5c77.zip
- added CTMF music support
- fixed dialogue in PART12 svn-id: r29552
Diffstat (limited to 'engines')
-rw-r--r--engines/igor/igor.cpp37
-rw-r--r--engines/igor/igor.h12
-rw-r--r--engines/igor/menu.cpp5
-rw-r--r--engines/igor/midi.cpp362
-rw-r--r--engines/igor/midi.h167
-rw-r--r--engines/igor/module.mk1
-rw-r--r--engines/igor/parts/part_12.cpp9
-rw-r--r--engines/igor/parts/part_13.cpp1
-rw-r--r--engines/igor/parts/part_14.cpp1
-rw-r--r--engines/igor/parts/part_16.cpp1
-rw-r--r--engines/igor/parts/part_17.cpp1
-rw-r--r--engines/igor/parts/part_95.cpp102
-rw-r--r--engines/igor/parts/part_main.cpp16
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