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 /engines | |
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).
Diffstat (limited to 'engines')
-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) |