diff options
author | Colin Snover | 2016-12-31 20:39:57 -0600 |
---|---|---|
committer | Colin Snover | 2017-03-27 19:42:31 -0500 |
commit | 31daa956d62b39429cb6638ed3fb549ac488833a (patch) | |
tree | fa831adefae05d82209b3f565055f7b761ca8691 /engines/sci/sound/drivers/midi.cpp | |
parent | 1298762b7665dc1b7aeedf0271eadfb284309ef1 (diff) | |
download | scummvm-rg350-31daa956d62b39429cb6638ed3fb549ac488833a.tar.gz scummvm-rg350-31daa956d62b39429cb6638ed3fb549ac488833a.tar.bz2 scummvm-rg350-31daa956d62b39429cb6638ed3fb549ac488833a.zip |
SCI: Implement bounds-checked reads of game resources
Diffstat (limited to 'engines/sci/sound/drivers/midi.cpp')
-rw-r--r-- | engines/sci/sound/drivers/midi.cpp | 248 |
1 files changed, 125 insertions, 123 deletions
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp index 7b2b102284..b9035b07ea 100644 --- a/engines/sci/sound/drivers/midi.cpp +++ b/engines/sci/sound/drivers/midi.cpp @@ -70,18 +70,18 @@ public: 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); + bool isMt32GmPatch(const SciSpan<const byte> &data); + void readMt32GmPatch(const SciSpan<const byte> &data); + void readMt32Patch(const SciSpan<const byte> &data); void readMt32DrvData(); - void mapMt32ToGm(byte *data, size_t size); + void mapMt32ToGm(const SciSpan<const byte> &data); 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 sendMt32SysEx(const uint32 addr, Common::SeekableReadStream &data, const int len, bool noDelay); + void sendMt32SysEx(const uint32 addr, const SciSpan<const byte> &data, bool noDelay); void setMt32Volume(byte volume); void resetMt32(); @@ -382,8 +382,9 @@ int MidiPlayer_Midi::getVolume() { void MidiPlayer_Midi::setReverb(int8 reverb) { assert(reverb < kReverbConfigNr); - if (_hasReverb && (_reverb != reverb)) - sendMt32SysEx(0x100001, _reverbConfig[reverb], 3, true); + if (_hasReverb && _reverb != reverb) { + sendMt32SysEx(0x100001, SciSpan<const byte>(_reverbConfig[reverb], 3), true); + } _reverb = reverb; } @@ -398,7 +399,9 @@ void MidiPlayer_Midi::playSwitch(bool play) { } } -bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) { +bool MidiPlayer_Midi::isMt32GmPatch(const SciSpan<const byte> &data) { + uint32 size = data.size(); + // WORKAROUND: Some Mac games (e.g. LSL5) may have an extra byte at the // end, so compensate for that here - bug #6725. if (size == 16890) @@ -419,21 +422,21 @@ bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) { // First, check for a GM patch. The presence of MIDI data after the // initial 1153 + 2 bytes indicates a GM patch - if (READ_LE_UINT16(data + 1153) + 1155 == size) + if (data.getUint16LEAt(1153) + 1155U == size) isMt32Gm = true; // Now check for a regular MT-32 patch. Check readMt32Patch() below for // more info. // 491 = 20 + 20 + 20 + 2 + 1 + 11 + 3 * 11 + 256 + 128 byte timbresNr = data[491]; - int pos = 492 + 246 * timbresNr; + uint pos = 492 + 246 * timbresNr; // Patches 49-96 - if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd)) + if (size >= pos + 386 && data.getUint16BEAt(pos) == 0xabcd) pos += 386; // 256 + 128 + 2 // Rhythm key map + partial reserve - if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba)) + if (size >= pos + 267 && data.getUint16BEAt(pos) == 0xdcba) pos += 267; // 256 + 9 + 2 if (size == pos) @@ -445,7 +448,7 @@ bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) { return isMt32Gm; } -void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay = false) { +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream &stream, int len, bool noDelay = false) { if (len + 8 > kMaxSysExSize) { warning("SysEx message exceed maximum size; ignoring"); return; @@ -457,8 +460,7 @@ void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStrea _sysExBuf[5] = (addr >> 8) & 0xff; _sysExBuf[6] = addr & 0xff; - for (int i = 0; i < len; i++) - _sysExBuf[7 + i] = str->readByte(); + stream.read(_sysExBuf + 7, len); for (int i = 4; i < 7 + len; i++) chk -= _sysExBuf[i]; @@ -471,81 +473,82 @@ void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStrea 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::sendMt32SysEx(const uint32 addr, const SciSpan<const byte> &buf, bool noDelay = false) { + Common::MemoryReadStream stream(buf.toStream()); + sendMt32SysEx(addr, stream, buf.size(), noDelay); } -void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { + +void MidiPlayer_Midi::readMt32Patch(const SciSpan<const byte> &data) { // MT-32 patch contents: - // - 20 bytes unkown - // - 20 bytes before-SysEx message - // - 20 bytes goodbye SysEx message - // - 2 bytes volume - // - 1 byte reverb - // - 11 bytes reverb Sysex message - // - 3 * 11 reverb data - // - 256 + 128 bytes patches 1-48 + // - 0-19 after-SysEx message + // - 20-39 before-SysEx message + // - 40-59 goodbye SysEx message + // - 60-61 volume + // - 62 reverb + // - 63-73 reverb Sysex message + // - 74-106 [3 * 11] reverb data + // - 107-490 [256 + 128] patches 1-48 // --> total: 491 bytes - // - 1 byte number of timbres (64 max) - // - 246 * timbres timbre data - // - 2 bytes flag (0xabcd) - // - 256 + 128 bytes patches 49-96 - // - 2 bytes flag (0xdcba) - // - 256 bytes rhythm key map - // - 9 bytes partial reserve + // - 491 number of timbres (64 max) + // - 492..n [246 * number of timbres] timbre data + // - n-n+1 flag (0xabcd) + // - n+2-n+385 [256 + 128] patches 49-96 + // - n+386-n+387 flag (0xdcba) + // - n+388-n+643 rhythm key map + // - n+644-n+652 partial reserve - Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size); + Common::MemoryReadStream stream(data.toStream()); // Send before-SysEx text - str->seek(20); - sendMt32SysEx(0x200000, str, 20); + stream.seek(20); + sendMt32SysEx(0x200000, stream, 20); // Save goodbye message - str->read(_goodbyeMsg, 20); + assert(sizeof(_goodbyeMsg) == 20); + stream.read(_goodbyeMsg, 20); - byte volume = CLIP<uint16>(str->readUint16LE(), 0, 100); + const uint8 volume = MIN<uint16>(stream.readUint16LE(), 100); setMt32Volume(volume); // Reverb default only used in (roughly) SCI0/SCI01 - byte reverb = str->readByte(); + byte reverb = stream.readByte(); _hasReverb = true; // Skip reverb SysEx message - str->seek(11, SEEK_CUR); + stream.seek(11, SEEK_CUR); // Read reverb data (stored vertically - patch #3117434) for (int j = 0; j < 3; ++j) { for (int i = 0; i < kReverbConfigNr; i++) { - _reverbConfig[i][j] = str->readByte(); + _reverbConfig[i][j] = stream.readByte(); } } // Patches 1-48 - sendMt32SysEx(0x50000, str, 256); - sendMt32SysEx(0x50200, str, 128); + sendMt32SysEx(0x50000, stream, 256); + sendMt32SysEx(0x50200, stream, 128); // Timbres - byte timbresNr = str->readByte(); + const uint8 timbresNr = stream.readByte(); for (int i = 0; i < timbresNr; i++) - sendMt32SysEx(0x80000 + (i << 9), str, 246); + sendMt32SysEx(0x80000 + (i << 9), stream, 246); - uint16 flag = str->readUint16BE(); + uint16 flag = stream.readUint16BE(); - if (!str->eos() && (flag == 0xabcd)) { + if (!stream.eos() && flag == 0xabcd) { // Patches 49-96 - sendMt32SysEx(0x50300, str, 256); - sendMt32SysEx(0x50500, str, 128); - flag = str->readUint16BE(); + sendMt32SysEx(0x50300, stream, 256); + sendMt32SysEx(0x50500, stream, 128); + flag = stream.readUint16BE(); } - if (!str->eos() && (flag == 0xdcba)) { + if (!stream.eos() && flag == 0xdcba) { // Rhythm key map - sendMt32SysEx(0x30110, str, 256); + sendMt32SysEx(0x30110, stream, 256); // Partial reserve - sendMt32SysEx(0x100004, str, 9); + sendMt32SysEx(0x100004, stream, 9); } // Reverb for SCI0 @@ -553,16 +556,15 @@ void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { setReverb(reverb); // Send after-SysEx text - str->seek(0); - sendMt32SysEx(0x200000, str, 20); + stream.seek(0); + sendMt32SysEx(0x200000, stream, 20); // Send the mystery SysEx - sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6); - - delete str; + Common::MemoryReadStream mystery((const byte *)"\x16\x16\x16\x16\x16\x16", 6); + sendMt32SysEx(0x52000a, mystery, 6); } -void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { +void MidiPlayer_Midi::readMt32GmPatch(const SciSpan<const byte> &data) { // GM patch contents: // - 128 bytes patch map // - 128 bytes key shift @@ -573,21 +575,21 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { // - 512 bytes velocity map // --> total: 1153 bytes - memcpy(_patchMap, data, 128); - memcpy(_keyShift, data + 128, 128); - memcpy(_volAdjust, data + 256, 128); - memcpy(_percussionMap, data + 384, 128); + data.subspan(0, sizeof(_patchMap)).unsafeCopyDataTo(_patchMap); + data.subspan(128, sizeof(_keyShift)).unsafeCopyDataTo(_keyShift); + data.subspan(256, sizeof(_volAdjust)).unsafeCopyDataTo(_volAdjust); + data.subspan(384, sizeof(_percussionMap)).unsafeCopyDataTo(_percussionMap); _channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[512]; - memcpy(_velocityMapIdx, data + 513, 128); - memcpy(_velocityMap, data + 641, 512); + data.subspan(513, sizeof(_velocityMapIdx)).unsafeCopyDataTo(_velocityMapIdx); + data.subspan(641, sizeof(_velocityMap)).unsafeCopyDataTo(_velocityMap); - uint16 midiSize = READ_LE_UINT16(data + 1153); + uint16 midiSize = data.getUint16LEAt(1153); if (midiSize > 0) { - if (size < midiSize + 1155) + if (data.size() < midiSize + 1155U) error("Failed to read MIDI data"); - const byte *midi = data + 1155; + const SciSpan<const byte> midi = data.subspan(1155, midiSize); byte command = 0; uint i = 0; @@ -599,15 +601,16 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { switch (command & 0xf0) { case 0xf0: { - const byte *sysExEnd = (const byte *)memchr(midi + i, 0xf7, midiSize - i); + const byte *sysExStart = midi.getUnsafeDataAt(i, midiSize - i); + const byte *sysExEnd = (const byte *)memchr(sysExStart, 0xf7, midiSize - i); if (!sysExEnd) error("Failed to find end of sysEx"); - int len = sysExEnd - (midi + i); - sysEx(midi + i, len); + int len = sysExEnd - sysExStart; + sysEx(sysExStart, len); - i += len + 1; // One more for the 0x7f + i += len + 1; // One more for the 0xf7 break; } case 0x80: @@ -656,13 +659,13 @@ void MidiPlayer_Midi::readMt32DrvData() { f.seek(-2, SEEK_CUR); // Send before-SysEx text - sendMt32SysEx(0x200000, &f, 20); + sendMt32SysEx(0x200000, f, 20); if (size != 2271) { // Send after-SysEx text (SSCI sends this before every song). // There aren't any SysEx calls in old drivers, so this can // be sent right after the before-SysEx text. - sendMt32SysEx(0x200000, &f, 20); + sendMt32SysEx(0x200000, f, 20); } else { // Skip the after-SysEx text in the newer patch version, we'll send // it after the SysEx messages are sent. @@ -696,14 +699,14 @@ void MidiPlayer_Midi::readMt32DrvData() { f.skip(2235); // skip driver code // Patches 1-48 - sendMt32SysEx(0x50000, &f, 256); - sendMt32SysEx(0x50200, &f, 128); + sendMt32SysEx(0x50000, f, 256); + sendMt32SysEx(0x50200, f, 128); setReverb(reverb); // Send the after-SysEx text f.seek(0x3d); - sendMt32SysEx(0x200000, &f, 20); + sendMt32SysEx(0x200000, f, 20); } else { byte reverbSysEx[13]; // This old driver should have a full reverb SysEx @@ -775,11 +778,11 @@ uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) { return Mt32Ins.gmInstr; } -void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { +void MidiPlayer_Midi::mapMt32ToGm(const SciSpan<const byte> &data) { // FIXME: Clean this up int memtimbres, patches; uint8 group, number, keyshift, /*finetune,*/ bender_range; - uint8 *patchpointer; + SciSpan<const byte> patchpointer; uint32 pos; int i; @@ -791,10 +794,10 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { for (i = 0; i < 128; i++) _percussionMap[i] = Mt32PresetRhythmKeymap[i]; - memtimbres = *(data + 0x1eb); + memtimbres = data[0x1eb]; pos = 0x1ec + memtimbres * 0xf6; - if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) { + if (data.size() > pos && data.getUint16BEAt(pos) == 0xabcd) { patches = 96; pos += 2 + 8 * 48; } else { @@ -807,18 +810,18 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches.."); for (i = 0; i < patches; i++) { - char name[11]; + Common::String name; if (i < 48) - patchpointer = data + 0x6b + 8 * i; + patchpointer = data.subspan(0x6b + 8 * i); else - patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2; + patchpointer = data.subspan(0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2); - group = *patchpointer; - number = *(patchpointer + 1); - keyshift = *(patchpointer + 2); - //finetune = *(patchpointer + 3); - bender_range = *(patchpointer + 4); + group = patchpointer[0]; + number = patchpointer[1]; + keyshift = patchpointer[2]; + //finetune = patchpointer[3]; + bender_range = patchpointer[4]; debugCN(kDebugLevelSound, " [%03d] ", i); @@ -832,10 +835,9 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { 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); + name = data.getStringAt(0x1ec + number * 0xf6, 10); + _patchMap[i] = lookupGmInstrument(name.c_str()); + debugCN(kDebugLevelSound, "%s -> ", name.c_str()); } else { _patchMap[i] = 0xff; debugCN(kDebugLevelSound, "[Invalid] -> "); @@ -865,21 +867,19 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { _pitchBendRange[i] = CLIP<uint8>(bender_range, 0, 24); } - if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) { + if (data.size() > pos && data.getUint16BEAt(pos) == 0xdcba) { debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion.."); for (i = 0; i < 64; i++) { - number = *(data + pos + 4 * i + 2); + 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); + Common::String name = data.getStringAt(0x1ec + number * 0xf6, 10); + debugCN(kDebugLevelSound, "%s -> ", name.c_str()); + _percussionMap[ins] = lookupGmRhythmKey(name.c_str()); } else { if (number < 94) { debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name); @@ -897,17 +897,19 @@ void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[ins]]); #endif - _percussionVelocityScale[ins] = *(data + pos + 4 * i + 3) * 127 / 100; + _percussionVelocityScale[ins] = data[pos + 4 * i + 3] * 127 / 100; } } } void MidiPlayer_Midi::setMt32Volume(byte volume) { - sendMt32SysEx(0x100016, &volume, 1); + Common::MemoryReadStream s(&volume, 1); + sendMt32SysEx(0x100016, s, 1); } void MidiPlayer_Midi::resetMt32() { - sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true); + Common::MemoryReadStream s((const byte *)"\x01\x00", 2); + sendMt32SysEx(0x7f0000, s, 2, true); // This seems to require a longer delay than usual g_system->delayMillis(150); @@ -937,11 +939,11 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { _percussionVelocityScale[i] = 127; } - Resource *res = NULL; + Resource *res = nullptr; if (g_sci && g_sci->_features->useAltWinGMSound()) { - res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0); - if (!(res && isMt32GmPatch(res->data, res->size))) { + res = resMan->findResource(ResourceId(kResourceTypePatch, 4), false); + if (!res || !isMt32GmPatch(*res)) { // Don't do any mapping when a Windows alternative track is selected // and no MIDI patch is available _useMT32Track = false; @@ -953,15 +955,15 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { // MT-32 resetMt32(); - res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); + res = resMan->findResource(ResourceId(kResourceTypePatch, 1), false); if (res) { - if (isMt32GmPatch(res->data, res->size)) { - readMt32GmPatch(res->data, res->size); + if (isMt32GmPatch(*res)) { + readMt32GmPatch(*res); // Note that _goodbyeMsg is not zero-terminated memcpy(_goodbyeMsg, " ScummVM ", 20); } else { - readMt32Patch(res->data, res->size); + readMt32Patch(*res); } } else { // Early SCI0 games have the sound bank embedded in the MT-32 driver @@ -969,22 +971,22 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { } } else { // General MIDI - res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0); + res = resMan->findResource(ResourceId(kResourceTypePatch, 4), false); - if (res && isMt32GmPatch(res->data, res->size)) { + if (res && isMt32GmPatch(*res)) { // There is a GM patch - readMt32GmPatch(res->data, res->size); + readMt32GmPatch(*res); if (g_sci && g_sci->_features->useAltWinGMSound()) { // Always use the GM track if an alternative GM Windows soundtrack is selected _useMT32Track = false; } else { // Detect the format of patch 1, so that we know what play mask to use - res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); + res = resMan->findResource(ResourceId(kResourceTypePatch, 1), false); if (!res) _useMT32Track = false; else - _useMT32Track = !isMt32GmPatch(res->data, res->size); + _useMT32Track = !isMt32GmPatch(*res); // Check if the songs themselves have a GM track if (!_useMT32Track) { @@ -1013,17 +1015,17 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { _velocityMap[3][i] = 0x20 + (i - 1) / 2; } - res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); + res = resMan->findResource(ResourceId(kResourceTypePatch, 1), false); if (res) { - if (!isMt32GmPatch(res->data, res->size)) { - mapMt32ToGm(res->data, res->size); + if (!isMt32GmPatch(*res)) { + mapMt32ToGm(*res); } else { if (getSciVersion() < SCI_VERSION_3) { error("MT-32 patch has wrong type"); } else { // Happens in the SCI3 interactive demo of Lighthouse - warning("TODO: Ignoring new SCI3 type of MT-32 patch for now (size = %d)", res->size); + warning("TODO: Ignoring new SCI3 type of MT-32 patch for now (size = %lu)", res->size()); } } } else { @@ -1053,7 +1055,7 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) { void MidiPlayer_Midi::close() { if (_isMt32) { // Send goodbye message - sendMt32SysEx(0x200000, _goodbyeMsg, 20, true); + sendMt32SysEx(0x200000, SciSpan<const byte>(_goodbyeMsg, 20), true); } _driver->close(); |