diff options
87 files changed, 2556 insertions, 1446 deletions
@@ -37,7 +37,7 @@ ScummVM Team Matthew Hoops Filippos Karapetis Pawel Kolodziejski - Walter van Niftrik + Walter van Niftrik - (retired) Kari Salminen Eugene Sandulenko David Symonds - (retired) @@ -141,7 +141,7 @@ ScummVM Team Max Horn - (retired) Filippos Karapetis Martin Kiewitz - Walter van Niftrik + Walter van Niftrik - (retired) Willem Jan Palenstijn Jordi Vilalta Prat Lars Skovlund @@ -5,7 +5,7 @@ For a more comprehensive changelog of the latest experimental code, see: SDL ports: - Added support for OpenGL (GSoC Task). -1.3.1 (????-??-??) +1.3.1 (2011-07-12) General: - Improved audio device detection and fallback. There should be no more silent errors due to invalid audio devices. diff --git a/audio/decoders/aac.cpp b/audio/decoders/aac.cpp index 874062a702..50325dc9f0 100644 --- a/audio/decoders/aac.cpp +++ b/audio/decoders/aac.cpp @@ -28,74 +28,34 @@ #ifdef USE_FAAD #include "common/debug.h" +#include "common/memstream.h" #include "common/stream.h" #include "common/textconsole.h" #include "common/util.h" #include "audio/audiostream.h" +#include "audio/decoders/codec.h" +#include "audio/decoders/raw.h" #include <neaacdec.h> namespace Audio { -class AACStream : public AudioStream { +class AACDecoder : public Codec { public: - AACStream(Common::SeekableReadStream *stream, - DisposeAfterUse::Flag disposeStream, - Common::SeekableReadStream *extraData, + AACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData); - ~AACStream(); + ~AACDecoder(); - int readBuffer(int16 *buffer, const int numSamples); - - bool endOfData() const { return _inBufferPos >= _inBufferSize && !_remainingSamples; } - bool isStereo() const { return _channels == 2; } - int getRate() const { return _rate; } + AudioStream *decodeFrame(Common::SeekableReadStream &stream); private: NeAACDecHandle _handle; byte _channels; unsigned long _rate; - - byte *_inBuffer; - uint32 _inBufferSize; - uint32 _inBufferPos; - - int16 *_remainingSamples; - uint32 _remainingSamplesSize; - uint32 _remainingSamplesPos; - - void init(Common::SeekableReadStream *extraData); }; -AACStream::AACStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeStream, - Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) { - - _remainingSamples = 0; - _inBufferPos = 0; - - init(extraData); - - // Copy all the data to a pointer so it can be passed through - // (At least MPEG-4 chunks shouldn't be large) - _inBufferSize = stream->size(); - _inBuffer = new byte[_inBufferSize]; - stream->read(_inBuffer, _inBufferSize); - - if (disposeStream == DisposeAfterUse::YES) - delete stream; - - if (disposeExtraData == DisposeAfterUse::YES) - delete extraData; -} - -AACStream::~AACStream() { - NeAACDecClose(_handle); - delete[] _inBuffer; - delete[] _remainingSamples; -} - -void AACStream::init(Common::SeekableReadStream *extraData) { +AACDecoder::AACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) { // Open the library _handle = NeAACDecOpen(); @@ -117,59 +77,55 @@ void AACStream::init(Common::SeekableReadStream *extraData) { if (err < 0) error("Could not initialize AAC decoder: %s", NeAACDecGetErrorMessage(err)); -} - -int AACStream::readBuffer(int16 *buffer, const int numSamples) { - int samples = 0; - assert((numSamples % _channels) == 0); + if (disposeExtraData == DisposeAfterUse::YES) + delete extraData; +} - // Dip into our remaining samples pool if it's available - if (_remainingSamples) { - samples = MIN<int>(numSamples, _remainingSamplesSize - _remainingSamplesPos); +AACDecoder::~AACDecoder() { + NeAACDecClose(_handle); +} - memcpy(buffer, _remainingSamples + _remainingSamplesPos, samples * 2); - _remainingSamplesPos += samples; +AudioStream *AACDecoder::decodeFrame(Common::SeekableReadStream &stream) { + // read everything into a buffer + uint32 inBufferPos = 0; + uint32 inBufferSize = stream.size(); + byte *inBuffer = new byte[inBufferSize]; + stream.read(inBuffer, inBufferSize); - if (_remainingSamplesPos == _remainingSamplesSize) { - delete[] _remainingSamples; - _remainingSamples = 0; - } - } + QueuingAudioStream *audioStream = makeQueuingAudioStream(_rate, _channels == 2); // Decode until we have enough samples (or there's no more left) - while (samples < numSamples && !endOfData()) { + while (inBufferPos < inBufferSize) { NeAACDecFrameInfo frameInfo; - uint16 *decodedSamples = (uint16 *)NeAACDecDecode(_handle, &frameInfo, _inBuffer + _inBufferPos, _inBufferSize - _inBufferPos); + void *decodedSamples = NeAACDecDecode(_handle, &frameInfo, inBuffer + inBufferPos, inBufferSize - inBufferPos); if (frameInfo.error != 0) error("Failed to decode AAC frame: %s", NeAACDecGetErrorMessage(frameInfo.error)); - int decodedSampleSize = frameInfo.samples; - int copySamples = (decodedSampleSize > (numSamples - samples)) ? (numSamples - samples) : decodedSampleSize; + byte *buffer = (byte *)malloc(frameInfo.samples * 2); + memcpy(buffer, decodedSamples, frameInfo.samples * 2); - memcpy(buffer + samples, decodedSamples, copySamples * 2); - samples += copySamples; + byte flags = FLAG_16BITS; - // Copy leftover samples for use in a later readBuffer() call - if (copySamples != decodedSampleSize) { - _remainingSamplesSize = decodedSampleSize - copySamples; - _remainingSamples = new int16[_remainingSamplesSize]; - _remainingSamplesPos = 0; - memcpy(_remainingSamples, decodedSamples + copySamples, _remainingSamplesSize * 2); - } + if (_channels == 2) + flags |= FLAG_STEREO; - _inBufferPos += frameInfo.bytesconsumed; +#ifdef SCUMM_LITTLE_ENDIAN + flags |= FLAG_LITTLE_ENDIAN; +#endif + + audioStream->queueBuffer(buffer, frameInfo.samples * 2, DisposeAfterUse::YES, flags); + + inBufferPos += frameInfo.bytesconsumed; } - return samples; + return audioStream; } // Factory function -AudioStream *makeAACStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeStream, - Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) { - - return new AACStream(stream, disposeStream, extraData, disposeExtraData); +Codec *makeAACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) { + return new AACDecoder(extraData, disposeExtraData); } } // End of namespace Audio diff --git a/audio/decoders/aac.h b/audio/decoders/aac.h index efcbcc6f42..c5085fadaa 100644 --- a/audio/decoders/aac.h +++ b/audio/decoders/aac.h @@ -43,23 +43,19 @@ namespace Common { namespace Audio { -class AudioStream; +class Codec; /** - * Create a new AudioStream from the AAC data of an MPEG-4 file in the given stream. + * Create a new Codec for decoding AAC data of an MPEG-4 file in the given stream. * * @note This should *only* be called by our QuickTime/MPEG-4 decoder since it relies * on the MPEG-4 extra data. If you want to decode a file using AAC, go use * makeQuickTimeStream() instead! - * @param stream the SeekableReadStream from which to read the AAC data - * @param disposeStream whether to delete the stream after use * @param extraData the SeekableReadStream from which to read the AAC extra data * @param disposeExtraData whether to delete the extra data stream after use - * @return a new AudioStream, or NULL, if an error occurred + * @return a new Codec, or NULL, if an error occurred */ -AudioStream *makeAACStream( - Common::SeekableReadStream *stream, - DisposeAfterUse::Flag disposeStream, +Codec *makeAACDecoder( Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO); diff --git a/audio/decoders/codec.h b/audio/decoders/codec.h new file mode 100644 index 0000000000..93b6878dee --- /dev/null +++ b/audio/decoders/codec.h @@ -0,0 +1,44 @@ +/* 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 AUDIO_DECODERS_CODEC_H +#define AUDIO_DECODERS_CODEC_H + +namespace Common { + class SeekableReadStream; +} + +namespace Audio { + +class AudioStream; + +class Codec { +public: + Codec() {} + virtual ~Codec() {} + + virtual AudioStream *decodeFrame(Common::SeekableReadStream &data) = 0; +}; + +} // End of namespace Audio + +#endif diff --git a/audio/decoders/qdm2.cpp b/audio/decoders/qdm2.cpp index a178c363b5..ec2911ef20 100644 --- a/audio/decoders/qdm2.cpp +++ b/audio/decoders/qdm2.cpp @@ -28,7 +28,9 @@ #ifdef AUDIO_QDM2_H #include "audio/audiostream.h" +#include "audio/decoders/codec.h" #include "audio/decoders/qdm2data.h" +#include "audio/decoders/raw.h" #include "common/array.h" #include "common/debug.h" @@ -150,19 +152,14 @@ struct RDFTContext { FFTContext fft; }; -class QDM2Stream : public AudioStream { +class QDM2Stream : public Codec { public: - QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); + QDM2Stream(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData); ~QDM2Stream(); - bool isStereo() const { return _channels == 2; } - bool endOfData() const { return _stream->pos() >= _stream->size() && _outputSamples.size() == 0 && _subPacket == 0; } - int getRate() const { return _sampleRate; } - int readBuffer(int16 *buffer, const int numSamples); + AudioStream *decodeFrame(Common::SeekableReadStream &stream); private: - Common::SeekableReadStream *_stream; - // Parameters from codec header, do not change during playback uint8 _channels; uint16 _sampleRate; @@ -204,7 +201,6 @@ private: // I/O data uint8 *_compressedData; float _outputBuffer[1024]; - Common::Array<int16> _outputSamples; // Synthesis filter int16 ff_mpa_synth_window[512]; @@ -285,7 +281,7 @@ private: void qdm2_fft_tone_synthesizer(uint8 sub_packet); void qdm2_calculate_fft(int channel); void qdm2_synthesis_filter(uint8 index); - int qdm2_decodeFrame(Common::SeekableReadStream *in); + bool qdm2_decodeFrame(Common::SeekableReadStream &in, QueuingAudioStream *audioStream); }; // Fix compilation for non C99-compliant compilers, like MSVC @@ -1711,7 +1707,7 @@ void QDM2Stream::initVlc(void) { } } -QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { +QDM2Stream::QDM2Stream(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) { uint32 tmp; int32 tmp_s; int tmp_val; @@ -1719,7 +1715,6 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS debug(1, "QDM2Stream::QDM2Stream() Call"); - _stream = stream; _compressedData = NULL; _subPacket = 0; _superBlockStart = 0; @@ -1906,11 +1901,13 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS initNoiseSamples(); _compressedData = new uint8[_packetSize]; + + if (disposeExtraData == DisposeAfterUse::YES) + delete extraData; } QDM2Stream::~QDM2Stream() { delete[] _compressedData; - delete _stream; } static int qdm2_get_vlc(GetBitContext *gb, VLC *vlc, int flag, int depth) { @@ -3158,30 +3155,30 @@ void QDM2Stream::qdm2_synthesis_filter(uint8 index) _outputBuffer[_channels * i + ch] += (float)(samples[_channels * sub_sampling * i + ch] >> (sizeof(int16)*8-16)); } -int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { - debug(1, "QDM2Stream::qdm2_decodeFrame in->pos(): %d in->size(): %d", in->pos(), in->size()); +bool QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream &in, QueuingAudioStream *audioStream) { + debug(1, "QDM2Stream::qdm2_decodeFrame in.pos(): %d in.size(): %d", in.pos(), in.size()); int ch, i; const int frame_size = (_sFrameSize * _channels); // If we're in any packet but the first, seek back to the first if (_subPacket == 0) - _superBlockStart = in->pos(); + _superBlockStart = in.pos(); else - in->seek(_superBlockStart); + in.seek(_superBlockStart); // select input buffer - if (in->eos() || in->pos() >= in->size()) { + if (in.eos() || in.pos() >= in.size()) { debug(1, "QDM2Stream::qdm2_decodeFrame End of Input Stream"); - return 0; + return false; } - if ((in->size() - in->pos()) < _packetSize) { - debug(1, "QDM2Stream::qdm2_decodeFrame Insufficient Packet Data in Input Stream Found: %d Need: %d", in->size() - in->pos(), _packetSize); - return 0; + if ((in.size() - in.pos()) < _packetSize) { + debug(1, "QDM2Stream::qdm2_decodeFrame Insufficient Packet Data in Input Stream Found: %d Need: %d", in.size() - in.pos(), _packetSize); + return false; } - if (!in->eos()) { - in->read(_compressedData, _packetSize); + if (!in.eos()) { + in.read(_compressedData, _packetSize); debug(1, "QDM2Stream::qdm2_decodeFrame constructed input data"); } @@ -3190,7 +3187,7 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { memset(&_outputBuffer[frame_size], 0, frame_size * sizeof(float)); debug(1, "QDM2Stream::qdm2_decodeFrame cleared outputBuffer"); - if (!in->eos()) { + if (!in.eos()) { // decode block of QDM2 compressed data debug(1, "QDM2Stream::qdm2_decodeFrame decode block of QDM2 compressed data"); if (_subPacket == 0) { @@ -3218,7 +3215,7 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { if (!_hasErrors && _subPacketListC[0].packet != NULL) { error("QDM2 : has errors, and C list is not empty"); - return 0; + return false; } } @@ -3236,6 +3233,12 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { debug(1, "QDM2Stream::qdm2_decodeFrame clip and convert output float[] to 16bit signed samples"); } + if (frame_size == 0) + return false; + + // Prepare a buffer for queuing + uint16 *outputBuffer = (uint16 *)malloc(frame_size * 2); + for (i = 0; i < frame_size; i++) { int value = (int)_outputBuffer[i]; @@ -3244,34 +3247,35 @@ int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { else if (value < -SOFTCLIP_THRESHOLD) value = (value < -HARDCLIP_THRESHOLD) ? -32767 : -_softclipTable[-value - SOFTCLIP_THRESHOLD]; - _outputSamples.push_back(value); + outputBuffer[i] = value; } - return frame_size; -} -int QDM2Stream::readBuffer(int16 *buffer, const int numSamples) { - debug(1, "QDM2Stream::readBuffer numSamples: %d", numSamples); - int32 decodedSamples = _outputSamples.size(); - int32 i; + // Queue the translated buffer to our stream + byte flags = FLAG_16BITS; - while (decodedSamples < numSamples) { - i = qdm2_decodeFrame(_stream); - if (i == 0) - break; // Out Of Decode Frames... - decodedSamples += i; - } + if (_channels == 2) + flags |= FLAG_STEREO; + +#ifdef SCUMM_LITTLE_ENDIAN + flags |= FLAG_LITTLE_ENDIAN; +#endif + + audioStream->queueBuffer((byte *)outputBuffer, frame_size * 2, DisposeAfterUse::YES, flags); + + return true; +} - if (decodedSamples > numSamples) - decodedSamples = numSamples; +AudioStream *QDM2Stream::decodeFrame(Common::SeekableReadStream &stream) { + QueuingAudioStream *audioStream = makeQueuingAudioStream(_sampleRate, _channels == 2); - for (i = 0; i < decodedSamples; i++) - buffer[i] = _outputSamples.remove_at(0); + while (qdm2_decodeFrame(stream, audioStream)) + ; - return decodedSamples; + return audioStream; } -AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { - return new QDM2Stream(stream, extraData); +Codec *makeQDM2Decoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) { + return new QDM2Stream(extraData, disposeExtraData); } } // End of namespace Audio diff --git a/audio/decoders/qdm2.h b/audio/decoders/qdm2.h index c0ec647bfd..f0793e3c1e 100644 --- a/audio/decoders/qdm2.h +++ b/audio/decoders/qdm2.h @@ -26,22 +26,25 @@ #ifndef AUDIO_QDM2_H #define AUDIO_QDM2_H +#include "common/types.h" + namespace Common { class SeekableReadStream; } namespace Audio { -class AudioStream; +class Codec; /** - * Create a new AudioStream from the QDM2 data in the given stream. + * Create a new Codec from the QDM2 data in the given stream. * - * @param stream the SeekableReadStream from which to read the FLAC data - * @param extraData the QuickTime extra data stream - * @return a new AudioStream, or NULL, if an error occurred + * @param extraData the QuickTime extra data stream + * @param disposeExtraData the QuickTime extra data stream + * @return a new Codec, or NULL, if an error occurred */ -AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); +Codec *makeQDM2Decoder(Common::SeekableReadStream *extraData, + DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO); } // End of namespace Audio diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 0ad2821cd5..a39fedc1d6 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -30,6 +30,7 @@ #include "common/textconsole.h" #include "audio/audiostream.h" +#include "audio/decoders/codec.h" #include "audio/decoders/quicktime.h" #include "audio/decoders/quicktime_intern.h" @@ -86,6 +87,9 @@ void QuickTimeAudioDecoder::init() { // Make sure the bits per sample transfers to the sample size if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's')) _tracks[_audioTrackIndex]->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels; + + // Initialize the codec (if necessary) + entry->initCodec(); } } } @@ -217,6 +221,9 @@ void QuickTimeAudioDecoder::setAudioStreamPos(const Timestamp &where) { Audio::QuickTimeAudioDecoder::AudioSampleDesc *entry = (Audio::QuickTimeAudioDecoder::AudioSampleDesc *)_tracks[_audioTrackIndex]->sampleDescs[0]; _audStream = Audio::makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2); + // Reinitialize the codec + entry->initCodec(); + // First, we need to track down what audio sample we need Audio::Timestamp curAudioTime = where.convertToFramerate(_tracks[_audioTrackIndex]->timeScale); uint32 sample = curAudioTime.totalNumberOfFrames(); @@ -266,6 +273,11 @@ QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser: _samplesPerFrame = 0; _bytesPerFrame = 0; _bitsPerSample = 0; + _codec = 0; +} + +QuickTimeAudioDecoder::AudioSampleDesc::~AudioSampleDesc() { + delete _codec; } bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const { @@ -313,7 +325,12 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S if (!stream) return 0; - if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) { + if (_codec) { + // If we've loaded a codec, make sure we use first + AudioStream *audioStream = _codec->decodeFrame(*stream); + delete stream; + return audioStream; + } else if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) { // Fortunately, most of the audio used in Myst videos is raw... uint16 flags = 0; if (_codecTag == MKTAG('r', 'a', 'w', ' ')) @@ -330,24 +347,32 @@ AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::S } else if (_codecTag == MKTAG('i', 'm', 'a', '4')) { // Riven uses this codec (as do some Myst ME videos) return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34); - } else if (_codecTag == MKTAG('m', 'p', '4', 'a')) { - // The 7th Guest iOS uses an MPEG-4 codec -#ifdef USE_FAAD - if (_parentTrack->objectTypeMP4 == 0x40) - return makeAACStream(stream, DisposeAfterUse::YES, _parentTrack->extraData); -#endif -#ifdef AUDIO_QDM2_H - } else if (_codecTag == MKTAG('Q', 'D', 'M', '2')) { - // Myst ME uses this codec for many videos - return makeQDM2Stream(stream, _parentTrack->extraData); -#endif } error("Unsupported audio codec"); - return NULL; } +void QuickTimeAudioDecoder::AudioSampleDesc::initCodec() { + delete _codec; _codec = 0; + + switch (_codecTag) { + case MKTAG('Q', 'D', 'M', '2'): +#ifdef AUDIO_QDM2_H + _codec = makeQDM2Decoder(_parentTrack->extraData); +#endif + break; + case MKTAG('m', 'p', '4', 'a'): +#ifdef USE_FAAD + if (_parentTrack->objectTypeMP4 == 0x40) + _codec = makeAACDecoder(_parentTrack->extraData); +#endif + break; + default: + break; + } +} + /** * A wrapper around QuickTimeAudioDecoder that implements the RewindableAudioStream API */ diff --git a/audio/decoders/quicktime_intern.h b/audio/decoders/quicktime_intern.h index f288d5604b..7ce06b85fe 100644 --- a/audio/decoders/quicktime_intern.h +++ b/audio/decoders/quicktime_intern.h @@ -45,6 +45,7 @@ namespace Common { namespace Audio { class AudioStream; +class Codec; class QueuingAudioStream; class QuickTimeAudioDecoder : public Common::QuickTimeParser { @@ -68,10 +69,12 @@ protected: class AudioSampleDesc : public Common::QuickTimeParser::SampleDesc { public: AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag); + ~AudioSampleDesc(); bool isAudioCodecSupported() const; uint32 getAudioChunkSampleCount(uint chunk) const; AudioStream *createAudioStream(Common::SeekableReadStream *stream) const; + void initCodec(); // TODO: Make private in the long run uint16 _bitsPerSample; @@ -79,6 +82,8 @@ protected: uint32 _sampleRate; uint32 _samplesPerFrame; uint32 _bytesPerFrame; + + Codec *_codec; }; // Common::QuickTimeParser API diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp index b71c02f991..27f02c9053 100644 --- a/audio/mididrv.cpp +++ b/audio/mididrv.cpp @@ -128,7 +128,8 @@ Common::String MidiDriver::getDeviceString(DeviceHandle handle, DeviceStringType MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { // Query the selected music device (defaults to MT_AUTO device). - DeviceHandle hdl = getDeviceHandle(ConfMan.get("music_driver")); + Common::String selDevStr = ConfMan.hasKey("music_driver") ? ConfMan.get("music_driver") : Common::String("auto"); + DeviceHandle hdl = getDeviceHandle(selDevStr.empty() ? Common::String("auto") : selDevStr); DeviceHandle reslt = 0; _forceTypeMT32 = false; @@ -200,7 +201,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { if (getMusicType(hdl) == MT_INVALID) { // If the expressly selected driver or device cannot be found (no longer compiled in, turned off, etc.) // we display a warning and continue. - failedDevStr = ConfMan.get("music_driver"); + failedDevStr = selDevStr; Common::String warningMsg = Common::String::format(_("The selected audio device '%s' was not found (e.g. might be turned off or disconnected). Attempting to fall back to the next available device..."), failedDevStr.c_str()); GUI::MessageDialog dialog(warningMsg); dialog.runModal(); @@ -230,13 +231,15 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) { // If a preferred MT32 or GM device has been selected that device gets returned if available. Common::String devStr; if (flags & MDT_PREFER_MT32) - devStr = ConfMan.get("mt32_device"); + devStr = ConfMan.hasKey("mt32_device") ? ConfMan.get("mt32_device") : Common::String("null"); else if (flags & MDT_PREFER_GM) - devStr = ConfMan.get("gm_device"); + devStr = ConfMan.hasKey("gm_device") ? ConfMan.get("gm_device") : Common::String("null"); else devStr = "auto"; - - hdl = getDeviceHandle(devStr); + + // Default to Null device here, since we also register a default null setting for + // the MT32 or GM device in the config manager. + hdl = getDeviceHandle(devStr.empty() ? Common::String("null") : devStr); const MusicType type = getMusicType(hdl); // If we have a "Don't use GM/MT-32" setting we skip this part and jump diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.cpp b/audio/softsynth/fmtowns_pc98/towns_midi.cpp index e415a0dda5..d69ed73ee6 100644 --- a/audio/softsynth/fmtowns_pc98/towns_midi.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_midi.cpp @@ -464,9 +464,10 @@ int TownsMidiOutputChannel::advanceEffectEnvelope(EffectEnvelope *s, EffectDef * s->currentLevel = t; s->modWheelLast = s->modWheelState; t = getEffectModLevel(t, s->modWheelState); - if (t != d->phase) + if (t != d->phase) { d->phase = t; - retFlags |= 1; + retFlags |= 1; + } } if (--s->stepCounter) diff --git a/backends/platform/gph/devices/gp2x/mmuhack/readme.txt b/backends/platform/gph/devices/gp2x/mmuhack/README index bea49d7d6d..6db7d81845 100644 --- a/backends/platform/gph/devices/gp2x/mmuhack/readme.txt +++ b/backends/platform/gph/devices/gp2x/mmuhack/README @@ -1,3 +1,10 @@ +PLEASE NOTE: + +The binary object 'mmuhack.o' is stored in the source tree as it is very awkward to +build it manually each time and would require the use of 2 toolchains to do so. + +Notes on how to rebuild from the included source can be found below. + About ----- @@ -107,4 +114,3 @@ Credits Original idea/implementation: Squidge (this whole thing is also known as squidgehack) Kernel module: NK Documentation: notaz - diff --git a/backends/platform/openpandora/build/PXML.xml b/backends/platform/openpandora/build/PXML.xml index f4d2e2a595..a87c49e2b8 100755 --- a/backends/platform/openpandora/build/PXML.xml +++ b/backends/platform/openpandora/build/PXML.xml @@ -1,34 +1,55 @@ <?xml version="1.0" encoding="UTF-8"?> <PXML xmlns="http://openpandora.org/namespaces/PXML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="PXML_schema.xsd"> - - <application id="scummvm.djwillis.0001" appdata="scummvm"> - - <title lang="en_US">ScummVM</title> - - <exec command="./runscummvm.sh"/> - <icon src="icon/scummvm.png"/> - - <previewpics> - <pic src="icon/preview-pic.png"/> - </previewpics> - - <info name="ScummVM Documentation" type="text/html" src="docs/index.html"/> - - <description lang="en_US">Point & click game interpreter.</description> - - <author name="DJWillis" website="http://www.scummvm.org/"/> - - <version major="1" minor="1" release="1" build="1"/><!--This programs version--> - <osversion major="1" minor="0" release="0" build="0"/><!--The minimum OS version required--> - - <categories> - <category name="Game"><!--category like "Games", "Graphics", "Internet" etc--> - <subcategory name="Adventure Games"/><!--subcategory, like "Board Games", "Strategy", "First Person Shooters"--> - </category> - </categories> - - <clockspeed frequency="500"/><!--Frequency in Hz--> - - </application> - + <!-- This is the package, in our case ScummVM --> + <package id="scummvm.djwillis.0001"> + <author name="DJWillis" website="http://www.scummvm.org/"/> + <!-- version type can be alpha, beta or release, set to release in branch --> + <version major="1" minor="4" release="0" build="1" type="alpha"/> + <!-- Both title and titles are needed --> + <title lang="en_US">ScummVM</title> + <titles> + <title lang="en_US">ScummVM</title> + </titles> + <descriptions> + <description lang="en_US"> + ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed! + + ScummVM supports many adventure games, including LucasArts SCUMM games (such as Monkey Island 1-3, Day of the Tentacle, Sam & Max, ...), many of Sierra's AGI and SCI games (such as King's Quest 1-6, Space Quest 1-5, ...), Discworld 1 and 2, Simon the Sorcerer 1 and 2, Beneath A Steel Sky, Lure of the Temptress, Broken Sword 1 and 2, Flight of the Amazon Queen, Gobliiins 1-3, The Legend of Kyrandia 1-3, many of Humongous Entertainment's children's SCUMM games (including Freddi Fish and Putt Putt games) and many more. + </description> + </descriptions> + <icon src="icon/scummvm.png"/> + </package> + + <!-- This is the application, the ScummVM binary --> + <application id="scummvm.djwillis.0001" appdata="scummvm"> + <exec command="./runscummvm.sh"/> + <author name="DJWillis" website="http://www.scummvm.org/"/> + <!-- version type can be alpha, beta or release, set to release in branch --> + <version major="1" minor="4" release="0" build="1" type="alpha"/> + <!-- Both title and titles are needed --> + <title lang="en_US">ScummVM</title> + <titles> + <title lang="en_US">ScummVM</title> + </titles> + <descriptions> + <description lang="en_US"> + ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed! + + ScummVM supports many adventure games, including LucasArts SCUMM games (such as Monkey Island 1-3, Day of the Tentacle, Sam & Max, ...), many of Sierra's AGI and SCI games (such as King's Quest 1-6, Space Quest 1-5, ...), Discworld 1 and 2, Simon the Sorcerer 1 and 2, Beneath A Steel Sky, Lure of the Temptress, Broken Sword 1 and 2, Flight of the Amazon Queen, Gobliiins 1-3, The Legend of Kyrandia 1-3, many of Humongous Entertainment's children's SCUMM games (including Freddi Fish and Putt Putt games) and many more. + </description> + </descriptions> + <licenses> + <license name="GPLv2" url="http://www.gnu.org/licenses/gpl-2.0.html" sourcecodeurl="http://www.scummvm.org"/> + </licenses> + <icon src="icon/scummvm.png"/> + <previewpics> + <pic src="icon/preview-pic.png"/> + </previewpics> + <info name="ScummVM Documentation" type="text/html" src="docs/index.html"/> + <categories> + <category name="Game"> + <subcategory name="AdventureGame"/> + </category> + </categories> + </application> </PXML> diff --git a/backends/platform/openpandora/build/PXML_schema.xsd b/backends/platform/openpandora/build/PXML_schema.xsd new file mode 100644 index 0000000000..335efe5002 --- /dev/null +++ b/backends/platform/openpandora/build/PXML_schema.xsd @@ -0,0 +1,341 @@ +<?xml version="1.0" encoding="utf-8"?> +<xs:schema targetNamespace="http://openpandora.org/namespaces/PXML" xmlns="http://openpandora.org/namespaces/PXML" xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified"> + + + <!-- declare some simpleTypes for later usage --> + + <!-- Specify params allows with the 'x11' entry in exec --> + <xs:simpleType name="x11Param"> + <xs:restriction base="xs:string"> + <xs:enumeration value="req" /> + <xs:enumeration value="stop" /> + <xs:enumeration value="ignore" /> + </xs:restriction> + </xs:simpleType> + + <!-- Specify the valid documentation formats in the <info> block --> + <xs:simpleType name="docType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="text/html" /> + <xs:enumeration value="text/plain" /> + </xs:restriction> + </xs:simpleType> + + <!-- Make sure that version numbers only consist of letters, numbers and + as well as - --> + <xs:simpleType name="versionNumber"> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value="[a-zA-Z0-9+-]*" /> + </xs:restriction> + </xs:simpleType> + + <!-- Specify what is valid as release type --> + <xs:simpleType name="releaseType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="alpha" /> + <xs:enumeration value="beta" /> + <xs:enumeration value="release" /> + </xs:restriction> + </xs:simpleType> + + <!-- Specify what makes an email address "valid" --> + <xs:simpleType name="emailAddress"> + <xs:restriction base="xs:string"> + <xs:pattern value="[^@]+@[^\.]+\..+"/> + </xs:restriction> + </xs:simpleType> + + <!-- some restrictions regarding file names that are eg not allowed/possible when using sd cards formated as fat32 --> + <xs:simpleType name="dumbPath"> + <xs:restriction base="xs:normalizedString"> + <xs:pattern value="[^?>:]+" /> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="dumbFolderName"> + <xs:restriction base="xs:normalizedString"> + <xs:pattern value="[^?>:/]+" /> + </xs:restriction> + </xs:simpleType> + + <!-- Specify lang codes --> + <xs:simpleType name="isoLangcode"> + <xs:restriction base="xs:string"> + <xs:minLength value="2"/> + <xs:pattern value="[a-zA-Z]{2,3}(_[a-zA-Z0-9]{2,3})*" /> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="isoLangcode_en_US"> + <xs:restriction base="xs:string"> + <xs:enumeration value="en_US" /> + </xs:restriction> + </xs:simpleType> + + <!-- Definition of all allowed categories following the FDO specs --> + <xs:simpleType name="fdoCategory"> + <xs:restriction base="xs:string"> + <xs:pattern value="AudioVideo|Audio|Video|Development|Education|Game|Graphics|Network|Office|Settings|System|Utility"/> + </xs:restriction> + </xs:simpleType> + <!-- Definition of all allowed subcategories following the FDO specs (should be based upon the given main categories, but would significantly increase complexity of the schema) --> + <xs:simpleType name="fdoSubCategory"> + <xs:restriction base="xs:string"> + <xs:pattern value="Building|Debugger|IDE|GUIDesigner|Profiling|RevisionControl|Translation|Calendar|ContactManagement|Database|Dictionary|Chart|Email|Finance|FlowChart|PDA|ProjectManagement|Presentation|Spreadsheet|WordProcessor|2DGraphics|VectorGraphics|RasterGraphics|3DGraphics|Scanning|OCR|Photography|Publishing|Viewer|TextTools|DesktopSettings|HardwareSettings|Printing|PackageManager|Dialup|InstantMessaging|Chat|IRCClient|FileTransfer|HamRadio|News|P2P|RemoteAccess|Telephony|TelephonyTools|VideoConference|WebBrowser|WebDevelopment|Midi|Mixer|Sequencer|Tuner|TV|AudioVideoEditing|Player|Recorder|DiscBurning|ActionGame|AdventureGame|ArcadeGame|BoardGame|BlocksGame|CardGame|KidsGame|LogicGame|RolePlaying|Simulation|SportsGame|StrategyGame|Art|Construction|Music|Languages|Science|ArtificialIntelligence|Astronomy|Biology|Chemistry|ComputerScience|DataVisualization|Economy|Electricity|Geography|Geology|Geoscience|History|ImageProcessing|Literature|Math|NumericalAnalysis|MedicalSoftware|Physics|Robotics|Sports|ParallelComputing|Amusement|Archiving|Compression|Electronics|Emulator|Engineering|FileTools|FileManager|TerminalEmulator|Filesystem|Monitor|Security|Accessibility|Calculator|Clock|TextEditor|Documentation|Core|KDE|GNOME|GTK|Qt|Motif|Java|ConsoleOnly"/> + </xs:restriction> + </xs:simpleType> + + <!-- Create some way to enforce entries to be nonempty --> + <xs:simpleType name="nonempty_token"> + <xs:restriction base="xs:token"> + <xs:minLength value="1"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="nonempty_string"> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="nonempty_normalizedString"> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + </xs:restriction> + </xs:simpleType> + + + + <!-- declare some complexTypes for later usage --> + + <!-- type used for file associations --> + <xs:complexType name="association_data"> + <xs:attribute name="name" use="required" type="nonempty_normalizedString" /> + <xs:attribute name="filetype" use="required" type="nonempty_token" /> + <xs:attribute name="exec" use="required" type="nonempty_token" /> + </xs:complexType> + + <!-- type used for author info --> + <xs:complexType name="author_data"> + <xs:attribute name="name" use="required" type="nonempty_normalizedString" /> + <xs:attribute name="website" use="optional" type="xs:anyURI" /> + <xs:attribute name="email" use="optional" type="emailAddress" /> + </xs:complexType> + + <!-- type used for version informations (full entry as well as os version) --> + <xs:complexType name="app_version_info"> + <xs:attribute name="major" use="required" type="versionNumber" /> + <xs:attribute name="minor" use="required" type="versionNumber" /> + <xs:attribute name="release" use="required" type="versionNumber" /> + <xs:attribute name="build" use="required" type="versionNumber" /> + <xs:attribute name="type" use="optional" type="releaseType" /> + </xs:complexType> + <xs:complexType name="os_version_info"> + <xs:attribute name="major" use="required" type="versionNumber" /> + <xs:attribute name="minor" use="required" type="versionNumber" /> + <xs:attribute name="release" use="required" type="versionNumber" /> + <xs:attribute name="build" use="required" type="versionNumber" /> + </xs:complexType> + + <!-- type used for exec entries --> + <xs:complexType name="exec_params"> + <xs:attribute name="command" use="required" type="nonempty_token" /> + <xs:attribute name="arguments" use="optional" type="nonempty_token" /> + <xs:attribute name="background" use="optional" type="xs:boolean" /> + <xs:attribute name="startdir" use="optional" type="dumbPath" /> + <xs:attribute name="standalone" use="optional" type="xs:boolean" /> + <xs:attribute name="x11" use="optional" type="x11Param" /> + </xs:complexType> + + <!-- type used for tiles or descriptions, once in 'normal' version, once enforcing usage of en_US --> + <xs:complexType name="title_or_description"> + <xs:simpleContent> + <xs:extension base="nonempty_string"> + <xs:attribute name="lang" use="required" type="isoLangcode" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + <xs:complexType name="title_or_description_enUS"> + <xs:simpleContent> + <xs:extension base="nonempty_string"> + <xs:attribute name="lang" use="required" type="isoLangcode_en_US" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <!-- type used for referencing images --> + <xs:complexType name="image_entry"> + <xs:attribute name="src" use="required" type="dumbPath" /> + </xs:complexType> + + <!-- type for referencing manuals/readme docs --> + <xs:complexType name="information_entry"> + <xs:attribute name="name" use="required" type="nonempty_normalizedString" /> + <xs:attribute name="type" use="required" type="docType" /> + <xs:attribute name="src" use="required" type="dumbPath" /> + </xs:complexType> + + <!-- type used for the license information --> + <xs:complexType name="license_info"> + <xs:attribute name="name" use="required" type="nonempty_normalizedString" /> + <xs:attribute name="url" use="optional" type="xs:anyURI" /> + <xs:attribute name="sourcecodeurl" use="optional" type="xs:anyURI" /> + </xs:complexType> + + + + <!-- Combine the symple and complex types into the "real" PXML specification --> + + <xs:element name="PXML"> + <xs:complexType> + <xs:sequence> + <!-- specify the <package> tag with info about the complete package, information providable: + author + version + title(s) + description(s) + icon + --> + <xs:element name="package" minOccurs="1" maxOccurs="1"> + <xs:complexType> + <xs:all> + <!--Author info--> + <xs:element name="author" type="author_data" minOccurs="1" /> + <!--App version info--> + <xs:element name="version" type="app_version_info" minOccurs="1" /> + <!--Title--> + <xs:element name="titles" minOccurs="1"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="title_or_description_enUS" minOccurs="1" maxOccurs="1" /> + <xs:element name="title" type="title_or_description" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Description--> + <xs:element name="descriptions" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="title_or_description_enUS" minOccurs="0" maxOccurs="1" /> + <xs:element name="description" type="title_or_description" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Icon--> + <xs:element name="icon" type="image_entry" minOccurs="0" /> + </xs:all> + <!--Package ID--> + <xs:attribute name="id" use="required" type="dumbFolderName" /> + </xs:complexType> + </xs:element> + <!-- specify the <application> tag with info about a single program + executable call + author + version (of the application) + osversion (min OS version supported) + title(s) (allowing compatibility to <HF6, too!) + description(s) (allowing compatibility to <HF6, too!) + icon + license + preview pictures + info/manual/readme entry + categories + associations to file types + clockspeed + --> + <xs:element name="application" minOccurs="1" maxOccurs="unbounded"> + <xs:complexType> + <xs:all> + <!--Execution params --> + <xs:element name="exec" type="exec_params" minOccurs="1" /> + <!--Author info--> + <xs:element name="author" type="author_data" minOccurs="1" /> + <!--App version info--> + <xs:element name="version" type="app_version_info" minOccurs="1" /> + <!--OS Version info--> + <xs:element name="osversion" type="os_version_info" minOccurs="0" /> + <!--Title--> + <!-- via <titles> element, used for HF6+ --> + <xs:element name="titles" minOccurs="1"> + <xs:complexType> + <xs:sequence> + <xs:element name="title" type="title_or_description_enUS" minOccurs="1" maxOccurs="1" /> + <xs:element name="title" type="title_or_description" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Title--> + <!-- via <title> element, only one for en_US allowed, meant for backwards compatibility with libpnd from <HF6 --> + <xs:element name="title" type="title_or_description_enUS" minOccurs="0" /> + <!--Description--> + <!-- via <descriptions> element, used for HF6+ --> + <xs:element name="descriptions" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="description" type="title_or_description_enUS" minOccurs="1" maxOccurs="1" /> + <xs:element name="description" type="title_or_description" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Description--> + <!-- via <description> element, only one for en_US allowed, meant for backwards compatibility with libpnd from <HF6 --> + <xs:element name="description" type="title_or_description_enUS" minOccurs="0" /> + <!--Icon--> + <xs:element name="icon" type="image_entry" minOccurs="0" /> + <!--License--> + <xs:element name="licenses" minOccurs="1"> + <xs:complexType> + <xs:sequence> + <xs:element name="license" type="license_info" minOccurs="1" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Preview pics--> + <xs:element name="previewpics" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="pic" type="image_entry" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Info (aka manual or readme entry)--> + <xs:element name="info" type="information_entry" minOccurs="0" /> + <!--Categories--> + <xs:element name="categories" minOccurs="1"> + <xs:complexType> + <xs:sequence> + <xs:element name="category" minOccurs="1" maxOccurs="unbounded"> + <xs:complexType> + <xs:sequence> + <xs:element name="subcategory" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="name" type="fdoSubCategory" /> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" use="required" type="fdoCategory" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Associations--> + <xs:element name="associations" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="association" type="association_data" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <!--Clockspeed--> + <xs:element name="clockspeed" minOccurs="0"> + <xs:complexType> + <xs:attribute name="frequency" use="required" type="xs:positiveInteger" /> + </xs:complexType> + </xs:element> + </xs:all> + <!--AppID--> + <xs:attribute name="id" use="required" type="dumbFolderName" /> + <xs:attribute name="appdata" use="optional" type="dumbFolderName" /> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema>
\ No newline at end of file diff --git a/backends/platform/openpandora/build/pnd_make.sh b/backends/platform/openpandora/build/pnd_make.sh index b19de87bb4..0c03e8154d 100755 --- a/backends/platform/openpandora/build/pnd_make.sh +++ b/backends/platform/openpandora/build/pnd_make.sh @@ -1,65 +1,321 @@ -#!/bin/sh +#!/bin/bash +# +# pnd_make.sh +# +# This script is meant to ease generation of a pnd file. Please consult the output +# when running --help for a list of available parameters and an explaination of +# those. +# +# Required tools when running the script: +# bash +# echo, cat, mv, rm +# mkisofs or mksquashfs (the latter when using the -c param!) +# xmllint (optional, only for validation of the PXML against the schema) -######adjust path of genpxml.sh if you want to use that "feture"##### -TEMP=`getopt -o p:d:x:i:c -- "$@"` +PXML_schema=$(dirname ${0})/PXML_schema.xsd +GENPXML_PATH=$(dirname ${0})/genpxml.sh -if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi +# useful functions ... +black='\E[30m' +red='\E[31m' +green='\E[32m' +yellow='\E[33m' +blue='\E[34m' +magenta='\E[35m' +cyan='\E[36m' +white='\E[37m' -eval set -- "$TEMP" -while true ; do - case "$1" in - -p) echo "PNDNAME set to $2" ;PNDNAME=$2;shift 2;; - -d) echo "FOLDER set to $2" ;FOLDER=$2;shift 2 ;; - -x) echo "PXML set to $2" ;PXML=$2;shift 2 ;; - -i) echo "ICON set to $2" ;ICON=$2;shift 2 ;; - -c) echo "-c set, will create compressed squasfs image instead of iso $2" ;SQUASH=1;shift 1 ;; - --) shift ; break ;; - *) echo "Error while parsing arguments! $2" ; exit 1 ;; - esac +check_for_tool() +{ + which $1 &> /dev/null + if [ "$?" -ne "0" ]; + then + cecho "ERROR: Could not find the program '$1'. Please make sure +that it is available in your PATH since it is required to complete your request." $red + exit 1 + fi +} + +cecho () # Color-echo. Argument $1 = message, Argument $2 = color +{ + local default_msg="No message passed." # Doesn't really need to be a local variable. + message=${1:-$default_msg} # Defaults to default message. + color=${2:-$black} # Defaults to black, if not specified. + echo -e "$color$message" + tput sgr0 # Reset to normal. + return +} + + +print_help() +{ + cat << EOSTREAM +pnd_make.sh - A script to package "something" into a PND. + +Usage: + $(basename ${0}) {--directory|-d} <folder> {--pndname|-p} <file> [{--compress-squashfs|-c}] + [{--genpxml} <file>] [{--icon|-i} <file>] [{--pxml|-x} <file>] + [{--schema|-s} <file>] [{--help|-h}] + + +Switches: + --compress-squashfs / -c Define whether or not the pnd should be compressed using + squashfs. If this parameter is selected, a compressed pnd + will be created. + + --directory / -d Sets the folder that is to be used for the resulting pnd + to <folder>. This option is mandatory for the script to + function correctly. + + --genpxml Sets the script used for generating a PXML file (if none + is available already) to <file>. Please make sure to either + provide a full path or prefix a script in the current folder + with './' so that the script can actually be executed. If + this variable is not specified, $GENPXML_PATH + will be used. + + --help / -h Displays this help text. + + --icon / -i Sets the icon that will be appended in the pnd to <file>. + + --pndname / -p Sets the output filename of the resulting pnd to <file>. + This option is mandatory for the script to function + correctly. + + --pxml / -x Sets the PXML file that is to be used to <file>. If you + neither provide a PXML file or set this entry to 'guess', + an existing 'PXML.xml' in your selected '--directory' + will be used, or the script $GENPXML_PATH + will be called to try to generate a basic PXML file for you. + + --schema / -s Sets the schema file, that is to be used for validation, + to <file. If this is not defined, the script will try to + use the file '$PXML_schema'. If this fails, + a warning is issued. + +If you select the option to create a compressed squashfs, a version >=4.0 of squashfs +is required to be available in your PATH. +EOSTREAM +} + + +# Parse command line parameters +while [ "${1}" != "" ]; do + if [ "${1}" = "--compress-squashfs" ] || [ "${1}" = "-c" ]; + then + SQUASH=1 + shift 1 + elif [ "${1}" = "--directory" ] || [ "${1}" = "-d" ]; + then + FOLDER=$2 + shift 2 + elif [ "${1}" = "--genpxml" ]; + then + GENPXML_PATH=$2 + shift 2 + elif [ "${1}" = "--help" ] || [ "${1}" = "-h" ]; + then + print_help + exit 0 + elif [ "${1}" = "--icon" ] || [ "${1}" = "-i" ]; + then + ICON=$2 + shift 2 + elif [ "${1}" = "--pndname" ] || [ "${1}" = "-p" ]; + then + PNDNAME=$2 + shift 2 + elif [ "${1}" = "--pxml" ] || [ "${1}" = "-x" ]; + then + PXML=$2 + shift 2 + elif [ "${1}" = "--schema" ] || [ "${1}" = "-f" ] + then + PXML_schema=$2 + shift 2 + else + cecho "ERROR: '$1' is not a known argument. Printing --help and aborting." $red + print_help + exit 1 + fi done -rnd=$RANDOM; # random number for genpxml and index$rnd.xml -#generate pxml if guess or empty -if [ ! $PXML ] || [ $PXML = "guess" ] && [ $PNDNAME ] && [ $FOLDER ]; then - PXMLtxt=$(/home/user/libpnd/pandora-libraries/testdata/scripts/genpxml.sh $FOLDER $ICON) - PXML=$FOLDER/PXML.xml - echo "$PXMLtxt" > $FOLDER/PXML.xml +# Generate a PXML if the param is set to Guess or it is empty. +if [ ! $PXML ] || [ $PXML = "guess" ] && [ $PNDNAME ] && [ $FOLDER ]; +then + if [ -f $FOLDER/PXML.xml ]; # use the already existing PXML.xml file if there is one... + then + PXML=$FOLDER/PXML.xml + PXML_ALREADY_EXISTING="true" + else + if [ -f $GENPXML_PATH ]; + then + $GENPXML_PATH --src $FOLDER --dest $FOLDER --author $USER + if [ -f $FOLDER/PXML.xml ]; + then + PXML_GENERATED="true" + else + cecho "ERROR: Generating a PXML file using '$GENPXML_PATH' failed. +Please generate a PXML file manually." $red + exit 1 + fi + else + cecho "ERROR: Could not find '$GENPXML_PATH' for generating a PXML file." $red + exit 1 + fi + fi fi -#check arguments -if [ ! $PNDNAME ] || [ ! $FOLDER ] || [ ! $PXML ]; then - echo " Usage: pnd_make.sh -p your.pnd -d folder/containing/your/app/ -x - your.pxml (or \"guess\" to try to generate it from the folder) -i icon.png" + +# Probe if required variables were set +echo -e +cecho "Checking if all required variables were set." $green +if [ ! $PNDNAME ] || [ ! $FOLDER ] || [ ! $PXML ]; +then + echo -e + cecho "ERROR: Not all required options were set! Please see the --help information below." $red + echo -e + print_help exit 1 +else + echo "PNDNAME set to '$PNDNAME'." +fi +# Check if the selected folder actually exists +if [ ! -d $FOLDER ]; +then + echo -e + cecho "ERROR: '$FOLDER' doesn't exist or is not a folder." $red + exit 1 +else + echo "FOLDER set to '$FOLDER'." +fi +# Check if the selected PXML file actually exists +if [ ! -f $PXML ]; +then + echo -e + cecho "ERROR: '$PXML' doesn't exist or is not a file." $red + exit 1 +else + if [ $PXML_ALREADY_EXISTING ]; + then + echo "You have not explicitly specified a PXML to use, but an existing file was +found. Will be using this one." + elif [ $PXML_GENERATED ]; + then + echo "A PXML file was generated for you using '$GENPXML_PATH'. This file will +not be removed at the end of this script because you might want to review it, adjust +single entries and rerun the script to generate a pnd with a PXML file with all the +information you want to have listed." + fi + echo "PXML set to '$PXML'." fi -if [ ! -d $FOLDER ]; then echo "$FOLDER doesnt exist"; exit 1; fi #check if folder actually exists -if [ ! -f $PXML ]; then echo "$PXML doesnt exist"; exit 1; fi #check if pxml actually exists -#make iso from folder -if [ ! $SQUASH ]; then - mkisofs -o $PNDNAME.iso -R $FOLDER +# Print the other variables: +if [ $ICON ]; +then + if [ ! -f $ICON ] + then + cecho "WARNING: '$ICON' doesn't exist, will not append the selected icon to the pnd." $red + else + echo "ICON set to '$ICON'." + USE_ICON="true" + fi +fi +if [ $SQUASH ]; +then + echo "Will use a squashfs for '$PNDNAME'." +fi + + +# Validate the PXML file (if xmllint is available) +# Errors and problems in this section will be shown but are not fatal. +echo -e +cecho "Trying to validate '$PXML' now. Will be using '$PXML_schema' to do so." $green +which xmllint &> /dev/null +if [ "$?" -ne "0" ]; +then + VALIDATED=false + cecho "WARNING: Could not find 'xmllint'. Validity check of '$PXML' is not possible!" $red else - if [ $(mksquashfs -version | awk '{if ($3 >= 4) print 1}') = 1 ]; then - echo "your squashfs version is older then version 4, please upgrade to 4.0 or later" + if [ ! -f "$PXML_schema" ]; + then + VALIDATED=false + cecho "WARNING: Could not find '$PXML_schema'. If you want to validate your +PXML file please make sure to provide a schema using the --schema option." $red + else + xmllint --noout --schema $PXML_schema $PXML + if [ "$?" -ne "0" ]; then VALIDATED=false; else VALIDATED=true; fi + fi +fi +# Print some message at the end about the validation in case the user missed the output above +if [ $VALIDATED = "false" ] +then + cecho "WARNING: Could not successfully validate '$PXML'. Please check the output +above. This does not mean that your pnd will be broken. Either you are not following the strict +syntax required for validation or you don't have all files/programs required for validating." $red +else + cecho "Your file '$PXML' was validated successfully. The resulting pnd should +work nicely with libpnd." $green +fi + + +# Make iso from folder +echo -e +cecho "Creating an iso file based on '$FOLDER'." $green +if [ $SQUASH ]; +then + check_for_tool mksquashfs + if [ $(mksquashfs -version | awk 'BEGIN{r=0} $3>=4{r=1} END{print r}') -eq 0 ]; + then + cecho "ERROR: Your squashfs version is older then version 4, please upgrade to 4.0 or later" $red exit 1 fi - mksquashfs -no-recovery -nopad $FOLDER $PNDNAME.iso + mksquashfs $FOLDER $PNDNAME.iso -nopad -no-recovery +else + check_for_tool mkisofs + mkisofs -o $PNDNAME.iso -R $FOLDER +fi + +# Check that the iso file was actually created before continuing +if [ ! -f $PNDNAME.iso ]; +then + cecho "ERROR: The temporary file '$PNDNAME.iso' could not be created. +Please check the output above for any errors and retry after fixing them. Aborting." $red + exit 1 fi -#append pxml to iso -cat $PNDNAME.iso $PXML > $PNDNAME + + +# Append pxml to iso +echo -e +cecho "Appending '$PXML' to the created iso file." $green +cat $PNDNAME.iso $PXML > $PNDNAME rm $PNDNAME.iso #cleanup -#append icon if specified -if [ $ICON ]; then # check if we want to add an icon - if [ ! -f $ICON ]; then #does the icon actually exist? - echo "$ICON doesnt exist" - else # yes + +# Append icon if specified and available +if [ $USE_ICON ]; +then + echo -e + cecho "Appending the icon '$ICON' to the pnd." $green mv $PNDNAME $PNDNAME.tmp cat $PNDNAME.tmp $ICON > $PNDNAME # append icon rm $PNDNAME.tmp #cleanup - fi fi -if [ $PXML = "guess" ];then rm $FOLDER/PXML.xml; fi #cleanup + +# Final message +echo -e +if [ -f $PNDNAME ]; +then + cecho "Successfully finished creating the pnd '$PNDNAME'." $green +else + cecho "There seems to have been a problem and '$PNDNAME' was not created. Please check +the output above for any error messages. A possible cause for this is that there was +not enough space available." $red + exit 1 +fi + + +#if [ $PXML = "guess" ];then rm $FOLDER/PXML.xml; fi #cleanup diff --git a/backends/platform/openpandora/build/runscummvm.sh b/backends/platform/openpandora/build/runscummvm.sh index 48d24a2b81..c641235219 100755 --- a/backends/platform/openpandora/build/runscummvm.sh +++ b/backends/platform/openpandora/build/runscummvm.sh @@ -11,4 +11,5 @@ mkdir saves mkdir runtime cd runtime -../bin/scummvm --fullscreen --gfx-mode=2x --config=../scummvm.config +../bin/scummvm --fullscreen --gfx-mode=2x --config=../scummvm.config --themepath=../data + diff --git a/backends/platform/openpandora/op-bundle.mk b/backends/platform/openpandora/op-bundle.mk index 163f4711ce..089430f43c 100755 --- a/backends/platform/openpandora/op-bundle.mk +++ b/backends/platform/openpandora/op-bundle.mk @@ -75,11 +75,10 @@ endif $(CP) $(libloc)/../arm-angstrom-linux-gnueabi/usr/lib/libFLAC.so.8.2.0 $(bundle_name)/scummvm/lib/libFLAC.so.8 - $(srcdir)/backends/platform/openpandora/build/pnd_make.sh -p $(bundle_name).pnd -d $(bundle_name)/scummvm -x $(bundle_name)/scummvm/data/PXML.xml -i $(bundle_name)/scummvm/icon/scummvm.png + $(srcdir)/backends/platform/openpandora/build/pnd_make.sh -p $(bundle_name).pnd -c -d $(bundle_name)/scummvm -x $(bundle_name)/scummvm/data/PXML.xml -i $(bundle_name)/scummvm/icon/scummvm.png $(CP) $(srcdir)/backends/platform/openpandora/build/README-PND.txt $(bundle_name) tar -cvjf $(bundle_name)-pnd.tar.bz2 $(bundle_name).pnd $(bundle_name)/README-PND.txt rm -R ./$(bundle_name) -# rm $(bundle_name).pnd .PHONY: op-bundle op-pnd diff --git a/backends/platform/sdl/ps3/ps3.cpp b/backends/platform/sdl/ps3/ps3.cpp index 16722ccdb7..33586ce693 100644 --- a/backends/platform/sdl/ps3/ps3.cpp +++ b/backends/platform/sdl/ps3/ps3.cpp @@ -21,7 +21,7 @@ */ #define FORBIDDEN_SYMBOL_EXCEPTION_mkdir -#define FORBIDDEN_SYMBOL_EXCEPTION_time_h //On IRIX, sys/stat.h includes sys/time.h +#define FORBIDDEN_SYMBOL_EXCEPTION_time_h // sys/stat.h includes sys/time.h #define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h #include "common/scummsys.h" diff --git a/base/commandLine.cpp b/base/commandLine.cpp index c831cd08fa..6550f60670 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -184,8 +184,8 @@ void registerDefaults() { ConfMan.registerDefault("native_mt32", false); ConfMan.registerDefault("enable_gs", false); ConfMan.registerDefault("midi_gain", 100); -// ConfMan.registerDefault("music_driver", ???); + ConfMan.registerDefault("music_driver", "auto"); ConfMan.registerDefault("mt32_device", "null"); ConfMan.registerDefault("gm_device", "null"); diff --git a/common/system.h b/common/system.h index 15fbe386b1..9b833c5b1a 100644 --- a/common/system.h +++ b/common/system.h @@ -154,9 +154,9 @@ protected: #if defined(USE_TASKBAR) /** - * No default value is provided for _savefileManager by OSystem. + * No default value is provided for _taskbarManager by OSystem. * - * @note _savefileManager is deleted by the OSystem destructor. + * @note _taskbarManager is deleted by the OSystem destructor. */ Common::TaskbarManager *_taskbarManager; #endif @@ -121,7 +121,6 @@ add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes add_engine tsage "Ringworld: Revenge Of The Patriarch" no add_engine tucker "Bud Tucker in Double Trouble" yes - # # Default settings # @@ -2065,6 +2064,7 @@ if test -n "$_host"; then gamecube) _backend="wii" _build_scalers=no + _vkeybd=yes _mt32emu=no _port_mk="backends/platform/wii/wii.mk" add_line_to_config_mk 'GAMECUBE = 1' @@ -2279,6 +2279,7 @@ if test -n "$_host"; then wii) _backend="wii" _build_scalers=no + _vkeybd=yes _port_mk="backends/platform/wii/wii.mk" add_line_to_config_mk 'GAMECUBE = 0' add_line_to_config_h '#define AUDIO_REVERSE_STEREO' diff --git a/devtools/create_project/msvc10/create_project.vcxproj b/devtools/create_project/msvc10/create_project.vcxproj index 3d7f8fdd3d..40c515f26b 100644 --- a/devtools/create_project/msvc10/create_project.vcxproj +++ b/devtools/create_project/msvc10/create_project.vcxproj @@ -59,11 +59,11 @@ </Link> <PostBuildEvent> <Command>@echo off -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\msvc10\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\msvc9\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\msvc8\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\codeblocks\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\iphone\</Command> +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc10\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc9\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc8\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\codeblocks\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\iphone\"</Command> </PostBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -84,10 +84,12 @@ xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\iphone\</Command> <TargetMachine>MachineX86</TargetMachine> </Link> <PostBuildEvent> - <Command>xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\msvc10\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\msvc9\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\msvc8\ -xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\codeblocks\</Command> + <Command>@echo off +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc10\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc9\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\msvc8\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\codeblocks\" +xcopy /Y "$(TargetPath)" "$(SolutionDir)\..\..\..\dists\iphone\"</Command> </PostBuildEvent> <PreBuildEvent> <Command> @@ -120,4 +122,4 @@ xcopy /Y $(TargetPath) $(SolutionDir)\..\..\..\dists\codeblocks\</Command> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/devtools/create_project/scripts/postbuild.cmd b/devtools/create_project/scripts/postbuild.cmd index a5051d8228..dd52c0217c 100644 --- a/devtools/create_project/scripts/postbuild.cmd +++ b/devtools/create_project/scripts/postbuild.cmd @@ -23,23 +23,7 @@ if "%~5"=="" goto error_installer echo Copying data files
echo.
-REM xcopy /F /Y "%~1/AUTHORS" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/COPYING.GPL" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/COPYING" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/COPYING.LGPL" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/COPYRIGHT" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/NEWS" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/README" %~2 1>NUL 2>&1
-
-REM xcopy /F /Y "%~1/dists/engine-data/*.dat" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/dists/engine-data/*.tbl" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/dists/engine-data/*.cpt" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/gui/themes/*.zip" %~2 1>NUL 2>&1
-REM xcopy /F /Y "%~1/gui/themes/translations.dat" %~2 1>NUL 2>&1
-
-xcopy /F /Y "%~4/lib/%~3/SDL.dll" "%~2" 1>NUL 2>&1
-xcopy /F /Y "%~4/README-SDL" "%~2" 1>NUL 2>&1
-
+xcopy /F /Y "%~4/lib/%~3/SDL.dll" "%~2" 1>NUL 2>&1
xcopy /F /Y "%~1/backends/vkeybd/packs/vkeybd_default.zip" "%~2" 1>NUL 2>&1
if "%~5"=="0" goto done
diff --git a/devtools/credits.pl b/devtools/credits.pl index 602dfb6a60..a124314670 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -486,7 +486,7 @@ begin_credits("Credits"); add_person("Matthew Hoops", "clone2727", ""); add_person("Filippos Karapetis", "[md5]", ""); add_person("Paweł Kołodziejski", "aquadran", ""); - add_person("Walter van Niftrik", "waltervn", ""); + add_person("Walter van Niftrik", "waltervn", "(retired)"); add_person("Kari Salminen", "Buddha^", ""); add_person("Eugene Sandulenko", "sev", ""); add_person("David Symonds", "dsymonds", "(retired)"); @@ -609,7 +609,7 @@ begin_credits("Credits"); add_person("Max Horn", "Fingolfin", "(retired)"); add_person("Filippos Karapetis", "[md5]", ""); add_person("Martin Kiewitz", "m_kiewitz", ""); - add_person("Walter van Niftrik", "waltervn", ""); + add_person("Walter van Niftrik", "waltervn", "(retired)"); add_person("Willem Jan Palenstijn", "wjp", ""); add_person("Jordi Vilalta Prat", "jvprat", ""); add_person("Lars Skovlund", "lskovlun", ""); diff --git a/dists/redhat/scummvm.spec b/dists/redhat/scummvm.spec index 777e0afc8a..cd51f45c0a 100644 --- a/dists/redhat/scummvm.spec +++ b/dists/redhat/scummvm.spec @@ -68,6 +68,8 @@ install -m644 -D dists/engine-data/queen.tbl %{buildroot}%{_datadir}/scummvm/que install -m644 -D dists/engine-data/sky.cpt %{buildroot}%{_datadir}/scummvm/sky.cpt install -m644 -D dists/engine-data/drascula.dat %{buildroot}%{_datadir}/scummvm/drascula.dat install -m644 -D dists/engine-data/teenagent.dat %{buildroot}%{_datadir}/scummvm/teenagent.dat +install -m644 -D dists/engine-data/hugo.dat %{buildroot}%{_datadir}/scummvm/hugo.dat +install -m644 -D dists/engine-data/toon.dat %{buildroot}%{_datadir}/scummvm/toon.dat desktop-file-install --vendor scummvm --dir=%{buildroot}/%{_datadir}/applications dists/scummvm.desktop %clean @@ -105,6 +107,8 @@ fi %{_datadir}/scummvm/lure.dat %{_datadir}/scummvm/drascula.dat %{_datadir}/scummvm/teenagent.dat +%{_datadir}/scummvm/hugo.dat +%{_datadir}/scummvm/toon.dat %{_mandir}/man6/scummvm.6* #------------------------------------------------------------------------------ diff --git a/dists/redhat/scummvm.spec.in b/dists/redhat/scummvm.spec.in index 13ce600d02..838a05411a 100644 --- a/dists/redhat/scummvm.spec.in +++ b/dists/redhat/scummvm.spec.in @@ -68,6 +68,8 @@ install -m644 -D dists/engine-data/queen.tbl %{buildroot}%{_datadir}/scummvm/que install -m644 -D dists/engine-data/sky.cpt %{buildroot}%{_datadir}/scummvm/sky.cpt install -m644 -D dists/engine-data/drascula.dat %{buildroot}%{_datadir}/scummvm/drascula.dat install -m644 -D dists/engine-data/teenagent.dat %{buildroot}%{_datadir}/scummvm/teenagent.dat +install -m644 -D dists/engine-data/hugo.dat %{buildroot}%{_datadir}/scummvm/hugo.dat +install -m644 -D dists/engine-data/toon.dat %{buildroot}%{_datadir}/scummvm/toon.dat desktop-file-install --vendor scummvm --dir=%{buildroot}/%{_datadir}/applications dists/scummvm.desktop %clean @@ -105,6 +107,8 @@ fi %{_datadir}/scummvm/lure.dat %{_datadir}/scummvm/drascula.dat %{_datadir}/scummvm/teenagent.dat +%{_datadir}/scummvm/hugo.dat +%{_datadir}/scummvm/toon.dat %{_mandir}/man6/scummvm.6* #------------------------------------------------------------------------------ diff --git a/dists/scummvm.rc b/dists/scummvm.rc index 55f9dfbc6c..9fa5489547 100644 --- a/dists/scummvm.rc +++ b/dists/scummvm.rc @@ -7,10 +7,13 @@ #define FILE 256 #define IDI_ICON 1001 #define IDI_COUNT 1002 +#define ID_GDF_XML __GDF_XML IDI_ICON ICON DISCARDABLE "icons/scummvm.ico" IDI_COUNT ICON DISCARDABLE "icons/count.ico" +ID_GDF_XML DATA "dists/win32/scummvm.gdf.xml" + scummmodern.zip FILE "gui/themes/scummmodern.zip" #ifdef USE_TRANSLATION translations.dat FILE "gui/themes/translations.dat" diff --git a/dists/scummvm.rc.in b/dists/scummvm.rc.in index 2fd3946cc5..b91e7ebb0c 100644 --- a/dists/scummvm.rc.in +++ b/dists/scummvm.rc.in @@ -7,10 +7,13 @@ #define FILE 256 #define IDI_ICON 1001 #define IDI_COUNT 1002 +#define ID_GDF_XML __GDF_XML IDI_ICON ICON DISCARDABLE "icons/scummvm.ico" IDI_COUNT ICON DISCARDABLE "icons/count.ico" +ID_GDF_XML DATA "dists/win32/scummvm.gdf.xml" + scummmodern.zip FILE "gui/themes/scummmodern.zip" #ifdef USE_TRANSLATION translations.dat FILE "gui/themes/translations.dat" diff --git a/dists/win32/plugins/Games.dll b/dists/win32/plugins/Games.dll Binary files differnew file mode 100644 index 0000000000..3a378b7064 --- /dev/null +++ b/dists/win32/plugins/Games.dll diff --git a/dists/win32/scummvm.gdf.xml b/dists/win32/scummvm.gdf.xml new file mode 100644 index 0000000000..1c50924e15 --- /dev/null +++ b/dists/win32/scummvm.gdf.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<GameDefinitionFile xmlns:baseTypes="urn:schemas-microsoft-com:GamesExplorerBaseTypes.v1" xmlns="urn:schemas-microsoft-com:GameDescription.v1"> + <GameDefinition gameID="{F2475C5C-EA7C-41F0-A56D-1ABF7CFEA389}"> + <Name>ScummVM</Name> + <Description>ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed!</Description> + <ReleaseDate>2011-09-30</ReleaseDate> + <Genres> + <Genre>Adventure</Genre> + </Genres> + <Version> + <VersionFile path="scummvm.exe" /> + </Version> + <WindowsSystemPerformanceRating minimum="1.0" recommended="2.0" /> + <Developers> + <Developer URI="http://www.scummvm.org">The ScummVM Team</Developer> + </Developers> + <Publishers> + <Publisher URI="http://www.scummvm.org">The ScummVM Team</Publisher> + </Publishers> + <GameExecutables> + <GameExecutable path="scummvm.exe" /> + </GameExecutables> + <ExtendedProperties> + <GameTasks> + <Play> + <Primary> + <FileTask path="scummvm.exe" arguments="--no-console" /> + </Primary> + <Task index="1" name="Play (console)"> + <FileTask path="scummvm.exe" arguments="" /> + </Task> + </Play> + <Support> + <Task index="0" name="View README"> + <FileTask path="README.txt" arguments="" /> + </Task> + <Task index="1" name="ScummVM Website"> + <URLTask Link="http://www.scummvm.org" /> + </Task> + </Support> + </GameTasks> + </ExtendedProperties> + </GameDefinition> +</GameDefinitionFile>
\ No newline at end of file diff --git a/dists/win32/scummvm.nsi b/dists/win32/scummvm.nsi index 480f8f47ee..01a7e72afe 100644 --- a/dists/win32/scummvm.nsi +++ b/dists/win32/scummvm.nsi @@ -20,12 +20,20 @@ #!define _DEBUG #!define _INCLUDE_DATA_FILES +!define _ENABLE_GAME_EXPLORER +#!define _LOG_BUILD +!define _CONVERT_TEXT Name ScummVM # Included files !include MUI2.nsh +# Plugins +!ifdef _ENABLE_GAME_EXPLORER +!AddPluginDir "./plugins" +!endif + ######################################################################################### # Command line options ######################################################################################### @@ -221,6 +229,9 @@ Var StartMenuGroup # Installer sections ######################################################################################### Section "ScummVM" SecMain +!ifdef _LOG_BUILD + LogSet on +!endif SetOutPath $INSTDIR SetOverwrite on @@ -232,6 +243,7 @@ Section "ScummVM" SecMain File /oname=NEWS.txt "${top_srcdir}\NEWS" File /oname=README.txt "${top_srcdir}\README" +!ifdef _CONVERT_TEXT # Convert line endings Push "$INSTDIR\AUTHORS.txt" Call unix2dos @@ -245,6 +257,7 @@ Section "ScummVM" SecMain Call unix2dos Push "$INSTDIR\README.txt" Call unix2dos +!endif !ifdef _INCLUDE_DATA_FILES # Engine data @@ -271,6 +284,23 @@ Section "ScummVM" SecMain File "${staging_dir}\SDL.dll" WriteRegStr HKCU "${REGKEY}" InstallPath "$INSTDIR" ; Store installation folder + + #Register with game explorer +!ifdef _ENABLE_GAME_EXPLORER + Games::registerGame "$INSTDIR\scummvm.exe" + pop $0 + # This is for Vista only, for 7 the tasks are defined in the gdf xml + ${If} $0 != "0" + ${AndIf} $0 != "" + ${AndIf} $0 != "$INSTDIR\scummvm.exe" + CreateDirectory "$0\PlayTasks\0" + CreateShortcut "$0\PlayTasks\0\Play.lnk" "$INSTDIR\scummvm.exe" "--no-console" + CreateDirectory "$0\PlayTasks\1" + CreateShortcut "$0\PlayTasks\1\Play (console).lnk" "$INSTDIR\scummvm.exe" + CreateDirectory "$0\SupportTasks\0" + CreateShortcut "$0\SupportTasks\0\Home Page.lnk" "${URL}" + ${EndIf} +!endif SectionEnd # Write Start menu entries and uninstaller @@ -280,7 +310,8 @@ Section -post SecMainPost !insertmacro MUI_STARTMENU_WRITE_BEGIN Application SetShellVarContext all ; Create shortcuts in the all-users folder CreateDirectory "$SMPROGRAMS\$StartMenuGroup" - CreateShortCut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\$(^Name).exe "" "$INSTDIR\$(^Name).exe" 0 ; Create shortcut with icon + CreateShortCut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\$(^Name).exe "" "$INSTDIR\$(^Name).exe" 0 ; Create shortcut with icon + CreateShortCut "$SMPROGRAMS\$StartMenuGroup\$(^Name) (No console).lnk" $INSTDIR\$(^Name).exe "--no-console" "$INSTDIR\$(^Name).exe" 0 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Readme.lnk" $INSTDIR\README.txt CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_END @@ -334,6 +365,10 @@ Section -un.Main SecUninstall Delete /REBOOTOK $INSTDIR\translations.dat !endif +!ifdef _ENABLE_GAME_EXPLORER + Games::unregisterGame "$INSTDIR\scummvm.exe" +!endif + Delete /REBOOTOK $INSTDIR\scummvm.exe Delete /REBOOTOK $INSTDIR\SDL.dll SectionEnd @@ -374,6 +409,7 @@ FunctionEnd # Helper functions ######################################################################################### +!ifdef _CONVERT_TEXT ;------------------------------------------------------------------------------- ; strips all CRs and then converts all LFs into CRLFs ; (this is roughly equivalent to "cat file | dos2unix | unix2dos") @@ -426,3 +462,4 @@ unix2dos_done: Pop $0 Delete $0.U2D FunctionEnd +!endif diff --git a/dists/win32/scummvm.nsi.in b/dists/win32/scummvm.nsi.in index a87f5d6244..cba1e81e33 100644 --- a/dists/win32/scummvm.nsi.in +++ b/dists/win32/scummvm.nsi.in @@ -20,12 +20,20 @@ #!define _DEBUG #!define _INCLUDE_DATA_FILES +!define _ENABLE_GAME_EXPLORER +#!define _LOG_BUILD +!define _CONVERT_TEXT Name ScummVM # Included files !include MUI2.nsh +# Plugins +!ifdef _ENABLE_GAME_EXPLORER +!AddPluginDir "./plugins" +!endif + ######################################################################################### # Command line options ######################################################################################### @@ -221,6 +229,9 @@ Var StartMenuGroup # Installer sections ######################################################################################### Section "ScummVM" SecMain +!ifdef _LOG_BUILD + LogSet on +!endif SetOutPath $INSTDIR SetOverwrite on @@ -232,6 +243,7 @@ Section "ScummVM" SecMain File /oname=NEWS.txt "${top_srcdir}\NEWS" File /oname=README.txt "${top_srcdir}\README" +!ifdef _CONVERT_TEXT # Convert line endings Push "$INSTDIR\AUTHORS.txt" Call unix2dos @@ -245,6 +257,7 @@ Section "ScummVM" SecMain Call unix2dos Push "$INSTDIR\README.txt" Call unix2dos +!endif !ifdef _INCLUDE_DATA_FILES # Engine data @@ -271,6 +284,23 @@ Section "ScummVM" SecMain File "${staging_dir}\SDL.dll" WriteRegStr HKCU "${REGKEY}" InstallPath "$INSTDIR" ; Store installation folder + + #Register with game explorer +!ifdef _ENABLE_GAME_EXPLORER + Games::registerGame "$INSTDIR\scummvm.exe" + pop $0 + # This is for Vista only, for 7 the tasks are defined in the gdf xml + ${If} $0 != "0" + ${AndIf} $0 != "" + ${AndIf} $0 != "$INSTDIR\scummvm.exe" + CreateDirectory "$0\PlayTasks\0" + CreateShortcut "$0\PlayTasks\0\Play.lnk" "$INSTDIR\scummvm.exe" "--no-console" + CreateDirectory "$0\PlayTasks\1" + CreateShortcut "$0\PlayTasks\1\Play (console).lnk" "$INSTDIR\scummvm.exe" + CreateDirectory "$0\SupportTasks\0" + CreateShortcut "$0\SupportTasks\0\Home Page.lnk" "${URL}" + ${EndIf} +!endif SectionEnd # Write Start menu entries and uninstaller @@ -280,7 +310,8 @@ Section -post SecMainPost !insertmacro MUI_STARTMENU_WRITE_BEGIN Application SetShellVarContext all ; Create shortcuts in the all-users folder CreateDirectory "$SMPROGRAMS\$StartMenuGroup" - CreateShortCut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\$(^Name).exe "" "$INSTDIR\$(^Name).exe" 0 ; Create shortcut with icon + CreateShortCut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\$(^Name).exe "" "$INSTDIR\$(^Name).exe" 0 ; Create shortcut with icon + CreateShortCut "$SMPROGRAMS\$StartMenuGroup\$(^Name) (No console).lnk" $INSTDIR\$(^Name).exe "--no-console" "$INSTDIR\$(^Name).exe" 0 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Readme.lnk" $INSTDIR\README.txt CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_END @@ -334,6 +365,10 @@ Section -un.Main SecUninstall Delete /REBOOTOK $INSTDIR\translations.dat !endif +!ifdef _ENABLE_GAME_EXPLORER + Games::unregisterGame "$INSTDIR\scummvm.exe" +!endif + Delete /REBOOTOK $INSTDIR\scummvm.exe Delete /REBOOTOK $INSTDIR\SDL.dll SectionEnd @@ -374,6 +409,7 @@ FunctionEnd # Helper functions ######################################################################################### +!ifdef _CONVERT_TEXT ;------------------------------------------------------------------------------- ; strips all CRs and then converts all LFs into CRLFs ; (this is roughly equivalent to "cat file | dos2unix | unix2dos") @@ -426,3 +462,4 @@ unix2dos_done: Pop $0 Delete $0.U2D FunctionEnd +!endif diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp index d32be6a2b7..ef4a746b62 100644 --- a/engines/dreamweb/detection.cpp +++ b/engines/dreamweb/detection.cpp @@ -43,7 +43,7 @@ public: AdvancedMetaEngine(DreamWeb::gameDescriptions, sizeof(DreamWeb::DreamWebGameDescription), dreamWebGames) { _singleid = "dreamweb"; - _guioptions = Common::GUIO_NOMIDI; + _guioptions = Common::GUIO_NOMIDI | Common::GUIO_NOLAUNCHLOAD; } virtual const char *getName() const { diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp index eebadfddae..85cb2c1983 100644 --- a/engines/dreamweb/dreamweb.cpp +++ b/engines/dreamweb/dreamweb.cpp @@ -591,7 +591,7 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const { return 'Z' + 4; case 154: return 'Z' + 6; - case 255: + case 225: return 'A' - 1; case 153: return 'Z' + 5; diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp index eac82ec2c5..eba2f8f279 100644 --- a/engines/kyra/gui_lok.cpp +++ b/engines/kyra/gui_lok.cpp @@ -576,6 +576,15 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) { if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header))) { Common::strlcpy(_savegameNames[i], header.description.c_str(), ARRAYSIZE(_savegameNames[0])); + // Trim long GMM save descriptions to fit our save slots + _screen->_charWidth = -2; + int fC = _screen->getTextWidth(_savegameNames[i]); + while (_savegameNames[i][0] && (fC > 240 )) { + _savegameNames[i][strlen(_savegameNames[i]) - 1] = 0; + fC = _screen->getTextWidth(_savegameNames[i]); + } + _screen->_charWidth = 0; + Util::convertISOToDOS(_savegameNames[i]); menu.item[i].itemString = _savegameNames[i]; @@ -693,12 +702,15 @@ void GUI_LoK::updateSavegameString() { if (_keyPressed.keycode) { length = strlen(_savegameName); + _screen->_charWidth = -2; + int width = _screen->getTextWidth(_savegameName) + 7; + _screen->_charWidth = 0; char inputKey = _keyPressed.ascii; Util::convertISOToDOS(inputKey); if ((uint8)inputKey > 31 && (uint8)inputKey < (_vm->gameFlags().lang == Common::JA_JPN ? 128 : 226)) { - if (length < ARRAYSIZE(_savegameName)-1) { + if ((length < ARRAYSIZE(_savegameName)-1) && (width <= 240)) { _savegameName[length] = inputKey; _savegameName[length+1] = 0; redrawTextfield(); diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp index fb11040168..c64d3e7723 100644 --- a/engines/kyra/gui_lol.cpp +++ b/engines/kyra/gui_lol.cpp @@ -2572,9 +2572,19 @@ void GUI_LoL::setupSaveMenuSlots(Menu &menu, int num) { slotOffs = 1; } + int saveSlotMaxLen = ((_screen->getScreenDim(8))->w << 3) - _screen->getCharWidth('W'); + for (int i = startSlot; i < num && _savegameOffset + i - slotOffs < _savegameListSize; ++i) { if (_savegameList[_saveSlots[i + _savegameOffset - slotOffs]]) { Common::strlcpy(s, _savegameList[_saveSlots[i + _savegameOffset - slotOffs]], 80); + + // Trim long GMM save descriptions to fit our save slots + int fC = _screen->getTextWidth(s); + while (s[0] && fC >= saveSlotMaxLen) { + s[strlen(s) - 1] = 0; + fC = _screen->getTextWidth(s); + } + menu.item[i].itemString = s; s += (strlen(s) + 1); menu.item[i].saveSlot = _saveSlots[i + _savegameOffset - slotOffs]; diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp index 0b82df8cd5..dcc53b7c9e 100644 --- a/engines/kyra/gui_v2.cpp +++ b/engines/kyra/gui_v2.cpp @@ -457,6 +457,15 @@ void GUI_v2::setupSavegameNames(Menu &menu, int num) { Common::strlcpy(s, header.description.c_str(), 80); Util::convertISOToDOS(s); + // Trim long GMM save descriptions to fit our save slots + _screen->_charWidth = -2; + int fC = _screen->getTextWidth(s); + while (s[0] && fC > 240) { + s[strlen(s) - 1] = 0; + fC = _screen->getTextWidth(s); + } + _screen->_charWidth = 0; + menu.item[i].saveSlot = _saveSlots[i + _savegameOffset]; menu.item[i].enabled = true; delete in; diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp index 3d81368d2d..3b2c9b67eb 100644 --- a/engines/kyra/kyra_v1.cpp +++ b/engines/kyra/kyra_v1.cpp @@ -83,7 +83,9 @@ KyraEngine_v1::KyraEngine_v1(OSystem *system, const GameFlags &flags) } void KyraEngine_v1::pauseEngineIntern(bool pause) { - _sound->pause(pause); + Engine::pauseEngineIntern(pause); + if (_sound) + _sound->pause(pause); _timer->pause(pause); } diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp index 3713537afd..4da35cc28b 100644 --- a/engines/kyra/sound.cpp +++ b/engines/kyra/sound.cpp @@ -43,10 +43,6 @@ Sound::Sound(KyraEngine_v1 *vm, Audio::Mixer *mixer) Sound::~Sound() { } -void Sound::pause(bool paused) { - _mixer->pauseAll(paused); -} - bool Sound::voiceFileIsPresent(const char *file) { for (int i = 0; _supportedCodecs[i].fileext; ++i) { Common::String f = file; diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h index 566b37ff43..c3c32331be 100644 --- a/engines/kyra/sound.h +++ b/engines/kyra/sound.h @@ -156,7 +156,7 @@ public: /** * Stops all audio playback when paused. Continues after end of pause. */ - virtual void pause(bool paused); + virtual void pause(bool paused) {} void enableMusic(int enable) { _musicEnabled = enable; } int musicEnabled() const { return _musicEnabled; } diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp index dc0f8c11ec..26b6b31d0a 100644 --- a/engines/kyra/sound_midi.cpp +++ b/engines/kyra/sound_midi.cpp @@ -716,9 +716,6 @@ void SoundMidiPC::beginFadeOut() { } void SoundMidiPC::pause(bool paused) { - // Stop all mixer related sounds - Sound::pause(paused); - Common::StackLock lock(_mutex); if (paused) { diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp index fa2d8a2e51..6845be8808 100644 --- a/engines/lastexpress/data/snd.cpp +++ b/engines/lastexpress/data/snd.cpp @@ -36,16 +36,336 @@ namespace LastExpress { +#pragma region Sound filters tables + +static const int stepTable[1424] = { + 0, 0, 0, 0, 128, 256, 384, 512, 0, 0, 0, 0, 128, 256, + 384, 512, 0, 0, 0, 0, 192, 320, 448, 576, 0, 0, 0, 0, + 192, 320, 448, 576, 64, 64, 64, 64, 256, 384, 512, 640, + 64, 64, 64, 64, 256, 384, 512, 640, 128, 128, 128, 128, + 320, 448, 576, 704, 128, 128, 128, 128, 320, 448, 576, + 704, 192, 192, 192, 192, 384, 512, 640, 768, 192, 192, + 192, 192, 384, 512, 640, 768, 256, 256, 256, 256, 448, + 576, 704, 832, 256, 256, 256, 256, 448, 576, 704, 832, + 320, 320, 320, 320, 512, 640, 768, 896, 320, 320, 320, + 320, 512, 640, 768, 896, 384, 384, 384, 384, 576, 704, + 832, 960, 384, 384, 384, 384, 576, 704, 832, 960, 448, + 448, 448, 448, 640, 768, 896, 1024, 448, 448, 448, 448, + 640, 768, 896, 1024, 512, 512, 512, 512, 704, 832, 960, + 1088, 512, 512, 512, 512, 704, 832, 960, 1088, 576, + 576, 576, 576, 768, 896, 1024, 1152, 576, 576, 576, + 576, 768, 896, 1024, 1152, 640, 640, 640, 640, 832, + 960, 1088, 1216, 640, 640, 640, 640, 832, 960, 1088, + 1216, 704, 704, 704, 704, 896, 1024, 1152, 1280, 704, + 704, 704, 704, 896, 1024, 1152, 1280, 768, 768, 768, + 768, 960, 1088, 1216, 1344, 768, 768, 768, 768, 960, + 1088, 1216, 1344, 832, 832, 832, 832, 1024, 1152, 1280, + 1408, 832, 832, 832, 832, 1024, 1152, 1280, 1408, 896, + 896, 896, 896, 1088, 1216, 1344, 1472, 896, 896, 896, + 896, 1088, 1216, 1344, 1472, 960, 960, 960, 960, 1152, + 1280, 1408, 1536, 960, 960, 960, 960, 1152, 1280, 1408, + 1536, 1024, 1024, 1024, 1024, 1216, 1344, 1472, 1600, + 1024, 1024, 1024, 1024, 1216, 1344, 1472, 1600, 1088, + 1088, 1088, 1088, 1280, 1408, 1536, 1664, 1088, 1088, + 1088, 1088, 1280, 1408, 1536, 1664, 1152, 1152, 1152, + 1152, 1344, 1472, 1600, 1728, 1152, 1152, 1152, 1152, + 1344, 1472, 1600, 1728, 1216, 1216, 1216, 1216, 1408, + 1536, 1664, 1792, 1216, 1216, 1216, 1216, 1408, 1536, + 1664, 1792, 1280, 1280, 1280, 1280, 1472, 1600, 1728, + 1856, 1280, 1280, 1280, 1280, 1472, 1600, 1728, 1856, + 1344, 1344, 1344, 1344, 1536, 1664, 1792, 1920, 1344, + 1344, 1344, 1344, 1536, 1664, 1792, 1920, 1408, 1408, + 1408, 1408, 1600, 1728, 1856, 1984, 1408, 1408, 1408, + 1408, 1600, 1728, 1856, 1984, 1472, 1472, 1472, 1472, + 1664, 1792, 1920, 2048, 1472, 1472, 1472, 1472, 1664, + 1792, 1920, 2048, 1536, 1536, 1536, 1536, 1728, 1856, + 1984, 2112, 1536, 1536, 1536, 1536, 1728, 1856, 1984, + 2112, 1600, 1600, 1600, 1600, 1792, 1920, 2048, 2176, + 1600, 1600, 1600, 1600, 1792, 1920, 2048, 2176, 1664, + 1664, 1664, 1664, 1856, 1984, 2112, 2240, 1664, 1664, + 1664, 1664, 1856, 1984, 2112, 2240, 1728, 1728, 1728, + 1728, 1920, 2048, 2176, 2304, 1728, 1728, 1728, 1728, + 1920, 2048, 2176, 2304, 1792, 1792, 1792, 1792, 1984, + 2112, 2240, 2368, 1792, 1792, 1792, 1792, 1984, 2112, + 2240, 2368, 1856, 1856, 1856, 1856, 2048, 2176, 2304, + 2432, 1856, 1856, 1856, 1856, 2048, 2176, 2304, 2432, + 1920, 1920, 1920, 1920, 2112, 2240, 2368, 2496, 1920, + 1920, 1920, 1920, 2112, 2240, 2368, 2496, 1984, 1984, + 1984, 1984, 2176, 2304, 2432, 2560, 1984, 1984, 1984, + 1984, 2176, 2304, 2432, 2560, 2048, 2048, 2048, 2048, + 2240, 2368, 2496, 2624, 2048, 2048, 2048, 2048, 2240, + 2368, 2496, 2624, 2112, 2112, 2112, 2112, 2304, 2432, + 2560, 2688, 2112, 2112, 2112, 2112, 2304, 2432, 2560, + 2688, 2176, 2176, 2176, 2176, 2368, 2496, 2624, 2752, + 2176, 2176, 2176, 2176, 2368, 2496, 2624, 2752, 2240, + 2240, 2240, 2240, 2432, 2560, 2688, 2816, 2240, 2240, + 2240, 2240, 2432, 2560, 2688, 2816, 2304, 2304, 2304, + 2304, 2496, 2624, 2752, 2880, 2304, 2304, 2304, 2304, + 2496, 2624, 2752, 2880, 2368, 2368, 2368, 2368, 2560, + 2688, 2816, 2944, 2368, 2368, 2368, 2368, 2560, 2688, + 2816, 2944, 2432, 2432, 2432, 2432, 2624, 2752, 2880, + 3008, 2432, 2432, 2432, 2432, 2624, 2752, 2880, 3008, + 2496, 2496, 2496, 2496, 2688, 2816, 2944, 3072, 2496, + 2496, 2496, 2496, 2688, 2816, 2944, 3072, 2560, 2560, + 2560, 2560, 2752, 2880, 3008, 3136, 2560, 2560, 2560, + 2560, 2752, 2880, 3008, 3136, 2624, 2624, 2624, 2624, + 2816, 2944, 3072, 3200, 2624, 2624, 2624, 2624, 2816, + 2944, 3072, 3200, 2688, 2688, 2688, 2688, 2880, 3008, + 3136, 3264, 2688, 2688, 2688, 2688, 2880, 3008, 3136, + 3264, 2752, 2752, 2752, 2752, 2944, 3072, 3200, 3328, + 2752, 2752, 2752, 2752, 2944, 3072, 3200, 3328, 2816, + 2816, 2816, 2816, 3008, 3136, 3264, 3392, 2816, 2816, + 2816, 2816, 3008, 3136, 3264, 3392, 2880, 2880, 2880, + 2880, 3072, 3200, 3328, 3456, 2880, 2880, 2880, 2880, + 3072, 3200, 3328, 3456, 2944, 2944, 2944, 2944, 3136, + 3264, 3392, 3520, 2944, 2944, 2944, 2944, 3136, 3264, + 3392, 3520, 3008, 3008, 3008, 3008, 3200, 3328, 3456, + 3584, 3008, 3008, 3008, 3008, 3200, 3328, 3456, 3584, + 3072, 3072, 3072, 3072, 3264, 3392, 3520, 3648, 3072, + 3072, 3072, 3072, 3264, 3392, 3520, 3648, 3136, 3136, + 3136, 3136, 3328, 3456, 3584, 3712, 3136, 3136, 3136, + 3136, 3328, 3456, 3584, 3712, 3200, 3200, 3200, 3200, + 3392, 3520, 3648, 3776, 3200, 3200, 3200, 3200, 3392, + 3520, 3648, 3776, 3264, 3264, 3264, 3264, 3456, 3584, + 3712, 3840, 3264, 3264, 3264, 3264, 3456, 3584, 3712, + 3840, 3328, 3328, 3328, 3328, 3520, 3648, 3776, 3904, + 3328, 3328, 3328, 3328, 3520, 3648, 3776, 3904, 3392, + 3392, 3392, 3392, 3584, 3712, 3840, 3968, 3392, 3392, + 3392, 3392, 3584, 3712, 3840, 3968, 3456, 3456, 3456, + 3456, 3648, 3776, 3904, 4032, 3456, 3456, 3456, 3456, + 3648, 3776, 3904, 4032, 3520, 3520, 3520, 3520, 3712, + 3840, 3968, 4096, 3520, 3520, 3520, 3520, 3712, 3840, + 3968, 4096, 3584, 3584, 3584, 3584, 3776, 3904, 4032, + 4160, 3584, 3584, 3584, 3584, 3776, 3904, 4032, 4160, + 3648, 3648, 3648, 3648, 3840, 3968, 4096, 4224, 3648, + 3648, 3648, 3648, 3840, 3968, 4096, 4224, 3712, 3712, + 3712, 3712, 3904, 4032, 4160, 4288, 3712, 3712, 3712, + 3712, 3904, 4032, 4160, 4288, 3776, 3776, 3776, 3776, + 3968, 4096, 4224, 4352, 3776, 3776, 3776, 3776, 3968, + 4096, 4224, 4352, 3840, 3840, 3840, 3840, 4032, 4160, + 4288, 4416, 3840, 3840, 3840, 3840, 4032, 4160, 4288, + 4416, 3904, 3904, 3904, 3904, 4096, 4224, 4352, 4480, + 3904, 3904, 3904, 3904, 4096, 4224, 4352, 4480, 3968, + 3968, 3968, 3968, 4160, 4288, 4416, 4544, 3968, 3968, + 3968, 3968, 4160, 4288, 4416, 4544, 4032, 4032, 4032, + 4032, 4224, 4352, 4480, 4608, 4032, 4032, 4032, 4032, + 4224, 4352, 4480, 4608, 4096, 4096, 4096, 4096, 4288, + 4416, 4544, 4672, 4096, 4096, 4096, 4096, 4288, 4416, + 4544, 4672, 4160, 4160, 4160, 4160, 4352, 4480, 4608, + 4736, 4160, 4160, 4160, 4160, 4352, 4480, 4608, 4736, + 4224, 4224, 4224, 4224, 4416, 4544, 4672, 4800, 4224, + 4224, 4224, 4224, 4416, 4544, 4672, 4800, 4288, 4288, + 4288, 4288, 4480, 4608, 4736, 4864, 4288, 4288, 4288, + 4288, 4480, 4608, 4736, 4864, 4352, 4352, 4352, 4352, + 4544, 4672, 4800, 4928, 4352, 4352, 4352, 4352, 4544, + 4672, 4800, 4928, 4416, 4416, 4416, 4416, 4608, 4736, + 4864, 4992, 4416, 4416, 4416, 4416, 4608, 4736, 4864, + 4992, 4480, 4480, 4480, 4480, 4672, 4800, 4928, 5056, + 4480, 4480, 4480, 4480, 4672, 4800, 4928, 5056, 4544, + 4544, 4544, 4544, 4736, 4864, 4992, 5120, 4544, 4544, + 4544, 4544, 4736, 4864, 4992, 5120, 4608, 4608, 4608, + 4608, 4800, 4928, 5056, 5184, 4608, 4608, 4608, 4608, + 4800, 4928, 5056, 5184, 4672, 4672, 4672, 4672, 4864, + 4992, 5120, 5248, 4672, 4672, 4672, 4672, 4864, 4992, + 5120, 5248, 4736, 4736, 4736, 4736, 4928, 5056, 5184, + 5312, 4736, 4736, 4736, 4736, 4928, 5056, 5184, 5312, + 4800, 4800, 4800, 4800, 4992, 5120, 5248, 5376, 4800, + 4800, 4800, 4800, 4992, 5120, 5248, 5376, 4864, 4864, + 4864, 4864, 5056, 5184, 5312, 5440, 4864, 4864, 4864, + 4864, 5056, 5184, 5312, 5440, 4928, 4928, 4928, 4928, + 5120, 5248, 5376, 5504, 4928, 4928, 4928, 4928, 5120, + 5248, 5376, 5504, 4992, 4992, 4992, 4992, 5184, 5312, + 5440, 5568, 4992, 4992, 4992, 4992, 5184, 5312, 5440, + 5568, 5056, 5056, 5056, 5056, 5248, 5376, 5504, 5632, + 5056, 5056, 5056, 5056, 5248, 5376, 5504, 5632, 5120, + 5120, 5120, 5120, 5312, 5440, 5568, 5632, 5120, 5120, + 5120, 5120, 5312, 5440, 5568, 5632, 5184, 5184, 5184, + 5184, 5376, 5504, 5632, 5632, 5184, 5184, 5184, 5184, + 5376, 5504, 5632, 5632, 5248, 5248, 5248, 5248, 5440, + 5568, 5632, 5632, 5248, 5248, 5248, 5248, 5440, 5568, + 5632, 5632, 5312, 5312, 5312, 5312, 5504, 5632, 5632, + 5632, 5312, 5312, 5312, 5312, 5504, 5632, 5632, 5632, + 5376, 5376, 5376, 5376, 5568, 5632, 5632, 5632, 5376, + 5376, 5376, 5376, 5568, 5632, 5632, 5632, 5440, 5440, + 5440, 5440, 5632, 5632, 5632, 5632, 5440, 5440, 5440, + 5440, 5632, 5632, 5632, 5632, 5504, 5504, 5504, 5504, + 5632, 5632, 5632, 5632, 5504, 5504, 5504, 5504, 5632, + 5632, 5632, 5632, 5568, 5568, 5568, 5568, 5632, 5632, + 5632, 5632, 5568, 5568, 5568, 5568, 5632, 5632, 5632, + 5632 +}; + +static const int imaTable[1424] = { + 0, 2, 4, 6, 7, 9, 11, 13, 0, -2, -4, -6, -7, -9, -11, + -13, 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, + -11, -13, -15, 1, 3, 5, 7, 10, 12, 14, 16, -1, -3, -5, + -7, -10, -12, -14, -16, 1, 3, 6, 8, 11, 13, 16, 18, + -1, -3, -6, -8, -11, -13, -16, -18, 1, 4, 6, 9, 12, + 15, 17, 20, -1, -4, -6, -9, -12, -15, -17, -20, 1, 4, + 7, 10, 13, 16, 19, 22, -1, -4, -7, -10, -13, -16, -19, + -22, 1, 4, 8, 11, 14, 17, 21, 24, -1, -4, -8, -11, -14, + -17, -21, -24, 1, 5, 8, 12, 15, 19, 22, 26, -1, -5, + -8, -12, -15, -19, -22, -26, 2, 6, 10, 14, 18, 22, 26, + 30, -2, -6, -10, -14, -18, -22, -26, -30, 2, 6, 10, + 14, 19, 23, 27, 31, -2, -6, -10, -14, -19, -23, -27, + -31, 2, 7, 11, 16, 21, 26, 30, 35, -2, -7, -11, -16, + -21, -26, -30, -35, 2, 7, 13, 18, 23, 28, 34, 39, -2, + -7, -13, -18, -23, -28, -34, -39, 2, 8, 14, 20, 25, + 31, 37, 43, -2, -8, -14, -20, -25, -31, -37, -43, 3, + 9, 15, 21, 28, 34, 40, 46, -3, -9, -15, -21, -28, -34, + -40, -46, 3, 10, 17, 24, 31, 38, 45, 52, -3, -10, -17, + -24, -31, -38, -45, -52, 3, 11, 19, 27, 34, 42, 50, + 58, -3, -11, -19, -27, -34, -42, -50, -58, 4, 12, 21, + 29, 38, 46, 55, 63, -4, -12, -21, -29, -38, -46, -55, + -63, 4, 13, 23, 32, 41, 50, 60, 69, -4, -13, -23, -32, + -41, -50, -60, -69, 5, 15, 25, 35, 46, 56, 66, 76, -5, + -15, -25, -35, -46, -56, -66, -76, 5, 16, 28, 39, 50, + 61, 73, 84, -5, -16, -28, -39, -50, -61, -73, -84, 6, + 18, 31, 43, 56, 68, 81, 93, -6, -18, -31, -43, -56, + -68, -81, -93, 6, 20, 34, 48, 61, 75, 89, 103, -6, -20, + -34, -48, -61, -75, -89, -103, 7, 22, 37, 52, 67, 82, + 97, 112, -7, -22, -37, -52, -67, -82, -97, -112, 8, + 24, 41, 57, 74, 90, 107, 123, -8, -24, -41, -57, -74, + -90, -107, -123, 9, 27, 45, 63, 82, 100, 118, 136, -9, + -27, -45, -63, -82, -100, -118, -136, 10, 30, 50, 70, + 90, 110, 130, 150, -10, -30, -50, -70, -90, -110, -130, + -150, 11, 33, 55, 77, 99, 121, 143, 165, -11, -33, -55, + -77, -99, -121, -143, -165, 12, 36, 60, 84, 109, 133, + 157, 181, -12, -36, -60, -84, -109, -133, -157, -181, + 13, 40, 66, 93, 120, 147, 173, 200, -13, -40, -66, -93, + -120, -147, -173, -200, 14, 44, 73, 103, 132, 162, 191, + 221, -14, -44, -73, -103, -132, -162, -191, -221, 16, + 48, 81, 113, 146, 178, 211, 243, -16, -48, -81, -113, + -146, -178, -211, -243, 17, 53, 89, 125, 160, 196, 232, + 268, -17, -53, -89, -125, -160, -196, -232, -268, 19, + 58, 98, 137, 176, 215, 255, 294, -19, -58, -98, -137, + -176, -215, -255, -294, 21, 64, 108, 151, 194, 237, + 281, 324, -21, -64, -108, -151, -194, -237, -281, -324, + 23, 71, 118, 166, 213, 261, 308, 356, -23, -71, -118, + -166, -213, -261, -308, -356, 26, 78, 130, 182, 235, + 287, 339, 391, -26, -78, -130, -182, -235, -287, -339, + -391, 28, 86, 143, 201, 258, 316, 373, 431, -28, -86, + -143, -201, -258, -316, -373, -431, 31, 94, 158, 221, + 284, 347, 411, 474, -31, -94, -158, -221, -284, -347, + -411, -474, 34, 104, 174, 244, 313, 383, 453, 523, -34, + -104, -174, -244, -313, -383, -453, -523, 38, 115, 191, + 268, 345, 422, 498, 575, -38, -115, -191, -268, -345, + -422, -498, -575, 42, 126, 210, 294, 379, 463, 547, + 631, -42, -126, -210, -294, -379, -463, -547, -631, + 46, 139, 231, 324, 417, 510, 602, 695, -46, -139, -231, + -324, -417, -510, -602, -695, 51, 153, 255, 357, 459, + 561, 663, 765, -51, -153, -255, -357, -459, -561, -663, + -765, 56, 168, 280, 392, 505, 617, 729, 841, -56, -168, + -280, -392, -505, -617, -729, -841, 61, 185, 308, 432, + 555, 679, 802, 926, -61, -185, -308, -432, -555, -679, + -802, -926, 68, 204, 340, 476, 612, 748, 884, 1020, + -68, -204, -340, -476, -612, -748, -884, -1020, 74, + 224, 373, 523, 672, 822, 971, 1121, -74, -224, -373, + -523, -672, -822, -971, -1121, 82, 246, 411, 575, 740, + 904, 1069, 1233, -82, -246, -411, -575, -740, -904, + -1069, -1233, 90, 271, 452, 633, 814, 995, 1176, 1357, + -90, -271, -452, -633, -814, -995, -1176, -1357, 99, + 298, 497, 696, 895, 1094, 1293, 1492, -99, -298, -497, + -696, -895, -1094, -1293, -1492, 109, 328, 547, 766, + 985, 1204, 1423, 1642, -109, -328, -547, -766, -985, + -1204, -1423, -1642, 120, 361, 601, 842, 1083, 1324, + 1564, 1805, -120, -361, -601, -842, -1083, -1324, -1564, + -1805, 132, 397, 662, 927, 1192, 1457, 1722, 1987, -132, + -397, -662, -927, -1192, -1457, -1722, -1987, 145, 437, + 728, 1020, 1311, 1603, 1894, 2186, -145, -437, -728, + -1020, -1311, -1603, -1894, -2186, 160, 480, 801, 1121, + 1442, 1762, 2083, 2403, -160, -480, -801, -1121, -1442, + -1762, -2083, -2403, 176, 529, 881, 1234, 1587, 1940, + 2292, 2645, -176, -529, -881, -1234, -1587, -1940, -2292, + -2645, 194, 582, 970, 1358, 1746, 2134, 2522, 2910, + -194, -582, -970, -1358, -1746, -2134, -2522, -2910, + 213, 640, 1066, 1493, 1920, 2347, 2773, 3200, -213, + -640, -1066, -1493, -1920, -2347, -2773, -3200, 234, + 704, 1173, 1643, 2112, 2582, 3051, 3521, -234, -704, + -1173, -1643, -2112, -2582, -3051, -3521, 258, 774, + 1291, 1807, 2324, 2840, 3357, 3873, -258, -774, -1291, + -1807, -2324, -2840, -3357, -3873, 284, 852, 1420, 1988, + 2556, 3124, 3692, 4260, -284, -852, -1420, -1988, -2556, + -3124, -3692, -4260, 312, 937, 1561, 2186, 2811, 3436, + 4060, 4685, -312, -937, -1561, -2186, -2811, -3436, + -4060, -4685, 343, 1030, 1718, 2405, 3092, 3779, 4467, + 5154, -343, -1030, -1718, -2405, -3092, -3779, -4467, + -5154, 378, 1134, 1890, 2646, 3402, 4158, 4914, 5670, + -378, -1134, -1890, -2646, -3402, -4158, -4914, -5670, + 415, 1247, 2079, 2911, 3742, 4574, 5406, 6238, -415, + -1247, -2079, -2911, -3742, -4574, -5406, -6238, 457, + 1372, 2287, 3202, 4117, 5032, 5947, 6862, -457, -1372, + -2287, -3202, -4117, -5032, -5947, -6862, 503, 1509, + 2516, 3522, 4529, 5535, 6542, 7548, -503, -1509, -2516, + -3522, -4529, -5535, -6542, -7548, 553, 1660, 2767, + 3874, 4981, 6088, 7195, 8302, -553, -1660, -2767, -3874, + -4981, -6088, -7195, -8302, 608, 1826, 3044, 4262, 5479, + 6697, 7915, 9133, -608, -1826, -3044, -4262, -5479, + -6697, -7915, -9133, 669, 2009, 3348, 4688, 6027, 7367, + 8706, 10046, -669, -2009, -3348, -4688, -6027, -7367, + -8706, -10046, 736, 2210, 3683, 5157, 6630, 8104, 9577, + 11051, -736, -2210, -3683, -5157, -6630, -8104, -9577, + -11051, 810, 2431, 4052, 5673, 7294, 8915, 10536, 12157, + -810, -2431, -4052, -5673, -7294, -8915, -10536, -12157, + 891, 2674, 4457, 6240, 8023, 9806, 11589, 13372, -891, + -2674, -4457, -6240, -8023, -9806, -11589, -13372, 980, + 2941, 4903, 6864, 8825, 10786, 12748, 14709, -980, -2941, + -4903, -6864, -8825, -10786, -12748, -14709, 1078, 3236, + 5393, 7551, 9708, 11866, 14023, 16181, -1078, -3236, + -5393, -7551, -9708, -11866, -14023, -16181, 1186, 3559, + 5933, 8306, 10679, 13052, 15426, 17799, -1186, -3559, + -5933, -8306, -10679, -13052, -15426, -17799, 1305, + 3915, 6526, 9136, 11747, 14357, 16968, 19578, -1305, + -3915, -6526, -9136, -11747, -14357, -16968, -19578, + 1435, 4307, 7179, 10051, 12922, 15794, 18666, 21538, + -1435, -4307, -7179, -10051, -12922, -15794, -18666, + -21538, 1579, 4738, 7896, 11055, 14214, 17373, 20531, + 23690, -1579, -4738, -7896, -11055, -14214, -17373, + -20531, -23690, 1737, 5212, 8686, 12161, 15636, 19111, + 22585, 26060, -1737, -5212, -8686, -12161, -15636, -19111, + -22585, -26060, 1911, 5733, 9555, 13377, 17200, 21022, + 24844, 28666, -1911, -5733, -9555, -13377, -17200, -21022, + -24844, -28666, 2102, 6306, 10511, 14715, 18920, 23124, + 27329, 31533, -2102, -6306, -10511, -14715, -18920, + -23124, -27329, -31533, 2312, 6937, 11562, 16187, 20812, + 25437, 30062, 32767, -2312, -6937, -11562, -16187, -20812, + -25437, -30062, -32767, 2543, 7631, 12718, 17806, 22893, + 27981, 32767, 32767, -2543, -7631, -12718, -17806, -22893, + -27981, -32767, -32767, 2798, 8394, 13990, 19586, 25183, + 30779, 32767, 32767, -2798, -8394, -13990, -19586, -25183, + -30779, -32767, -32767, 3077, 9233, 15389, 21545, 27700, + 32767, 32767, 32767, -3077, -9233, -15389, -21545, -27700, + -32767, -32767, -32767, 3385, 10157, 16928, 23700, 30471, + 32767, 32767, 32767, -3385, -10157, -16928, -23700, + -30471, -32767, -32767, -32767, 3724, 11172, 18621, + 26069, 32767, 32767, 32767, 32767, -3724, -11172, -18621, + -26069, -32767, -32767, -32767, -32767, 4095, 12287, + 20479, 28671, 32767, 32767, 32767, 32767, -4095, -12287, + -20479, -28671, -32767, -32767, -32767, -32767 +}; + +static const int p1s[17] = { 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4, 0 }; +static const int p2s[17] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15, 1 }; + +#pragma endregion + // Last Express ADPCM is similar to MS IMA mono, but inverts its nibbles // and does not have the 4 byte per channel requirement -class LastExpress_ADPCMStream : public Audio::Ima_ADPCMStream { +class LastExpress_ADPCMStream : public Audio::ADPCMStream { public: - LastExpress_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, uint32 blockSize) : - Audio::Ima_ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) {} + LastExpress_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, uint32 blockSize, int32 filterId) : + Audio::ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) { + _currentFilterId = -1; + _nextFilterId = filterId; + } int readBuffer(int16 *buffer, const int numSamples) { int samples = 0; + // Temporary data + int step = 0; + int sample = 0; + byte idx = 0; assert(numSamples % 2 == 0); @@ -53,20 +373,50 @@ public: if (_blockPos[0] == _blockAlign) { // read block header _status.ima_ch[0].last = _stream->readSint16LE(); - _status.ima_ch[0].stepIndex = _stream->readSint16LE(); + _status.ima_ch[0].stepIndex = _stream->readSint16LE() << 6; _blockPos[0] = 4; + + // Get current filter + _currentFilterId = _nextFilterId; + _nextFilterId = -1; + + // No filter: skip decoding + if (_currentFilterId == -1) + break; + + // Compute step adjustment + _stepAdjust1 = p1s[_currentFilterId]; + _stepAdjust2 = p2s[_currentFilterId]; } for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) { byte data = _stream->readByte(); _blockPos[0]++; - buffer[samples] = decodeIMA((data >> 4) & 0x0f); - buffer[samples + 1] = decodeIMA(data & 0x0f); + + // First nibble + idx = data >> 4; + step = stepTable[idx + _status.ima_ch[0].stepIndex / 4]; + sample = CLIP(imaTable[idx + _status.ima_ch[0].stepIndex / 4] + _status.ima_ch[0].last, -32767, 32767); + buffer[samples] = (_stepAdjust2 * sample) >> _stepAdjust1; + + // Second nibble + idx = data & 0xF; + _status.ima_ch[0].stepIndex = stepTable[idx + step / 4]; + _status.ima_ch[0].last = CLIP(imaTable[idx + step / 4] + sample, -32767, 32767); + buffer[samples + 1] = (_stepAdjust2 * _status.ima_ch[0].last) >> _stepAdjust1; } } return samples; } + + void setFilterId(int32 filterId) { _nextFilterId = filterId; } + +private: + int32 _currentFilterId; + int32 _nextFilterId; // the sound filter id, -1 for none + int32 _stepAdjust1; + int32 _stepAdjust2; }; ////////////////////////////////////////////////////////////////////////// @@ -92,8 +442,8 @@ void SimpleSound::loadHeader(Common::SeekableReadStream *in) { _blockSize = _size / _blocks; } -Audio::AudioStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size) const { - return new LastExpress_ADPCMStream(in, DisposeAfterUse::YES, size, _blockSize); +Audio::AudioStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size, int32 filterId) const { + return new LastExpress_ADPCMStream(in, DisposeAfterUse::YES, size, _blockSize, filterId); } void SimpleSound::play(Audio::AudioStream *as) { @@ -103,11 +453,11 @@ void SimpleSound::play(Audio::AudioStream *as) { ////////////////////////////////////////////////////////////////////////// // StreamedSound ////////////////////////////////////////////////////////////////////////// -StreamedSound::StreamedSound() : _loaded(false) {} +StreamedSound::StreamedSound() : _as(NULL), _loaded(false) {} StreamedSound::~StreamedSound() {} -bool StreamedSound::load(Common::SeekableReadStream *stream) { +bool StreamedSound::load(Common::SeekableReadStream *stream, int32 filterId) { if (!stream) return false; @@ -116,10 +466,10 @@ bool StreamedSound::load(Common::SeekableReadStream *stream) { loadHeader(stream); // Start decoding the input stream - Audio::AudioStream *as = makeDecoder(stream, _size); + _as = makeDecoder(stream, _size, filterId); // Start playing the decoded audio stream - play(as); + play(_as); _loaded = true; @@ -133,6 +483,10 @@ bool StreamedSound::isFinished() { return !g_system->getMixer()->isSoundHandleActive(_handle); } +void StreamedSound::setFilterId(int32 filterId) { + ((LastExpress_ADPCMStream *)_as)->setFilterId(filterId); +} + ////////////////////////////////////////////////////////////////////////// // StreamedSound ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/data/snd.h b/engines/lastexpress/data/snd.h index 1c34e4f950..7111d939e7 100644 --- a/engines/lastexpress/data/snd.h +++ b/engines/lastexpress/data/snd.h @@ -59,7 +59,7 @@ public: protected: void loadHeader(Common::SeekableReadStream *in); - Audio::AudioStream *makeDecoder(Common::SeekableReadStream *in, uint32 size) const; + Audio::AudioStream *makeDecoder(Common::SeekableReadStream *in, uint32 size, int32 filterId = -1) const; void play(Audio::AudioStream *as); uint32 _size; ///< data size @@ -76,11 +76,13 @@ public: StreamedSound(); ~StreamedSound(); - bool load(Common::SeekableReadStream *stream); - + bool load(Common::SeekableReadStream *stream, int32 filterId = -1); virtual bool isFinished(); + void setFilterId(int32 filterId); + private: + Audio::AudioStream *_as; bool _loaded; }; diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index ebada5dd4e..57c18b5697 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -45,12 +45,12 @@ namespace LastExpress { static const struct { const char *saveFile; } gameInfo[6] = { - {"blue.egg"}, - {"red.egg"}, - {"green.egg"}, - {"purple.egg"}, - {"teal.egg"}, - {"gold.egg"} + {"lastexpress-blue.egg"}, + {"lastexpress-red.egg"}, + {"lastexpress-green.egg"}, + {"lastexpress-purple.egg"}, + {"lastexpress-teal.egg"}, + {"lastexpress-gold.egg"} }; ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h index b4ced96ee1..d60a498447 100644 --- a/engines/lastexpress/shared.h +++ b/engines/lastexpress/shared.h @@ -84,29 +84,26 @@ enum SoundFlag { }; enum SoundState { - kSoundState0 = 0, - kSoundState1 = 1, - kSoundState2 = 2 + kSoundStateNone = 0, + kSoundState1 = 1, + kSoundState2 = 2 }; enum SoundStatus { + kSoundStatusClear0 = 0x10, + kSoundStatusFilter = 0x1F, kSoundStatus_20 = 0x20, kSoundStatus_40 = 0x40, + kSoundStatusCached = 0x80, kSoundStatus_180 = 0x180, - kSoundStatusRemoved = 0x200, + kSoundStatusClosed = 0x200, kSoundStatus_400 = 0x400, - + kSoundStatusClear4 = 0x800, kSoundStatus_8000 = 0x8000, kSoundStatus_20000 = 0x20000, kSoundStatus_100000 = 0x100000, kSoundStatus_20000000 = 0x20000000, kSoundStatus_40000000 = 0x40000000, - - kSoundStatusClear0 = 0x10, - kSoundStatusFilterVariant = 0x1F, - kSoundStatusClear2 = 0x80, - kSoundStatusClear3 = 0x200, - kSoundStatusClear4 = 0x800, kSoundStatusClearAll = 0xFFFFFFE0 }; diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp index 2840d85ca7..44cc68a57b 100644 --- a/engines/lastexpress/sound/entry.cpp +++ b/engines/lastexpress/sound/entry.cpp @@ -47,9 +47,6 @@ namespace LastExpress { SoundEntry::SoundEntry(LastExpressEngine *engine) : _engine(engine) { _type = kSoundTypeNone; - _currentDataPtr = NULL; - _soundBuffer = NULL; - _blockCount = 0; _time = 0; @@ -76,8 +73,6 @@ SoundEntry::~SoundEntry() { SAFE_DELETE(_stream); delete _soundStream; - free(_soundBuffer); - // Zero passed pointers _engine = NULL; } @@ -86,18 +81,15 @@ void SoundEntry::open(Common::String name, SoundFlag flag, int priority) { _priority = priority; setType(flag); setupStatus(flag); - - // Add entry to cache and load sound data - setupCache(); - loadSoundData(name); + loadStream(name); } void SoundEntry::close() { - _status.status |= kSoundStatusRemoved; + _status.status |= kSoundStatusClosed; // Loop until ready - while (!(_status.status1 & 4) && !(getSoundQueue()->getFlag() & 8) && (getSoundQueue()->getFlag() & 1)) - ; // empty loop body + //while (!(_status.status1 & 4) && !(getSoundQueue()->getFlag() & 8) && (getSoundQueue()->getFlag() & 1)) + // ; // empty loop body // The original game remove the entry from the cache here, // but since we are called from within an iterator loop @@ -123,34 +115,24 @@ void SoundEntry::play() { return; } - if (_queued) - return; - - // Apply filter - int16 *buffer = (int16 *)malloc(FILTER_BUFFER_SIZE); - memset(buffer, 0, FILTER_BUFFER_SIZE); - - applyFilter(buffer); - - // Queue the filtered data -#if 0 + // Prepare sound stream if (!_soundStream) - _soundStream = new AppendableSound(); + _soundStream = new StreamedSound(); - // FIXME: make sure the filtered sound buffer is disposed - _soundStream->queueBuffer((const byte *)buffer, FILTER_BUFFER_SIZE /* true */); -#else - free(buffer); + // Compute current filter id + int32 filterId = _status.status & kSoundStatusFilter; + // TODO adjust status (based on stepIndex) - // DEBUG: unfiltered stream - if (!_soundStream) - _soundStream = new StreamedSound(); + if (_queued) { + _soundStream->setFilterId(filterId); + } else { + _stream->seek(0); - _stream->seek(0); - _soundStream->load(_stream); -#endif + // Load the stream and start playing + _soundStream->load(_stream, filterId); - _queued = true; + _queued = true; + } } bool SoundEntry::isFinished() { @@ -238,8 +220,8 @@ void SoundEntry::setType(SoundFlag flag) { void SoundEntry::setupStatus(SoundFlag flag) { SoundStatus statusFlag = (SoundStatus)flag; - if (!((statusFlag & 0xFF) & kSoundStatusFilterVariant)) - statusFlag = (SoundStatus)(statusFlag | kSoundStatusClear2); + if (!((statusFlag & 0xFF) & kSoundStatusFilter)) + statusFlag = (SoundStatus)(statusFlag | kSoundStatusCached); if (((statusFlag & 0xFF00) >> 8) & kSoundStatusClear0) _status.status = (uint32)statusFlag; @@ -247,23 +229,7 @@ void SoundEntry::setupStatus(SoundFlag flag) { _status.status = (statusFlag | kSoundStatusClear4); } -void SoundEntry::setupCache() { - if (_soundBuffer) - return; - - // Original has a priority-based shared buffer (of 6 entries) - // We simply allocate a new buffer for each sound entry that needs it - _soundBuffer = (byte *)malloc(SOUNDCACHE_ENTRY_SIZE); - memset(_soundBuffer, 0, SOUNDCACHE_ENTRY_SIZE); - - setInCache(); -} - -void SoundEntry::setInCache() { - _status.status |= kSoundStatusClear2; -} - -void SoundEntry::loadSoundData(Common::String name) { +void SoundEntry::loadStream(Common::String name) { _name2 = name; // Load sound data @@ -272,12 +238,8 @@ void SoundEntry::loadSoundData(Common::String name) { if (!_stream) _stream = getArchive("DEFAULT.SND"); - if (_stream) { - _stream->read(_soundBuffer, MIN(SOUNDCACHE_ENTRY_SIZE, _stream->size())); - _currentDataPtr = _soundBuffer + 6; - } else { - _status.status = kSoundStatusRemoved; - } + if (!_stream) + _status.status = kSoundStatusClosed; } void SoundEntry::update(uint val) { @@ -300,10 +262,60 @@ void SoundEntry::update(uint val) { } } +bool SoundEntry::updateSound() { + bool result; + char sub[16]; + + if (_status.status2 & 4) { + result = false; + } else { + if (_status.status2 & 0x80) { + if (_field_48 <= getSound()->getData2()) { + _status.status |= 0x20; + _status.status &= ~0x8000; + strcpy(sub, _name2.c_str()); + + int l = strlen(sub) + 1; + if (l - 1 > 4) + sub[l - 1 - 4] = 0; + showSubtitle(sub); + } + } else { + if (!(getSoundQueue()->getFlag() & 0x20)) { + if (!(_status.status3 & 8)) { + if (_entity) { + if (_entity < 0x80) { + updateEntryFlag(getSound()->getSoundFlag(_entity)); + } + } + } + } + //if (status.status2 & 0x40 && !((uint32)_status.status & 0x180) && v1->soundBuffer) + // Sound_FillSoundBuffer(v1); + } + result = true; + } + + return result; +} + +void SoundEntry::updateEntryFlag(SoundFlag flag) { + if (flag) { + if (getSoundQueue()->getFlag() & 0x20 && _type != kSoundType9 && _type != kSoundType7) + update(flag); + else + _status.status = flag + (_status.status & ~0x1F); + } else { + _variant = 0; + _status.status |= 0x80u; + _status.status &= ~0x10001F; + } +} + void SoundEntry::updateState() { if (getSoundQueue()->getFlag() & 32) { if (_type != kSoundType9 && _type != kSoundType7 && _type != kSoundType5) { - uint32 variant = _status.status & kSoundStatusFilterVariant; + uint32 variant = _status.status & kSoundStatusFilter; _status.status &= kSoundStatusClearAll; @@ -316,7 +328,7 @@ void SoundEntry::updateState() { } void SoundEntry::reset() { - _status.status |= kSoundStatusRemoved; + _status.status |= kSoundStatusClosed; _entity = kEntityPlayer; if (_stream) { @@ -372,366 +384,6 @@ void SoundEntry::saveLoadWithSerializer(Common::Serializer &s) { } ////////////////////////////////////////////////////////////////////////// -// Sound filters -////////////////////////////////////////////////////////////////////////// -static const int filterData[1424] = { - 0, 0, 0, 0, 128, 256, 384, 512, 0, 0, 0, 0, 128, 256, - 384, 512, 0, 0, 0, 0, 192, 320, 448, 576, 0, 0, 0, 0, - 192, 320, 448, 576, 64, 64, 64, 64, 256, 384, 512, 640, - 64, 64, 64, 64, 256, 384, 512, 640, 128, 128, 128, 128, - 320, 448, 576, 704, 128, 128, 128, 128, 320, 448, 576, - 704, 192, 192, 192, 192, 384, 512, 640, 768, 192, 192, - 192, 192, 384, 512, 640, 768, 256, 256, 256, 256, 448, - 576, 704, 832, 256, 256, 256, 256, 448, 576, 704, 832, - 320, 320, 320, 320, 512, 640, 768, 896, 320, 320, 320, - 320, 512, 640, 768, 896, 384, 384, 384, 384, 576, 704, - 832, 960, 384, 384, 384, 384, 576, 704, 832, 960, 448, - 448, 448, 448, 640, 768, 896, 1024, 448, 448, 448, 448, - 640, 768, 896, 1024, 512, 512, 512, 512, 704, 832, 960, - 1088, 512, 512, 512, 512, 704, 832, 960, 1088, 576, - 576, 576, 576, 768, 896, 1024, 1152, 576, 576, 576, - 576, 768, 896, 1024, 1152, 640, 640, 640, 640, 832, - 960, 1088, 1216, 640, 640, 640, 640, 832, 960, 1088, - 1216, 704, 704, 704, 704, 896, 1024, 1152, 1280, 704, - 704, 704, 704, 896, 1024, 1152, 1280, 768, 768, 768, - 768, 960, 1088, 1216, 1344, 768, 768, 768, 768, 960, - 1088, 1216, 1344, 832, 832, 832, 832, 1024, 1152, 1280, - 1408, 832, 832, 832, 832, 1024, 1152, 1280, 1408, 896, - 896, 896, 896, 1088, 1216, 1344, 1472, 896, 896, 896, - 896, 1088, 1216, 1344, 1472, 960, 960, 960, 960, 1152, - 1280, 1408, 1536, 960, 960, 960, 960, 1152, 1280, 1408, - 1536, 1024, 1024, 1024, 1024, 1216, 1344, 1472, 1600, - 1024, 1024, 1024, 1024, 1216, 1344, 1472, 1600, 1088, - 1088, 1088, 1088, 1280, 1408, 1536, 1664, 1088, 1088, - 1088, 1088, 1280, 1408, 1536, 1664, 1152, 1152, 1152, - 1152, 1344, 1472, 1600, 1728, 1152, 1152, 1152, 1152, - 1344, 1472, 1600, 1728, 1216, 1216, 1216, 1216, 1408, - 1536, 1664, 1792, 1216, 1216, 1216, 1216, 1408, 1536, - 1664, 1792, 1280, 1280, 1280, 1280, 1472, 1600, 1728, - 1856, 1280, 1280, 1280, 1280, 1472, 1600, 1728, 1856, - 1344, 1344, 1344, 1344, 1536, 1664, 1792, 1920, 1344, - 1344, 1344, 1344, 1536, 1664, 1792, 1920, 1408, 1408, - 1408, 1408, 1600, 1728, 1856, 1984, 1408, 1408, 1408, - 1408, 1600, 1728, 1856, 1984, 1472, 1472, 1472, 1472, - 1664, 1792, 1920, 2048, 1472, 1472, 1472, 1472, 1664, - 1792, 1920, 2048, 1536, 1536, 1536, 1536, 1728, 1856, - 1984, 2112, 1536, 1536, 1536, 1536, 1728, 1856, 1984, - 2112, 1600, 1600, 1600, 1600, 1792, 1920, 2048, 2176, - 1600, 1600, 1600, 1600, 1792, 1920, 2048, 2176, 1664, - 1664, 1664, 1664, 1856, 1984, 2112, 2240, 1664, 1664, - 1664, 1664, 1856, 1984, 2112, 2240, 1728, 1728, 1728, - 1728, 1920, 2048, 2176, 2304, 1728, 1728, 1728, 1728, - 1920, 2048, 2176, 2304, 1792, 1792, 1792, 1792, 1984, - 2112, 2240, 2368, 1792, 1792, 1792, 1792, 1984, 2112, - 2240, 2368, 1856, 1856, 1856, 1856, 2048, 2176, 2304, - 2432, 1856, 1856, 1856, 1856, 2048, 2176, 2304, 2432, - 1920, 1920, 1920, 1920, 2112, 2240, 2368, 2496, 1920, - 1920, 1920, 1920, 2112, 2240, 2368, 2496, 1984, 1984, - 1984, 1984, 2176, 2304, 2432, 2560, 1984, 1984, 1984, - 1984, 2176, 2304, 2432, 2560, 2048, 2048, 2048, 2048, - 2240, 2368, 2496, 2624, 2048, 2048, 2048, 2048, 2240, - 2368, 2496, 2624, 2112, 2112, 2112, 2112, 2304, 2432, - 2560, 2688, 2112, 2112, 2112, 2112, 2304, 2432, 2560, - 2688, 2176, 2176, 2176, 2176, 2368, 2496, 2624, 2752, - 2176, 2176, 2176, 2176, 2368, 2496, 2624, 2752, 2240, - 2240, 2240, 2240, 2432, 2560, 2688, 2816, 2240, 2240, - 2240, 2240, 2432, 2560, 2688, 2816, 2304, 2304, 2304, - 2304, 2496, 2624, 2752, 2880, 2304, 2304, 2304, 2304, - 2496, 2624, 2752, 2880, 2368, 2368, 2368, 2368, 2560, - 2688, 2816, 2944, 2368, 2368, 2368, 2368, 2560, 2688, - 2816, 2944, 2432, 2432, 2432, 2432, 2624, 2752, 2880, - 3008, 2432, 2432, 2432, 2432, 2624, 2752, 2880, 3008, - 2496, 2496, 2496, 2496, 2688, 2816, 2944, 3072, 2496, - 2496, 2496, 2496, 2688, 2816, 2944, 3072, 2560, 2560, - 2560, 2560, 2752, 2880, 3008, 3136, 2560, 2560, 2560, - 2560, 2752, 2880, 3008, 3136, 2624, 2624, 2624, 2624, - 2816, 2944, 3072, 3200, 2624, 2624, 2624, 2624, 2816, - 2944, 3072, 3200, 2688, 2688, 2688, 2688, 2880, 3008, - 3136, 3264, 2688, 2688, 2688, 2688, 2880, 3008, 3136, - 3264, 2752, 2752, 2752, 2752, 2944, 3072, 3200, 3328, - 2752, 2752, 2752, 2752, 2944, 3072, 3200, 3328, 2816, - 2816, 2816, 2816, 3008, 3136, 3264, 3392, 2816, 2816, - 2816, 2816, 3008, 3136, 3264, 3392, 2880, 2880, 2880, - 2880, 3072, 3200, 3328, 3456, 2880, 2880, 2880, 2880, - 3072, 3200, 3328, 3456, 2944, 2944, 2944, 2944, 3136, - 3264, 3392, 3520, 2944, 2944, 2944, 2944, 3136, 3264, - 3392, 3520, 3008, 3008, 3008, 3008, 3200, 3328, 3456, - 3584, 3008, 3008, 3008, 3008, 3200, 3328, 3456, 3584, - 3072, 3072, 3072, 3072, 3264, 3392, 3520, 3648, 3072, - 3072, 3072, 3072, 3264, 3392, 3520, 3648, 3136, 3136, - 3136, 3136, 3328, 3456, 3584, 3712, 3136, 3136, 3136, - 3136, 3328, 3456, 3584, 3712, 3200, 3200, 3200, 3200, - 3392, 3520, 3648, 3776, 3200, 3200, 3200, 3200, 3392, - 3520, 3648, 3776, 3264, 3264, 3264, 3264, 3456, 3584, - 3712, 3840, 3264, 3264, 3264, 3264, 3456, 3584, 3712, - 3840, 3328, 3328, 3328, 3328, 3520, 3648, 3776, 3904, - 3328, 3328, 3328, 3328, 3520, 3648, 3776, 3904, 3392, - 3392, 3392, 3392, 3584, 3712, 3840, 3968, 3392, 3392, - 3392, 3392, 3584, 3712, 3840, 3968, 3456, 3456, 3456, - 3456, 3648, 3776, 3904, 4032, 3456, 3456, 3456, 3456, - 3648, 3776, 3904, 4032, 3520, 3520, 3520, 3520, 3712, - 3840, 3968, 4096, 3520, 3520, 3520, 3520, 3712, 3840, - 3968, 4096, 3584, 3584, 3584, 3584, 3776, 3904, 4032, - 4160, 3584, 3584, 3584, 3584, 3776, 3904, 4032, 4160, - 3648, 3648, 3648, 3648, 3840, 3968, 4096, 4224, 3648, - 3648, 3648, 3648, 3840, 3968, 4096, 4224, 3712, 3712, - 3712, 3712, 3904, 4032, 4160, 4288, 3712, 3712, 3712, - 3712, 3904, 4032, 4160, 4288, 3776, 3776, 3776, 3776, - 3968, 4096, 4224, 4352, 3776, 3776, 3776, 3776, 3968, - 4096, 4224, 4352, 3840, 3840, 3840, 3840, 4032, 4160, - 4288, 4416, 3840, 3840, 3840, 3840, 4032, 4160, 4288, - 4416, 3904, 3904, 3904, 3904, 4096, 4224, 4352, 4480, - 3904, 3904, 3904, 3904, 4096, 4224, 4352, 4480, 3968, - 3968, 3968, 3968, 4160, 4288, 4416, 4544, 3968, 3968, - 3968, 3968, 4160, 4288, 4416, 4544, 4032, 4032, 4032, - 4032, 4224, 4352, 4480, 4608, 4032, 4032, 4032, 4032, - 4224, 4352, 4480, 4608, 4096, 4096, 4096, 4096, 4288, - 4416, 4544, 4672, 4096, 4096, 4096, 4096, 4288, 4416, - 4544, 4672, 4160, 4160, 4160, 4160, 4352, 4480, 4608, - 4736, 4160, 4160, 4160, 4160, 4352, 4480, 4608, 4736, - 4224, 4224, 4224, 4224, 4416, 4544, 4672, 4800, 4224, - 4224, 4224, 4224, 4416, 4544, 4672, 4800, 4288, 4288, - 4288, 4288, 4480, 4608, 4736, 4864, 4288, 4288, 4288, - 4288, 4480, 4608, 4736, 4864, 4352, 4352, 4352, 4352, - 4544, 4672, 4800, 4928, 4352, 4352, 4352, 4352, 4544, - 4672, 4800, 4928, 4416, 4416, 4416, 4416, 4608, 4736, - 4864, 4992, 4416, 4416, 4416, 4416, 4608, 4736, 4864, - 4992, 4480, 4480, 4480, 4480, 4672, 4800, 4928, 5056, - 4480, 4480, 4480, 4480, 4672, 4800, 4928, 5056, 4544, - 4544, 4544, 4544, 4736, 4864, 4992, 5120, 4544, 4544, - 4544, 4544, 4736, 4864, 4992, 5120, 4608, 4608, 4608, - 4608, 4800, 4928, 5056, 5184, 4608, 4608, 4608, 4608, - 4800, 4928, 5056, 5184, 4672, 4672, 4672, 4672, 4864, - 4992, 5120, 5248, 4672, 4672, 4672, 4672, 4864, 4992, - 5120, 5248, 4736, 4736, 4736, 4736, 4928, 5056, 5184, - 5312, 4736, 4736, 4736, 4736, 4928, 5056, 5184, 5312, - 4800, 4800, 4800, 4800, 4992, 5120, 5248, 5376, 4800, - 4800, 4800, 4800, 4992, 5120, 5248, 5376, 4864, 4864, - 4864, 4864, 5056, 5184, 5312, 5440, 4864, 4864, 4864, - 4864, 5056, 5184, 5312, 5440, 4928, 4928, 4928, 4928, - 5120, 5248, 5376, 5504, 4928, 4928, 4928, 4928, 5120, - 5248, 5376, 5504, 4992, 4992, 4992, 4992, 5184, 5312, - 5440, 5568, 4992, 4992, 4992, 4992, 5184, 5312, 5440, - 5568, 5056, 5056, 5056, 5056, 5248, 5376, 5504, 5632, - 5056, 5056, 5056, 5056, 5248, 5376, 5504, 5632, 5120, - 5120, 5120, 5120, 5312, 5440, 5568, 5632, 5120, 5120, - 5120, 5120, 5312, 5440, 5568, 5632, 5184, 5184, 5184, - 5184, 5376, 5504, 5632, 5632, 5184, 5184, 5184, 5184, - 5376, 5504, 5632, 5632, 5248, 5248, 5248, 5248, 5440, - 5568, 5632, 5632, 5248, 5248, 5248, 5248, 5440, 5568, - 5632, 5632, 5312, 5312, 5312, 5312, 5504, 5632, 5632, - 5632, 5312, 5312, 5312, 5312, 5504, 5632, 5632, 5632, - 5376, 5376, 5376, 5376, 5568, 5632, 5632, 5632, 5376, - 5376, 5376, 5376, 5568, 5632, 5632, 5632, 5440, 5440, - 5440, 5440, 5632, 5632, 5632, 5632, 5440, 5440, 5440, - 5440, 5632, 5632, 5632, 5632, 5504, 5504, 5504, 5504, - 5632, 5632, 5632, 5632, 5504, 5504, 5504, 5504, 5632, - 5632, 5632, 5632, 5568, 5568, 5568, 5568, 5632, 5632, - 5632, 5632, 5568, 5568, 5568, 5568, 5632, 5632, 5632, - 5632 -}; - -static const int filterData2[1424] = { - 0, 2, 4, 6, 7, 9, 11, 13, 0, -2, -4, -6, -7, -9, -11, - -13, 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, - -11, -13, -15, 1, 3, 5, 7, 10, 12, 14, 16, -1, -3, -5, - -7, -10, -12, -14, -16, 1, 3, 6, 8, 11, 13, 16, 18, - -1, -3, -6, -8, -11, -13, -16, -18, 1, 4, 6, 9, 12, - 15, 17, 20, -1, -4, -6, -9, -12, -15, -17, -20, 1, 4, - 7, 10, 13, 16, 19, 22, -1, -4, -7, -10, -13, -16, -19, - -22, 1, 4, 8, 11, 14, 17, 21, 24, -1, -4, -8, -11, -14, - -17, -21, -24, 1, 5, 8, 12, 15, 19, 22, 26, -1, -5, - -8, -12, -15, -19, -22, -26, 2, 6, 10, 14, 18, 22, 26, - 30, -2, -6, -10, -14, -18, -22, -26, -30, 2, 6, 10, - 14, 19, 23, 27, 31, -2, -6, -10, -14, -19, -23, -27, - -31, 2, 7, 11, 16, 21, 26, 30, 35, -2, -7, -11, -16, - -21, -26, -30, -35, 2, 7, 13, 18, 23, 28, 34, 39, -2, - -7, -13, -18, -23, -28, -34, -39, 2, 8, 14, 20, 25, - 31, 37, 43, -2, -8, -14, -20, -25, -31, -37, -43, 3, - 9, 15, 21, 28, 34, 40, 46, -3, -9, -15, -21, -28, -34, - -40, -46, 3, 10, 17, 24, 31, 38, 45, 52, -3, -10, -17, - -24, -31, -38, -45, -52, 3, 11, 19, 27, 34, 42, 50, - 58, -3, -11, -19, -27, -34, -42, -50, -58, 4, 12, 21, - 29, 38, 46, 55, 63, -4, -12, -21, -29, -38, -46, -55, - -63, 4, 13, 23, 32, 41, 50, 60, 69, -4, -13, -23, -32, - -41, -50, -60, -69, 5, 15, 25, 35, 46, 56, 66, 76, -5, - -15, -25, -35, -46, -56, -66, -76, 5, 16, 28, 39, 50, - 61, 73, 84, -5, -16, -28, -39, -50, -61, -73, -84, 6, - 18, 31, 43, 56, 68, 81, 93, -6, -18, -31, -43, -56, - -68, -81, -93, 6, 20, 34, 48, 61, 75, 89, 103, -6, -20, - -34, -48, -61, -75, -89, -103, 7, 22, 37, 52, 67, 82, - 97, 112, -7, -22, -37, -52, -67, -82, -97, -112, 8, - 24, 41, 57, 74, 90, 107, 123, -8, -24, -41, -57, -74, - -90, -107, -123, 9, 27, 45, 63, 82, 100, 118, 136, -9, - -27, -45, -63, -82, -100, -118, -136, 10, 30, 50, 70, - 90, 110, 130, 150, -10, -30, -50, -70, -90, -110, -130, - -150, 11, 33, 55, 77, 99, 121, 143, 165, -11, -33, -55, - -77, -99, -121, -143, -165, 12, 36, 60, 84, 109, 133, - 157, 181, -12, -36, -60, -84, -109, -133, -157, -181, - 13, 40, 66, 93, 120, 147, 173, 200, -13, -40, -66, -93, - -120, -147, -173, -200, 14, 44, 73, 103, 132, 162, 191, - 221, -14, -44, -73, -103, -132, -162, -191, -221, 16, - 48, 81, 113, 146, 178, 211, 243, -16, -48, -81, -113, - -146, -178, -211, -243, 17, 53, 89, 125, 160, 196, 232, - 268, -17, -53, -89, -125, -160, -196, -232, -268, 19, - 58, 98, 137, 176, 215, 255, 294, -19, -58, -98, -137, - -176, -215, -255, -294, 21, 64, 108, 151, 194, 237, - 281, 324, -21, -64, -108, -151, -194, -237, -281, -324, - 23, 71, 118, 166, 213, 261, 308, 356, -23, -71, -118, - -166, -213, -261, -308, -356, 26, 78, 130, 182, 235, - 287, 339, 391, -26, -78, -130, -182, -235, -287, -339, - -391, 28, 86, 143, 201, 258, 316, 373, 431, -28, -86, - -143, -201, -258, -316, -373, -431, 31, 94, 158, 221, - 284, 347, 411, 474, -31, -94, -158, -221, -284, -347, - -411, -474, 34, 104, 174, 244, 313, 383, 453, 523, -34, - -104, -174, -244, -313, -383, -453, -523, 38, 115, 191, - 268, 345, 422, 498, 575, -38, -115, -191, -268, -345, - -422, -498, -575, 42, 126, 210, 294, 379, 463, 547, - 631, -42, -126, -210, -294, -379, -463, -547, -631, - 46, 139, 231, 324, 417, 510, 602, 695, -46, -139, -231, - -324, -417, -510, -602, -695, 51, 153, 255, 357, 459, - 561, 663, 765, -51, -153, -255, -357, -459, -561, -663, - -765, 56, 168, 280, 392, 505, 617, 729, 841, -56, -168, - -280, -392, -505, -617, -729, -841, 61, 185, 308, 432, - 555, 679, 802, 926, -61, -185, -308, -432, -555, -679, - -802, -926, 68, 204, 340, 476, 612, 748, 884, 1020, - -68, -204, -340, -476, -612, -748, -884, -1020, 74, - 224, 373, 523, 672, 822, 971, 1121, -74, -224, -373, - -523, -672, -822, -971, -1121, 82, 246, 411, 575, 740, - 904, 1069, 1233, -82, -246, -411, -575, -740, -904, - -1069, -1233, 90, 271, 452, 633, 814, 995, 1176, 1357, - -90, -271, -452, -633, -814, -995, -1176, -1357, 99, - 298, 497, 696, 895, 1094, 1293, 1492, -99, -298, -497, - -696, -895, -1094, -1293, -1492, 109, 328, 547, 766, - 985, 1204, 1423, 1642, -109, -328, -547, -766, -985, - -1204, -1423, -1642, 120, 361, 601, 842, 1083, 1324, - 1564, 1805, -120, -361, -601, -842, -1083, -1324, -1564, - -1805, 132, 397, 662, 927, 1192, 1457, 1722, 1987, -132, - -397, -662, -927, -1192, -1457, -1722, -1987, 145, 437, - 728, 1020, 1311, 1603, 1894, 2186, -145, -437, -728, - -1020, -1311, -1603, -1894, -2186, 160, 480, 801, 1121, - 1442, 1762, 2083, 2403, -160, -480, -801, -1121, -1442, - -1762, -2083, -2403, 176, 529, 881, 1234, 1587, 1940, - 2292, 2645, -176, -529, -881, -1234, -1587, -1940, -2292, - -2645, 194, 582, 970, 1358, 1746, 2134, 2522, 2910, - -194, -582, -970, -1358, -1746, -2134, -2522, -2910, - 213, 640, 1066, 1493, 1920, 2347, 2773, 3200, -213, - -640, -1066, -1493, -1920, -2347, -2773, -3200, 234, - 704, 1173, 1643, 2112, 2582, 3051, 3521, -234, -704, - -1173, -1643, -2112, -2582, -3051, -3521, 258, 774, - 1291, 1807, 2324, 2840, 3357, 3873, -258, -774, -1291, - -1807, -2324, -2840, -3357, -3873, 284, 852, 1420, 1988, - 2556, 3124, 3692, 4260, -284, -852, -1420, -1988, -2556, - -3124, -3692, -4260, 312, 937, 1561, 2186, 2811, 3436, - 4060, 4685, -312, -937, -1561, -2186, -2811, -3436, - -4060, -4685, 343, 1030, 1718, 2405, 3092, 3779, 4467, - 5154, -343, -1030, -1718, -2405, -3092, -3779, -4467, - -5154, 378, 1134, 1890, 2646, 3402, 4158, 4914, 5670, - -378, -1134, -1890, -2646, -3402, -4158, -4914, -5670, - 415, 1247, 2079, 2911, 3742, 4574, 5406, 6238, -415, - -1247, -2079, -2911, -3742, -4574, -5406, -6238, 457, - 1372, 2287, 3202, 4117, 5032, 5947, 6862, -457, -1372, - -2287, -3202, -4117, -5032, -5947, -6862, 503, 1509, - 2516, 3522, 4529, 5535, 6542, 7548, -503, -1509, -2516, - -3522, -4529, -5535, -6542, -7548, 553, 1660, 2767, - 3874, 4981, 6088, 7195, 8302, -553, -1660, -2767, -3874, - -4981, -6088, -7195, -8302, 608, 1826, 3044, 4262, 5479, - 6697, 7915, 9133, -608, -1826, -3044, -4262, -5479, - -6697, -7915, -9133, 669, 2009, 3348, 4688, 6027, 7367, - 8706, 10046, -669, -2009, -3348, -4688, -6027, -7367, - -8706, -10046, 736, 2210, 3683, 5157, 6630, 8104, 9577, - 11051, -736, -2210, -3683, -5157, -6630, -8104, -9577, - -11051, 810, 2431, 4052, 5673, 7294, 8915, 10536, 12157, - -810, -2431, -4052, -5673, -7294, -8915, -10536, -12157, - 891, 2674, 4457, 6240, 8023, 9806, 11589, 13372, -891, - -2674, -4457, -6240, -8023, -9806, -11589, -13372, 980, - 2941, 4903, 6864, 8825, 10786, 12748, 14709, -980, -2941, - -4903, -6864, -8825, -10786, -12748, -14709, 1078, 3236, - 5393, 7551, 9708, 11866, 14023, 16181, -1078, -3236, - -5393, -7551, -9708, -11866, -14023, -16181, 1186, 3559, - 5933, 8306, 10679, 13052, 15426, 17799, -1186, -3559, - -5933, -8306, -10679, -13052, -15426, -17799, 1305, - 3915, 6526, 9136, 11747, 14357, 16968, 19578, -1305, - -3915, -6526, -9136, -11747, -14357, -16968, -19578, - 1435, 4307, 7179, 10051, 12922, 15794, 18666, 21538, - -1435, -4307, -7179, -10051, -12922, -15794, -18666, - -21538, 1579, 4738, 7896, 11055, 14214, 17373, 20531, - 23690, -1579, -4738, -7896, -11055, -14214, -17373, - -20531, -23690, 1737, 5212, 8686, 12161, 15636, 19111, - 22585, 26060, -1737, -5212, -8686, -12161, -15636, -19111, - -22585, -26060, 1911, 5733, 9555, 13377, 17200, 21022, - 24844, 28666, -1911, -5733, -9555, -13377, -17200, -21022, - -24844, -28666, 2102, 6306, 10511, 14715, 18920, 23124, - 27329, 31533, -2102, -6306, -10511, -14715, -18920, - -23124, -27329, -31533, 2312, 6937, 11562, 16187, 20812, - 25437, 30062, 32767, -2312, -6937, -11562, -16187, -20812, - -25437, -30062, -32767, 2543, 7631, 12718, 17806, 22893, - 27981, 32767, 32767, -2543, -7631, -12718, -17806, -22893, - -27981, -32767, -32767, 2798, 8394, 13990, 19586, 25183, - 30779, 32767, 32767, -2798, -8394, -13990, -19586, -25183, - -30779, -32767, -32767, 3077, 9233, 15389, 21545, 27700, - 32767, 32767, 32767, -3077, -9233, -15389, -21545, -27700, - -32767, -32767, -32767, 3385, 10157, 16928, 23700, 30471, - 32767, 32767, 32767, -3385, -10157, -16928, -23700, - -30471, -32767, -32767, -32767, 3724, 11172, 18621, - 26069, 32767, 32767, 32767, 32767, -3724, -11172, -18621, - -26069, -32767, -32767, -32767, -32767, 4095, 12287, - 20479, 28671, 32767, 32767, 32767, 32767, -4095, -12287, - -20479, -28671, -32767, -32767, -32767, -32767 -}; - -static const int p1s[17] = { 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4, 0 }; -static const int p2s[17] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15, 1 }; - -static void soundFilter(byte *data, int16 *buffer, int p1, int p2); - -void SoundEntry::applyFilter(int16 *buffer) { - if ((READ_UINT16((int16 *)(_currentDataPtr + 2)) << 6) > 5632) { - _status.status |= kSoundStatus_20000000; - } else { - int variant = _status.status & kSoundStatusFilterVariant; - - soundFilter(_currentDataPtr, buffer, p1s[variant], p2s[variant]); - _currentDataPtr += 739; - } -} - -static void soundFilter(byte *data, int16 *buffer, int p1, int p2) { - int data1, data2, data1p, data2p; - byte idx; - - data2 = data[0]; - data1 = data[1] << 6; - - data += 2; - - for (int count = 0; count < 735; count++) { - idx = data[count] >> 4; - - if ((idx + data1) > ARRAYSIZE(filterData)) { - warning("Error in sound filter, aborting..."); - return; - } - - data1p = filterData[idx + data1]; - data2p = CLIP(filterData2[idx + data1] + data2, -32767, 32767); - - buffer[2 * count] = (p2 * data2p) >> p1; - - idx = data[count] & 0xF; - - if ((idx + data1p) > ARRAYSIZE(filterData)) { - warning("Error in sound filter, aborting..."); - return; - } - - data1 = filterData[idx + data1p]; - data2 = CLIP(filterData2[idx + data1p] + data2p, -32767, 32767); - buffer[2 * count + 1] = (p2 * data2) >> p1; - } -} - -////////////////////////////////////////////////////////////////////////// // SubtitleEntry ////////////////////////////////////////////////////////////////////////// SubtitleEntry::SubtitleEntry(LastExpressEngine *engine) : _engine(engine) { diff --git a/engines/lastexpress/sound/entry.h b/engines/lastexpress/sound/entry.h index a466d31b3a..a88b0b7210 100644 --- a/engines/lastexpress/sound/entry.h +++ b/engines/lastexpress/sound/entry.h @@ -102,7 +102,9 @@ public: void reset(); bool isFinished(); void update(uint val); + bool updateSound(); void updateState(); + void updateEntryFlag(SoundFlag flag); // Subtitles void showSubtitle(Common::String filename); @@ -124,8 +126,7 @@ public: Common::String getName2() { return _name2; } // Streams - SimpleSound *getSoundStream() { return _soundStream; } - byte *getSoundBuffer() { return _soundBuffer; } + SimpleSound *getSoundStream() { return _soundStream; } private: LastExpressEngine *_engine; @@ -141,7 +142,7 @@ private: //int _size; //int _field_28; Common::SeekableReadStream *_stream; // The file stream - //int _field_30; + //int _archive; int _field_34; int _field_38; int _field_3C; @@ -156,16 +157,11 @@ private: // Sound buffer & stream bool _queued; - byte *_soundBuffer; StreamedSound *_soundStream; // the filtered sound stream void setType(SoundFlag flag); void setupStatus(SoundFlag flag); - void setupCache(); - void setInCache(); - void loadSoundData(Common::String name); - - void applyFilter(int16 *buffer); + void loadStream(Common::String name); }; ////////////////////////////////////////////////////////////////////////// @@ -184,7 +180,7 @@ public: // Accessors SoundStatusUnion getStatus() { return _status; } - SoundEntry *getSoundEntry() { return _sound; } + SoundEntry *getSoundEntry() { return _sound; } private: LastExpressEngine *_engine; diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp index ad5c9bf4e3..33b4c06793 100644 --- a/engines/lastexpress/sound/queue.cpp +++ b/engines/lastexpress/sound/queue.cpp @@ -102,9 +102,41 @@ void SoundQueue::removeFromQueue(Common::String filename) { } void SoundQueue::updateQueue() { - //Common::StackLock locker(_mutex); + Common::StackLock locker(_mutex); + + ++_flag; + + if (getSoundState() & kSoundState1) { + SoundEntry *entry = getEntry(kSoundType1); + if (!entry || getFlags()->flag_3 || (entry && entry->getTime() > getSound()->getLoopingSoundDuration())) { + getSound()->playLoopingSound(0x45); + } else { + if (getSound()->getData1() && getSound()->getData2() >= getSound()->getData1()) { + entry->update(getSound()->getData0()); + getSound()->setData1(0); + } + } + } + + for (Common::List<SoundEntry *>::iterator it = _soundList.begin(); it != _soundList.end(); ++it) { + SoundEntry *entry = *it; + + // Original removes the entry data from the cache and sets the archive as not loaded + // and if the sound data buffer is not full, loads a new entry to be played based on + // its priority and filter id - //warning("[Sound::updateQueue] Not implemented"); + if (!entry->updateSound() && !(entry->getStatus().status3 & 0x8)) { + entry->close(); + SAFE_DELETE(entry); + it = _soundList.reverse_erase(it); + } + } + + // Original update the current entry, loading another set of samples to be decoded + + getFlags()->flag_3 = 0; + + --_flag; } void SoundQueue::resetQueue() { @@ -138,17 +170,10 @@ void SoundQueue::resetQueue(SoundType type1, SoundType type2) { } void SoundQueue::clearQueue() { - _flag |= 4; - - // FIXME: Wait a while for a flag to be set - //for (int i = 0; i < 3000000; i++) - // if (_flag & 8) - // break; + Common::StackLock locker(_mutex); _flag |= 8; - Common::StackLock locker(_mutex); - for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) { SoundEntry *entry = (*i); @@ -169,7 +194,7 @@ void SoundQueue::clearStatus() { Common::StackLock locker(_mutex); for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) - (*i)->setStatus((*i)->getStatus().status | kSoundStatusClear3); + (*i)->setStatus((*i)->getStatus().status | kSoundStatusClosed); } ////////////////////////////////////////////////////////////////////////// @@ -292,11 +317,11 @@ void SoundQueue::updateSubtitles() { if (!(status & kSoundStatus_40) || status & kSoundStatus_180 || soundEntry->getTime() == 0 - || (status & kSoundStatusFilterVariant) < 6 + || (status & kSoundStatusFilter) < 6 || ((getFlags()->nis & 0x8000) && soundEntry->getPriority() < 90)) { current_index = 0; } else { - current_index = soundEntry->getPriority() + (status & kSoundStatusFilterVariant); + current_index = soundEntry->getPriority() + (status & kSoundStatusFilter); if (_currentSubtitle == (*i)) current_index += 4; diff --git a/engines/lastexpress/sound/sound.cpp b/engines/lastexpress/sound/sound.cpp index c04b6d361f..2f7bb4a601 100644 --- a/engines/lastexpress/sound/sound.cpp +++ b/engines/lastexpress/sound/sound.cpp @@ -1305,8 +1305,8 @@ void SoundManager::playLoopingSound(int param) { int partNumber = 1; int fnameLen = 6; - if (_queue->getSoundState() & 1 && param >= 0x45 && param <= 0x46) { - if (_queue->getSoundState() & 2) { + if (_queue->getSoundState() & kSoundState1 && param >= 0x45 && param <= 0x46) { + if (_queue->getSoundState() & kSoundState2) { strcpy(tmp, "STEAM.SND"); _loopingSoundDuration = 32767; diff --git a/engines/lastexpress/sound/sound.h b/engines/lastexpress/sound/sound.h index 797e52646e..517543f470 100644 --- a/engines/lastexpress/sound/sound.h +++ b/engines/lastexpress/sound/sound.h @@ -64,7 +64,13 @@ public: // Accessors SoundQueue *getQueue() { return _queue; } - uint32 getData2() { return _data2; } + uint32 getData0() { return _data0; } + int32 getData1() { return _data1; } + int32 getData2() { return _data2; } + uint32 getLoopingSoundDuration() { return _loopingSoundDuration; } + + // Setters + void setData1(int32 data) { _data1 = data; } private: LastExpressEngine *_engine; @@ -78,8 +84,8 @@ private: // Unknown data uint32 _data0; - uint32 _data1; - uint32 _data2; + int32 _data1; + int32 _data2; }; } // End of namespace LastExpress diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index 3284a3228f..78e099ccfe 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -252,6 +252,17 @@ void LivingBooksCursorManager_v2::setCursor(uint16 id) { } } +void LivingBooksCursorManager_v2::setCursor(const Common::String &name) { + if (!_sysArchive) + return; + + uint16 id = _sysArchive->findResourceID(ID_TCUR, name); + if (id == 0xffff) + error("Could not find cursor '%s'", name.c_str()); + else + setCursor(id); +} + PECursorManager::PECursorManager(const Common::String &appName) { _exe = new Common::PEResources(); diff --git a/engines/mohawk/cursors.h b/engines/mohawk/cursors.h index d92b6b4285..7bfa491904 100644 --- a/engines/mohawk/cursors.h +++ b/engines/mohawk/cursors.h @@ -56,6 +56,7 @@ public: virtual void showCursor(); virtual void hideCursor(); virtual void setCursor(uint16 id); + virtual void setCursor(const Common::String &name) {} virtual void setDefaultCursor(); virtual bool hasSource() const { return false; } @@ -157,6 +158,7 @@ public: ~LivingBooksCursorManager_v2(); void setCursor(uint16 id); + void setCursor(const Common::String &name); bool hasSource() const { return _sysArchive != 0; } private: diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index f9d18ff7ff..65073bd970 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -204,9 +204,12 @@ Common::Error MohawkEngine_LivingBooks::run() { break; case Common::EVENT_LBUTTONDOWN: - for (uint16 i = 0; i < _items.size(); i++) - if (_items[i]->contains(event.mouse)) - found = _items[i]; + for (Common::List<LBItem *>::const_iterator i = _orderedItems.begin(); i != _orderedItems.end(); ++i) { + if ((*i)->contains(event.mouse)) { + found = *i; + break; + } + } if (found) found->handleMouseDown(event.mouse); @@ -341,6 +344,7 @@ void MohawkEngine_LivingBooks::destroyPage() { delete _page; assert(_items.empty()); + assert(_orderedItems.empty()); _page = NULL; _notifyEvents.clear(); @@ -567,6 +571,7 @@ void MohawkEngine_LivingBooks::updatePage() { case kLBDelayedEventDestroy: _items.remove_at(i); i--; + _orderedItems.remove(delayedEvent.item); delete delayedEvent.item; _page->itemDestroyed(delayedEvent.item); if (_focus == delayedEvent.item) @@ -613,6 +618,8 @@ void MohawkEngine_LivingBooks::removeArchive(Archive *archive) { void MohawkEngine_LivingBooks::addItem(LBItem *item) { _items.push_back(item); + _orderedItems.push_front(item); + item->_iterator = _orderedItems.begin(); } void MohawkEngine_LivingBooks::removeItems(const Common::Array<LBItem *> &items) { @@ -626,6 +633,7 @@ void MohawkEngine_LivingBooks::removeItems(const Common::Array<LBItem *> &items) break; } assert(found); + _orderedItems.erase(items[i]->_iterator); } } @@ -1319,8 +1327,13 @@ void MohawkEngine_LivingBooks::handleNotify(NotifyEvent &event) { if (getGameType() == GType_LIVINGBOOKSV1) { debug(2, "kLBNotifyChangeMode: %d", event.param); quitGame(); - } else { - debug(2, "kLBNotifyChangeMode: mode %d, page %d.%d", + break; + } + + debug(2, "kLBNotifyChangeMode: v2 type %d", event.param); + switch (event.param) { + case 1: + debug(2, "kLBNotifyChangeMode:, mode %d, page %d.%d", event.newMode, event.newPage, event.newSubpage); // TODO: what is entry.newUnknown? if (!event.newMode) @@ -1331,6 +1344,13 @@ void MohawkEngine_LivingBooks::handleNotify(NotifyEvent &event) { error("kLBNotifyChangeMode failed to move to mode %d, page %d.%d", event.newMode, event.newPage, event.newSubpage); } + break; + case 3: + debug(2, "kLBNotifyChangeMode: new cursor '%s'", event.newCursor.c_str()); + _cursor->setCursor(event.newCursor); + break; + default: + error("unknown v2 kLBNotifyChangeMode type %d", event.param); } break; @@ -2084,16 +2104,32 @@ LBScriptEntry *LBItem::parseScriptEntry(uint16 type, uint16 &size, Common::Memor } if (type == kLBNotifyScript && entry->opcode == kLBNotifyChangeMode && _vm->getGameType() != GType_LIVINGBOOKSV1) { - if (size < 8) { - error("%d unknown bytes in notify entry kLBNotifyChangeMode", size); + switch (entry->param) { + case 1: + if (size < 8) + error("%d unknown bytes in notify entry kLBNotifyChangeMode", size); + entry->newUnknown = stream->readUint16(); + entry->newMode = stream->readUint16(); + entry->newPage = stream->readUint16(); + entry->newSubpage = stream->readUint16(); + debug(4, "kLBNotifyChangeMode: unknown %04x, mode %d, page %d.%d", + entry->newUnknown, entry->newMode, entry->newPage, entry->newSubpage); + size -= 8; + break; + case 3: + { + Common::String newCursor = _vm->readString(stream); + entry->newCursor = newCursor; + if (size < newCursor.size() + 1) + error("failed to read newCursor in notify entry"); + size -= newCursor.size() + 1; + debug(4, "kLBNotifyChangeMode: new cursor '%s'", newCursor.c_str()); + } + break; + default: + // the original engine also does something when param==2 (but not a notify) + error("unknown v2 kLBNotifyChangeMode type %d", entry->param); } - entry->newUnknown = stream->readUint16(); - entry->newMode = stream->readUint16(); - entry->newPage = stream->readUint16(); - entry->newSubpage = stream->readUint16(); - debug(4, "kLBNotifyChangeMode: unknown %04x, mode %d, page %d.%d", - entry->newUnknown, entry->newMode, entry->newPage, entry->newSubpage); - size -= 8; } if (entry->opcode == kLBOpSendExpression) { if (size < 4) @@ -2577,6 +2613,7 @@ void LBItem::runScript(uint event, uint16 data, uint16 from) { notifyEvent.newMode = entry->newMode; notifyEvent.newPage = entry->newPage; notifyEvent.newSubpage = entry->newSubpage; + notifyEvent.newCursor = entry->newCursor; _vm->addNotifyEvent(notifyEvent); } else _vm->addNotifyEvent(NotifyEvent(entry->opcode, entry->param)); diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index ad2fe56a52..27e703a578 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -133,7 +133,9 @@ enum { kLBEventMouseUp = 5, kLBEventPhaseMain = 6, kLBEventNotified = 7, + kLBEventDragStart = 8, kLBEventDragMove = 9, + kLBEventDragEnd = 0xa, kLBEventRolloverBegin = 0xb, kLBEventRolloverMove = 0xc, kLBEventRolloverEnd = 0xd, @@ -271,6 +273,7 @@ struct LBScriptEntry { uint16 newMode; uint16 newPage; uint16 newSubpage; + Common::String newCursor; // kLBEventNotified uint16 matchFrom; @@ -405,6 +408,8 @@ public: uint16 getSoundPriority() { return _soundMode; } bool isAmbient() { return _isAmbient; } + Common::List<LBItem *>::iterator _iterator; + protected: MohawkEngine_LivingBooks *_vm; LBPage *_page; @@ -608,6 +613,7 @@ struct NotifyEvent { uint16 newMode; uint16 newPage; uint16 newSubpage; + Common::String newCursor; }; enum DelayedEventType { @@ -667,7 +673,7 @@ public: GUI::Debugger *getDebugger() { return _console; } void addArchive(Archive *archive); - void removeArchive(Archive *Archive); + void removeArchive(Archive *archive); void addItem(LBItem *item); void removeItems(const Common::Array<LBItem *> &items); @@ -714,6 +720,7 @@ private: uint16 _phase; LBPage *_page; Common::Array<LBItem *> _items; + Common::List<LBItem *> _orderedItems; Common::Queue<DelayedEvent> _eventQueue; LBItem *_focus; void destroyPage(); diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp index e9ef2516e2..80b5fe9660 100644 --- a/engines/mohawk/livingbooks_code.cpp +++ b/engines/mohawk/livingbooks_code.cpp @@ -686,8 +686,8 @@ struct CodeCommandInfo { CodeCommandInfo generalCommandInfo[NUM_GENERAL_COMMANDS] = { { "eval", &LBCode::cmdEval }, { "random", &LBCode::cmdRandom }, - { "stringLen", 0 }, - { "substring", 0 }, + { "stringLen", &LBCode::cmdStringLen }, + { "substring", &LBCode::cmdSubstring }, { "max", 0 }, { "min", 0 }, { "abs", 0 }, @@ -861,6 +861,31 @@ void LBCode::cmdRandom(const Common::Array<LBValue> ¶ms) { _stack.push(_vm->_rnd->getRandomNumberRng(min, max)); } +void LBCode::cmdStringLen(const Common::Array<LBValue> ¶ms) { + if (params.size() != 1) + error("incorrect number of parameters (%d) to stringLen", params.size()); + + const Common::String &string = params[0].toString(); + _stack.push(string.size()); +} + +void LBCode::cmdSubstring(const Common::Array<LBValue> ¶ms) { + if (params.size() != 3) + error("incorrect number of parameters (%d) to substring", params.size()); + + const Common::String &string = params[0].toString(); + uint begin = params[1].toInt(); + uint end = params[2].toInt(); + if (begin == 0) + error("invalid substring call (%d to %d)", begin, end); + if (begin > end || end > string.size()) { + _stack.push(Common::String()); + return; + } + Common::String substring(string.c_str() + (begin - 1), end - begin + 1); + _stack.push(substring); +} + void LBCode::cmdGetRect(const Common::Array<LBValue> ¶ms) { if (params.size() < 2) { _stack.push(getRectFromParams(params)); @@ -1156,8 +1181,8 @@ bool LBCode::parseCodeSymbol(const Common::String &name, uint &pos, Common::Arra // first, check whether the name matches a known function for (uint i = 0; i < 2; i++) { byte cmdToken; - CodeCommandInfo *cmdInfo; - uint cmdCount; + CodeCommandInfo *cmdInfo = NULL; + uint cmdCount = 0; switch (i) { case 0: diff --git a/engines/mohawk/livingbooks_code.h b/engines/mohawk/livingbooks_code.h index 9c58ed7a46..79c9af94f7 100644 --- a/engines/mohawk/livingbooks_code.h +++ b/engines/mohawk/livingbooks_code.h @@ -222,6 +222,8 @@ public: void cmdUnimplemented(const Common::Array<LBValue> ¶ms); void cmdEval(const Common::Array<LBValue> ¶ms); void cmdRandom(const Common::Array<LBValue> ¶ms); + void cmdStringLen(const Common::Array<LBValue> ¶ms); + void cmdSubstring(const Common::Array<LBValue> ¶ms); void cmdGetRect(const Common::Array<LBValue> ¶ms); void cmdTopLeft(const Common::Array<LBValue> ¶ms); void cmdBottomRight(const Common::Array<LBValue> ¶ms); diff --git a/engines/saga/actor.h b/engines/saga/actor.h index a4f475660d..d9d4b70168 100644 --- a/engines/saga/actor.h +++ b/engines/saga/actor.h @@ -650,7 +650,7 @@ private: public: #ifdef ACTOR_DEBUG #ifndef SAGA_DEBUG - you must also define SAGA_DEBUG + #error You must also define SAGA_DEBUG #endif //path debug - use with care struct DebugPoint { diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index 7a98fe4164..2f1b61eed8 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -67,9 +67,6 @@ int SagaEngine::getGameId() const { return _gameDescription->gameId; } uint32 SagaEngine::getFeatures() const { uint32 result = _gameDescription->features; - if (_gf_wyrmkeep) - result |= GF_WYRMKEEP; - return result; } diff --git a/engines/saga/detection_tables.h b/engines/saga/detection_tables.h index ab73fcba6e..a29d835a54 100644 --- a/engines/saga/detection_tables.h +++ b/engines/saga/detection_tables.h @@ -221,7 +221,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NOSPEECH }, GID_ITE, - GF_WYRMKEEP | GF_SCENE_SUBSTITUTES | GF_MONO_MUSIC | GF_LE_VOICES, + 0, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), @@ -247,7 +247,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NOSPEECH }, GID_ITE, - GF_WYRMKEEP | GF_LE_VOICES, + 0, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), @@ -273,7 +273,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NONE }, GID_ITE, - GF_WYRMKEEP | GF_SCENE_SUBSTITUTES, + 0, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), @@ -299,7 +299,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NONE }, GID_ITE, - GF_WYRMKEEP | GF_8BIT_UNSIGNED_PCM, + GF_8BIT_UNSIGNED_PCM, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), @@ -356,7 +356,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NONE }, GID_ITE, - GF_WYRMKEEP, + 0, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITEWINDEMO_GameFonts), @@ -388,7 +388,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NONE }, GID_ITE, - GF_WYRMKEEP, + 0, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITE_GameFonts), @@ -418,7 +418,7 @@ static const SAGAGameDescription gameDescriptions[] = { GUIO_NONE }, GID_ITE, - GF_WYRMKEEP, + 0, ITE_DEFAULT_SCENE, &ITE_Resources, ARRAYSIZE(ITE_GameFonts), diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp index 87fd48e2d2..9248f2b530 100644 --- a/engines/saga/introproc_ite.cpp +++ b/engines/saga/introproc_ite.cpp @@ -179,21 +179,22 @@ enum { EventColumns *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]) { int game; Common::Language lang; + bool hasWyrmkeepCredits = (Common::File::exists("credit3n.dlt") || // PC + Common::File::exists("credit3m.dlt")); // Mac // The assumption here is that all WyrmKeep versions have the same // credits, regardless of which operating system they're for. lang = _vm->getLanguage(); - if (_vm->getFeatures() & GF_WYRMKEEP) { + if (hasWyrmkeepCredits) game = kITEWyrmKeep; - } else if (_vm->getPlatform() == Common::kPlatformMacintosh) { + else if (_vm->getPlatform() == Common::kPlatformMacintosh) game = kITEMac; - } else if (_vm->getFeatures() & GF_EXTRA_ITE_CREDITS) { + else if (_vm->getFeatures() & GF_EXTRA_ITE_CREDITS) game = kITEPCCD; - } else { + else game = kITEPC; - } int line_spacing = 0; int paragraph_spacing; @@ -303,6 +304,11 @@ int Scene::SC_ITEIntroAnimProc(int param, void *refCon) { int Scene::ITEIntroAnimProc(int param) { Event event; EventColumns *eventColumns; + bool isMac = _vm->getPlatform() == Common::kPlatformMacintosh; + bool isMultiCD = _vm->getPlatform() == Common::kPlatformUnknown; + bool hasWyrmkeepCredits = (Common::File::exists("credit3n.dlt") || // PC + Common::File::exists("credit3m.dlt")); // Mac + bool isDemo = Common::File::exists("scriptsd.rsc"); switch (param) { case SCENE_BEGIN:{ @@ -324,19 +330,10 @@ int Scene::ITEIntroAnimProc(int param) { // playback int lastAnim; - if (_vm->getFeatures() & GF_WYRMKEEP) { - if (_vm->getPlatform() == Common::kPlatformMacintosh) { - lastAnim = 3; - } else { - lastAnim = 2; - } - } else { - if (_vm->getPlatform() == Common::kPlatformMacintosh) { - lastAnim = 4; - } else { - lastAnim = 5; - } - } + if (hasWyrmkeepCredits || isMultiCD || isDemo) + lastAnim = isMac ? 3 : 2; + else + lastAnim = isMac ? 4 : 5; for (int i = 0; i < lastAnim; i++) _vm->_anim->link(i, i+1); diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp index 21f3cc489e..49d3f91d77 100644 --- a/engines/saga/music.cpp +++ b/engines/saga/music.cpp @@ -287,7 +287,12 @@ void Music::play(uint32 resourceId, MusicFlags flags) { if (_vm->isBigEndian()) musicFlags &= ~Audio::FLAG_LITTLE_ENDIAN; - if (_vm->getFeatures() & GF_MONO_MUSIC) + // The newer ITE Mac demo version contains a music file, but it has mono music. + // This is the only music file that is about 7MB, whereas all the other ones + // are much larger. Thus, we use this simple heuristic to determine if we got + // mono music in the ITE demos or not. + if (!strcmp(_digitalMusicContext->fileName(), "musicd.rsc") && + _digitalMusicContext->fileSize() < 8000000) musicFlags &= ~Audio::FLAG_STEREO; audioStream = Audio::makeRawStream(musicStream, 11025, musicFlags, DisposeAfterUse::YES); @@ -368,10 +373,12 @@ void Music::play(uint32 resourceId, MusicFlags flags) { void Music::pause() { _player->pause(); + _player->setVolume(0); } void Music::resume() { _player->resume(); + _player->setVolume(_vm->_musicVolume); } void Music::stop() { diff --git a/engines/saga/resource.cpp b/engines/saga/resource.cpp index 72b021309c..1b0dfa2f22 100644 --- a/engines/saga/resource.cpp +++ b/engines/saga/resource.cpp @@ -162,12 +162,6 @@ bool Resource::createContexts() { uint16 voiceFileAddType; }; - - // If the Wyrmkeep credits file is found, set the Wyrmkeep version flag to true - if (Common::File::exists("credit3n.dlt")) { - _vm->_gf_wyrmkeep = true; - } - for (const ADGameFileDescription *gameFileDescription = _vm->getFilesDescriptions(); gameFileDescription->fileName; gameFileDescription++) { addContext(gameFileDescription->fileName, gameFileDescription->fileType); diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index d168605e99..6e272d37c0 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -72,9 +72,8 @@ SagaEngine::SagaEngine(OSystem *syst, const SAGAGameDescription *gameDesc) _readingSpeed = 0; _copyProtection = false; - _gf_wyrmkeep = false; _musicWasPlaying = false; - + _hasITESceneSubstitutes = false; _sndRes = NULL; _sound = NULL; @@ -211,9 +210,9 @@ Common::Error SagaEngine::run() { _subtitlesEnabled = ConfMan.getBool("subtitles"); _readingSpeed = getTalkspeed(); _copyProtection = ConfMan.getBool("copy_protection"); - _gf_wyrmkeep = false; _musicWasPlaying = false; _isIHNMDemo = Common::File::exists("music.res"); + _hasITESceneSubstitutes = Common::File::exists("boarhall.bbm"); if (_readingSpeed > 3) _readingSpeed = 0; diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 23258e1277..336883680a 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -137,16 +137,12 @@ enum GameFileTypes { }; enum GameFeatures { - GF_WYRMKEEP = 1 << 0, - GF_ITE_FLOPPY = 1 << 1, - GF_SCENE_SUBSTITUTES = 1 << 2, + GF_ITE_FLOPPY = 1 << 0, #if 0 - GF_OLD_ITE_DOS = 1 << 3, // Currently unused + GF_OLD_ITE_DOS = 1 << 1, // Currently unused #endif - GF_MONO_MUSIC = 1 << 4, - GF_EXTRA_ITE_CREDITS = 1 << 5, - GF_LE_VOICES = 1 << 6, - GF_8BIT_UNSIGNED_PCM = 1 << 7 + GF_EXTRA_ITE_CREDITS = 1 << 2, + GF_8BIT_UNSIGNED_PCM = 1 << 3 }; enum VerbTypeIds { @@ -532,9 +528,9 @@ public: int _readingSpeed; bool _copyProtection; - bool _gf_wyrmkeep; bool _musicWasPlaying; bool _isIHNMDemo; + bool _hasITESceneSubstitutes; SndRes *_sndRes; Sound *_sound; diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp index 66ee8f4504..61e62d5626 100644 --- a/engines/saga/scene.cpp +++ b/engines/saga/scene.cpp @@ -451,7 +451,7 @@ void Scene::changeScene(int16 sceneNumber, int actorsEntrance, SceneTransitionTy // This is used for latter ITE demos where all places on world map except // Tent Faire are substituted with LBM picture and short description - if (_vm->getFeatures() & GF_SCENE_SUBSTITUTES) { + if (_vm->_hasITESceneSubstitutes) { for (int i = 0; i < ARRAYSIZE(sceneSubstitutes); i++) { if (sceneSubstitutes[i].sceneId == sceneNumber) { Surface bbmBuffer; diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp index 2433c93e93..add34e22a2 100644 --- a/engines/saga/sndres.cpp +++ b/engines/saga/sndres.cpp @@ -262,9 +262,12 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff buffer.flags |= Audio::FLAG_UNSIGNED; buffer.flags &= ~Audio::FLAG_16BITS; } else { - // Voice files in newer ITE demo versions are OKI ADPCM (VOX) encoded - if (!uncompressedSound && !scumm_stricmp(context->fileName(), "voicesd.rsc")) + // Voice files in newer ITE demo versions are OKI ADPCM (VOX) encoded. + // These are LE in all the Windows and Mac demos + if (!uncompressedSound && !scumm_stricmp(context->fileName(), "voicesd.rsc")) { resourceType = kSoundVOX; + buffer.flags |= Audio::FLAG_LITTLE_ENDIAN; + } } } buffer.buffer = NULL; @@ -272,8 +275,6 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff // Check for LE sounds if (!context->isBigEndian()) buffer.flags |= Audio::FLAG_LITTLE_ENDIAN; - if ((context->fileType() & GAME_VOICEFILE) && (_vm->getFeatures() & GF_LE_VOICES)) - buffer.flags |= Audio::FLAG_LITTLE_ENDIAN; // Older Mac versions of ITE were Macbinary packed int soundOffset = (context->fileType() & GAME_MACBINARY) ? 36 : 0; diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 6641f243e6..3b18a1f68d 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -3150,6 +3150,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + + // Slater & Charlie Go Camping - English DOS/Windows (Sierra Originals) + {"slater", "", { + {"resource.000", 0, "d7b4cc8e2c0b3a4768f8dfb5de27f206", 2256126}, + {"resource.map", 0, "21f85414124dc23e54544a5536dc35cd", 4044}, + {"resource.msg", 0, "c44f51fb955eae266fecf360ebcd5ad2", 1132}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Space Quest 1 VGA Remake - English Amiga (from www.back2roots.org) // SCI interpreter version 1.000.510 (just a guess) {"sq1sci", "SCI", { diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 9f10691767..7b8db22e3f 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -730,6 +730,10 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { case 8: { // Dup const char *rawString = 0; uint32 size = 0; + reg_t stringHandle; + // We allocate the new string first because if the StringTable needs to + // grow, our rawString pointer will be invalidated + SciString *dupString = s->_segMan->allocateString(&stringHandle); if (argv[1].segment == s->_segMan->getStringSegmentId()) { SciString *string = s->_segMan->lookupString(argv[1]); @@ -741,8 +745,6 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { size = string.size() + 1; } - reg_t stringHandle; - SciString *dupString = s->_segMan->allocateString(&stringHandle); dupString->setSize(size); for (uint32 i = 0; i < size; i++) diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index eaae64dc77..b38bd3b674 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -49,35 +49,28 @@ void ScummEngine::loadCJKFont() { _newLineCharacter = 0; if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji -#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE +#if defined(DISABLE_TOWNS_DUAL_LAYER_MODE) || !defined(USE_RGB_COLOR) GUIErrorMessage("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build"); error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build"); #else // use FM-TOWNS font rom, since game files don't have kanji font resources - _cjkFont = Graphics::FontSJIS::createFont(Common::kPlatformFMTowns); + _cjkFont = Graphics::FontSJIS::createFont(_game.platform); if (!_cjkFont) error("SCUMM::Font: Could not open file 'FMT_FNT.ROM'"); _textSurfaceMultiplier = 2; _useCJKMode = true; #endif } else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) { - int numChar = 3418; - _2byteWidth = 12; - _2byteHeight = 12; +#ifdef USE_RGB_COLOR // use PC-Engine System Card, since game files don't have kanji font resources - if (!fp.open("pce.cdbios")) { - error("SCUMM::Font: Could not open System Card pce.cdbios"); - } else { - _useCJKMode = true; - debug(2, "Loading PC-Engine System Card"); - - // A 0x200 byte header can be present at the beginning of the syscard. Seek past it too. - fp.seek((fp.size() & 0x200) ? 0x30200 : 0x30000); + _cjkFont = Graphics::FontSJIS::createFont(_game.platform); + if (!_cjkFont) + error("SCUMM::Font: Could not open file 'pce.cdbios'"); - _2byteFontPtr = new byte[_2byteWidth * _2byteHeight * numChar / 8]; - fp.read(_2byteFontPtr, _2byteWidth * _2byteHeight * numChar / 8); - fp.close(); - } + _cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode); + _2byteWidth = _2byteHeight = 12; + _useCJKMode = true; +#endif } else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) { int numChar = 1413; _2byteWidth = 16; @@ -161,71 +154,16 @@ void ScummEngine::loadCJKFont() { } } -static int SJIStoPCEChunk(int f, int s) { //converts sjis code to pce font offset - // rangeTbl maps SJIS char-codes to the PCE System Card font rom. - // Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range. - const int rangeCnt = 45; - static const uint16 rangeTbl[rangeCnt][2] = { - // Symbols - {0x8140,0x817E},{0x8180,0x81AC}, - // 0-9 - {0x824F,0x8258}, - // Latin upper - {0x8260,0x8279}, - // Latin lower - {0x8281,0x829A}, - // Kana - {0x829F,0x82F1},{0x8340,0x837E},{0x8380,0x8396}, - // Greek upper - {0x839F,0x83B6}, - // Greek lower - {0x83BF,0x83D6}, - // Cyrillic upper - {0x8440,0x8460}, - // Cyrillic lower - {0x8470,0x847E},{0x8480,0x8491}, - // Kanji - {0x889F,0x88FC}, - {0x8940,0x897E},{0x8980,0x89FC}, - {0x8A40,0x8A7E},{0x8A80,0x8AFC}, - {0x8B40,0x8B7E},{0x8B80,0x8BFC}, - {0x8C40,0x8C7E},{0x8C80,0x8CFC}, - {0x8D40,0x8D7E},{0x8D80,0x8DFC}, - {0x8E40,0x8E7E},{0x8E80,0x8EFC}, - {0x8F40,0x8F7E},{0x8F80,0x8FFC}, - {0x9040,0x907E},{0x9080,0x90FC}, - {0x9140,0x917E},{0x9180,0x91FC}, - {0x9240,0x927E},{0x9280,0x92FC}, - {0x9340,0x937E},{0x9380,0x93FC}, - {0x9440,0x947E},{0x9480,0x94FC}, - {0x9540,0x957E},{0x9580,0x95FC}, - {0x9640,0x967E},{0x9680,0x96FC}, - {0x9740,0x977E},{0x9780,0x97FC}, - {0x9840,0x9872} - }; - - int ch = (f << 8) | (s & 0xFF); - int offset = 0; - for (int i = 0; i < rangeCnt; ++i) { - if (ch >= rangeTbl[i][0] && ch <= rangeTbl[i][1]) - return offset + ch - rangeTbl[i][0]; - offset += rangeTbl[i][1] - rangeTbl[i][0] + 1; - } - - debug(4, "Invalid Char: 0x%x", ch); - return 0; -} - byte *ScummEngine::get2byteCharPtr(int idx) { + if (_game.platform == Common::kPlatformFMTowns || _game.platform == Common::kPlatformPCEngine) + return 0; + switch (_language) { case Common::KO_KOR: idx = ((idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1; break; case Common::JA_JPN: - if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) { - idx = SJIStoPCEChunk((idx % 256), (idx / 256)); - return _2byteFontPtr + (_2byteWidth * _2byteHeight / 8) * idx; - } else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) { + if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) { // init pointer to charset resource if (_2byteFontPtr[0] == 0xFF) { int charsetId = 5; @@ -314,7 +252,7 @@ CharsetRenderer::~CharsetRenderer() { CharsetRendererCommon::CharsetRendererCommon(ScummEngine *vm) : CharsetRenderer(vm), _bytesPerPixel(0), _fontHeight(0), _numChars(0) { - _shadowMode = kNoShadowMode; + _shadowMode = false; _shadowColor = 0; } @@ -362,17 +300,9 @@ void CharsetRendererV3::setCurID(int32 id) { } int CharsetRendererCommon::getFontHeight() { - if (_vm->_useCJKMode) { - if (_vm->_game.platform == Common::kPlatformFMTowns) { - static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 }; - static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 }; - static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 }; - const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2); - return (_vm->_game.version == 3) ? 8 : htbl[_curId]; - } else { - return MAX(_vm->_2byteHeight + 1, _fontHeight); - } - } else + if (_vm->_useCJKMode) + return MAX(_vm->_2byteHeight + 1, _fontHeight); + else return _fontHeight; } @@ -380,57 +310,16 @@ int CharsetRendererCommon::getFontHeight() { int CharsetRendererClassic::getCharWidth(uint16 chr) { int spacing = 0; - if (_vm->_useCJKMode) { - if (_vm->_game.platform == Common::kPlatformFMTowns) { - if ((chr & 0xff00) == 0xfd00) { - chr &= 0xff; - } else if (chr >= 256) { - spacing = 8; - } else if (useTownsFontRomCharacter(chr)) { - spacing = 4; - } + if (_vm->_useCJKMode && chr >= 0x80) + return _vm->_2byteWidth / 2; - if (spacing) { - if (_vm->_game.id == GID_MONKEY) { - spacing++; - if (_curId == 2) - spacing++; - } else if (_vm->_game.id != GID_INDY4 && _curId == 1) { - spacing++; - } - } - - } else if (chr >= 0x80) { - return _vm->_2byteWidth / 2; - } - } - - if (!spacing) { - int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - if (offs) { - spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2]; - } - } + int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); + if (offs) + spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2]; return spacing; } -bool CharsetRendererClassic::useTownsFontRomCharacter(uint16 chr) { -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (_vm->_game.platform != Common::kPlatformFMTowns || !_vm->_useCJKMode) - return false; - - if (chr < 128) { - if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127)) - return true; - return false; - } - return true; -#else - return false; -#endif -} - int CharsetRenderer::getStringWidth(int arg, const byte *text) { int pos = 0; int width = 1; @@ -608,22 +497,51 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { int CharsetRendererV3::getCharWidth(uint16 chr) { int spacing = 0; - if (_vm->_useCJKMode) { - if (_vm->_game.platform == Common::kPlatformFMTowns) { - if (chr >= 256) - spacing = 8; - else if (chr >= 128) - spacing = 4; - } else if (chr & 0x80) { - spacing = _vm->_2byteWidth / 2; + if (_vm->_useCJKMode && (chr & 0x80)) + spacing = _vm->_2byteWidth / 2; + + if (!spacing) + spacing = *(_widthTable + chr); + + return spacing; +} + +void CharsetRendererV3::enableShadow(bool enable) { + _shadowColor = 0; + _shadowMode = enable; +} + +void CharsetRendererV3::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { + int y, x; + byte bits = 0; + uint8 col = _color; + int pitch = s.pitch - width * bitDepth; + byte *dst2 = dst + s.pitch; + + for (y = 0; y < height && y + drawTop < s.h; y++) { + for (x = 0; x < width; x++) { + if ((x % 8) == 0) + bits = *src++; + if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) { + if (_shadowMode) + dst[1] = dst2[0] = dst2[1] = _shadowColor; + dst[0] = col; + } + dst += bitDepth; + dst2 += bitDepth; } - } - if (!spacing) { - spacing = *(_widthTable + chr); + dst += pitch; + dst2 += pitch; } +} - return spacing; +int CharsetRendererV3::getDrawWidthIntern(uint16 chr) { + return getCharWidth(chr); +} + +int CharsetRendererV3::getDrawHeightIntern(uint16) { + return 8; } void CharsetRendererV3::setColor(byte color) { @@ -662,43 +580,6 @@ void CharsetRendererPCE::setColor(byte color) { } #endif -void CharsetRendererCommon::enableShadow(bool enable) { - if (enable) { - if (_vm->_game.platform == Common::kPlatformFMTowns) { - _shadowColor = 8; -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - _shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88; - if (_vm->_cjkFont) { - if (_vm->_game.version == 5) { - if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) || - ((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) || - ((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) { - _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode); - } else { - _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode); - } - _vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3); - } else { - _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode); - } - } -#endif - _shadowMode = kFMTOWNSShadowMode; - } else { - _shadowColor = 0; - _shadowMode = kNormalShadowMode; - } - } else { -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (_vm->_cjkFont) { - _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode); - _vm->_cjkFont->toggleFlippedMode(false); - } -#endif - _shadowMode = kNoShadowMode; - } -} - void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { // WORKAROUND for bug #1509509: Indy3 Mac does not show black // characters (such as in the grail diary) if ignoreCharsetMask @@ -721,33 +602,19 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { if (chr == '@') return; -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (_vm->_useCJKMode && chr > 127) { - if (_vm->_game.platform == Common::kPlatformFMTowns) { - charPtr = 0; - width = _vm->_cjkFont->getCharWidth(chr); - height = _vm->_cjkFont->getFontHeight(); - } else { - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; - charPtr = _vm->get2byteCharPtr(chr); - } - } else -#endif - { - charPtr = _fontPtr + chr * 8; - width = getCharWidth(chr); - height = 8; - } + charPtr = (_vm->_useCJKMode && chr > 127) ? _vm->get2byteCharPtr(chr) : _fontPtr + chr * 8; + width = getDrawWidthIntern(chr); + height = getDrawHeightIntern(chr); + setDrawCharIntern(chr); + + origWidth = width; + origHeight = height; // Clip at the right side (to avoid drawing "outside" the screen bounds). if (_left + origWidth > _right + 1) return; - origWidth = width; - origHeight = height; - - if (_shadowMode != kNoShadowMode) { + if (_shadowMode) { width++; height++; } @@ -769,30 +636,17 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { _textScreenID = vs->number; } - if ( -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - (_vm->_game.platform != Common::kPlatformFMTowns) && -#endif - (ignoreCharsetMask || !vs->hasTwoBuffers)) { + if ((ignoreCharsetMask || !vs->hasTwoBuffers)) { dst = vs->getPixels(_left, drawTop); - if (charPtr) - drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->format.bytesPerPixel); -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - else if (_vm->_cjkFont) - _vm->_cjkFont->drawChar(*vs, chr, _left, drawTop, _color, _shadowColor); -#endif + drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->format.bytesPerPixel); } else { dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier); - if (charPtr) - drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.format.bytesPerPixel, (_vm->_textSurfaceMultiplier == 2 && !is2byte)); -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - else if (_vm->_cjkFont) - _vm->_cjkFont->drawChar(_vm->_textSurface, chr, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, _color, _shadowColor); -#endif - if (is2byte) { - origWidth /= _vm->_textSurfaceMultiplier; - height /= _vm->_textSurfaceMultiplier; - } + drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.format.bytesPerPixel); + } + + if (is2byte) { + origWidth /= _vm->_textSurfaceMultiplier; + height /= _vm->_textSurfaceMultiplier; } if (_str.left > _left) @@ -802,7 +656,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { if (_str.right < _left) { _str.right = _left; - if (_shadowMode != kNoShadowMode) + if (_shadowMode) _str.right++; } @@ -811,30 +665,12 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { } void CharsetRendererV3::drawChar(int chr, Graphics::Surface &s, int x, int y) { - const byte *charPtr; - byte *dst; - int width, height; - int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; - if (is2byte) { -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (_vm->_game.platform == Common::kPlatformFMTowns) { - _vm->_cjkFont->drawChar(s, chr, x * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, _color, _shadowColor); - return; - } - else -#endif - { - charPtr = _vm->get2byteCharPtr(chr); - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; - } - } else { - charPtr = _fontPtr + chr * 8; -// width = height = 8; - width = getCharWidth(chr); - height = 8; - } - dst = (byte *)s.pixels + y * s.pitch + x; + const byte *charPtr = (_vm->_useCJKMode && chr > 127) ? _vm->get2byteCharPtr(chr) : _fontPtr + chr * 8; + int width = getDrawWidthIntern(chr); + int height = getDrawHeightIntern(chr); + setDrawCharIntern(chr); + + byte *dst = (byte *)s.pixels + y * s.pitch + x; drawBits1(s, dst, charPtr, y, width, height, s.format.bytesPerPixel); } @@ -853,29 +689,6 @@ void CharsetRenderer::translateColor() { } } -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE -void CharsetRenderer::processTownsCharsetColors(uint8 bytesPerPixel) { - if (_vm->_game.platform == Common::kPlatformFMTowns) { - for (int i = 0; i < (1 << bytesPerPixel); i++) { - uint8 c = _vm->_charsetColorMap[i]; - - if (c > 16) { - uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12; - t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10); - t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9); - c = t; - } - - if (c == 0) - c = _vm->_townsOverrideShadowColor; - - c = ((c & 0x0f) << 4) | (c & 0x0f); - _vm->_townsCharsetColorMap[i] = c; - } - } -} -#endif - void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) { static const SaveLoadEntry charsetRendererEntries[] = { MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)), @@ -893,10 +706,7 @@ void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) { } void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { - int width, height, origWidth, origHeight; - int offsX, offsY; VirtScreen *vs; - const byte *charPtr; bool is2byte = (chr >= 256 && _vm->_useCJKMode); assertRange(1, _curId, _vm->_numCharsets - 1, "charset"); @@ -911,64 +721,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _vm->_charsetColorMap[1] = _color; -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - processTownsCharsetColors(_bytesPerPixel); - bool noSjis = false; - - if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) { - if ((chr & 0x00ff) == 0x00fd) { - chr >>= 8; - noSjis = true; - } - } - - if (useTownsFontRomCharacter(chr) && !noSjis) { - charPtr = 0; - _vm->_cjkChar = chr; - enableShadow(true); - - width = getCharWidth(chr); - // For whatever reason MI1 uses a different font width - // for alignment calculation and for drawing when - // charset 2 is active. This fixes some subtle glitches. - if (_vm->_game.id == GID_MONKEY && _curId == 2) - width--; - origWidth = width; - - origHeight = height = getFontHeight(); - offsX = offsY = 0; - } else if (_vm->_useCJKMode && (chr >= 128) && !noSjis) { - enableShadow(true); - origWidth = width = _vm->_2byteWidth; - origHeight = height = _vm->_2byteHeight; - charPtr = _vm->get2byteCharPtr(chr); - offsX = offsY = 0; - if (_shadowMode != kNoShadowMode) { - width++; - height++; - } - } else -#endif - { - uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - assert(charOffs < 0x14000); - if (!charOffs) - return; - charPtr = _fontPtr + charOffs; - - width = origWidth = charPtr[0]; - height = origHeight = charPtr[1]; - - if (_disableOffsX) { - offsX = 0; - } else { - offsX = (signed char)charPtr[2]; - } - - offsY = (signed char)charPtr[3]; - - charPtr += 4; // Skip over char header - } + prepareDraw(chr); if (_firstChar) { _str.left = 0; @@ -977,12 +730,12 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _str.bottom = 0; } - _top += offsY; - _left += offsX; + _top += _offsY; + _left += _offsX; - if (_left + origWidth > _right + 1 || _left < 0) { - _left += origWidth; - _top -= offsY; + if (_left + _origWidth > _right + 1 || _left < 0) { + _left += _origWidth; + _top -= _offsY; return; } @@ -1004,33 +757,29 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { int drawTop = _top - vs->topline; - _vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height); + _vm->markRectAsDirty(vs->number, _left, _left + _width, drawTop, drawTop + _height); // This check for kPlatformFMTowns and kMainVirtScreen is at least required for the chat with // the navigator's head in front of the ghost ship in Monkey Island 1 - if (!ignoreCharsetMask -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen) -#endif - ) { + if (!ignoreCharsetMask || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)) { _hasMask = true; _textScreenID = vs->number; } - printCharIntern(is2byte, charPtr, origWidth, origHeight, width, height, vs, ignoreCharsetMask); + printCharIntern(is2byte, _charPtr, _origWidth, _origHeight, _width, _height, vs, ignoreCharsetMask); - _left += origWidth; + _left += _origWidth; if (_str.right < _left) { _str.right = _left; - if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode != kNoShadowMode) + if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode) _str.right++; } - if (_str.bottom < _top + origHeight) - _str.bottom = _top + origHeight; + if (_str.bottom < _top + _origHeight) + _str.bottom = _top + _origHeight; - _top -= offsY; + _top -= _offsY; } void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask) { @@ -1068,11 +817,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, } else { Graphics::Surface dstSurface; Graphics::Surface backSurface; - if ((ignoreCharsetMask || !vs->hasTwoBuffers) -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - && (_vm->_game.platform != Common::kPlatformFMTowns) -#endif - ) { + if ((ignoreCharsetMask || !vs->hasTwoBuffers)) { dstSurface = *vs; dstPtr = vs->getPixels(_left, drawTop); } else { @@ -1091,16 +836,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, drawTop = _top - _vm->_screenTop; } -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (!charPtr && _vm->_cjkFont) { - _vm->_cjkFont->drawChar(dstSurface, _vm->_cjkChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor); - } else -#endif - if (is2byte) { - drawBits1(dstSurface, dstPtr, charPtr, drawTop, origWidth, origHeight, dstSurface.format.bytesPerPixel); - } else { - drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight, _vm->_textSurfaceMultiplier == 2); - } + drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight); if (_blitAlso && vs->hasTwoBuffers) { // FIXME: Revisiting this code, I think the _blitAlso mode is likely broken @@ -1139,54 +875,34 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, } } -void CharsetRendererClassic::drawChar(int chr, Graphics::Surface &s, int x, int y) { - const byte *charPtr; - byte *dst; - int width, height; - int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; - - if (is2byte) { - enableShadow(true); -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (_vm->_game.platform == Common::kPlatformFMTowns) { - _vm->_cjkFont->drawChar(s, chr, x * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, _color, _shadowColor); - return; - } else -#endif - { - charPtr = _vm->get2byteCharPtr(chr); - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; - } - } else { - uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - assert(charOffs < 0x10000); - if (!charOffs) - return; - charPtr = _fontPtr + charOffs; +void CharsetRendererClassic::prepareDraw(uint16 chr) { + uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); + assert(charOffs < 0x14000); + if (!charOffs) + return; + _charPtr = _fontPtr + charOffs; - width = charPtr[0]; - height = charPtr[1]; + _width = _origWidth = _charPtr[0]; + _height = _origHeight = _charPtr[1]; - charPtr += 4; // Skip over char header + if (_disableOffsX) { + _offsX = 0; + } else { + _offsX = (signed char)_charPtr[2]; } - dst = (byte *)s.pixels + y * s.pitch + x; + _offsY = (signed char)_charPtr[3]; - if (is2byte) { - drawBits1(s, dst, charPtr, y, width, height, s.format.bytesPerPixel); - } else { - drawBitsN(s, dst, charPtr, *_fontPtr, y, width, height); - } + _charPtr += 4; // Skip over char header } -void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - bool scale2x) { -#else - bool) { -#endif +void CharsetRendererClassic::drawChar(int chr, Graphics::Surface &s, int x, int y) { + prepareDraw(chr); + byte *dst = (byte *)s.pixels + y * s.pitch + x; + drawBitsN(s, dst, _charPtr, *_fontPtr, y, _width, _height); +} +void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) { int y, x; int color; byte numbits, bits; @@ -1198,38 +914,13 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co numbits = 8; byte *cmap = _vm->_charsetColorMap; -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - byte *dst2 = dst; - - if (_vm->_game.platform == Common::kPlatformFMTowns) - cmap = _vm->_townsCharsetColorMap; - if (scale2x) { - dst2 += s.pitch; - pitch <<= 1; - } -#endif - for (y = 0; y < height && y + drawTop < s.h; y++) { for (x = 0; x < width; x++) { color = (bits >> (8 - bpp)) & 0xFF; - if (color && y + drawTop >= 0) { + if (color && y + drawTop >= 0) *dst = cmap[color]; - -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (scale2x) - dst[1] = dst2[0] = dst2[1] = dst[0]; -#endif - } dst++; - -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - if (scale2x) { - dst++; - dst2 += 2; - } -#endif - bits <<= bpp; numbits -= bpp; if (numbits == 0) { @@ -1238,52 +929,93 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co } } dst += pitch; + } +} + +CharsetRendererTownsV3::CharsetRendererTownsV3(ScummEngine *vm) : CharsetRendererV3(vm), _sjisCurChar(0) { +} + +int CharsetRendererTownsV3::getCharWidth(uint16 chr) { + int spacing = 0; + + if (_vm->_useCJKMode) { + if (chr >= 256) + spacing = 8; + else if (chr >= 128) + spacing = 4; + } + + if (!spacing) + spacing = *(_widthTable + chr); + + return spacing; +} + +int CharsetRendererTownsV3::getFontHeight() { + return _vm->_useCJKMode ? 8 : _fontHeight; +} + +void CharsetRendererTownsV3::enableShadow(bool enable) { + _shadowColor = 8; + _shadowMode = enable; + #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - dst2 += pitch; + _shadowColor = 0x88; +#ifdef USE_RGB_COLOR + if (_vm->_cjkFont) + _vm->_cjkFont->setDrawingMode(enable ? Graphics::FontSJIS::kFMTownsShadowMode : Graphics::FontSJIS::kDefaultMode); +#endif #endif - } } -void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, +void CharsetRendererTownsV3::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - bool scale2x) { +#ifdef USE_RGB_COLOR + if (_sjisCurChar) { + assert(_vm->_cjkFont); + _vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, _color, _shadowColor); + return; + } +#endif + + dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier); + int sfPitch = _vm->_textSurface.pitch; + int sfHeight = _vm->_textSurface.h; + bool scale2x = (_vm->_textSurfaceMultiplier == 2 && !(_sjisCurChar >= 256 && _vm->_useCJKMode)); #else - bool) { + int sfPitch = s.pitch; + int sfHeight = s.h; #endif int y, x; byte bits = 0; uint8 col = _color; - int pitch = s.pitch - width * bitDepth; - byte *dst2 = dst + s.pitch; + int pitch = sfPitch - width * bitDepth; + byte *dst2 = dst + sfPitch; #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE byte *dst3 = dst2; byte *dst4 = dst2; if (scale2x) { - dst3 = dst2 + s.pitch; - dst4 = dst3 + s.pitch; + dst3 = dst2 + sfPitch; + dst4 = dst3 + sfPitch; pitch <<= 1; } - if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) - col = _vm->_townsCharsetColorMap[1]; #endif - for (y = 0; y < height && y + drawTop < s.h; y++) { + for (y = 0; y < height && y + drawTop < sfHeight; y++) { for (x = 0; x < width; x++) { if ((x % 8) == 0) bits = *src++; if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) { if (bitDepth == 2) { - if (_shadowMode != kNoShadowMode) { + if (_shadowMode) { WRITE_UINT16(dst + 2, _vm->_16BitPalette[_shadowColor]); - WRITE_UINT16(dst + s.pitch, _vm->_16BitPalette[_shadowColor]); - if (_shadowMode != kFMTOWNSShadowMode) - WRITE_UINT16(dst + s.pitch + 2, _vm->_16BitPalette[_shadowColor]); + WRITE_UINT16(dst + sfPitch, _vm->_16BitPalette[_shadowColor]); } WRITE_UINT16(dst, _vm->_16BitPalette[_color]); } else { - if (_shadowMode != kNoShadowMode) { + if (_shadowMode) { #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE if (scale2x) { dst[2] = dst[3] = dst2[2] = dst2[3] = _shadowColor; @@ -1292,8 +1024,6 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con #endif { dst[1] = dst2[0] = _shadowColor; - if (_shadowMode != kFMTOWNSShadowMode) - dst2[1] = _shadowColor; } } dst[0] = col; @@ -1324,31 +1054,64 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con #endif } } +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE +int CharsetRendererTownsV3::getDrawWidthIntern(uint16 chr) { +#ifdef USE_RGB_COLOR + if (_vm->_useCJKMode && chr > 127) { + assert(_vm->_cjkFont); + return _vm->_cjkFont->getCharWidth(chr); + } +#endif + return CharsetRendererV3::getDrawWidthIntern(chr); +} +int CharsetRendererTownsV3::getDrawHeightIntern(uint16 chr) { #ifdef USE_RGB_COLOR -void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) { + if (_vm->_useCJKMode && chr > 127) { + assert(_vm->_cjkFont); + return _vm->_cjkFont->getFontHeight(); + } +#endif + return CharsetRendererV3::getDrawHeightIntern(chr); +} + +void CharsetRendererTownsV3::setDrawCharIntern(uint16 chr) { + _sjisCurChar = (_vm->_useCJKMode && chr > 127) ? chr : 0; +} +#endif + +#ifdef USE_RGB_COLOR +void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { + if (_sjisCurChar) { + assert(_vm->_cjkFont); + uint16 col1 = _color; + uint16 col2 = _shadowColor; + + if (s.format.bytesPerPixel == 2) { + col1 = _vm->_16BitPalette[col1]; + col2 = _vm->_16BitPalette[col2]; + } + + _vm->_cjkFont->drawChar(dst, _sjisCurChar, s.pitch, s.format.bytesPerPixel, col1, col2, -1, -1); + return; + } + int y, x; - int bitCount = 0; byte bits = 0; - const bool resetLineBitCount = (_vm->_language != Common::JA_JPN || width != 12); - for (y = 0; y < height && y + drawTop < s.h; y++) { - if (resetLineBitCount) - bitCount = 0; + int bitCount = 0; for (x = 0; x < width; x++) { if ((bitCount % 8) == 0) bits = *src++; if ((bits & revBitMask(bitCount % 8)) && y + drawTop >= 0) { if (bitDepth == 2) { - if (_shadowMode != kNoShadowMode) { + if (_shadowMode) WRITE_UINT16(dst + s.pitch + 2, _vm->_16BitPalette[_shadowColor]); - } WRITE_UINT16(dst, _vm->_16BitPalette[_color]); } else { - if (_shadowMode != kNoShadowMode) { + if (_shadowMode) *(dst + s.pitch + 1) = _shadowColor; - } *dst = _color; } } @@ -1359,6 +1122,22 @@ void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const dst += s.pitch - width * bitDepth; } } + +int CharsetRendererPCE::getDrawWidthIntern(uint16 chr) { + if (_vm->_useCJKMode && chr > 127) + return _vm->_2byteWidth; + return CharsetRendererV3::getDrawWidthIntern(chr); +} + +int CharsetRendererPCE::getDrawHeightIntern(uint16 chr) { + if (_vm->_useCJKMode && chr > 127) + return _vm->_2byteHeight; + return CharsetRendererV3::getDrawHeightIntern(chr); +} + +void CharsetRendererPCE::setDrawCharIntern(uint16 chr) { + _sjisCurChar = (_vm->_useCJKMode && chr > 127) ? chr : 0; +} #endif #ifdef ENABLE_SCUMM_7_8 @@ -1533,7 +1312,7 @@ void CharsetRendererNES::printChar(int chr, bool ignoreCharsetMask) { if (_str.right < _left) { _str.right = _left; - if (_shadowMode != kNoShadowMode) + if (_shadowMode) _str.right++; } @@ -1556,7 +1335,203 @@ void CharsetRendererNES::drawChar(int chr, Graphics::Surface &s, int x, int y) { drawBits1(s, dst, charPtr, y, width, height, s.format.bytesPerPixel); } -void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) { +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE +CharsetRendererTownsClassic::CharsetRendererTownsClassic(ScummEngine *vm) : CharsetRendererClassic(vm), _sjisCurChar(0) { +} + +int CharsetRendererTownsClassic::getCharWidth(uint16 chr) { + int spacing = 0; + + if (_vm->_useCJKMode) { + if ((chr & 0xff00) == 0xfd00) { + chr &= 0xff; + } else if (chr >= 256) { + spacing = 8; + } else if (useFontRomCharacter(chr)) { + spacing = 4; + } + + if (spacing) { + if (_vm->_game.id == GID_MONKEY) { + spacing++; + if (_curId == 2) + spacing++; + } else if (_vm->_game.id != GID_INDY4 && _curId == 1) { + spacing++; + } + } + } + + if (!spacing) { + int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); + if (offs) + spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2]; + } + + return spacing; +} + +int CharsetRendererTownsClassic::getFontHeight() { + static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 }; + static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 }; + static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 }; + const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2); + return _vm->_useCJKMode ? htbl[_curId] : _fontHeight; +} + +void CharsetRendererTownsClassic::drawBitsN(const Graphics::Surface&, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) { + if (_sjisCurChar) { + assert(_vm->_cjkFont); + _vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor); + return; + } + + bool scale2x = (_vm->_textSurfaceMultiplier == 2); + dst = (byte *)_vm->_textSurface.pixels + (_top - _vm->_screenTop) * _vm->_textSurface.pitch * _vm->_textSurfaceMultiplier + _left * _vm->_textSurfaceMultiplier; + + int y, x; + int color; + byte numbits, bits; + + int pitch = _vm->_textSurface.pitch - width; + + assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8); + bits = *src++; + numbits = 8; + byte *cmap = _vm->_charsetColorMap; + byte *dst2 = dst; + + if (_vm->_game.platform == Common::kPlatformFMTowns) + cmap = _vm->_townsCharsetColorMap; + if (scale2x) { + dst2 += _vm->_textSurface.pitch; + pitch <<= 1; + } + + for (y = 0; y < height && y + drawTop < _vm->_textSurface.h; y++) { + for (x = 0; x < width; x++) { + color = (bits >> (8 - bpp)) & 0xFF; + + if (color && y + drawTop >= 0) { + *dst = cmap[color]; + if (scale2x) + dst[1] = dst2[0] = dst2[1] = dst[0]; + } + dst++; + + if (scale2x) { + dst++; + dst2 += 2; + } + + bits <<= bpp; + numbits -= bpp; + if (numbits == 0) { + bits = *src++; + numbits = 8; + } + } + dst += pitch; + dst2 += pitch; + } +} + +void CharsetRendererTownsClassic::prepareDraw(uint16 chr) { + processCharsetColors(); + bool noSjis = false; + + if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) { + if ((chr & 0x00ff) == 0x00fd) { + chr >>= 8; + noSjis = true; + } + } + + if (useFontRomCharacter(chr) && !noSjis) { + setupShadowMode(); + _charPtr = 0; + _sjisCurChar = chr; + + _width = getCharWidth(chr); + // For whatever reason MI1 uses a different font width + // for alignment calculation and for drawing when + // charset 2 is active. This fixes some subtle glitches. + if (_vm->_game.id == GID_MONKEY && _curId == 2) + _width--; + _origWidth = _width; + + _origHeight = _height = getFontHeight(); + _offsX = _offsY = 0; + } else if (_vm->_useCJKMode && (chr >= 128) && !noSjis) { + setupShadowMode(); + _origWidth = _width = _vm->_2byteWidth; + _origHeight = _height = _vm->_2byteHeight; + _charPtr = _vm->get2byteCharPtr(chr); + _offsX = _offsY = 0; + if (_shadowMode) { + _width++; + _height++; + } + } else { + _sjisCurChar = 0; + CharsetRendererClassic::prepareDraw(chr); + } +} + +void CharsetRendererTownsClassic::setupShadowMode() { + _shadowMode = true; + _shadowColor = _vm->_townsCharsetColorMap[0]; + assert(_vm->_cjkFont); + + if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) || + ((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) || + ((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) { + _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode); + } else { + _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode); + } + + _vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3); +} + +bool CharsetRendererTownsClassic::useFontRomCharacter(uint16 chr) { + if (!_vm->_useCJKMode) + return false; + + // Some SCUMM 5 games contain hard coded logic to determine whether to use + // the SCUMM fonts or the FM-Towns font rom to draw a character. For the other + // games we will simply check for a character greater 127. + if (chr < 128) { + if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127)) + return true; + return false; + } + return true; +} + +void CharsetRendererTownsClassic::processCharsetColors() { + for (int i = 0; i < (1 << _bytesPerPixel); i++) { + uint8 c = _vm->_charsetColorMap[i]; + + if (c > 16) { + uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12; + t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10); + t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9); + c = t; + } + + if (c == 0) + c = _vm->_townsOverrideShadowColor; + + c = ((c & 0x0f) << 4) | (c & 0x0f); + _vm->_townsCharsetColorMap[i] = c; + } +} +#endif +#endif + +void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { for (int i = 0; i < 8; i++) { byte c0 = src[i]; byte c1 = src[i + 8]; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index 4c657b475e..b80db73a58 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -25,6 +25,7 @@ #include "common/scummsys.h" #include "common/rect.h" #include "graphics/sjis.h" +#include "scumm/scumm.h" #include "scumm/gfx.h" #include "scumm/saveload.h" @@ -78,10 +79,6 @@ public: void addLinebreaks(int a, byte *str, int pos, int maxwidth); void translateColor(); -#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE - void processTownsCharsetColors(uint8 bytesPerPixel); -#endif - virtual void setCurID(int32 id) = 0; int getCurID() { return _curId; } @@ -101,31 +98,26 @@ protected: int _fontHeight; int _numChars; - enum ShadowMode { - kNoShadowMode, - kFMTOWNSShadowMode, - kNormalShadowMode - }; byte _shadowColor; - ShadowMode _shadowMode; - - void enableShadow(bool enable); - virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false); - + bool _shadowMode; public: CharsetRendererCommon(ScummEngine *vm); void setCurID(int32 id); - int getFontHeight(); + virtual int getFontHeight(); }; class CharsetRendererClassic : public CharsetRendererCommon { protected: - void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, bool scale2x = false); + virtual void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height); + void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); + virtual void prepareDraw(uint16 chr); - void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); + int _width, _height, _origWidth, _origHeight; + int _offsX, _offsY; + const byte *_charPtr; public: CharsetRendererClassic(ScummEngine *vm) : CharsetRendererCommon(vm) {} @@ -134,18 +126,34 @@ public: void drawChar(int chr, Graphics::Surface &s, int x, int y); int getCharWidth(uint16 chr); +}; + +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE +class CharsetRendererTownsClassic : public CharsetRendererClassic { +public: + CharsetRendererTownsClassic(ScummEngine *vm); + + int getCharWidth(uint16 chr); + int getFontHeight(); - // Some SCUMM 5 games contain hard coded logic to determine whether to use - // the SCUMM fonts or the FM-Towns font rom to draw a character. For the other - // games we will simply check for a character greater 127. - bool useTownsFontRomCharacter(uint16 chr); +private: + void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height); + void prepareDraw(uint16 chr); + void setupShadowMode(); + bool useFontRomCharacter(uint16 chr); + void processCharsetColors(); + + uint16 _sjisCurChar; }; +#endif +#endif class CharsetRendererNES : public CharsetRendererCommon { protected: byte *_trTable; - void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false); + void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); public: CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {} @@ -160,6 +168,12 @@ public: class CharsetRendererV3 : public CharsetRendererCommon { protected: + virtual void enableShadow(bool enable); + virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); + virtual int getDrawWidthIntern(uint16 chr); + virtual int getDrawHeightIntern(uint16 chr); + virtual void setDrawCharIntern(uint16 chr) {} + const byte *_widthTable; public: @@ -169,16 +183,40 @@ public: void drawChar(int chr, Graphics::Surface &s, int x, int y); void setCurID(int32 id); void setColor(byte color); + virtual int getCharWidth(uint16 chr); +}; + +class CharsetRendererTownsV3 : public CharsetRendererV3 { +public: + CharsetRendererTownsV3(ScummEngine *vm); + int getCharWidth(uint16 chr); + int getFontHeight(); + +private: + void enableShadow(bool enable); + void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + int getDrawWidthIntern(uint16 chr); + int getDrawHeightIntern(uint16 chr); + void setDrawCharIntern(uint16 chr); +#endif + uint16 _sjisCurChar; }; #ifdef USE_RGB_COLOR class CharsetRendererPCE : public CharsetRendererV3 { -protected: - void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false); +private: + void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); + + int getDrawWidthIntern(uint16 chr); + int getDrawHeightIntern(uint16 chr); + void setDrawCharIntern(uint16 chr); + + uint16 _sjisCurChar; public: - CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm) {} + CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm), _sjisCurChar(0) {} void setColor(byte color); }; diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 5268231cff..0f01e39459 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -284,8 +284,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _16BitPalette = NULL; #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE _townsScreen = 0; +#ifdef USE_RGB_COLOR _cjkFont = 0; - _cjkChar = 0; +#endif #endif _shadowPalette = NULL; _shadowPaletteSize = 0; @@ -634,8 +635,10 @@ ScummEngine::~ScummEngine() { #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE delete _townsScreen; +#ifdef USE_RGB_COLOR delete _cjkFont; #endif +#endif delete _debugger; @@ -1351,13 +1354,23 @@ void ScummEngine::setupCharsetRenderer() { _charset = new CharsetRendererPCE(this); else #endif + if (_game.platform == Common::kPlatformFMTowns) + _charset = new CharsetRendererTownsV3(this); + else _charset = new CharsetRendererV3(this); #ifdef ENABLE_SCUMM_7_8 } else if (_game.version == 8) { _charset = new CharsetRendererNut(this); #endif } else { - _charset = new CharsetRendererClassic(this); +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) + _charset = new CharsetRendererTownsClassic(this); + else +#endif +#endif + _charset = new CharsetRendererClassic(this); } } diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 01bde90e1c..e503af750d 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -46,9 +46,10 @@ /* This disables the dual layer mode which is used in FM-Towns versions * of SCUMM games and which emulates the behavior of the original code. * The only purpose is code size reduction for certain backends. - * SCUMM 3 (FM-Towns) games will run in normal (DOS VGA) mode, which should - * work just fine in most situations. Some glitches might occur. SCUMM 5 games - * will not work without dual layer (and 16 bit color) support. + * SCUMM 3 (FM-Towns) games will run in English in normal (DOS VGA) mode, + * which should work just fine in most situations. Some glitches might + * occur. Japanese mode and SCUMM 5 FM-Towns games will not work without + * dual layer (and 16 bit color) support. */ #define DISABLE_TOWNS_DUAL_LAYER_MODE #endif @@ -345,6 +346,7 @@ class ResourceManager; class ScummEngine : public Engine { friend class ScummDebugger; friend class CharsetRenderer; + friend class CharsetRendererTownsClassic; friend class ResourceManager; public: @@ -1326,14 +1328,17 @@ public: // Exists both in V7 and in V72HE: byte VAR_NUM_GLOBAL_OBJS; +#ifdef USE_RGB_COLOR + // FM-Towns / PC-Engine specific + Graphics::FontSJIS *_cjkFont; +#endif + // FM-Towns specific #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE public: bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); byte _townsPaletteFlags; - byte _townsCharsetColorMap[16]; - Graphics::FontSJIS *_cjkFont; - uint16 _cjkChar; + byte _townsCharsetColorMap[16]; protected: void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h); diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp index 45d43c465e..81f4fc2ad5 100644 --- a/engines/sword25/gfx/image/vectorimage.cpp +++ b/engines/sword25/gfx/image/vectorimage.cpp @@ -247,6 +247,9 @@ VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, co return; } + // readout SWF size + flashRectToBSRect(bs); + // Get frame rate and frame count /* uint32 frameRate = */ bs.getUInt16(); diff --git a/engines/sword25/kernel/inputpersistenceblock.cpp b/engines/sword25/kernel/inputpersistenceblock.cpp index 2d45dfb640..cdce539c31 100644 --- a/engines/sword25/kernel/inputpersistenceblock.cpp +++ b/engines/sword25/kernel/inputpersistenceblock.cpp @@ -55,8 +55,8 @@ void InputPersistenceBlock::read(int16 &value) { void InputPersistenceBlock::read(signed int &value) { if (checkMarker(SINT_MARKER)) { - rawRead(&value, sizeof(signed int)); - value = convertEndianessFromStorageToSystem(value); + value = (int32)READ_LE_UINT32(_iter); + _iter += 4; } else { value = 0; } @@ -64,8 +64,8 @@ void InputPersistenceBlock::read(signed int &value) { void InputPersistenceBlock::read(uint &value) { if (checkMarker(UINT_MARKER)) { - rawRead(&value, sizeof(uint)); - value = convertEndianessFromStorageToSystem(value); + value = READ_LE_UINT32(_iter); + _iter += 4; } else { value = 0; } @@ -73,8 +73,10 @@ void InputPersistenceBlock::read(uint &value) { void InputPersistenceBlock::read(float &value) { if (checkMarker(FLOAT_MARKER)) { - rawRead(&value, sizeof(float)); - value = convertEndianessFromStorageToSystem(value); + uint32 tmp[1]; + tmp[0] = READ_LE_UINT32(_iter); + value = ((float *)tmp)[0]; + _iter += 4; } else { value = 0.0f; } @@ -82,12 +84,11 @@ void InputPersistenceBlock::read(float &value) { void InputPersistenceBlock::read(bool &value) { if (checkMarker(BOOL_MARKER)) { - uint uintBool; - rawRead(&uintBool, sizeof(float)); - uintBool = convertEndianessFromStorageToSystem(uintBool); + uint uintBool = READ_LE_UINT32(_iter); + _iter += 4; value = uintBool == 0 ? false : true; } else { - value = 0.0f; + value = false; } } @@ -117,13 +118,6 @@ void InputPersistenceBlock::readByteArray(Common::Array<byte> &value) { } } -void InputPersistenceBlock::rawRead(void *destPtr, size_t size) { - if (checkBlockSize(size)) { - memcpy(destPtr, &*_iter, size); - _iter += size; - } -} - bool InputPersistenceBlock::checkBlockSize(int size) { if (_data.end() - _iter >= size) { return true; diff --git a/engines/sword25/kernel/inputpersistenceblock.h b/engines/sword25/kernel/inputpersistenceblock.h index 7e68137246..2518d7e32c 100644 --- a/engines/sword25/kernel/inputpersistenceblock.h +++ b/engines/sword25/kernel/inputpersistenceblock.h @@ -69,7 +69,6 @@ public: private: bool checkMarker(byte marker); bool checkBlockSize(int size); - void rawRead(void *destPtr, size_t size); Common::Array<byte> _data; Common::Array<byte>::const_iterator _iter; diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp index cf28ea401f..e29d956e5f 100644 --- a/engines/sword25/kernel/outputpersistenceblock.cpp +++ b/engines/sword25/kernel/outputpersistenceblock.cpp @@ -43,19 +43,23 @@ OutputPersistenceBlock::OutputPersistenceBlock() { void OutputPersistenceBlock::write(signed int value) { writeMarker(SINT_MARKER); - value = convertEndianessFromSystemToStorage(value); + value = TO_LE_32(value); rawWrite(&value, sizeof(value)); } void OutputPersistenceBlock::write(uint value) { writeMarker(UINT_MARKER); - value = convertEndianessFromSystemToStorage(value); + value = TO_LE_32(value); rawWrite(&value, sizeof(value)); } void OutputPersistenceBlock::write(float value) { writeMarker(FLOAT_MARKER); - value = convertEndianessFromSystemToStorage(value); + uint32 tmp[1]; + + ((float *)tmp)[0] = value; + tmp[0] = TO_LE_32(tmp[0]); + rawWrite(&value, sizeof(value)); } @@ -63,7 +67,7 @@ void OutputPersistenceBlock::write(bool value) { writeMarker(BOOL_MARKER); uint uintBool = value ? 1 : 0; - uintBool = convertEndianessFromSystemToStorage(uintBool); + uintBool = TO_LE_32(uintBool); rawWrite(&uintBool, sizeof(uintBool)); } diff --git a/engines/sword25/kernel/persistenceblock.h b/engines/sword25/kernel/persistenceblock.h index d8440faa50..8ac3e84a41 100644 --- a/engines/sword25/kernel/persistenceblock.h +++ b/engines/sword25/kernel/persistenceblock.h @@ -64,48 +64,6 @@ protected: BLOCK_MARKER }; - // ----------------------------------------------------------------------------- - // Endianess Conversions - // ----------------------------------------------------------------------------- - // - // Everything is stored in Little Endian - // Big Endian Systems will need to be byte swapped during both saving and reading of saved values - // - - template<typename T> - static T convertEndianessFromSystemToStorage(T value) { - if (isBigEndian()) - reverseByteOrder(&value); - return value; - } - - template<typename T> - static T convertEndianessFromStorageToSystem(T value) { - if (isBigEndian()) - reverseByteOrder(&value); - return value; - } - -private: - static bool isBigEndian() { - uint dummy = 1; - byte *dummyPtr = reinterpret_cast<byte *>(&dummy); - return dummyPtr[0] == 0; - } - - template<typename T> - static void swap(T &one, T &two) { - T temp = one; - one = two; - two = temp; - } - - static void reverseByteOrder(void *ptr) { - // Reverses the byte order of the 32-bit word pointed to by Ptr - byte *charPtr = static_cast<byte *>(ptr); - swap(charPtr[0], charPtr[3]); - swap(charPtr[1], charPtr[2]); - } }; #define CTASSERT(ex) typedef char ctassert_type[(ex) ? 1 : -1] diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp index 06c6e21d21..022214157a 100644 --- a/engines/toon/character.cpp +++ b/engines/toon/character.cpp @@ -596,7 +596,8 @@ int32 Character::getId() { void Character::save(Common::WriteStream *stream) { debugC(1, kDebugCharacter, "save(stream)"); - stream->writeSint32LE(_flags); + // we have to save visibility too, put in flags to not invalidate old savegames. + stream->writeSint32LE(_flags | ((_visible == false) ? 0x100 : 0)); stream->writeSint32LE(_x); stream->writeSint32LE(_y); stream->writeSint32LE(_z); @@ -633,6 +634,12 @@ void Character::load(Common::ReadStream *stream) { if (_sceneAnimationId > -1) { setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance); } + + // "not visible" flag. + if (_flags & 0x100) { + _flags &= ~0x100; + setVisible(false); + } } void Character::setAnimScript(int32 animScriptId) { diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index 2318eaaac7..7637f4e62f 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -94,7 +94,7 @@ void Movie::play(Common::String video, int32 flags) { _vm->getAudioManager()->setMusicVolume(0); _decoder->loadFile(video.c_str()); playVideo(isFirstIntroVideo); - _vm->flushPalette(false); + _vm->flushPalette(true); if (flags & 1) _vm->getAudioManager()->setMusicVolume(_vm->getAudioManager()->isMusicMuted() ? 0 : 255); _decoder->close(); @@ -103,7 +103,6 @@ void Movie::play(Common::String video, int32 flags) { bool Movie::playVideo(bool isFirstIntroVideo) { debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo); - while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp index 0257964fb5..295e304765 100644 --- a/engines/toon/picture.cpp +++ b/engines/toon/picture.cpp @@ -29,16 +29,14 @@ namespace Toon { -bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { - debugC(1, kDebugPicture, "loadPicture(%s, %d)", file.c_str(), (totalPalette) ? 1 : 0); +bool Picture::loadPicture(Common::String file) { + debugC(1, kDebugPicture, "loadPicture(%s)", file.c_str()); uint32 size = 0; uint8 *fileData = _vm->resources()->getFileData(file, &size); if (!fileData) return false; - _useFullPalette = totalPalette; - uint32 compId = READ_BE_UINT32(fileData); switch (compId) { @@ -57,6 +55,8 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { // do we have a palette ? _paletteEntries = (dstsize & 0x7ff) / 3; + _useFullPalette = (_paletteEntries == 256); + // _useFullPalette = true; if (_paletteEntries) { _palette = new uint8[_paletteEntries * 3]; memcpy(_palette, _data + dstsize - (dstsize & 0x7ff), _paletteEntries * 3); @@ -70,7 +70,8 @@ bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) { uint32 decSize = READ_LE_UINT32(fileData + 10); _data = new uint8[decSize + 100]; _paletteEntries = READ_LE_UINT16(fileData + 14) / 3; - + _useFullPalette = (_paletteEntries == 256); + if (_paletteEntries) { _palette = new uint8[_paletteEntries * 3]; memcpy(_palette, fileData + 16, _paletteEntries * 3); diff --git a/engines/toon/picture.h b/engines/toon/picture.h index 23edbc91da..ee0e006702 100644 --- a/engines/toon/picture.h +++ b/engines/toon/picture.h @@ -38,7 +38,7 @@ class Picture { public: Picture(ToonEngine *vm); ~Picture(); - bool loadPicture(Common::String file, bool totalPalette = false); + bool loadPicture(Common::String file); void setupPalette(); void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy); void drawWithRectList(Graphics::Surface& surface, int32 x, int32 y, int32 dx, int32 dy, Common::Array<Common::Rect>& rectArray); diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index 26639d71f7..401ab3d70e 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -614,7 +614,7 @@ struct MainMenuEntry { bool ToonEngine::showMainmenu(bool &loadedGame) { Picture *mainmenuPicture = new Picture(this); - mainmenuPicture->loadPicture("TITLESCR.CPS", true); + mainmenuPicture->loadPicture("TITLESCR.CPS"); mainmenuPicture->setupPalette(); flushPalette(false); @@ -690,6 +690,11 @@ bool ToonEngine::showMainmenu(bool &loadedGame) { } } + if (_needPaletteFlush) { + flushPalette(false); + _needPaletteFlush = false; + } + parseInput(); copyToVirtualScreen(true); _system->delayMillis(17); @@ -2600,7 +2605,7 @@ int32 ToonEngine::showInventory() { delete _inventoryPicture; _inventoryPicture = new Picture(this); fadeOut(5); - _inventoryPicture->loadPicture("SACK128.CPS", true); + _inventoryPicture->loadPicture("SACK128.CPS"); _inventoryPicture->setupPalette(); dirtyAllScreen(); @@ -2786,7 +2791,7 @@ void ToonEngine::showCutaway(Common::String cutawayPicture) { if (cutawayPicture == "") { cutawayPicture = Common::String(_gameState->_locations[_gameState->_currentScene]._cutaway) + ".CPS"; } - _currentCutaway->loadPicture(cutawayPicture, false); + _currentCutaway->loadPicture(cutawayPicture); _currentCutaway->setupPalette(); _oldScrollValue = _gameState->_currentScrollValue; _gameState->_currentScrollValue = 0; @@ -3418,7 +3423,7 @@ void ToonEngine::viewInventoryItem(Common::String str, int32 lineId, int32 itemD fadeOut(5); Picture *pic = new Picture(this); - pic->loadPicture(str, false); + pic->loadPicture(str); pic->setupPalette(); dirtyAllScreen(); flushPalette(); diff --git a/engines/tsage/detection_tables.h b/engines/tsage/detection_tables.h index 8b80edf89d..f9ced562c2 100644 --- a/engines/tsage/detection_tables.h +++ b/engines/tsage/detection_tables.h @@ -24,7 +24,7 @@ namespace tSage { static const tSageGameDescription gameDescriptions[] = { - // Ringworld CD and First Wave versions + // Ringworld English CD and First Wave versions { { "ring", @@ -38,6 +38,20 @@ static const tSageGameDescription gameDescriptions[] = { GType_Ringworld, GF_CD | GF_ALT_REGIONS }, + // Ringworld Spanish CD + { + { + "ring", + "CD", + AD_ENTRY1s("ring.rlb", "cb8bba91b30cd172712371d7123bd763", 7427980), + Common::ES_ESP, + Common::kPlatformPC, + ADGF_UNSTABLE, + Common::GUIO_NOSPEECH | Common::GUIO_NOSFX + }, + GType_Ringworld, + GF_CD | GF_ALT_REGIONS + }, // Ringworld English Floppy version { { diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp index 8dd584ef24..e26b3d1544 100644 --- a/engines/tsage/sound.cpp +++ b/engines/tsage/sound.cpp @@ -2485,7 +2485,10 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() { _mixer = _vm->_mixer; _sampleRate = _mixer->getOutputRate(); - _opl = makeAdLibOPL(_sampleRate); + _opl = OPL::Config::create(); + assert(_opl); + _opl->init(_sampleRate); + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); Common::set_to(_channelVoiced, _channelVoiced + ADLIB_CHANNEL_COUNT, false); @@ -2504,7 +2507,7 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() { AdlibSoundDriver::~AdlibSoundDriver() { DEALLOCATE(_patchData); _mixer->stopHandle(_soundHandle); - OPLDestroy(_opl); + delete _opl; } bool AdlibSoundDriver::open() { @@ -2622,7 +2625,7 @@ void AdlibSoundDriver::flush() { while (!_queue.empty()) { RegisterValue v = _queue.pop(); - OPLWriteReg(_opl, v._regNum, v._value); + _opl->writeReg(v._regNum, v._value); } } @@ -2760,7 +2763,7 @@ void AdlibSoundDriver::update(int16 *buf, int len) { } samplesLeft -= count; len -= count; - YM3812UpdateOne(_opl, buf, count); + _opl->readBuffer(buf, count); if (samplesLeft == 0) { flush(); samplesLeft = _sampleRate / 50; diff --git a/graphics/sjis.cpp b/graphics/sjis.cpp index 10c780b156..be078a4da9 100644 --- a/graphics/sjis.cpp +++ b/graphics/sjis.cpp @@ -40,10 +40,27 @@ FontSJIS *FontSJIS::createFont(const Common::Platform platform) { // Try the font ROM of the specified platform if (platform == Common::kPlatformFMTowns) { ret = new FontTowns(); - if (ret && ret->loadData()) - return ret; + if (ret) { + if (ret->loadData()) + return ret; + } delete ret; - } + } else if (platform == Common::kPlatformPCEngine) { + ret = new FontPCEngine(); + if (ret) { + if (ret->loadData()) + return ret; + } + delete ret; + } // TODO: PC98 font rom support + /* else if (platform == Common::kPlatformPC98) { + ret = new FontPC98(); + if (ret) { + if (ret->loadData()) + return ret; + } + delete ret; + }*/ // Try ScummVM's font. ret = new FontSjisSVM(platform); @@ -59,15 +76,21 @@ void FontSJIS::drawChar(Graphics::Surface &dst, uint16 ch, int x, int y, uint32 } FontSJISBase::FontSJISBase() - : _drawMode(kDefaultMode), _flippedMode(false), _fontWidth(16), _fontHeight(16) { + : _drawMode(kDefaultMode), _flippedMode(false), _fontWidth(16), _fontHeight(16), _bitPosNewLineMask(0) { } void FontSJISBase::setDrawingMode(DrawingMode mode) { - _drawMode = mode; + if (hasFeature(1 << mode)) + _drawMode = mode; + else + warning("Unsupported drawing mode selected"); } void FontSJISBase::toggleFlippedMode(bool enable) { - _flippedMode = enable; + if (hasFeature(kFeatFlipped)) + _flippedMode = enable; + else + warning("Flipped mode unsupported by this font"); } uint FontSJISBase::getFontHeight() const { @@ -98,26 +121,30 @@ uint FontSJISBase::getMaxFontWidth() const { uint FontSJISBase::getCharWidth(uint16 ch) const { if (isASCII(ch)) - return (_drawMode == kOutlineMode) ? 10 : (_drawMode == kDefaultMode ? 8 : 9); + return ((_drawMode == kOutlineMode) ? 10 : (_drawMode == kDefaultMode ? 8 : 9)); else return getMaxFontWidth(); } template<typename Color> void FontSJISBase::blitCharacter(const uint8 *glyph, const int w, const int h, uint8 *dst, int pitch, Color c) const { + uint8 bitPos = 0; + uint8 mask = 0; + for (int y = 0; y < h; ++y) { Color *d = (Color *)dst; dst += pitch; - uint8 mask = 0; + bitPos &= _bitPosNewLineMask; for (int x = 0; x < w; ++x) { - if (!(x % 8)) + if (!(bitPos % 8)) mask = *glyph++; if (mask & 0x80) *d = c; ++d; + ++bitPos; mask <<= 1; } } @@ -176,9 +203,6 @@ const uint8 *FontSJISBase::flipCharacter(const uint8 *glyph, const int w) const 0x0F, 0x8F, 0x4F, 0xC7, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x97, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF }; - // TODO: This code looks like it will only work with 16 pixel wide - // characters we should really take care that we only call it on these - // or we fix this to support a generic width. for (int i = 0; i < w; i++) { _tempGlyph[i] = flipData[glyph[(w * 2 - 1) - i]]; _tempGlyph[(w * 2 - 1) - i] = flipData[glyph[i]]; @@ -225,9 +249,6 @@ void FontSJISBase::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1, } #ifndef DISABLE_FLIPPED_MODE - // TODO: This code inside flopCharater looks like it will only work with - // 16 pixel wide characters we should really take care that we only call - // it on these or we fix it to support a generic width. if (_flippedMode) glyphSource = flipCharacter(glyphSource, width); #endif @@ -303,7 +324,7 @@ const uint8 *FontTowns::getCharData(uint16 ch) const { uint8 f = ch & 0xFF; uint8 s = ch >> 8; - // copied from scumm\charset.cpp + // moved from scumm\charset.cpp enum { KANA = 0, KANJI = 1, @@ -392,6 +413,98 @@ const uint8 *FontTowns::getCharData(uint16 ch) const { } } +bool FontTowns::hasFeature(int feat) const { + static const int features = kFeatDefault | kFeatOutline | kFeatShadow | kFeatFMTownsShadow | kFeatFlipped; + return (features & feat) ? true : false; +} + +// PC-Engine ROM font + +bool FontPCEngine::loadData() { + Common::SeekableReadStream *data = SearchMan.createReadStreamForMember("pce.cdbios"); + if (!data) + return false; + + data->seek((data->size() & 0x200) ? 0x30200 : 0x30000); + data->read(_fontData12x12, kFont12x12Chars * 18); + + _fontWidth = _fontHeight = 12; + _bitPosNewLineMask = _fontWidth & 7; + + bool retValue = !data->err(); + delete data; + return retValue; +} + +const uint8 *FontPCEngine::getCharData(uint16 ch) const { + // Converts sjis code to pce font offset + // (moved from scumm\charset.cpp). + // rangeTbl maps SJIS char-codes to the PCE System Card font rom. + // Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range. + const int rangeCnt = 45; + static const uint16 rangeTbl[rangeCnt][2] = { + // Symbols + {0x8140,0x817E},{0x8180,0x81AC}, + // 0-9 + {0x824F,0x8258}, + // Latin upper + {0x8260,0x8279}, + // Latin lower + {0x8281,0x829A}, + // Kana + {0x829F,0x82F1},{0x8340,0x837E},{0x8380,0x8396}, + // Greek upper + {0x839F,0x83B6}, + // Greek lower + {0x83BF,0x83D6}, + // Cyrillic upper + {0x8440,0x8460}, + // Cyrillic lower + {0x8470,0x847E},{0x8480,0x8491}, + // Kanji + {0x889F,0x88FC}, + {0x8940,0x897E},{0x8980,0x89FC}, + {0x8A40,0x8A7E},{0x8A80,0x8AFC}, + {0x8B40,0x8B7E},{0x8B80,0x8BFC}, + {0x8C40,0x8C7E},{0x8C80,0x8CFC}, + {0x8D40,0x8D7E},{0x8D80,0x8DFC}, + {0x8E40,0x8E7E},{0x8E80,0x8EFC}, + {0x8F40,0x8F7E},{0x8F80,0x8FFC}, + {0x9040,0x907E},{0x9080,0x90FC}, + {0x9140,0x917E},{0x9180,0x91FC}, + {0x9240,0x927E},{0x9280,0x92FC}, + {0x9340,0x937E},{0x9380,0x93FC}, + {0x9440,0x947E},{0x9480,0x94FC}, + {0x9540,0x957E},{0x9580,0x95FC}, + {0x9640,0x967E},{0x9680,0x96FC}, + {0x9740,0x977E},{0x9780,0x97FC}, + {0x9840,0x9872} + }; + + ch = (ch << 8) | (ch >> 8); + int offset = 0; + for (int i = 0; i < rangeCnt; ++i) { + if (ch >= rangeTbl[i][0] && ch <= rangeTbl[i][1]) { + return _fontData12x12 + 18 * (offset + ch - rangeTbl[i][0]); + break; + } + offset += rangeTbl[i][1] - rangeTbl[i][0] + 1; + } + + debug(4, "Invalid Char: 0x%x", ch); + return 0; +} + +bool FontPCEngine::hasFeature(int feat) const { + // Outline mode not supported due to use of _bitPosNewLineMask. This could be implemented, + // but is not needed for any particular target at the moment. + // Flipped mode is also not supported since the hard coded table (taken from SCUMM 5 FM-TOWNS) + // is set up for font sizes of 8/16. This mode is also not required at the moment, since + // there aren't any SCUMM 5 PC-Engine games. + static const int features = kFeatDefault | kFeatShadow | kFeatFMTownsShadow; + return (features & feat) ? true : false; +} + // ScummVM SJIS font FontSjisSVM::FontSjisSVM(const Common::Platform platform) @@ -464,6 +577,15 @@ const uint8 *FontSjisSVM::getCharData(uint16 c) const { return getCharDataDefault(c); } +bool FontSjisSVM::hasFeature(int feat) const { + // Flipped mode is not supported since the hard coded table (taken from SCUMM 5 FM-TOWNS) + // is set up for font sizes of 8/16. This mode is also not required at the moment, since + // there aren't any SCUMM 5 PC-Engine games. + static const int features16 = kFeatDefault | kFeatOutline | kFeatShadow | kFeatFMTownsShadow | kFeatFlipped; + static const int features12 = kFeatDefault | kFeatOutline | kFeatShadow | kFeatFMTownsShadow; + return (((_fontWidth == 12) ? features12 : features16) & feat) ? true : false; +} + const uint8 *FontSjisSVM::getCharDataPCE(uint16 c) const { if (isASCII(c)) return 0; diff --git a/graphics/sjis.h b/graphics/sjis.h index 62e68013da..4b54da53b4 100644 --- a/graphics/sjis.h +++ b/graphics/sjis.h @@ -75,7 +75,7 @@ public: virtual bool loadData() = 0; /** - * Enable drawing with outline or shadow. + * Enable drawing with outline or shadow if supported by the Font. * * After changing outline state, getFontHeight and getMaxFontWidth / getCharWidth might return * different values! @@ -90,11 +90,17 @@ public: virtual void setDrawingMode(DrawingMode mode) {} /** - * Enable flipped character drawing (e.g. in the MI1 circus scene after Guybrush gets shot out of the cannon). + * Enable flipped character drawing if supported by the Font (e.g. in the MI1 circus scene after Guybrush gets shot out of the cannon). */ virtual void toggleFlippedMode(bool enable) {} /** + * Set spacing between characters and lines. This affects font height / char width + */ + virtual void setCharSpacing(int spacing) {} + virtual void setLineSpacing(int spacing) {} + + /** * Returns the height of the font. */ virtual uint getFontHeight() const = 0; @@ -162,16 +168,27 @@ protected: DrawingMode _drawMode; bool _flippedMode; int _fontWidth, _fontHeight; - + uint8 _bitPosNewLineMask; + bool isASCII(uint16 ch) const; virtual const uint8 *getCharData(uint16 c) const = 0; + + enum DrawingFeature { + kFeatDefault = 1 << 0, + kFeatOutline = 1 << 1, + kFeatShadow = 1 << 2, + kFeatFMTownsShadow = 1 << 3, + kFeatFlipped = 1 << 4 + }; + + virtual bool hasFeature(int feat) const = 0; }; /** * FM-TOWNS ROM based SJIS compatible font. * - * This is used in KYRA and SCI. + * This is used in KYRA, SCUMM and SCI. */ class FontTowns : public FontSJISBase { public: @@ -189,6 +206,31 @@ private: uint8 _fontData8x16[kFont8x16Chars * 32]; virtual const uint8 *getCharData(uint16 c) const; + + bool hasFeature(int feat) const; +}; + +/** + * PC-Engine System Card based SJIS compatible font. + * + * This is used in LOOM. + */ +class FontPCEngine : public FontSJISBase { +public: + /** + * Loads the ROM data from "pce.cdbios". + */ + bool loadData(); +private: + enum { + kFont12x12Chars = 3418 + }; + + uint8 _fontData12x12[kFont12x12Chars * 18]; + + virtual const uint8 *getCharData(uint16 c) const; + + bool hasFeature(int feat) const; }; /** @@ -215,6 +257,8 @@ private: virtual const uint8 *getCharData(uint16 c) const; + bool hasFeature(int feat) const; + const uint8 *getCharDataPCE(uint16 c) const; const uint8 *getCharDataDefault(uint16 c) const; diff --git a/gui/credits.h b/gui/credits.h index 0ed7fa1e41..cc9698195a 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -46,6 +46,7 @@ static const char *credits[] = { "C0""Filippos Karapetis", "C0""Pawel Kolodziejski", "C0""Walter van Niftrik", +"C2""(retired)", "C0""Kari Salminen", "C0""Eugene Sandulenko", "C0""David Symonds", @@ -158,6 +159,7 @@ static const char *credits[] = { "C0""Filippos Karapetis", "C0""Martin Kiewitz", "C0""Walter van Niftrik", +"C2""(retired)", "C0""Willem Jan Palenstijn", "C0""Jordi Vilalta Prat", "C0""Lars Skovlund", |