scummvm/engines/sci/sound/midiparser_sci.h
Colin Snover 4946f149b4 SCI: Improve MidiParser_SCI robustness against bad sound resources
1. KQ4 sound 104 has an extra 0xFC (MIDI Stop command/kEndOfTrack)
   at the end of the resource, which causes an out-of-bounds read
   because the filtering loop continues after the first 0xFC and
   unconditionally attempts to read 2 bytes (expecting there to
   always be a delta value + a command, whereas in this file there
   is only another kEndOfTrack command). This is corrected by
   exiting the filtering loop when a kEndOfTrack is encountered
   and there is not enough data remaining in the resource to
   continue reading.

2. KQ5 sound 699 is truncated, which causes the parser to attempt
   to read past the end of the resource. This is addressed by
   adding bounds checks that exit the mix loop early if there is
   no more data available to read. This allows truncated sounds
   to be played as far as possible (previously, trying to read
   truncated resources would result in a fatal error).

3. midiMixChannels allocates an arbitrary amount of raw memory
   for the mixed MIDI sequence, without performing any bounds
   checking when writing to this memory, potentially leading to
   a crash or silent corruption of adjacent memory. This is
   mitigated by using SciSpan instead of a raw pointer for the
   mixed data.

Fixes Trac#9727.
2017-04-16 12:23:35 -05:00

137 lines
3.8 KiB
C++

/* 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.
*
*/
#ifndef SCI_MIDIPARSER_H
#define SCI_MIDIPARSER_H
#include "sci/resource.h"
#include "sci/sound/music.h"
#include "audio/midiparser.h"
/*
Sound drivers info: (from driver cmd0)
AdLib/SB : track 0 , voices 9 , patch 3 ah=1
ProAudioSp: track 0 , voices 9 , patch 3 ah=17
GenerlMIDI: track 7 , voices 32, patch 4 ah=1 SCI1.1
Game Blast: track 9 , voices 12, patch 101 ah=1
MT-32 : track 12, voices 32, patch 1 ah=1
PC Speaker: track 18, voices 1 , patch 0xFF ah=1
Tandy : track 19, voices 3 , patch 101 ah=1
IBM PS/1 : track 19, voices 3 , patch 101 ah=1
*/
namespace Sci {
/**
* An extended standard MIDI (SMF) parser. Sierra used an extra channel
* with special commands for extended functionality and animation syncing.
* Refer to MidiParser_SMF() in /sound/midiparser_smf.cpp for the standard
* MIDI (SMF) parser functionality that the SCI MIDI parser is based on
*/
class MidiParser_SCI : public MidiParser {
public:
MidiParser_SCI(SciVersion soundVersion, SciMusic *music);
~MidiParser_SCI();
void mainThreadBegin();
void mainThreadEnd();
bool loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion);
bool loadMusic(byte *, uint32) {
return false;
}
void sendInitCommands();
void unloadMusic();
void setMasterVolume(byte masterVolume);
void setVolume(byte volume);
void stop() {
_abortParse = true;
allNotesOff();
}
void pause() {
allNotesOff();
if (_resetOnPause)
jumpToTick(0);
}
void allNotesOff();
const SciSpan<const byte> &getMixedData() const { return *_mixedData; }
byte getSongReverb();
void sendFromScriptToDriver(uint32 midi);
void sendToDriver(uint32 midi);
void sendToDriver(byte status, byte firstOp, byte secondOp) {
sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));
}
void remapChannel(int channel, int devChannel);
protected:
void parseNextEvent(EventInfo &info);
bool processEvent(const EventInfo &info, bool fireEvents = true);
void midiMixChannels();
void midiFilterChannels(int channelMask);
byte midiGetNextChannel(long ticker);
void resetStateTracking();
void trackState(uint32 midi);
void sendToDriver_raw(uint32 midi);
SciMusic *_music;
// this is set, when main thread calls us -> we send commands to queue instead to driver
bool _mainThreadCalled;
SciVersion _soundVersion;
Common::SpanOwner<SciSpan<const byte> > _mixedData;
SoundResource::Track *_track;
MusicEntry *_pSnd;
uint32 _loopTick;
byte _masterVolume; // the overall master volume (same for all tracks)
byte _volume; // the global volume of the current track
bool _resetOnPause;
bool _channelUsed[16];
int16 _channelRemap[16];
bool _channelMuted[16];
byte _channelVolume[16];
struct ChannelState {
int8 _modWheel;
int8 _pan;
int8 _patch;
int8 _note;
bool _sustain;
int16 _pitchWheel;
int8 _voices;
};
ChannelState _channelState[16];
};
} // End of namespace Sci
#endif