From db6673c408865b201808e01ab5db796f2e794058 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Tue, 15 Jun 2010 10:38:39 +0000 Subject: AGI: rearranged class methods a bit and cleanup. svn-id: r49758 --- engines/agi/sound_2gs.cpp | 644 +++++++++++++++++++++++----------------------- 1 file changed, 322 insertions(+), 322 deletions(-) (limited to 'engines/agi/sound_2gs.cpp') diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp index 056eac2d5c..cc1cd0f6d5 100644 --- a/engines/agi/sound_2gs.cpp +++ b/engines/agi/sound_2gs.cpp @@ -181,19 +181,6 @@ uint32 SoundGen2GS::mixSound() { return IIGS_BUFFER_SIZE; } -/** - * Convert sample from 8-bit unsigned to 8-bit signed format. - * @param source Source stream containing the 8-bit unsigned sample data. - * @param dest Destination buffer for the 8-bit signed sample data. - * @param length Length of the sample data to be converted. - */ -static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint length) { - // Convert the wave from 8-bit unsigned to 8-bit signed format - for (uint i = 0; i < length; i++) - dest[i] = (int8) ((int) source.readByte() - 128); - return !(source.eos() || source.err()); -} - void SoundGen2GS::fillAudio(int16 *stream, uint len) { uint32 p = 0; @@ -221,220 +208,6 @@ void SoundGen2GS::fillAudio(int16 *stream, uint len) { data_available -= len; } -IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) { - _data = data; // Save the resource pointer - _ptr = _data + 2; // Set current position to just after the header - _len = len; // Save the resource's length - _type = READ_LE_UINT16(data); // Read sound resource's type - _midiTicks = _soundBufTicks = 0; - _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2); - - if (!_isValid) // Check for errors - warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len); -} - -IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) { - Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES); - - // Check that the header was read ok and that it's of the correct type - if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource - uint32 sampleStartPos = stream.pos(); - uint32 tailLen = stream.size() - sampleStartPos; - - if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream - // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes - // of sample data although header says it should have 16384 bytes. - warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left", - resnum, tailLen, _header.sampleSize); - - _header.sampleSize = (uint16) tailLen; // Use the part that's left - } - - if (_header.pitch > 0x7F) { // Check if the pitch is invalid - warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch); - - _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too - } - - // Finalize the header info using the 8-bit unsigned sample data - _header.finalize(stream); - - // Convert sample data from 8-bit unsigned to 8-bit signed format - stream.seek(sampleStartPos); - _sample = new int8[_header.sampleSize]; - - if (_sample != NULL) - _isValid = convertWave(stream, _sample, _header.sampleSize); - } - - if (!_isValid) // Check for errors - warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len); -} - -/** Reads an Apple IIGS envelope from then given stream. */ -bool IIgsEnvelope::read(Common::SeekableReadStream &stream) { - for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) { - seg[segNum].bp = stream.readByte(); - seg[segNum].inc = stream.readUint16LE(); - } - - return !(stream.eos() || stream.err()); -} - -/** Reads an Apple IIGS wave information structure from the given stream. */ -bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) { - top = stream.readByte(); - addr = stream.readByte() * 256; - size = (1 << (stream.readByte() & 7)) * 256; - - // Read packed mode byte and parse it into parts - byte packedModeByte = stream.readByte(); - channel = (packedModeByte >> 4) & 1; // Bit 4 - mode = (packedModeByte >> 1) & 3; // Bits 1-2 - halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean) - - relPitch = stream.readSint16LE(); - - // Zero the wave address if we want to ignore the wave address info - if (ignoreAddr) - addr = 0; - - return !(stream.eos() || stream.err()); -} - -bool IIgsWaveInfo::finalize(Common::SeekableReadStream &uint8Wave) { - uint32 startPos = uint8Wave.pos(); // Save stream's starting position - uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address - - // Calculate the true sample size (A zero ends the sample prematurely) - uint trueSize = size; // Set a default value for the result - for (uint i = 0; i < size; i++) { - if (uint8Wave.readByte() == 0) { - trueSize = i; - // A zero in the sample stream turns off looping - // (At least that's what MESS 0.117 and KEGS32 0.91 seem to do) - if (mode == OSC_MODE_LOOP) - mode = OSC_MODE_ONESHOT; - break; - } - } - size = trueSize; // Set the true sample size - - uint8Wave.seek(startPos); // Seek back to the stream's starting position - - return true; -} - -bool IIgsOscillator::finalize(Common::SeekableReadStream &uint8Wave) { - for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++) - if (!waves[i].finalize(uint8Wave)) - return false; - - return true; -} - -bool IIgsOscillatorList::read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr) { - // First read the A waves and then the B waves for the oscillators - for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++) - for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++) - if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr)) - return false; - - count = oscillatorCount; // Set the oscillator count - - return true; -} - -bool IIgsOscillatorList::finalize(Common::SeekableReadStream &uint8Wave) { - for (uint i = 0; i < count; i++) - if (!osc[i].finalize(uint8Wave)) - return false; - - return true; -} - -bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreAddr) { - env.read(stream); - relseg = stream.readByte(); - /*byte priority =*/ stream.readByte(); // Not needed? 32 in all tested data. - bendrange = stream.readByte(); - vibdepth = stream.readByte(); - vibspeed = stream.readByte(); - /*byte spare =*/ stream.readByte(); // Not needed? 0 in all tested data. - byte wac = stream.readByte(); // Read A wave count - byte wbc = stream.readByte(); // Read B wave count - oscList.read(stream, wac, ignoreAddr); // Read the oscillators - return (wac == wbc) && !(stream.eos() || stream.err()); // A and B wave counts must match -} - -bool IIgsInstrumentHeader::finalize(Common::SeekableReadStream &uint8Wave) { - return oscList.finalize(uint8Wave); -} - -bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) { - type = stream.readUint16LE(); - pitch = stream.readByte(); - unknownByte_Ofs3 = stream.readByte(); - volume = stream.readByte(); - unknownByte_Ofs5 = stream.readByte(); - instrumentSize = stream.readUint16LE(); - sampleSize = stream.readUint16LE(); - // Read the instrument header *ignoring* its wave address info - - return instrument.read(stream, true); -} - -bool IIgsSampleHeader::finalize(Common::SeekableReadStream &uint8Wave) { - return instrument.finalize(uint8Wave); -} - -/** Older Apple IIGS AGI MIDI program change to instrument number mapping. */ -static const MidiProgramChangeMapping progToInstMappingV1 = { - {19, 20, 22, 23, 21, 24, 5, 5, 5, 5, - 6, 7, 10, 9, 11, 9, 15, 8, 5, 5, - 17, 16, 18, 12, 14, 5, 5, 5, 5, 5, - 0, 1, 2, 9, 3, 4, 15, 2, 2, 2, - 25, 13, 13, 25}, - 5 -}; - -/** Newer Apple IIGS AGI MIDI program change to instrument number mapping. */ -static const MidiProgramChangeMapping progToInstMappingV2 = { - {21, 22, 24, 25, 23, 26, 6, 6, 6, 6, - 7, 9, 12, 8, 13, 11, 17, 10, 6, 6, - 19, 18, 20, 14, 16, 6, 6, 6, 6, 6, - 0, 1, 2, 4, 3, 5, 17, 2, 2, 2, - 27, 15, 15, 27}, - 6 -}; - -/** Older Apple IIGS AGI instrument set. Used only by Space Quest I (AGI v1.002). */ -static const InstrumentSetInfo instSetV1 = { - 1192, 26, "7ee16bbc135171ffd6b9120cc7ff1af2", "edd3bf8905d9c238e02832b732fb2e18", progToInstMappingV1 -}; - -/** Newer Apple IIGS AGI instrument set (AGI v1.003+). Used by all others than Space Quest I. */ -static const InstrumentSetInfo instSetV2 = { - 1292, 28, "b7d428955bb90721996de1cbca25e768", "c05fb0b0e11deefab58bc68fbd2a3d07", progToInstMappingV2 -}; - -/** Information about different Apple IIGS AGI executables. */ -static const IIgsExeInfo IIgsExeInfos[] = { - {GID_SQ1, "SQ", 0x1002, 138496, 0x80AD, instSetV1}, - {GID_LSL1, "LL", 0x1003, 141003, 0x844E, instSetV2}, - {GID_AGIDEMO, "DEMO", 0x1005, 141884, 0x8469, instSetV2}, - {GID_KQ1, "KQ", 0x1006, 141894, 0x8469, instSetV2}, - {GID_PQ1, "PQ", 0x1007, 141882, 0x8469, instSetV2}, - {GID_MIXEDUP, "MG", 0x1013, 142552, 0x84B7, instSetV2}, - {GID_KQ2, "KQ2", 0x1013, 143775, 0x84B7, instSetV2}, - {GID_KQ3, "KQ3", 0x1014, 144312, 0x84B7, instSetV2}, - {GID_SQ2, "SQ2", 0x1014, 107882, 0x6563, instSetV2}, - {GID_MH1, "MH", 0x2004, 147678, 0x8979, instSetV2}, - {GID_KQ4, "KQ4", 0x2006, 147652, 0x8979, instSetV2}, - {GID_BC, "BC", 0x3001, 148192, 0x8979, instSetV2}, - {GID_GOLDRUSH, "GR", 0x3003, 148268, 0x8979, instSetV2} -}; - void SoundGen2GS::playSampleSound() { if (_vm->_soundemu != SOUND_EMU_APPLE2GS) { warning("Trying to play a sample but not using Apple IIGS sound emulation mode"); @@ -462,14 +235,6 @@ bool SoundGen2GS::playSampleSound(const IIgsSampleHeader &sampleHeader, const in return true; } -void IIgsMidiChannel::stopSounds() { - // Stops all sounds on this single MIDI channel - for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter) - iter->stop(); - - _gsChannels.clear(); -} - void SoundGen2GS::playMidiSound() { if (_disabledMidi) return; @@ -625,12 +390,6 @@ void SoundGen2GS::removeStoppedSounds() { iter->removeStoppedSounds(); } -void IIgsMidiChannel::removeStoppedSounds() { - for (int i = _gsChannels.size() - 1; i >= 0; i--) - if (!_gsChannels[i].playing()) - _gsChannels.remove_at(i); -} - uint SoundGen2GS::activeSounds() const { uint result = 0; @@ -640,12 +399,206 @@ uint SoundGen2GS::activeSounds() const { return result; } -uint IIgsMidiChannel::activeSounds() const { - uint result = 0; +IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) { + _data = data; // Save the resource pointer + _ptr = _data + 2; // Set current position to just after the header + _len = len; // Save the resource's length + _type = READ_LE_UINT16(data); // Read sound resource's type + _midiTicks = _soundBufTicks = 0; + _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2); - for (const_iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter) - if (!iter->end) - result++; + if (!_isValid) // Check for errors + warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len); +} + +/** + * Convert sample from 8-bit unsigned to 8-bit signed format. + * @param source Source stream containing the 8-bit unsigned sample data. + * @param dest Destination buffer for the 8-bit signed sample data. + * @param length Length of the sample data to be converted. + */ +static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint length) { + // Convert the wave from 8-bit unsigned to 8-bit signed format + for (uint i = 0; i < length; i++) + dest[i] = (int8) ((int) source.readByte() - 128); + return !(source.eos() || source.err()); +} + +IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) { + Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES); + + // Check that the header was read ok and that it's of the correct type + if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource + uint32 sampleStartPos = stream.pos(); + uint32 tailLen = stream.size() - sampleStartPos; + + if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream + // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes + // of sample data although header says it should have 16384 bytes. + warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left", + resnum, tailLen, _header.sampleSize); + + _header.sampleSize = (uint16) tailLen; // Use the part that's left + } + + if (_header.pitch > 0x7F) { // Check if the pitch is invalid + warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch); + + _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too + } + + // Finalize the header info using the 8-bit unsigned sample data + _header.finalize(stream); + + // Convert sample data from 8-bit unsigned to 8-bit signed format + stream.seek(sampleStartPos); + _sample = new int8[_header.sampleSize]; + + if (_sample != NULL) + _isValid = convertWave(stream, _sample, _header.sampleSize); + } + + if (!_isValid) // Check for errors + warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len); +} + +/** Reads an Apple IIGS envelope from then given stream. */ +bool IIgsEnvelope::read(Common::SeekableReadStream &stream) { + for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) { + seg[segNum].bp = stream.readByte(); + seg[segNum].inc = stream.readUint16LE(); + } + + return !(stream.eos() || stream.err()); +} + +/** Reads an Apple IIGS wave information structure from the given stream. */ +bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) { + top = stream.readByte(); + addr = stream.readByte() * 256; + size = (1 << (stream.readByte() & 7)) * 256; + + // Read packed mode byte and parse it into parts + byte packedModeByte = stream.readByte(); + channel = (packedModeByte >> 4) & 1; // Bit 4 + mode = (packedModeByte >> 1) & 3; // Bits 1-2 + halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean) + + relPitch = stream.readSint16LE(); + + // Zero the wave address if we want to ignore the wave address info + if (ignoreAddr) + addr = 0; + + return !(stream.eos() || stream.err()); +} + +bool IIgsWaveInfo::finalize(Common::SeekableReadStream &uint8Wave) { + uint32 startPos = uint8Wave.pos(); // Save stream's starting position + uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address + + // Calculate the true sample size (A zero ends the sample prematurely) + uint trueSize = size; // Set a default value for the result + for (uint i = 0; i < size; i++) { + if (uint8Wave.readByte() == 0) { + trueSize = i; + // A zero in the sample stream turns off looping + // (At least that's what MESS 0.117 and KEGS32 0.91 seem to do) + if (mode == OSC_MODE_LOOP) + mode = OSC_MODE_ONESHOT; + break; + } + } + size = trueSize; // Set the true sample size + + uint8Wave.seek(startPos); // Seek back to the stream's starting position + + return true; +} + +bool IIgsOscillator::finalize(Common::SeekableReadStream &uint8Wave) { + for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++) + if (!waves[i].finalize(uint8Wave)) + return false; + + return true; +} + +bool IIgsOscillatorList::read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr) { + // First read the A waves and then the B waves for the oscillators + for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++) + for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++) + if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr)) + return false; + + count = oscillatorCount; // Set the oscillator count + + return true; +} + +bool IIgsOscillatorList::finalize(Common::SeekableReadStream &uint8Wave) { + for (uint i = 0; i < count; i++) + if (!osc[i].finalize(uint8Wave)) + return false; + + return true; +} + +bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreAddr) { + env.read(stream); + relseg = stream.readByte(); + /*byte priority =*/ stream.readByte(); // Not needed? 32 in all tested data. + bendrange = stream.readByte(); + vibdepth = stream.readByte(); + vibspeed = stream.readByte(); + /*byte spare =*/ stream.readByte(); // Not needed? 0 in all tested data. + byte wac = stream.readByte(); // Read A wave count + byte wbc = stream.readByte(); // Read B wave count + oscList.read(stream, wac, ignoreAddr); // Read the oscillators + return (wac == wbc) && !(stream.eos() || stream.err()); // A and B wave counts must match +} + +bool IIgsInstrumentHeader::finalize(Common::SeekableReadStream &uint8Wave) { + return oscList.finalize(uint8Wave); +} + +bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) { + type = stream.readUint16LE(); + pitch = stream.readByte(); + unknownByte_Ofs3 = stream.readByte(); + volume = stream.readByte(); + unknownByte_Ofs5 = stream.readByte(); + instrumentSize = stream.readUint16LE(); + sampleSize = stream.readUint16LE(); + // Read the instrument header *ignoring* its wave address info + + return instrument.read(stream, true); +} + +bool IIgsSampleHeader::finalize(Common::SeekableReadStream &uint8Wave) { + return instrument.finalize(uint8Wave); +} + +void IIgsMidiChannel::stopSounds() { + // Stops all sounds on this single MIDI channel + for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter) + iter->stop(); + + _gsChannels.clear(); +} + +void IIgsMidiChannel::removeStoppedSounds() { + for (int i = _gsChannels.size() - 1; i >= 0; i--) + if (!_gsChannels[i].playing()) + _gsChannels.remove_at(i); +} + +uint IIgsMidiChannel::activeSounds() const { + uint result = 0; + + for (const_iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter) + if (!iter->end) + result++; return result; } @@ -740,6 +693,129 @@ bool IIgsChannelInfo::playing() { return !this->end; } +/** + * A function object (i.e. a functor) for testing if a Common::FSNode + * object's name is equal (Ignoring case) to a string or to at least + * one of the strings in a list of strings. Can be used e.g. with find_if(). + */ +struct fsnodeNameEqualsIgnoreCase : public Common::UnaryFunction { +// FIXME: This should be replaced; use SearchMan instead + fsnodeNameEqualsIgnoreCase(const Common::StringArray &str) : _str(str) {} + fsnodeNameEqualsIgnoreCase(const Common::String str) { _str.push_back(str); } + bool operator()(const Common::FSNode ¶m) const { + for (Common::StringArray::const_iterator iter = _str.begin(); iter != _str.end(); ++iter) + if (param.getName().equalsIgnoreCase(*iter)) + return true; + return false; + } +private: + Common::StringArray _str; +}; + +bool SoundGen2GS::loadInstruments() { + // Check that the platform is Apple IIGS, as only it uses custom instruments + if (_vm->getPlatform() != Common::kPlatformApple2GS) { + debugC(3, kDebugLevelSound, "Platform isn't Apple IIGS so not loading any instruments"); + return true; + } + + // Get info on the particular Apple IIGS AGI game's executable + const IIgsExeInfo *exeInfo = getIIgsExeInfo((enum AgiGameID) _vm->getGameID()); + if (exeInfo == NULL) { + warning("Unsupported Apple IIGS game, not loading instruments"); + return false; + } + + // List files in the game path + Common::FSList fslist; + Common::FSNode dir(ConfMan.get("path")); + if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) { + warning("Invalid game path (\"%s\"), not loading Apple IIGS instruments", dir.getPath().c_str()); + return false; + } + + // Populate executable filenames list (Long filename and short filename) for searching + Common::StringArray exeNames; + exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS16"); + exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS"); + + // Populate wave filenames list (Long filename and short filename) for searching + Common::StringArray waveNames; + waveNames.push_back("SIERRASTANDARD"); + waveNames.push_back("SIERRAST"); + + // Search for the executable file and the wave file (i.e. check if any of the filenames match) + Common::FSList::const_iterator exeFsnode, waveFsnode; + exeFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(exeNames)); + waveFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(waveNames)); + + // Make sure that we found the executable file + if (exeFsnode == fslist.end()) { + warning("Couldn't find Apple IIGS game executable (%s), not loading instruments", exeNames.begin()->c_str()); + return false; + } + + // Make sure that we found the wave file + if (waveFsnode == fslist.end()) { + warning("Couldn't find Apple IIGS wave file (%s), not loading instruments", waveNames.begin()->c_str()); + return false; + } + + // Set the MIDI program change to instrument number mapping and + // load the instrument headers and their sample data. + // None of the tested SIERRASTANDARD-files have zeroes in them so + // there's no need to check for prematurely ending samples here. + setProgramChangeMapping(&exeInfo->instSet.progToInst); + return loadWaveFile(*waveFsnode, *exeInfo) && loadInstrumentHeaders(*exeFsnode, *exeInfo); +} + +/** Older Apple IIGS AGI MIDI program change to instrument number mapping. */ +static const MidiProgramChangeMapping progToInstMappingV1 = { + {19, 20, 22, 23, 21, 24, 5, 5, 5, 5, + 6, 7, 10, 9, 11, 9, 15, 8, 5, 5, + 17, 16, 18, 12, 14, 5, 5, 5, 5, 5, + 0, 1, 2, 9, 3, 4, 15, 2, 2, 2, + 25, 13, 13, 25}, + 5 +}; + +/** Newer Apple IIGS AGI MIDI program change to instrument number mapping. */ +static const MidiProgramChangeMapping progToInstMappingV2 = { + {21, 22, 24, 25, 23, 26, 6, 6, 6, 6, + 7, 9, 12, 8, 13, 11, 17, 10, 6, 6, + 19, 18, 20, 14, 16, 6, 6, 6, 6, 6, + 0, 1, 2, 4, 3, 5, 17, 2, 2, 2, + 27, 15, 15, 27}, + 6 +}; + +/** Older Apple IIGS AGI instrument set. Used only by Space Quest I (AGI v1.002). */ +static const InstrumentSetInfo instSetV1 = { + 1192, 26, "7ee16bbc135171ffd6b9120cc7ff1af2", "edd3bf8905d9c238e02832b732fb2e18", progToInstMappingV1 +}; + +/** Newer Apple IIGS AGI instrument set (AGI v1.003+). Used by all others than Space Quest I. */ +static const InstrumentSetInfo instSetV2 = { + 1292, 28, "b7d428955bb90721996de1cbca25e768", "c05fb0b0e11deefab58bc68fbd2a3d07", progToInstMappingV2 +}; + +/** Information about different Apple IIGS AGI executables. */ +static const IIgsExeInfo IIgsExeInfos[] = { + {GID_SQ1, "SQ", 0x1002, 138496, 0x80AD, instSetV1}, + {GID_LSL1, "LL", 0x1003, 141003, 0x844E, instSetV2}, + {GID_AGIDEMO, "DEMO", 0x1005, 141884, 0x8469, instSetV2}, + {GID_KQ1, "KQ", 0x1006, 141894, 0x8469, instSetV2}, + {GID_PQ1, "PQ", 0x1007, 141882, 0x8469, instSetV2}, + {GID_MIXEDUP, "MG", 0x1013, 142552, 0x84B7, instSetV2}, + {GID_KQ2, "KQ2", 0x1013, 143775, 0x84B7, instSetV2}, + {GID_KQ3, "KQ3", 0x1014, 144312, 0x84B7, instSetV2}, + {GID_SQ2, "SQ2", 0x1014, 107882, 0x6563, instSetV2}, + {GID_MH1, "MH", 0x2004, 147678, 0x8979, instSetV2}, + {GID_KQ4, "KQ4", 0x2006, 147652, 0x8979, instSetV2}, + {GID_BC, "BC", 0x3001, 148192, 0x8979, instSetV2}, + {GID_GOLDRUSH, "GR", 0x3003, 148268, 0x8979, instSetV2} +}; + /** * Finds information about an Apple IIGS AGI executable based on the game ID. * @return A non-null IIgsExeInfo pointer if successful, otherwise NULL. @@ -840,80 +916,4 @@ bool SoundGen2GS::loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo } } -/** - * A function object (i.e. a functor) for testing if a Common::FSNode - * object's name is equal (Ignoring case) to a string or to at least - * one of the strings in a list of strings. Can be used e.g. with find_if(). - */ -struct fsnodeNameEqualsIgnoreCase : public Common::UnaryFunction { -// FIXME: This should be replaced; use SearchMan instead - fsnodeNameEqualsIgnoreCase(const Common::StringArray &str) : _str(str) {} - fsnodeNameEqualsIgnoreCase(const Common::String str) { _str.push_back(str); } - bool operator()(const Common::FSNode ¶m) const { - for (Common::StringArray::const_iterator iter = _str.begin(); iter != _str.end(); ++iter) - if (param.getName().equalsIgnoreCase(*iter)) - return true; - return false; - } -private: - Common::StringArray _str; -}; - -bool SoundGen2GS::loadInstruments() { - // Check that the platform is Apple IIGS, as only it uses custom instruments - if (_vm->getPlatform() != Common::kPlatformApple2GS) { - debugC(3, kDebugLevelSound, "Platform isn't Apple IIGS so not loading any instruments"); - return true; - } - - // Get info on the particular Apple IIGS AGI game's executable - const IIgsExeInfo *exeInfo = getIIgsExeInfo((enum AgiGameID) _vm->getGameID()); - if (exeInfo == NULL) { - warning("Unsupported Apple IIGS game, not loading instruments"); - return false; - } - - // List files in the game path - Common::FSList fslist; - Common::FSNode dir(ConfMan.get("path")); - if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) { - warning("Invalid game path (\"%s\"), not loading Apple IIGS instruments", dir.getPath().c_str()); - return false; - } - - // Populate executable filenames list (Long filename and short filename) for searching - Common::StringArray exeNames; - exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS16"); - exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS"); - - // Populate wave filenames list (Long filename and short filename) for searching - Common::StringArray waveNames; - waveNames.push_back("SIERRASTANDARD"); - waveNames.push_back("SIERRAST"); - - // Search for the executable file and the wave file (i.e. check if any of the filenames match) - Common::FSList::const_iterator exeFsnode, waveFsnode; - exeFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(exeNames)); - waveFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(waveNames)); - - // Make sure that we found the executable file - if (exeFsnode == fslist.end()) { - warning("Couldn't find Apple IIGS game executable (%s), not loading instruments", exeNames.begin()->c_str()); - return false; - } - - // Make sure that we found the wave file - if (waveFsnode == fslist.end()) { - warning("Couldn't find Apple IIGS wave file (%s), not loading instruments", waveNames.begin()->c_str()); - return false; - } - - // Set the MIDI program change to instrument number mapping and - // load the instrument headers and their sample data. - // None of the tested SIERRASTANDARD-files have zeroes in them so - // there's no need to check for prematurely ending samples here. - setProgramChangeMapping(&exeInfo->instSet.progToInst); - return loadWaveFile(*waveFsnode, *exeInfo) && loadInstrumentHeaders(*exeFsnode, *exeInfo); -} - } // End of namespace Agi -- cgit v1.2.3