diff options
author | Martin Kiewitz | 2016-01-27 21:04:02 +0100 |
---|---|---|
committer | Martin Kiewitz | 2016-01-27 21:04:02 +0100 |
commit | a85350aa3fc12e330e556668006def114c625978 (patch) | |
tree | dc585ad9b0ff3077365f8592e628a1d53618739d | |
parent | 3fc63ee72da2be130aa1c48e87dffe6e8a528305 (diff) | |
download | scummvm-rg350-a85350aa3fc12e330e556668006def114c625978.tar.gz scummvm-rg350-a85350aa3fc12e330e556668006def114c625978.tar.bz2 scummvm-rg350-a85350aa3fc12e330e556668006def114c625978.zip |
AGI: fix invalid memory access in Apple IIgs sound
fixes crash in Manhunter 1, when looking at corpse right at the
start. Sound resource is actually corrupt (missing bytes).
-rw-r--r-- | engines/agi/sound_2gs.cpp | 74 | ||||
-rw-r--r-- | engines/agi/sound_2gs.h | 17 |
2 files changed, 52 insertions, 39 deletions
diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp index aa3c4df6df..6495d221d0 100644 --- a/engines/agi/sound_2gs.cpp +++ b/engines/agi/sound_2gs.cpp @@ -400,7 +400,7 @@ void SoundGen2GS::midiNoteOn(int channel, int note, int velocity) { wb++; // Prepare the generator. - generator->osc[0].base = curInstrument->base + curInstrument->wave[0][wa].offset; + generator->osc[0].base = curInstrument->wavetableBase + curInstrument->wave[0][wa].offset; generator->osc[0].size = curInstrument->wave[0][wa].size; generator->osc[0].pd = doubleToFrac(midiKeyToFreq(note, (double)curInstrument->wave[0][wa].tune / 256.0) / (double)_sampleRate); generator->osc[0].p = 0; @@ -409,7 +409,7 @@ void SoundGen2GS::midiNoteOn(int channel, int note, int velocity) { generator->osc[0].swap = curInstrument->wave[0][wa].swap; generator->osc[0].rightChannel = curInstrument->wave[0][wa].rightChannel; - generator->osc[1].base = curInstrument->base + curInstrument->wave[1][wb].offset; + generator->osc[1].base = curInstrument->wavetableBase + curInstrument->wave[1][wb].offset; generator->osc[1].size = curInstrument->wave[1][wb].size; generator->osc[1].pd = doubleToFrac(midiKeyToFreq(note, (double)curInstrument->wave[1][wb].tune / 256.0) / (double)_sampleRate); generator->osc[1].p = 0; @@ -479,7 +479,7 @@ static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint len return !(source.eos() || source.err()); } -IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum) : AgiSound() { +IIgsSample::IIgsSample(uint8 *data, uint32 len, int16 resourceNr) : AgiSound() { Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES); // Check that the header was read ok and that it's of the correct type @@ -490,14 +490,14 @@ IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum) : AgiSound() { 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); + warning("Apple IIGS sample (%d) expected %d bytes, got %d bytes only", + resourceNr, _header.sampleSize, tailLen); _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); + warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resourceNr, _header.pitch); _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too } @@ -508,13 +508,16 @@ IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum) : AgiSound() { if (_sample != NULL) { _isValid = convertWave(stream, _sample, _header.sampleSize); - // Finalize header info using sample data - _header.finalize(_sample); + + if (_isValid) { + // Finalize header info using sample data + _header.finalize(_sample); + } } } if (!_isValid) // Check for errors - warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len); + warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resourceNr, _header.type, len); } @@ -544,12 +547,6 @@ bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreA if (ignoreAddr) wave[i][k].offset = 0; - // Check for samples that extend out of the wavetable. - if (wave[i][k].offset + wave[i][k].size >= SIERRASTANDARD_SIZE) { - warning("Invalid data detected in the instrument set of Apple IIGS AGI. Continuing anyway..."); - wave[i][k].size = SIERRASTANDARD_SIZE - wave[i][k].offset; - } - // Parse the generator mode byte to separate fields. wave[i][k].halt = b & 0x1; // Bit 0 = HALT wave[i][k].loop = !(b & 0x2); // Bit 1 =!LOOP @@ -566,18 +563,35 @@ bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreA return !(stream.eos() || stream.err()); } -bool IIgsInstrumentHeader::finalize(int8 *wavetable) { - // Calculate final pointers to sample data and detect true sample size - // in case the sample ends prematurely. - for (int i = 0; i < 2; i++) - for (int k = 0; k < waveCount[i]; k++) { - base = wavetable; - int8 *p = base + wave[i][k].offset; - uint trueSize; - for (trueSize = 0; trueSize < wave[i][k].size; trueSize++) - if (p[trueSize] == -ZERO_OFFSET) - break; - wave[i][k].size = trueSize; +bool IIgsInstrumentHeader::finalize(int8 *wavetable, uint32 wavetableSize) { + wavetableBase = wavetable; + + // Go through all offsets and sizes and make sure, they point to within wavetable + for (int i = 0; i < 2; i++) { + for (int k = 0; k < waveCount[i]; k++) { + uint32 waveOffset = wave[i][k].offset; + uint32 waveSize = wave[i][k].size; + + if (waveOffset >= wavetableSize) { + error("Apple IIgs sound: sample data points outside of wavetable"); + } + + if ((waveOffset + waveSize) > wavetableSize) { + // size seems to be incorrect + // actually happens for at least Manhunter 1, when looking at corpse at the start + warning("Apple IIgs sound: sample exceeds size of wavetable. sample got cut"); + wave[i][k].size = wavetableSize - waveOffset; + } + + // Detect true sample size in case the sample ends prematurely. + int8 *p = wavetableBase + wave[i][k].offset; + uint32 trueSize; + for (trueSize = 0; trueSize < wave[i][k].size; trueSize++) { + if (p[trueSize] == -ZERO_OFFSET) + break; + } + wave[i][k].size = trueSize; + } } return true; @@ -595,8 +609,8 @@ bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) { return instrument.read(stream, true); } -bool IIgsSampleHeader::finalize(int8 *sample) { - return instrument.finalize(sample); +bool IIgsSampleHeader::finalize(int8 *sampleData) { + return instrument.finalize(sampleData, sampleSize); } //### @@ -773,7 +787,7 @@ bool SoundGen2GS::loadInstrumentHeaders(Common::String &exePath, const IIgsExeIn i + 1, exeInfo.instSet->instCount, exePath.c_str()); break; } - instrument.finalize(_wavetable); + instrument.finalize(_wavetable, SIERRASTANDARD_SIZE); _instruments.push_back(instrument); } diff --git a/engines/agi/sound_2gs.h b/engines/agi/sound_2gs.h index 42415ee6ba..41d5bf2cc6 100644 --- a/engines/agi/sound_2gs.h +++ b/engines/agi/sound_2gs.h @@ -80,8 +80,8 @@ struct IIgsInstrumentHeader { uint8 waveCount[2]; ///< Wave count for both generators struct { uint8 key; ///< Highest MIDI key to use this wave - int offset; ///< Offset of wave data, relative to base - uint size; ///< Wave size + uint32 offset; ///< Offset of wave data, relative to base + uint32 size; ///< Wave size bool halt; ///< Oscillator halted? bool loop; ///< Loop mode? bool swap; ///< Swap mode? @@ -89,7 +89,7 @@ struct IIgsInstrumentHeader { int16 tune; ///< Fine tune in semitones (8.8 fixed point) } wave[2][MAX_OSCILLATOR_WAVES]; - int8* base; ///< Base of wave data + int8* wavetableBase; ///< Base of wave data /** * Read an Apple IIGS instrument header from the given stream. @@ -98,7 +98,7 @@ struct IIgsInstrumentHeader { * @return True if successful, false otherwise. */ bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false); - bool finalize(int8 *); + bool finalize(int8 *wavetable, uint32 wavetableSize); }; struct IIgsSampleHeader { @@ -107,8 +107,8 @@ struct IIgsSampleHeader { uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others. uint8 volume; ///< Current guess: Logarithmic in 6 dB steps uint8 unknownByte_Ofs5; ///< 0 in all tested samples. - uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess. - uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16. + uint16 instrumentSize; ///< 44 in all tested samples. A guess. + uint16 sampleSize; ///< Accurate in all tested samples excluding Manhunter I's sound resource 16. IIgsInstrumentHeader instrument; /** @@ -117,7 +117,7 @@ struct IIgsSampleHeader { * @return True if successful, false otherwise. */ bool read(Common::SeekableReadStream &stream); - bool finalize(int8 *sample); + bool finalize(int8 *sampleData); }; class IIgsGenerator { @@ -165,11 +165,10 @@ public: class IIgsSample : public AgiSound { public: - IIgsSample(uint8 *data, uint32 len, int resnum); + IIgsSample(uint8 *data, uint32 len, int16 resourceNr); ~IIgsSample() { delete[] _sample; } virtual uint16 type() { return _header.type; } const IIgsSampleHeader &getHeader() const { return _header; } - const int8 *getSample() const { return _sample; } protected: IIgsSampleHeader _header; ///< Apple IIGS AGI sample header int8 *_sample; ///< Sample data (8-bit signed format) |