aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--audio/decoders/aac.cpp120
-rw-r--r--audio/decoders/aac.h12
-rw-r--r--audio/decoders/codec.h44
-rw-r--r--audio/decoders/qdm2.cpp96
-rw-r--r--audio/decoders/qdm2.h15
-rw-r--r--audio/decoders/quicktime.cpp51
-rw-r--r--audio/decoders/quicktime_intern.h5
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_midi.cpp5
-rw-r--r--backends/platform/gph/devices/gp2x/mmuhack/README (renamed from backends/platform/gph/devices/gp2x/mmuhack/readme.txt)8
-rwxr-xr-xbackends/platform/openpandora/build/PXML.xml83
-rw-r--r--backends/platform/openpandora/build/PXML_schema.xsd341
-rwxr-xr-xbackends/platform/openpandora/build/pnd_make.sh340
-rwxr-xr-xbackends/platform/openpandora/build/runscummvm.sh3
-rwxr-xr-xbackends/platform/openpandora/op-bundle.mk3
-rw-r--r--backends/platform/sdl/ps3/ps3.cpp2
-rw-r--r--common/system.h4
-rwxr-xr-xconfigure3
-rw-r--r--devtools/create_project/msvc10/create_project.vcxproj22
-rw-r--r--dists/redhat/scummvm.spec4
-rw-r--r--dists/redhat/scummvm.spec.in4
-rw-r--r--engines/dreamweb/dreamweb.cpp2
-rw-r--r--engines/lastexpress/data/snd.cpp81
-rw-r--r--engines/lastexpress/game/savegame.cpp12
-rw-r--r--engines/lastexpress/shared.h17
-rw-r--r--engines/lastexpress/sound/entry.cpp6
-rw-r--r--engines/lastexpress/sound/queue.cpp88
-rw-r--r--engines/lastexpress/sound/sound.cpp4
-rw-r--r--engines/mohawk/livingbooks.cpp65
-rw-r--r--engines/mohawk/livingbooks.h9
-rw-r--r--engines/mohawk/livingbooks_code.cpp33
-rw-r--r--engines/mohawk/livingbooks_code.h2
-rw-r--r--engines/saga/actor.h2
-rw-r--r--engines/saga/detection.cpp3
-rw-r--r--engines/saga/detection_tables.h14
-rw-r--r--engines/saga/introproc_ite.cpp33
-rw-r--r--engines/saga/music.cpp9
-rw-r--r--engines/saga/resource.cpp6
-rw-r--r--engines/saga/saga.cpp5
-rw-r--r--engines/saga/saga.h14
-rw-r--r--engines/saga/scene.cpp2
-rw-r--r--engines/saga/sndres.cpp9
-rw-r--r--engines/sci/detection_tables.h9
-rw-r--r--engines/sci/engine/kstring.cpp6
-rw-r--r--engines/scumm/charset.cpp903
-rw-r--r--engines/scumm/charset.h88
-rw-r--r--engines/scumm/detection_tables.h8
-rw-r--r--engines/scumm/imuse/imuse.cpp137
-rw-r--r--engines/scumm/imuse/imuse.h7
-rw-r--r--engines/scumm/imuse/imuse_internal.h42
-rw-r--r--engines/scumm/imuse/imuse_part.cpp12
-rw-r--r--engines/scumm/imuse/imuse_player.cpp49
-rw-r--r--engines/scumm/imuse/instrument.cpp100
-rw-r--r--engines/scumm/imuse/instrument.h6
-rw-r--r--engines/scumm/imuse/pcspk.cpp835
-rw-r--r--engines/scumm/imuse/pcspk.h161
-rw-r--r--engines/scumm/imuse/sysex_samnmax.cpp3
-rw-r--r--engines/scumm/imuse/sysex_scumm.cpp32
-rw-r--r--engines/scumm/module.mk1
-rw-r--r--engines/scumm/scumm.cpp26
-rw-r--r--engines/scumm/scumm.h17
-rw-r--r--engines/scumm/sound.cpp4
-rw-r--r--engines/sword25/gfx/image/vectorimage.cpp3
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.cpp28
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.h1
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.cpp12
-rw-r--r--engines/sword25/kernel/persistenceblock.h42
-rw-r--r--engines/toon/character.cpp9
-rw-r--r--engines/toon/movie.cpp3
-rw-r--r--engines/toon/picture.cpp11
-rw-r--r--engines/toon/picture.h2
-rw-r--r--engines/toon/toon.cpp15
-rw-r--r--engines/tsage/detection_tables.h16
-rw-r--r--graphics/sjis.cpp154
-rw-r--r--graphics/sjis.h52
75 files changed, 3166 insertions, 1212 deletions
diff --git a/NEWS b/NEWS
index 9f1e7c698e..4d5fa5c325 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@ For a more comprehensive changelog of the latest experimental code, see:
https://github.com/scummvm/scummvm/commits/
1.4.0 (????-??-??)
+ SCUMM:
+ - Implemented PC Speaker support for SCUMM v5 games.
+
SDL ports:
- Added support for OpenGL (GSoC Task).
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/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 &amp; 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 &amp; 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/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
diff --git a/configure b/configure
index 78998f8100..74541e3d98 100755
--- a/configure
+++ b/configure
@@ -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/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/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/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
index 28d20df9bd..6845be8808 100644
--- a/engines/lastexpress/data/snd.cpp
+++ b/engines/lastexpress/data/snd.cpp
@@ -38,7 +38,7 @@ namespace LastExpress {
#pragma region Sound filters tables
-static const int filterData[1424] = {
+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,
@@ -152,7 +152,7 @@ static const int filterData[1424] = {
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,
- 4.6, 4160, 4160, 4160, 4160, 4352, 4480, 4608, 4736,
+ 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,
@@ -195,7 +195,7 @@ static const int filterData[1424] = {
5632
};
-static const int filterData2[1424] = {
+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,
@@ -352,16 +352,20 @@ static const int p2s[17] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15,
// 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, int32 filterId) :
- Audio::Ima_ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) {
+ 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);
@@ -369,15 +373,37 @@ 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;
}
}
@@ -387,41 +413,10 @@ public:
void setFilterId(int32 filterId) { _nextFilterId = filterId; }
private:
- int32 _currentFilterId;
- int32 _nextFilterId; // the sound filter id, -1 for none
-
- /**
- * Sound filter
- *
- * @param [in] data If non-null, the input data
- * @param [in,out] buffer If non-null, the output buffer.
- * @param p1 The first filter input.
- * @param p2 The second filter input.
- */
- 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;
-
- data1p = filterData[idx + data1];
- data2p = CLIP(filterData2[idx + data1] + data2, -32767, 32767);
-
- buffer[2 * count] = (p2 * data2p) >> p1;
-
- idx = data[count] & 0xF;
-
- data1 = filterData[idx + data1p];
- data2 = CLIP(filterData2[idx + data1p] + data2p, -32767, 32767);
- buffer[2 * count + 1] = (p2 * data2) >> p1;
- }
- }
+ int32 _currentFilterId;
+ int32 _nextFilterId; // the sound filter id, -1 for none
+ int32 _stepAdjust1;
+ int32 _stepAdjust2;
};
//////////////////////////////////////////////////////////////////////////
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 7b640c773a..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,
kSoundStatusClosed = 0x200,
kSoundStatus_400 = 0x400,
-
+ kSoundStatusClear4 = 0x800,
kSoundStatus_8000 = 0x8000,
kSoundStatus_20000 = 0x20000,
kSoundStatus_100000 = 0x100000,
kSoundStatus_20000000 = 0x20000000,
kSoundStatus_40000000 = 0x40000000,
-
- kSoundStatusClear0 = 0x10,
- kSoundStatusFilter = 0x1F,
- kSoundStatusCached = 0x80,
- kSoundStatusClear3 = 0x200,
- kSoundStatusClear4 = 0x800,
kSoundStatusClearAll = 0xFFFFFFE0
};
diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp
index 87d8ccdb30..44cc68a57b 100644
--- a/engines/lastexpress/sound/entry.cpp
+++ b/engines/lastexpress/sound/entry.cpp
@@ -88,8 +88,8 @@ void SoundEntry::close() {
_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
@@ -290,7 +290,7 @@ bool SoundEntry::updateSound() {
}
}
}
- //if (status.status2 & 0x40 && !((uint32)_status.status & 0x180) && v1->soundBuffer)
+ //if (status.status2 & 0x40 && !((uint32)_status.status & 0x180) && v1->soundBuffer)
// Sound_FillSoundBuffer(v1);
}
result = true;
diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp
index 0a6442ceed..33b4c06793 100644
--- a/engines/lastexpress/sound/queue.cpp
+++ b/engines/lastexpress/sound/queue.cpp
@@ -102,80 +102,41 @@ void SoundQueue::removeFromQueue(Common::String filename) {
}
void SoundQueue::updateQueue() {
- //Common::StackLock locker(_mutex);
-
- //warning("[Sound::updateQueue] Not implemented");
-
- int maxPriority = 0;
- Common::List<SoundEntry *>::iterator lsnd;
- SoundEntry *msnd;
-
- bool loopedPlaying;
-
- loopedPlaying = 0;
- //++g_sound_flag;
+ Common::StackLock locker(_mutex);
- for (lsnd = _soundList.begin(); lsnd != _soundList.end(); ++lsnd) {
- if ((*lsnd)->getType() == kSoundType1)
- break;
- }
+ ++_flag;
- if (getSoundState() & 1) {
- if (!(*lsnd) || getFlags()->flag_3 || (*lsnd && (*lsnd)->getTime() > getSound()->getLoopingSoundDuration())) {
+ 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()) {
- (*lsnd)->update(getSound()->getData0());
+ entry->update(getSound()->getData0());
getSound()->setData1(0);
}
}
}
- msnd = NULL;
-
- for (lsnd = _soundList.begin(); lsnd != _soundList.end(); ++lsnd) {
- if ((*lsnd)->getStatus().status2 & 0x1) { // Sound is stopped
- // original code
- //if ((*lsnd)->soundBuffer)
- // Sound_RemoveSoundDataFromCache(*lsnd);
- //if ((*lsnd)->archive) {
- // Archive_SetStatusNotLoaded((*lsnd)->archive);
- // (*lsnd)->archive = 0;
- // (*lsnd)->field_28 = 3;
- //}
-
- if (_soundList.size() < 6) {
- if ((*lsnd)->getStatus().status1 & 0x1F) {
- int pri = (*lsnd)->getPriority() + ((*lsnd)->getStatus().status1 & 0x1F);
-
- if (pri > maxPriority) {
- msnd = *lsnd;
- maxPriority = pri;
- }
- }
- }
- }
+ for (Common::List<SoundEntry *>::iterator it = _soundList.begin(); it != _soundList.end(); ++it) {
+ SoundEntry *entry = *it;
- if (!(*lsnd)->updateSound() && !((*lsnd)->getStatus().status3 & 0x8)) {
- if (msnd == *lsnd) {
- maxPriority = 0;
- msnd = 0;
- }
- if (*lsnd) {
- (*lsnd)->close();
- SAFE_DELETE(*lsnd);
- lsnd = _soundList.reverse_erase(lsnd);
- }
+ // 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
+
+ if (!entry->updateSound() && !(entry->getStatus().status3 & 0x8)) {
+ entry->close();
+ SAFE_DELETE(entry);
+ it = _soundList.reverse_erase(it);
}
}
-
- // We don't need this
- //if (msnd)
- // msnd->updateEntryInternal();
+ // Original update the current entry, loading another set of samples to be decoded
getFlags()->flag_3 = 0;
- //--g_sound_flag;
+
+ --_flag;
}
void SoundQueue::resetQueue() {
@@ -209,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);
@@ -240,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);
}
//////////////////////////////////////////////////////////////////////////
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/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> &params) {
_stack.push(_vm->_rnd->getRandomNumberRng(min, max));
}
+void LBCode::cmdStringLen(const Common::Array<LBValue> &params) {
+ 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> &params) {
+ 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> &params) {
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> &params);
void cmdEval(const Common::Array<LBValue> &params);
void cmdRandom(const Common::Array<LBValue> &params);
+ void cmdStringLen(const Common::Array<LBValue> &params);
+ void cmdSubstring(const Common::Array<LBValue> &params);
void cmdGetRect(const Common::Array<LBValue> &params);
void cmdTopLeft(const Common::Array<LBValue> &params);
void cmdBottomRight(const Common::Array<LBValue> &params);
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/detection_tables.h b/engines/scumm/detection_tables.h
index e510c46cf2..11901f7565 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -243,11 +243,11 @@ static const GameSettings gameVariantsTable[] = {
{"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
- {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_MIDITOWNS | GUIO_MIDIADLIB | GUIO_MIDIMT32},
+ {"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+ {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_MIDITOWNS | GUIO_MIDIADLIB | GUIO_MIDIMT32},
- {"atlantis", "", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
- {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+ {"atlantis", "", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
+ {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"atlantis", "FM-TOWNS", 0, GID_INDY4, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_MIDITOWNS | GUIO_MIDIADLIB | GUIO_MIDIMT32},
{"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 317ef36cb9..451721cf70 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -44,30 +44,31 @@ namespace Scumm {
////////////////////////////////////////
IMuseInternal::IMuseInternal() :
-_native_mt32(false),
-_enable_gs(false),
-_sc55(false),
-_midi_adlib(NULL),
-_midi_native(NULL),
-_sysex(NULL),
-_paused(false),
-_initialized(false),
-_tempoFactor(0),
-_player_limit(ARRAYSIZE(_players)),
-_recycle_players(false),
-_queue_end(0),
-_queue_pos(0),
-_queue_sound(0),
-_queue_adding(0),
-_queue_marker(0),
-_queue_cleared(0),
-_master_volume(0),
-_music_volume(0),
-_trigger_count(0),
-_snm_trigger_index(0) {
- memset(_channel_volume,0,sizeof(_channel_volume));
- memset(_channel_volume_eff,0,sizeof(_channel_volume_eff));
- memset(_volchan_table,0,sizeof(_volchan_table));
+ _native_mt32(false),
+ _enable_gs(false),
+ _sc55(false),
+ _midi_adlib(NULL),
+ _midi_native(NULL),
+ _sysex(NULL),
+ _paused(false),
+ _initialized(false),
+ _tempoFactor(0),
+ _player_limit(ARRAYSIZE(_players)),
+ _recycle_players(false),
+ _queue_end(0),
+ _queue_pos(0),
+ _queue_sound(0),
+ _queue_adding(0),
+ _queue_marker(0),
+ _queue_cleared(0),
+ _master_volume(0),
+ _music_volume(0),
+ _trigger_count(0),
+ _snm_trigger_index(0),
+ _pcSpeaker(false) {
+ memset(_channel_volume, 0, sizeof(_channel_volume));
+ memset(_channel_volume_eff, 0, sizeof(_channel_volume_eff));
+ memset(_volchan_table, 0, sizeof(_volchan_table));
}
IMuseInternal::~IMuseInternal() {
@@ -119,7 +120,7 @@ byte *IMuseInternal::findStartOfSound(int sound, int ct) {
// Check for old-style headers first, like 'RO'
int trFlag = (kMThd | kFORM);
- if (ptr[0] == 'R' && ptr[1] == 'O'&& ptr[2] != 'L')
+ if (ptr[0] == 'R' && ptr[1] == 'O' && ptr[2] != 'L')
return ct == trFlag ? ptr : 0;
if (ptr[4] == 'S' && ptr[5] == 'O')
return ct == trFlag ? ptr + 4 : 0;
@@ -153,22 +154,22 @@ bool IMuseInternal::isMT32(int sound) {
uint32 tag = READ_BE_UINT32(ptr);
switch (tag) {
- case MKTAG('A','D','L',' '):
- case MKTAG('A','S','F','X'): // Special AD class for old AdLib sound effects
- case MKTAG('S','P','K',' '):
+ case MKTAG('A', 'D', 'L', ' '):
+ case MKTAG('A', 'S', 'F', 'X'): // Special AD class for old AdLib sound effects
+ case MKTAG('S', 'P', 'K', ' '):
return false;
- case MKTAG('A','M','I',' '):
- case MKTAG('R','O','L',' '):
+ case MKTAG('A', 'M', 'I', ' '):
+ case MKTAG('R', 'O', 'L', ' '):
return true;
- case MKTAG('M','A','C',' '): // Occurs in the Mac version of FOA and MI2
+ case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
return true;
- case MKTAG('G','M','D',' '):
+ case MKTAG('G', 'M', 'D', ' '):
return false;
- case MKTAG('M','I','D','I'): // Occurs in Sam & Max
+ case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max
// HE games use Roland music
if (ptr[8] == 'H' && ptr[9] == 'S')
return true;
@@ -195,20 +196,20 @@ bool IMuseInternal::isMIDI(int sound) {
uint32 tag = READ_BE_UINT32(ptr);
switch (tag) {
- case MKTAG('A','D','L',' '):
- case MKTAG('A','S','F','X'): // Special AD class for old AdLib sound effects
- case MKTAG('S','P','K',' '):
+ case MKTAG('A', 'D', 'L', ' '):
+ case MKTAG('A', 'S', 'F', 'X'): // Special AD class for old AdLib sound effects
+ case MKTAG('S', 'P', 'K', ' '):
return false;
- case MKTAG('A','M','I',' '):
- case MKTAG('R','O','L',' '):
+ case MKTAG('A', 'M', 'I', ' '):
+ case MKTAG('R', 'O', 'L', ' '):
return true;
- case MKTAG('M','A','C',' '): // Occurs in the Mac version of FOA and MI2
+ case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
return true;
- case MKTAG('G','M','D',' '):
- case MKTAG('M','I','D','I'): // Occurs in Sam & Max
+ case MKTAG('G', 'M', 'D', ' '):
+ case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max
return true;
}
@@ -381,7 +382,8 @@ int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) {
for (i = 0; i < ARRAYSIZE(_parts); ++i)
_parts[i].saveLoadWithSerializer(ser);
- { // Load/save the instrument definitions, which were revamped with V11.
+ {
+ // Load/save the instrument definitions, which were revamped with V11.
Part *part = &_parts[0];
if (ser->getVersion() >= VER(11)) {
for (i = ARRAYSIZE(_parts); i; --i, ++part) {
@@ -467,6 +469,10 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
case IMuse::PROP_GAME_ID:
_game_id = value;
break;
+
+ case IMuse::PROP_PC_SPEAKER:
+ _pcSpeaker = (value != 0);
+ break;
}
return 0;
@@ -522,7 +528,7 @@ void IMuseInternal::stopAllSounds() {
int IMuseInternal::getSoundStatus(int sound) const {
Common::StackLock lock(_mutex, "IMuseInternal::getSoundStatus()");
- return getSoundStatus_internal (sound, true);
+ return getSoundStatus_internal(sound, true);
}
int IMuseInternal::getMusicTimer() {
@@ -565,7 +571,7 @@ bool IMuseInternal::startSound_internal(int sound, int offset) {
int i;
ImTrigger *trigger = _snm_triggers;
for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trigger) {
- if (trigger->sound && trigger->id && trigger->command[0] == 8 && trigger->command[1] == sound && getSoundStatus_internal (trigger->sound,true))
+ if (trigger->sound && trigger->id && trigger->command[0] == 8 && trigger->command[1] == sound && getSoundStatus_internal(trigger->sound, true))
return false;
}
@@ -663,9 +669,7 @@ int IMuseInternal::getSoundStatus_internal(int sound, bool ignoreFadeouts) const
return (sound == -1) ? 0 : get_queue_sound_status(sound);
}
-int32 IMuseInternal::doCommand_internal
- (int a, int b, int c, int d, int e, int f, int g, int h)
-{
+int32 IMuseInternal::doCommand_internal(int a, int b, int c, int d, int e, int f, int g, int h) {
int args[8];
args[0] = a;
args[1] = b;
@@ -733,7 +737,7 @@ int32 IMuseInternal::doCommand_internal(int numargs, int a[]) {
}
return -1;
case 13:
- return getSoundStatus_internal (a[1], true);
+ return getSoundStatus_internal(a[1], true);
case 14:
// Sam and Max: Parameter fade
player = findActivePlayer(a[1]);
@@ -779,8 +783,7 @@ int32 IMuseInternal::doCommand_internal(int numargs, int a[]) {
a[0] = 0;
for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
if (_snm_triggers[i].sound == a[1] && _snm_triggers[i].id &&
- (a[3] == -1 || _snm_triggers[i].id == a[3]))
- {
+ (a[3] == -1 || _snm_triggers[i].id == a[3])) {
++a[0];
}
}
@@ -1002,9 +1005,9 @@ int IMuseInternal::get_queue_sound_status(int sound) const {
i = (i + 1) % ARRAYSIZE(_cmd_queue);
}
- for (i = 0; i < ARRAYSIZE (_deferredCommands); ++i) {
+ for (i = 0; i < ARRAYSIZE(_deferredCommands); ++i) {
if (_deferredCommands[i].time_left && _deferredCommands[i].a == 8 &&
- _deferredCommands[i].b == sound) {
+ _deferredCommands[i].b == sound) {
return 2;
}
}
@@ -1213,7 +1216,7 @@ int32 IMuseInternal::ImSetTrigger(int sound, int id, int a, int b, int c, int d,
// NOTE: We ONLY do this if the sound that will trigger the command is actually
// playing. Otherwise, there's a problem when exiting and re-entering the
// Bumpusville mansion. Ref Bug #780918.
- if (trig->command[0] == 8 && getSoundStatus_internal(trig->command[1],true) && getSoundStatus_internal(sound,true))
+ if (trig->command[0] == 8 && getSoundStatus_internal(trig->command[1], true) && getSoundStatus_internal(sound, true))
stopSound_internal(trig->command[1]);
return 0;
}
@@ -1246,8 +1249,7 @@ int32 IMuseInternal::ImFireAllTriggers(int sound) {
return (count > 0) ? 0 : -1;
}
-int IMuseInternal::set_channel_volume(uint chan, uint vol)
-{
+int IMuseInternal::set_channel_volume(uint chan, uint vol) {
if (chan >= 8 || vol > 127)
return -1;
@@ -1427,7 +1429,7 @@ void IMuseInternal::initMT32(MidiDriver *midi) {
// Display a welcome message on MT-32 displays.
memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
memcpy(&buffer[7], " ", 20);
- memcpy(buffer + 7 +(20 - len) / 2, info, len);
+ memcpy(buffer + 7 + (20 - len) / 2, info, len);
byte checksum = 0;
for (int i = 4; i < 27; ++i)
checksum -= buffer[i];
@@ -1473,9 +1475,9 @@ void IMuseInternal::initGM(MidiDriver *midi) {
// Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation
for (i = 0; i < 16; ++i) {
- midi->send(( 127 << 16) | (0 << 8) | (0xB0 | i));
- midi->send(( 1 << 16) | (32 << 8) | (0xB0 | i));
- midi->send(( 0 << 16) | (0 << 8) | (0xC0 | i));
+ midi->send((127 << 16) | (0 << 8) | (0xB0 | i));
+ midi->send((1 << 16) | (32 << 8) | (0xB0 | i));
+ midi->send((0 << 16) | (0 << 8) | (0xC0 | i));
}
debug(2, "GS Program Change: CM-64/32L Map Selected");
@@ -1496,7 +1498,7 @@ void IMuseInternal::initGM(MidiDriver *midi) {
// Set Channels 1-16 Reverb to 64, which is the
// equivalent of MT-32 default Reverb Level 5
for (i = 0; i < 16; ++i)
- midi->send(( 64 << 16) | (91 << 8) | (0xB0 | i));
+ midi->send((64 << 16) | (91 << 8) | (0xB0 | i));
debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
// Set Channels 1-16 Pitch Bend Sensitivity to
@@ -1637,8 +1639,8 @@ void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
hipart = NULL;
for (i = 32, part = _parts; i; i--, part++) {
if (part->_player && part->_player->getMidiDriver() == midi &&
- !part->_percussion && part->_on &&
- !part->_mc && part->_pri_eff >= hipri) {
+ !part->_percussion && part->_on &&
+ !part->_mc && part->_pri_eff >= hipri) {
hipri = part->_pri_eff;
hipart = part;
}
@@ -1668,16 +1670,19 @@ void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
}
}
-void IMuseInternal::setGlobalAdLibInstrument(byte slot, byte *data) {
+void IMuseInternal::setGlobalInstrument(byte slot, byte *data) {
if (slot < 32) {
- _global_adlib_instruments[slot].adlib(data);
+ if (_pcSpeaker)
+ _global_instruments[slot].pcspk(data);
+ else
+ _global_instruments[slot].adlib(data);
}
}
-void IMuseInternal::copyGlobalAdLibInstrument(byte slot, Instrument *dest) {
+void IMuseInternal::copyGlobalInstrument(byte slot, Instrument *dest) {
if (slot >= 32)
return;
- _global_adlib_instruments[slot].copy_to(dest);
+ _global_instruments[slot].copy_to(dest);
}
diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h
index 8014b13409..23449e470b 100644
--- a/engines/scumm/imuse/imuse.h
+++ b/engines/scumm/imuse/imuse.h
@@ -37,7 +37,7 @@ class Player;
class ScummEngine;
class Serializer;
-typedef void (*sysexfunc) (Player *, const byte *, uint16);
+typedef void (*sysexfunc)(Player *, const byte *, uint16);
/**
* iMuse implementation interface.
@@ -55,7 +55,8 @@ public:
PROP_GS,
PROP_LIMIT_PLAYERS,
PROP_RECYCLE_PLAYERS,
- PROP_GAME_ID
+ PROP_GAME_ID,
+ PROP_PC_SPEAKER
};
public:
@@ -66,7 +67,7 @@ public:
virtual int32 doCommand(int numargs, int args[]) = 0;
virtual int clear_queue() = 0;
virtual uint32 property(int prop, uint32 value) = 0;
- virtual void addSysexHandler (byte mfgID, sysexfunc handler) = 0;
+ virtual void addSysexHandler(byte mfgID, sysexfunc handler) = 0;
public:
virtual void startSoundWithNoteOffset(int sound, int offset) = 0;
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index 6a7b9fc7d9..3b0d36e119 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -135,7 +135,7 @@ struct ImTrigger {
int sound;
byte id;
uint16 expire;
- int command [8];
+ int command[8];
ImTrigger() { memset(this, 0, sizeof(ImTrigger)); }
};
@@ -153,12 +153,12 @@ struct CommandQueue {
//////////////////////////////////////////////////
class Player : public MidiDriver_BASE {
-/*
- * External SysEx handler functions shall each be defined in
- * a separate file. This header file shall be included at the
- * top of the file immediately following this special #define:
- * #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
- */
+ /*
+ * External SysEx handler functions shall each be defined in
+ * a separate file. This header file shall be included at the
+ * top of the file immediately following this special #define:
+ * #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
+ */
#ifdef SYSEX_CALLBACK_FUNCTION
friend void SYSEX_CALLBACK_FUNCTION(Player *, const byte *, uint16);
#endif
@@ -244,7 +244,7 @@ public:
void clear();
void clearLoop();
void fixAfterLoad();
- Part * getActivePart(uint8 part);
+ Part *getActivePart(uint8 part);
uint getBeatIndex();
int8 getDetune() const { return _detune; }
byte getEffectiveVolume() const { return _vol_eff; }
@@ -252,7 +252,7 @@ public:
MidiDriver *getMidiDriver() const { return _midi; }
int getParam(int param, byte chan);
int8 getPan() const { return _pan; }
- Part * getPart(uint8 part);
+ Part *getPart(uint8 part);
byte getPriority() const { return _priority; }
uint getTicksPerBeat() const { return TICKS_PER_BEAT; }
int8 getTranspose() const { return _transpose; }
@@ -342,6 +342,7 @@ struct Part : public Serializable {
void off();
void set_instrument(uint b);
void set_instrument(byte *data);
+ void set_instrument_pcspk(byte *data);
void load_global_instrument(byte b);
void set_transpose(int8 transpose);
@@ -375,12 +376,12 @@ class IMuseInternal : public IMuse {
friend class Player;
friend struct Part;
-/*
- * External SysEx handler functions shall each be defined in
- * a separate file. This header file shall be included at the
- * top of the file immediately following this special #define:
- * #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
- */
+ /*
+ * External SysEx handler functions shall each be defined in
+ * a separate file. This header file shall be included at the
+ * top of the file immediately following this special #define:
+ * #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
+ */
#ifdef SYSEX_CALLBACK_FUNCTION
friend void SYSEX_CALLBACK_FUNCTION(Player *, const byte *, uint16);
#endif
@@ -433,7 +434,8 @@ protected:
Player _players[8];
Part _parts[32];
- Instrument _global_adlib_instruments[32];
+ bool _pcSpeaker;
+ Instrument _global_instruments[32];
CommandQueue _cmd_queue[64];
DeferredCommand _deferredCommands[4];
@@ -449,8 +451,8 @@ protected:
enum ChunkType {
kMThd = 1,
kFORM = 2,
- kMDhd = 4, // Used in MI2 and INDY4. Contain certain start parameters (priority, volume, etc. ) for the player.
- kMDpg = 8 // These chunks exist in DOTT and SAMNMAX. They don't get processed, however.
+ kMDhd = 4, // Used in MI2 and INDY4. Contain certain start parameters (priority, volume, etc. ) for the player.
+ kMDpg = 8 // These chunks exist in DOTT and SAMNMAX. They don't get processed, however.
};
byte *findStartOfSound(int sound, int ct = (kMThd | kFORM));
@@ -498,8 +500,8 @@ protected:
int setImuseMasterVolume(uint vol);
void reallocateMidiChannels(MidiDriver *midi);
- void setGlobalAdLibInstrument(byte slot, byte *data);
- void copyGlobalAdLibInstrument(byte slot, Instrument *dest);
+ void setGlobalInstrument(byte slot, byte *data);
+ void copyGlobalInstrument(byte slot, Instrument *dest);
bool isNativeMT32() { return _native_mt32; }
protected:
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 5df8407a96..73e7704469 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -193,14 +193,18 @@ void Part::set_onoff(bool on) {
}
}
-void Part::set_instrument(byte * data) {
- _instrument.adlib(data);
+void Part::set_instrument(byte *data) {
+ if (_se->_pcSpeaker)
+ _instrument.pcspk(data);
+ else
+ _instrument.adlib(data);
+
if (clearToTransmit())
_instrument.send(_mc);
}
void Part::load_global_instrument(byte slot) {
- _player->_se->copyGlobalAdLibInstrument(slot, &_instrument);
+ _player->_se->copyGlobalInstrument(slot, &_instrument);
if (clearToTransmit())
_instrument.send(_mc);
}
@@ -234,7 +238,7 @@ void Part::noteOn(byte note, byte velocity) {
// should be implemented as a class static var. As it is, using
// a function level static var in most cases is arcane and evil.
static byte prev_vol_eff = 128;
- if (_vol_eff != prev_vol_eff){
+ if (_vol_eff != prev_vol_eff) {
mc->volume(_vol_eff);
prev_vol_eff = _vol_eff;
}
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 61b9cad2cb..73be2174cd 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -79,7 +79,7 @@ Player::Player() :
_isMT32(false),
_isMIDI(false),
_se(0),
- _vol_chan(0){
+ _vol_chan(0) {
}
Player::~Player() {
@@ -133,7 +133,7 @@ bool Player::isFadingOut() const {
int i;
for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) {
if (_parameterFaders[i].param == ParameterFader::pfVolume &&
- _parameterFaders[i].end == 0) {
+ _parameterFaders[i].end == 0) {
return true;
}
}
@@ -371,11 +371,13 @@ void Player::sysEx(const byte *p, uint16 len) {
if (a != IMUSE_SYSEX_ID) {
if (a == ROLAND_SYSEX_ID) {
// Roland custom instrument definition.
- part = getPart(p[0] & 0x0F);
- if (part) {
- part->_instrument.roland(p - 1);
- if (part->clearToTransmit())
- part->_instrument.send(part->_mc);
+ if (_isMIDI || _isMT32) {
+ part = getPart(p[0] & 0x0F);
+ if (part) {
+ part->_instrument.roland(p - 1);
+ if (part->clearToTransmit())
+ part->_instrument.send(part->_mc);
+ }
}
} else if (a == YM2612_SYSEX_ID) {
// FM-TOWNS custom instrument definition
@@ -399,13 +401,13 @@ void Player::sysEx(const byte *p, uint16 len) {
if (!_scanning) {
for (a = 0; a < len + 1 && a < 19; ++a) {
- sprintf((char *)&buf[a*3], " %02X", p[a]);
+ sprintf((char *)&buf[a * 3], " %02X", p[a]);
} // next for
if (a < len + 1) {
- buf[a*3] = buf[a*3+1] = buf[a*3+2] = '.';
+ buf[a * 3] = buf[a * 3 + 1] = buf[a * 3 + 2] = '.';
++a;
} // end if
- buf[a*3] = '\0';
+ buf[a * 3] = '\0';
debugC(DEBUG_IMUSE, "[%02d] SysEx:%s", _id, buf);
}
@@ -814,7 +816,7 @@ int Player::query_part_param(int param, byte chan) {
return part->_vol;
case 16:
// FIXME: Need to know where this occurs...
-error("Trying to cast instrument (%d, %d) -- please tell Fingolfin", param, chan);
+ error("Trying to cast instrument (%d, %d) -- please tell Fingolfin", param, chan);
// In old versions of the code, this used to return part->_program.
// This was changed in revision 2.29 of imuse.cpp (where this code used
// to reside).
@@ -845,9 +847,8 @@ void Player::onTimer() {
uint beat_index = target_tick / TICKS_PER_BEAT + 1;
uint tick_index = target_tick % TICKS_PER_BEAT;
- if (_loop_counter &&(beat_index > _loop_from_beat ||
- (beat_index == _loop_from_beat && tick_index >= _loop_from_tick)))
- {
+ if (_loop_counter && (beat_index > _loop_from_beat ||
+ (beat_index == _loop_from_beat && tick_index >= _loop_from_tick))) {
_loop_counter--;
jump(_track_index, _loop_to_beat, _loop_to_tick);
}
@@ -891,15 +892,15 @@ int Player::addParameterFader(int param, int target, int time) {
// target = target * 128 / 100;
break;
- case 127:
- { // FIXME? I *think* this clears all parameter faders.
- ParameterFader *ptr = &_parameterFaders[0];
- int i;
- for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr)
- ptr->param = 0;
- return 0;
- }
- break;
+ case 127: {
+ // FIXME? I *think* this clears all parameter faders.
+ ParameterFader *ptr = &_parameterFaders[0];
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr)
+ ptr->param = 0;
+ return 0;
+ }
+ break;
default:
debug(0, "Player::addParameterFader(%d, %d, %d): Unknown parameter", param, target, time);
@@ -1085,7 +1086,7 @@ void Player::saveLoadWithSerializer(Serializer *ser) {
}
ser->saveLoadEntries(this, playerEntries);
ser->saveLoadArrayOf(_parameterFaders, ARRAYSIZE(_parameterFaders),
- sizeof(ParameterFader), parameterFaderEntries);
+ sizeof(ParameterFader), parameterFaderEntries);
return;
}
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index 955700fc2b..11bb4e7605 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -114,14 +114,15 @@ roland_to_gm_map[] = {
// { "trickle4 ", ??? }
};
+// This emulates the percussion bank setup LEC used with the MT-32,
+// where notes 24 - 34 were assigned instruments without reverb.
+// It also fixes problems on GS devices that map sounds to these
+// notes by default.
const byte Instrument::_gmRhythmMap[35] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, 66, 47,
- 65, 48, 56};
- // This emulates the percussion bank setup LEC used with the MT-32,
- // where notes 24 - 34 were assigned instruments without reverb.
- // It also fixes problems on GS devices that map sounds to these
- // notes by default.
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, 66, 47,
+ 65, 48, 56
+};
class Instrument_Program : public InstrumentInternal {
private:
@@ -136,15 +137,16 @@ public:
void copy_to(Instrument *dest) { dest->program(_program, _mt32); }
bool is_valid() {
return (_program < 128) &&
- ((_native_mt32 == _mt32) || _native_mt32
- ? (MidiDriver::_gmToMt32[_program] < 128)
- : (MidiDriver::_mt32ToGm[_program] < 128)); }
+ ((_native_mt32 == _mt32) || _native_mt32
+ ? (MidiDriver::_gmToMt32[_program] < 128)
+ : (MidiDriver::_mt32ToGm[_program] < 128));
+ }
};
class Instrument_AdLib : public InstrumentInternal {
private:
-#include "common/pack-start.h" // START STRUCT PACKING
+#include "common/pack-start.h" // START STRUCT PACKING
struct AdLibInstrument {
byte flags_1;
@@ -159,13 +161,17 @@ private:
byte waveform_2;
byte feedback;
byte flags_a;
- struct { byte a,b,c,d,e,f,g,h; } extra_a;
+ struct {
+ byte a, b, c, d, e, f, g, h;
+ } extra_a;
byte flags_b;
- struct { byte a,b,c,d,e,f,g,h; } extra_b;
+ struct {
+ byte a, b, c, d, e, f, g, h;
+ } extra_b;
byte duration;
} PACKED_STRUCT;
-#include "common/pack-end.h" // END STRUCT PACKING
+#include "common/pack-end.h" // END STRUCT PACKING
AdLibInstrument _instrument;
@@ -181,7 +187,7 @@ public:
class Instrument_Roland : public InstrumentInternal {
private:
-#include "common/pack-start.h" // START STRUCT PACKING
+#include "common/pack-start.h" // START STRUCT PACKING
struct RolandInstrument {
byte roland_id;
@@ -242,11 +248,11 @@ private:
byte checksum;
} PACKED_STRUCT;
-#include "common/pack-end.h" // END STRUCT PACKING
+#include "common/pack-end.h" // END STRUCT PACKING
RolandInstrument _instrument;
- char _instrument_name [11];
+ char _instrument_name[11];
uint8 getEquivalentGM();
@@ -259,6 +265,19 @@ public:
bool is_valid() { return (_native_mt32 ? true : (_instrument_name[0] != '\0')); }
};
+class Instrument_PcSpk : public InstrumentInternal {
+public:
+ Instrument_PcSpk(const byte *data);
+ Instrument_PcSpk(Serializer *s);
+ void saveOrLoad(Serializer *s);
+ void send(MidiChannel *mc);
+ void copy_to(Instrument *dest) { dest->pcspk((byte *)&_instrument); }
+ bool is_valid() { return true; }
+
+private:
+ byte _instrument[23];
+};
+
////////////////////////////////////////
//
// Instrument class members
@@ -299,7 +318,15 @@ void Instrument::roland(const byte *instrument) {
_instrument = new Instrument_Roland(instrument);
}
-void Instrument::saveOrLoad (Serializer *s) {
+void Instrument::pcspk(const byte *instrument) {
+ clear();
+ if (!instrument)
+ return;
+ _type = itPcSpk;
+ _instrument = new Instrument_PcSpk(instrument);
+}
+
+void Instrument::saveOrLoad(Serializer *s) {
if (s->isSaving()) {
s->saveByte(_type);
if (_instrument)
@@ -319,6 +346,9 @@ void Instrument::saveOrLoad (Serializer *s) {
case itRoland:
_instrument = new Instrument_Roland(s);
break;
+ case itPcSpk:
+ _instrument = new Instrument_PcSpk(s);
+ break;
default:
warning("No known instrument classification #%d", (int)_type);
_type = itNone;
@@ -333,8 +363,8 @@ void Instrument::saveOrLoad (Serializer *s) {
////////////////////////////////////////
Instrument_Program::Instrument_Program(byte program, bool mt32) :
-_program (program),
-_mt32 (mt32) {
+ _program(program),
+ _mt32(mt32) {
if (program > 127)
_program = 255;
}
@@ -413,7 +443,7 @@ Instrument_Roland::Instrument_Roland(const byte *data) {
Instrument_Roland::Instrument_Roland(Serializer *s) {
_instrument_name[0] = '\0';
if (!s->isSaving())
- saveOrLoad (s);
+ saveOrLoad(s);
else
memset(&_instrument, 0, sizeof(_instrument));
}
@@ -470,4 +500,32 @@ uint8 Instrument_Roland::getEquivalentGM() {
return 255;
}
+////////////////////////////////////////
+//
+// Instrument_PcSpk class members
+//
+////////////////////////////////////////
+
+Instrument_PcSpk::Instrument_PcSpk(const byte *data) {
+ memcpy(_instrument, data, sizeof(_instrument));
+}
+
+Instrument_PcSpk::Instrument_PcSpk(Serializer *s) {
+ if (!s->isSaving())
+ saveOrLoad(s);
+ else
+ memset(_instrument, 0, sizeof(_instrument));
+}
+
+void Instrument_PcSpk::saveOrLoad(Serializer *s) {
+ if (s->isSaving())
+ s->saveBytes(_instrument, sizeof(_instrument));
+ else
+ s->loadBytes(_instrument, sizeof(_instrument));
+}
+
+void Instrument_PcSpk::send(MidiChannel *mc) {
+ mc->sysEx_customInstrument('SPK ', (byte *)&_instrument);
+}
+
} // End of namespace Scumm
diff --git a/engines/scumm/imuse/instrument.h b/engines/scumm/imuse/instrument.h
index 79cbd49032..a855c64155 100644
--- a/engines/scumm/imuse/instrument.h
+++ b/engines/scumm/imuse/instrument.h
@@ -51,10 +51,11 @@ public:
itNone = 0,
itProgram = 1,
itAdLib = 2,
- itRoland = 3
+ itRoland = 3,
+ itPcSpk = 4
};
- Instrument() : _type (0), _instrument (0) { }
+ Instrument() : _type(0), _instrument(0) { }
~Instrument() { delete _instrument; }
static void nativeMT32(bool native);
static const byte _gmRhythmMap[35];
@@ -70,6 +71,7 @@ public:
void program(byte program, bool mt32);
void adlib(const byte *instrument);
void roland(const byte *instrument);
+ void pcspk(const byte *instrument);
byte getType() { return _type; }
bool isValid() { return (_instrument ? _instrument->is_valid() : false); }
diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp
new file mode 100644
index 0000000000..01e2ab3b7d
--- /dev/null
+++ b/engines/scumm/imuse/pcspk.cpp
@@ -0,0 +1,835 @@
+/* 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.
+ */
+
+#include "scumm/imuse/pcspk.h"
+
+#include "common/util.h"
+
+namespace Scumm {
+
+PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer)
+ : MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) {
+}
+
+PcSpkDriver::~PcSpkDriver() {
+ close();
+}
+
+int PcSpkDriver::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ for (uint i = 0; i < 6; ++i)
+ _channels[i].init(this, i);
+ _activeChannel = 0;
+ _effectTimer = 0;
+ _randBase = 1;
+
+ // We need to take care we only send note frequencies, when the internal
+ // settings actually changed, thus we need some extra state to keep track
+ // of that.
+ _lastActiveChannel = 0;
+ _lastActiveOut = 0;
+
+ // We set the output sound type to music here to allow sound volume
+ // adjustment. The drawback here is that we can not control the music and
+ // sfx separately here. But the AdLib output has the same issue so it
+ // should not be that bad.
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+ return 0;
+}
+
+void PcSpkDriver::close() {
+ if (!_isOpen)
+ return;
+ _isOpen = false;
+
+ _mixer->stopHandle(_mixerSoundHandle);
+}
+
+void PcSpkDriver::send(uint32 d) {
+ assert((d & 0x0F) < 6);
+ _channels[(d & 0x0F)].send(d);
+}
+
+void PcSpkDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
+ assert(channel < 6);
+ if (type == 'SPK ')
+ _channels[channel].sysEx_customInstrument(type, instr);
+}
+
+MidiChannel *PcSpkDriver::allocateChannel() {
+ for (uint i = 0; i < 6; ++i) {
+ if (_channels[i].allocate())
+ return &_channels[i];
+ }
+
+ return 0;
+}
+
+void PcSpkDriver::generateSamples(int16 *buf, int len) {
+ _pcSpk.readBuffer(buf, len);
+}
+
+void PcSpkDriver::onTimer() {
+ if (!_activeChannel)
+ return;
+
+ for (uint i = 0; i < 6; ++i) {
+ OutputChannel &out = _channels[i]._out;
+
+ if (!out.active)
+ continue;
+
+ if (out.length == 0 || --out.length != 0) {
+ if (out.unkB && out.unkC) {
+ out.unkA += out.unkB;
+ if (out.instrument)
+ out.unkE = ((int8)out.instrument[out.unkA] * out.unkC) >> 4;
+ }
+
+ ++_effectTimer;
+ if (_effectTimer > 3) {
+ _effectTimer = 0;
+
+ if (out.effectEnvelopeA.state)
+ updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA);
+ if (out.effectEnvelopeB.state)
+ updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB);
+ }
+ } else {
+ out.active = 0;
+ updateNote();
+ return;
+ }
+ }
+
+ if (_activeChannel->_tl) {
+ output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE);
+ } else {
+ _pcSpk.stop();
+ _lastActiveChannel = 0;
+ _lastActiveOut = 0;
+ }
+}
+
+void PcSpkDriver::updateNote() {
+ uint8 priority = 0;
+ _activeChannel = 0;
+ for (uint i = 0; i < 6; ++i) {
+ if (_channels[i]._allocated && _channels[i]._out.active && _channels[i]._priority >= priority) {
+ priority = _channels[i]._priority;
+ _activeChannel = &_channels[i];
+ }
+ }
+
+ if (_activeChannel == 0 || _activeChannel->_tl == 0) {
+ _pcSpk.stop();
+ _lastActiveChannel = 0;
+ _lastActiveOut = 0;
+ } else {
+ output(_activeChannel->_pitchBend + (_activeChannel->_out.note << 7));
+ }
+}
+
+void PcSpkDriver::output(uint16 out) {
+ byte v1 = (out >> 7) & 0xFF;
+ byte v2 = (out >> 2) & 0x1E;
+
+ byte shift = _outputTable1[v1];
+ uint16 indexBase = _outputTable2[v1] << 5;
+ uint16 frequency = _frequencyTable[(indexBase + v2) / 2] >> shift;
+
+ // Only output in case the active channel changed or the frequency changed.
+ // This is not faithful to the original. Since our timings differ we would
+ // get distorted sound otherwise though.
+ if (_lastActiveChannel != _activeChannel || _lastActiveOut != out) {
+ _pcSpk.play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / frequency, -1);
+ _lastActiveChannel = _activeChannel;
+ _lastActiveOut = out;
+ }
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) {
+ _owner = owner;
+ _channel = channel;
+ _allocated = false;
+ memset(&_out, 0, sizeof(_out));
+}
+
+bool PcSpkDriver::MidiChannel_PcSpk::allocate() {
+ if (_allocated)
+ return false;
+
+ memset(&_out, 0, sizeof(_out));
+ memset(_instrument, 0, sizeof(_instrument));
+ _out.effectDefA.envelope = &_out.effectEnvelopeA;
+ _out.effectDefB.envelope = &_out.effectEnvelopeB;
+
+ _allocated = true;
+ return true;
+}
+
+MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() {
+ return _owner;
+}
+
+byte PcSpkDriver::MidiChannel_PcSpk::getNumber() {
+ return _channel;
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::release() {
+ _out.active = 0;
+ _allocated = false;
+ _owner->updateNote();
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) {
+ uint8 type = b & 0xF0;
+ uint8 p1 = (b >> 8) & 0xFF;
+ uint8 p2 = (b >> 16) & 0xFF;
+
+ switch (type) {
+ case 0x80:
+ noteOff(p1);
+ break;
+
+ case 0x90:
+ if (p2)
+ noteOn(p1, p2);
+ else
+ noteOff(p1);
+ break;
+
+ case 0xB0:
+ controlChange(p1, p2);
+ break;
+
+ case 0xE0:
+ pitchBend((p1 | (p2 << 7)) - 0x2000);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) {
+ if (!_allocated)
+ return;
+
+ if (_sustain) {
+ if (_out.note == note)
+ _out.sustainNoteOff = 1;
+ } else {
+ if (_out.note == note) {
+ _out.active = 0;
+ _owner->updateNote();
+ }
+ }
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
+ if (!_allocated)
+ return;
+
+ _out.note = note;
+ _out.sustainNoteOff = 0;
+ _out.length = _instrument[0];
+
+ if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData))
+ _out.instrument = _owner->_outInstrumentData + _instrument[4] * 256;
+ else
+ _out.instrument = 0;
+
+ _out.unkA = 0;
+ _out.unkB = _instrument[1];
+ _out.unkC = _instrument[2];
+ _out.unkE = 0;
+ _out.unk60 = 0;
+ _out.active = 1;
+
+ // In case we get a note on event on the last active channel, we reset the
+ // last active channel, thus we assure the frequency is correctly set, even
+ // when the same note was sent.
+ if (_owner->_lastActiveChannel == this) {
+ _owner->_lastActiveChannel = 0;
+ _owner->_lastActiveOut = 0;
+ }
+ _owner->updateNote();
+
+ _out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4));
+ if (_out.unkC > 63)
+ _out.unkC = 63;
+
+ if ((_instrument[5] & 0x80) != 0)
+ _owner->setupEffects(*this, _out.effectEnvelopeA, _out.effectDefA, _instrument[5], _instrument + 6);
+
+ if ((_instrument[14] & 0x80) != 0)
+ _owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15);
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::programChange(byte program) {
+ // Nothing to implement here, the iMuse code takes care of passing us the
+ // instrument data.
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::pitchBend(int16 bend) {
+ _pitchBend = (bend * _pitchBendFactor) >> 6;
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) {
+ switch (control) {
+ case 1:
+ if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel)
+ _out.effectEnvelopeA.modWheelState = (value >> 2);
+ if (_out.effectEnvelopeB.state && _out.effectDefB.useModWheel)
+ _out.effectEnvelopeB.modWheelState = (value >> 2);
+ break;
+
+ case 7:
+ _tl = value;
+ if (_owner->_activeChannel == this) {
+ if (_tl == 0) {
+ _owner->_lastActiveChannel = 0;
+ _owner->_lastActiveOut = 0;
+ _owner->_pcSpk.stop();
+ } else {
+ _owner->output((_out.note << 7) + _pitchBend + _out.unk60 + _out.unkE);
+ }
+ }
+ break;
+
+ case 64:
+ _sustain = value;
+ if (!value && _out.sustainNoteOff) {
+ _out.active = 0;
+ _owner->updateNote();
+ }
+ break;
+
+ case 123:
+ _out.active = 0;
+ _owner->updateNote();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) {
+ _pitchBendFactor = value;
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) {
+ _priority = value;
+}
+
+void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
+ memcpy(_instrument, instr, sizeof(_instrument));
+}
+
+uint8 PcSpkDriver::getEffectModifier(uint16 level) {
+ uint8 base = level / 32;
+ uint8 index = level % 32;
+
+ if (index == 0)
+ return 0;
+
+ return (base * (index + 1)) >> 5;
+}
+
+int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) {
+ if (!mod) {
+ return 0;
+ } else if (mod == 31) {
+ return level;
+ } else if (level < -63 || level > 63) {
+ return (mod * (level + 1)) >> 6;
+ } else if (mod < 0) {
+ if (level < 0)
+ return getEffectModifier(((-level) << 5) - mod);
+ else
+ return -getEffectModifier((level << 5) - mod);
+ } else {
+ if (level < 0)
+ return -getEffectModifier(((-level) << 5) + mod);
+ else
+ return getEffectModifier(((-level) << 5) + mod);
+ }
+}
+
+int16 PcSpkDriver::getRandMultipy(int16 input) {
+ if (_randBase & 1)
+ _randBase = (_randBase >> 1) ^ 0xB8;
+ else
+ _randBase >>= 1;
+
+ return (_randBase * input) >> 8;
+}
+
+void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) {
+ def.phase = 0;
+ def.useModWheel = flags & 0x40;
+ env.loop = flags & 0x20;
+ def.type = flags & 0x1F;
+
+ env.modWheelSensitivity = 31;
+ if (def.useModWheel)
+ env.modWheelState = chan._modWheel >> 2;
+ else
+ env.modWheelState = 31;
+
+ switch (def.type) {
+ case 0:
+ env.maxLevel = 767;
+ env.startLevel = 383;
+ break;
+
+ case 1:
+ env.maxLevel = 31;
+ env.startLevel = 15;
+ break;
+
+ case 2:
+ env.maxLevel = 63;
+ env.startLevel = chan._out.unkB;
+ break;
+
+ case 3:
+ env.maxLevel = 63;
+ env.startLevel = chan._out.unkC;
+ break;
+
+ case 4:
+ env.maxLevel = 3;
+ env.startLevel = chan._instrument[4];
+ break;
+
+ case 5:
+ env.maxLevel = 62;
+ env.startLevel = 31;
+ env.modWheelState = 0;
+ break;
+
+ case 6:
+ env.maxLevel = 31;
+ env.startLevel = 0;
+ env.modWheelSensitivity = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ startEffect(env, data);
+}
+
+void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) {
+ env.state = 1;
+ env.currentLevel = 0;
+ env.modWheelLast = 31;
+ env.duration = data[0] * 63;
+
+ env.stateTargetLevels[0] = data[1];
+ env.stateTargetLevels[1] = data[3];
+ env.stateTargetLevels[2] = data[5];
+ env.stateTargetLevels[3] = data[6];
+
+ env.stateModWheelLevels[0] = data[2];
+ env.stateModWheelLevels[1] = data[4];
+ env.stateModWheelLevels[2] = 0;
+ env.stateModWheelLevels[3] = data[7];
+
+ initNextEnvelopeState(env);
+}
+
+void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) {
+ uint8 lastState = env.state - 1;
+
+ uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)];
+ if (env.stateTargetLevels[lastState] & 0x80)
+ stepCount = getRandMultipy(stepCount);
+ if (!stepCount)
+ stepCount = 1;
+
+ env.stateNumSteps = env.stateStepCounter = stepCount;
+
+ int16 totalChange = 0;
+ if (lastState != 2) {
+ totalChange = getEffectModLevel(env.maxLevel, (env.stateModWheelLevels[lastState] & 0x7F) - 31);
+ if (env.stateModWheelLevels[lastState] & 0x80)
+ totalChange = getRandMultipy(totalChange);
+
+ if (totalChange + env.startLevel > env.maxLevel)
+ totalChange = env.maxLevel - env.startLevel;
+ else if (totalChange + env.startLevel < 0)
+ totalChange = -env.startLevel;
+
+ totalChange -= env.currentLevel;
+ }
+
+ env.changePerStep = totalChange / stepCount;
+ if (totalChange < 0) {
+ totalChange = -totalChange;
+ env.dir = -1;
+ } else {
+ env.dir = 1;
+ }
+ env.changePerStepRem = totalChange % stepCount;
+ env.changeCountRem = 0;
+}
+
+void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) {
+ if (advanceEffectEnvelope(env, def) & 1) {
+ switch (def.type) {
+ case 0: case 1:
+ chan._out.unk60 = def.phase << 4;
+ break;
+
+ case 2:
+ chan._out.unkB = (def.phase & 0xFF) + chan._instrument[1];
+ break;
+
+ case 3:
+ chan._out.unkC = (def.phase & 0xFF) + chan._instrument[2];
+ break;
+
+ case 4:
+ if ((chan._instrument[4] + (def.phase & 0xFF)) * 256 < ARRAYSIZE(_outInstrumentData))
+ chan._out.instrument = _outInstrumentData + (chan._instrument[4] + (def.phase & 0xFF)) * 256;
+ else
+ chan._out.instrument = 0;
+ break;
+
+ case 5:
+ env.modWheelState = (def.phase & 0xFF);
+ break;
+
+ case 6:
+ env.modWheelSensitivity = (def.phase & 0xFF);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) {
+ if (env.duration != 0) {
+ env.duration -= 17;
+ if (env.duration <= 0) {
+ env.state = 0;
+ return 0;
+ }
+ }
+
+ uint8 changedFlags = 0;
+ int16 newLevel = env.currentLevel + env.changePerStep;
+ env.changeCountRem += env.changePerStepRem;
+ if (env.changeCountRem >= env.stateNumSteps) {
+ env.changeCountRem -= env.stateNumSteps;
+ newLevel += env.dir;
+ }
+
+ if (env.currentLevel != newLevel || env.modWheelLast != env.modWheelState) {
+ env.currentLevel = newLevel;
+ env.modWheelLast = env.modWheelState;
+
+ int16 newPhase = getEffectModLevel(newLevel, env.modWheelState);
+ if (def.phase != newPhase) {
+ changedFlags |= 1;
+ def.phase = newPhase;
+ }
+ }
+
+ --env.stateStepCounter;
+ if (!env.stateStepCounter) {
+ ++env.state;
+ if (env.state > 4) {
+ if (env.loop) {
+ env.state = 1;
+ changedFlags |= 2;
+ } else {
+ env.state = 0;
+ return changedFlags;
+ }
+ }
+
+ initNextEnvelopeState(env);
+ }
+
+ return changedFlags;
+}
+
+const byte PcSpkDriver::_outInstrumentData[1024] = {
+ 0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15,
+ 0x18, 0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x2D,
+ 0x30, 0x33, 0x36, 0x39, 0x3B, 0x3E, 0x41, 0x43,
+ 0x46, 0x49, 0x4B, 0x4E, 0x50, 0x52, 0x55, 0x57,
+ 0x59, 0x5B, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x67,
+ 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71, 0x72, 0x74,
+ 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7B,
+ 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
+ 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D,
+ 0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76,
+ 0x75, 0x74, 0x72, 0x71, 0x70, 0x6E, 0x6C, 0x6B,
+ 0x69, 0x67, 0x66, 0x64, 0x62, 0x60, 0x5E, 0x5B,
+ 0x59, 0x57, 0x55, 0x52, 0x50, 0x4E, 0x4B, 0x49,
+ 0x46, 0x43, 0x41, 0x3E, 0x3B, 0x39, 0x36, 0x33,
+ 0x30, 0x2D, 0x2A, 0x27, 0x24, 0x21, 0x1E, 0x1B,
+ 0x18, 0x15, 0x12, 0x0F, 0x0C, 0x09, 0x06, 0x03,
+ 0x00, 0xFD, 0xFA, 0xF7, 0xF4, 0xF1, 0xEE, 0xEB,
+ 0xE8, 0xE5, 0xE2, 0xDF, 0xDC, 0xD9, 0xD6, 0xD3,
+ 0xD0, 0xCD, 0xCA, 0xC7, 0xC5, 0xC2, 0xBF, 0xBD,
+ 0xBA, 0xB7, 0xB5, 0xB2, 0xB0, 0xAE, 0xAB, 0xA9,
+ 0xA7, 0xA5, 0xA2, 0xA0, 0x9E, 0x9C, 0x9A, 0x99,
+ 0x97, 0x95, 0x94, 0x92, 0x90, 0x8F, 0x8E, 0x8C,
+ 0x8B, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x85, 0x85,
+ 0x84, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83,
+ 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A,
+ 0x8B, 0x8C, 0x8E, 0x8F, 0x90, 0x92, 0x94, 0x95,
+ 0x97, 0x99, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA5,
+ 0xA7, 0xA9, 0xAB, 0xAE, 0xB0, 0xB2, 0xB5, 0xB7,
+ 0xBA, 0xBD, 0xBF, 0xC2, 0xC5, 0xC7, 0xCA, 0xCD,
+ 0xD0, 0xD3, 0xD6, 0xD9, 0xDC, 0xDF, 0xE2, 0xE5,
+ 0xE8, 0xEB, 0xEE, 0xF1, 0xF4, 0xF7, 0xFA, 0xFD,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0x29, 0x23, 0xBE, 0x84, 0xE1, 0x6C, 0xD6, 0xAE,
+ 0x52, 0x90, 0x49, 0xF1, 0xF1, 0xBB, 0xE9, 0xEB,
+ 0xB3, 0xA6, 0xDB, 0x3C, 0x87, 0x0C, 0x3E, 0x99,
+ 0x24, 0x5E, 0x0D, 0x1C, 0x06, 0xB7, 0x47, 0xDE,
+ 0xB3, 0x12, 0x4D, 0xC8, 0x43, 0xBB, 0x8B, 0xA6,
+ 0x1F, 0x03, 0x5A, 0x7D, 0x09, 0x38, 0x25, 0x1F,
+ 0x5D, 0xD4, 0xCB, 0xFC, 0x96, 0xF5, 0x45, 0x3B,
+ 0x13, 0x0D, 0x89, 0x0A, 0x1C, 0xDB, 0xAE, 0x32,
+ 0x20, 0x9A, 0x50, 0xEE, 0x40, 0x78, 0x36, 0xFD,
+ 0x12, 0x49, 0x32, 0xF6, 0x9E, 0x7D, 0x49, 0xDC,
+ 0xAD, 0x4F, 0x14, 0xF2, 0x44, 0x40, 0x66, 0xD0,
+ 0x6B, 0xC4, 0x30, 0xB7, 0x32, 0x3B, 0xA1, 0x22,
+ 0xF6, 0x22, 0x91, 0x9D, 0xE1, 0x8B, 0x1F, 0xDA,
+ 0xB0, 0xCA, 0x99, 0x02, 0xB9, 0x72, 0x9D, 0x49,
+ 0x2C, 0x80, 0x7E, 0xC5, 0x99, 0xD5, 0xE9, 0x80,
+ 0xB2, 0xEA, 0xC9, 0xCC, 0x53, 0xBF, 0x67, 0xD6,
+ 0xBF, 0x14, 0xD6, 0x7E, 0x2D, 0xDC, 0x8E, 0x66,
+ 0x83, 0xEF, 0x57, 0x49, 0x61, 0xFF, 0x69, 0x8F,
+ 0x61, 0xCD, 0xD1, 0x1E, 0x9D, 0x9C, 0x16, 0x72,
+ 0x72, 0xE6, 0x1D, 0xF0, 0x84, 0x4F, 0x4A, 0x77,
+ 0x02, 0xD7, 0xE8, 0x39, 0x2C, 0x53, 0xCB, 0xC9,
+ 0x12, 0x1E, 0x33, 0x74, 0x9E, 0x0C, 0xF4, 0xD5,
+ 0xD4, 0x9F, 0xD4, 0xA4, 0x59, 0x7E, 0x35, 0xCF,
+ 0x32, 0x22, 0xF4, 0xCC, 0xCF, 0xD3, 0x90, 0x2D,
+ 0x48, 0xD3, 0x8F, 0x75, 0xE6, 0xD9, 0x1D, 0x2A,
+ 0xE5, 0xC0, 0xF7, 0x2B, 0x78, 0x81, 0x87, 0x44,
+ 0x0E, 0x5F, 0x50, 0x00, 0xD4, 0x61, 0x8D, 0xBE,
+ 0x7B, 0x05, 0x15, 0x07, 0x3B, 0x33, 0x82, 0x1F,
+ 0x18, 0x70, 0x92, 0xDA, 0x64, 0x54, 0xCE, 0xB1,
+ 0x85, 0x3E, 0x69, 0x15, 0xF8, 0x46, 0x6A, 0x04,
+ 0x96, 0x73, 0x0E, 0xD9, 0x16, 0x2F, 0x67, 0x68,
+ 0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76
+};
+
+const byte PcSpkDriver::_outputTable1[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7
+};
+
+const byte PcSpkDriver::_outputTable2[] = {
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 8, 9, 10, 11,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7
+};
+
+const uint16 PcSpkDriver::_effectEnvStepTable[] = {
+ 1, 2, 4, 5,
+ 6, 7, 8, 9,
+ 10, 12, 14, 16,
+ 18, 21, 24, 30,
+ 36, 50, 64, 82,
+ 100, 136, 160, 192,
+ 240, 276, 340, 460,
+ 600, 860, 1200, 1600
+};
+
+const uint16 PcSpkDriver::_frequencyTable[] = {
+ 0x8E84, 0x8E00, 0x8D7D, 0x8CFA,
+ 0x8C78, 0x8BF7, 0x8B76, 0x8AF5,
+ 0x8A75, 0x89F5, 0x8976, 0x88F7,
+ 0x8879, 0x87FB, 0x877D, 0x8700,
+ 0x8684, 0x8608, 0x858C, 0x8511,
+ 0x8496, 0x841C, 0x83A2, 0x8328,
+ 0x82AF, 0x8237, 0x81BF, 0x8147,
+ 0x80D0, 0x8059, 0x7FE3, 0x7F6D,
+ 0x7EF7, 0x7E82, 0x7E0D, 0x7D99,
+ 0x7D25, 0x7CB2, 0x7C3F, 0x7BCC,
+ 0x7B5A, 0x7AE8, 0x7A77, 0x7A06,
+ 0x7995, 0x7925, 0x78B5, 0x7846,
+ 0x77D7, 0x7768, 0x76FA, 0x768C,
+ 0x761F, 0x75B2, 0x7545, 0x74D9,
+ 0x746D, 0x7402, 0x7397, 0x732C,
+ 0x72C2, 0x7258, 0x71EF, 0x7186,
+ 0x711D, 0x70B5, 0x704D, 0x6FE5,
+ 0x6F7E, 0x6F17, 0x6EB0, 0x6E4A,
+ 0x6DE5, 0x6D7F, 0x6D1A, 0x6CB5,
+ 0x6C51, 0x6BED, 0x6B8A, 0x6B26,
+ 0x6AC4, 0x6A61, 0x69FF, 0x699D,
+ 0x693C, 0x68DB, 0x687A, 0x681A,
+ 0x67BA, 0x675A, 0x66FA, 0x669B,
+ 0x663D, 0x65DF, 0x6581, 0x6523,
+ 0x64C6, 0x6469, 0x640C, 0x63B0,
+ 0x6354, 0x62F8, 0x629D, 0x6242,
+ 0x61E7, 0x618D, 0x6133, 0x60D9,
+ 0x6080, 0x6027, 0x5FCE, 0x5F76,
+ 0x5F1E, 0x5EC6, 0x5E6E, 0x5E17,
+ 0x5DC1, 0x5D6A, 0x5D14, 0x5CBE,
+ 0x5C68, 0x5C13, 0x5BBE, 0x5B6A,
+ 0x5B15, 0x5AC1, 0x5A6E, 0x5A1A,
+ 0x59C7, 0x5974, 0x5922, 0x58CF,
+ 0x587D, 0x582C, 0x57DA, 0x5789,
+ 0x5739, 0x56E8, 0x5698, 0x5648,
+ 0x55F9, 0x55A9, 0x555A, 0x550B,
+ 0x54BD, 0x546F, 0x5421, 0x53D3,
+ 0x5386, 0x5339, 0x52EC, 0x52A0,
+ 0x5253, 0x5207, 0x51BC, 0x5170,
+ 0x5125, 0x50DA, 0x5090, 0x5046,
+ 0x4FFB, 0x4FB2, 0x4F68, 0x4F1F,
+ 0x4ED6, 0x4E8D, 0x4E45, 0x4DFC,
+ 0x4DB5, 0x4D6D, 0x4D25, 0x4CDE,
+ 0x4C97, 0x4C51, 0x4C0A, 0x4BC4,
+ 0x4B7E, 0x4B39, 0x4AF3, 0x4AAE,
+ 0x4A69, 0x4A24, 0x49E0, 0x499C,
+ 0x4958, 0x4914, 0x48D1, 0x488E,
+ 0x484B, 0x4808, 0x47C6, 0x4783
+};
+
+} // End of namespace Scumm
+
diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/pcspk.h
new file mode 100644
index 0000000000..e77ac8c1bf
--- /dev/null
+++ b/engines/scumm/imuse/pcspk.h
@@ -0,0 +1,161 @@
+/* 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 SCUMM_IMUSE_PCSPK_H
+#define SCUMM_IMUSE_PCSPK_H
+
+#include "audio/softsynth/emumidi.h"
+#include "audio/softsynth/pcspk.h"
+
+namespace Scumm {
+
+class PcSpkDriver : public MidiDriver_Emulated {
+public:
+ PcSpkDriver(Audio::Mixer *mixer);
+ ~PcSpkDriver();
+
+ virtual int open();
+ virtual void close();
+
+ virtual void send(uint32 d);
+ virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
+
+ virtual MidiChannel *allocateChannel();
+ virtual MidiChannel *getPercussionChannel() { return 0; }
+
+ bool isStereo() const { return _pcSpk.isStereo(); }
+ int getRate() const { return _pcSpk.getRate(); }
+protected:
+ void generateSamples(int16 *buf, int len);
+ void onTimer();
+
+private:
+ Audio::PCSpeaker _pcSpk;
+ int _effectTimer;
+ uint8 _randBase;
+
+ void updateNote();
+ void output(uint16 out);
+
+ static uint8 getEffectModifier(uint16 level);
+ int16 getEffectModLevel(int16 level, int8 mod);
+ int16 getRandMultipy(int16 input);
+
+ struct EffectEnvelope {
+ uint8 state;
+ int16 currentLevel;
+ int16 duration;
+ int16 maxLevel;
+ int16 startLevel;
+ uint8 loop;
+ uint8 stateTargetLevels[4];
+ uint8 stateModWheelLevels[4];
+ uint8 modWheelSensitivity;
+ uint8 modWheelState;
+ uint8 modWheelLast;
+ int16 stateNumSteps;
+ int16 stateStepCounter;
+ int16 changePerStep;
+ int8 dir;
+ int16 changePerStepRem;
+ int16 changeCountRem;
+ };
+
+ struct EffectDefinition {
+ int16 phase;
+ uint8 type;
+ uint8 useModWheel;
+ EffectEnvelope *envelope;
+ };
+
+ struct OutputChannel {
+ uint8 active;
+ uint8 note;
+ uint8 sustainNoteOff;
+ uint8 length;
+ const uint8 *instrument;
+ uint8 unkA;
+ uint8 unkB;
+ uint8 unkC;
+ int16 unkE;
+ EffectEnvelope effectEnvelopeA;
+ EffectDefinition effectDefA;
+ EffectEnvelope effectEnvelopeB;
+ EffectDefinition effectDefB;
+ int16 unk60;
+ };
+
+ struct MidiChannel_PcSpk : public MidiChannel {
+ virtual MidiDriver *device();
+ virtual byte getNumber();
+ virtual void release();
+
+ virtual void send(uint32 b);
+ virtual void noteOff(byte note);
+ virtual void noteOn(byte note, byte velocity);
+ virtual void programChange(byte program);
+ virtual void pitchBend(int16 bend);
+ virtual void controlChange(byte control, byte value);
+ virtual void pitchBendFactor(byte value);
+ virtual void priority(byte value);
+ virtual void sysEx_customInstrument(uint32 type, const byte *instr);
+
+ void init(PcSpkDriver *owner, byte channel);
+ bool allocate();
+
+ PcSpkDriver *_owner;
+ bool _allocated;
+ byte _channel;
+
+ OutputChannel _out;
+ uint8 _instrument[23];
+ uint8 _programNr;
+ uint8 _priority;
+ uint8 _tl;
+ uint8 _modWheel;
+ uint8 _sustain;
+ uint8 _pitchBendFactor;
+ int16 _pitchBend;
+ };
+
+ void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data);
+ void startEffect(EffectEnvelope &env, const byte *data);
+ void initNextEnvelopeState(EffectEnvelope &env);
+ void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def);
+ uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def);
+
+ MidiChannel_PcSpk _channels[6];
+ MidiChannel_PcSpk *_activeChannel;
+
+ MidiChannel_PcSpk *_lastActiveChannel;
+ uint16 _lastActiveOut;
+
+ static const byte _outInstrumentData[1024];
+ static const byte _outputTable1[];
+ static const byte _outputTable2[];
+ static const uint16 _effectEnvStepTable[];
+ static const uint16 _frequencyTable[];
+};
+
+} // End of namespace Scumm
+
+#endif
+
diff --git a/engines/scumm/imuse/sysex_samnmax.cpp b/engines/scumm/imuse/sysex_samnmax.cpp
index 4c4219e7bb..a4f525da56 100644
--- a/engines/scumm/imuse/sysex_samnmax.cpp
+++ b/engines/scumm/imuse/sysex_samnmax.cpp
@@ -53,8 +53,7 @@ void sysexHandler_SamNMax(Player *player, const byte *msg, uint16 len) {
// something magical is supposed to happen....
for (a = 0; a < ARRAYSIZE(se->_snm_triggers); ++a) {
if (se->_snm_triggers[a].sound == player->_id &&
- se->_snm_triggers[a].id == *p)
- {
+ se->_snm_triggers[a].id == *p) {
se->_snm_triggers[a].sound = se->_snm_triggers[a].id = 0;
se->doCommand(8, se->_snm_triggers[a].command);
break;
diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp
index c3bec93a60..ec64800b20 100644
--- a/engines/scumm/imuse/sysex_scumm.cpp
+++ b/engines/scumm/imuse/sysex_scumm.cpp
@@ -54,7 +54,8 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
// BYTE 00: Channel #
// BYTE 02: BIT 01(0x01): Part on?(1 = yes)
// BIT 02(0x02): Reverb? (1 = yes) [bug #1088045]
- // BYTE 04: Priority adjustment [guessing]
+ // BYTE 03: Priority adjustment(upper 4 bits)
+ // BYTE 04: Priority adjustment(lower 4 bits)
// BYTE 05: Volume(upper 4 bits) [guessing]
// BYTE 06: Volume(lower 4 bits) [guessing]
// BYTE 07: Pan(upper 4 bits) [bug #1088045]
@@ -73,8 +74,8 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
if (part) {
part->set_onoff(p[2] & 0x01);
part->effectLevel((p[2] & 0x02) ? 127 : 0);
- part->set_pri(p[4]);
- part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
+ part->set_pri((p[3] << 4) | p[4]);
+ part->volume((p[5] & 0x0F) << 4 | (p[6] & 0x0F));
part->set_pan((p[7] & 0x0F) << 4 | (p[8] & 0x0F));
part->_percussion = player->_isMIDI ? ((p[9] & 0x08) > 0) : false;
part->set_transpose((p[9] & 0x0F) << 4 | (p[10] & 0x0F));
@@ -91,8 +92,16 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
// 0 is a valid program number. MI2 tests show that in such
// cases, a regular program change message always seems to follow
// anyway.
- if (player->_isMIDI)
- part->_instrument.program((p[15] & 0x0F) << 4 |(p[16] & 0x0F), player->_isMT32);
+ if (player->_isMIDI) {
+ part->_instrument.program((p[15] & 0x0F) << 4 | (p[16] & 0x0F), player->_isMT32);
+ } else if (se->_pcSpeaker) {
+ // FIXME/HACK: This is only needed here, since when we use the following line:
+ // se->copyGlobalInstrument((p[15] & 0x0F) << 4 |(p[16] & 0x0F), &part->_instrument);
+ // We would not get any instrument for PC Speaker. Because we don't default to an
+ // "empty" instrument in case the global instrument specified is not set up.
+ byte empty[23] = {0};
+ part->_instrument.pcspk(empty);
+ }
part->sendAll();
}
}
@@ -113,11 +122,10 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
++p; // Skip hardware type
part = player->getPart(a);
if (part) {
- if (len == 62) {
+ if (len == 62 || len == 48) {
player->decode_sysex_bytes(p, buf, len - 2);
part->set_instrument((byte *)buf);
} else {
- // SPK tracks have len == 48 here, and are not supported
part->programChange(254); // Must be invalid, but not 255 (which is reserved)
}
}
@@ -127,7 +135,8 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
p += 2; // Skip hardware type and... whatever came right before it
a = *p++;
player->decode_sysex_bytes(p, buf, len - 3);
- se->setGlobalAdLibInstrument(a, buf);
+ if (len == 63 || len == 49)
+ se->setGlobalInstrument(a, buf);
break;
case 33: // Parameter adjust
@@ -185,10 +194,9 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
case 80: // Loop
player->decode_sysex_bytes(p + 1, buf, len - 1);
- player->setLoop
- (READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2),
- READ_BE_UINT16(buf + 4), READ_BE_UINT16(buf + 6),
- READ_BE_UINT16(buf + 8));
+ player->setLoop(READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2),
+ READ_BE_UINT16(buf + 4), READ_BE_UINT16(buf + 6),
+ READ_BE_UINT16(buf + 8));
break;
case 81: // End loop
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 1a60564a9e..99ffdf7f21 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -27,6 +27,7 @@ MODULE_OBJS := \
imuse/imuse_part.o \
imuse/imuse_player.o \
imuse/instrument.o \
+ imuse/pcspk.o \
imuse/sysex_samnmax.o \
imuse/sysex_scumm.o \
input.o \
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 7b136dc36d..0f01e39459 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -71,6 +71,7 @@
#include "scumm/he/cup_player_he.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
+#include "scumm/imuse/pcspk.h"
#include "backends/audiocd/audiocd.h"
@@ -283,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;
@@ -633,8 +635,10 @@ ScummEngine::~ScummEngine() {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
delete _townsScreen;
+#ifdef USE_RGB_COLOR
delete _cjkFont;
#endif
+#endif
delete _debugger;
@@ -1350,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);
}
}
@@ -1857,14 +1871,16 @@ void ScummEngine::setupMusic(int midi) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
- if (_musicType != MDT_ADLIB && _musicType != MDT_TOWNS)
+ if (_musicType != MDT_ADLIB && _musicType != MDT_TOWNS && _musicType != MDT_PCSPK)
nativeMidiDriver = MidiDriver::createMidi(dev);
if (nativeMidiDriver != NULL && _native_mt32)
nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
- bool multi_midi = ConfMan.getBool("multi_midi") && _musicType != MDT_NONE && (midi & MDT_ADLIB);
+ bool multi_midi = ConfMan.getBool("multi_midi") && _musicType != MDT_NONE && _musicType != MDT_PCSPK && (midi & MDT_ADLIB);
if (_musicType == MDT_ADLIB || _musicType == MDT_TOWNS || multi_midi) {
adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
+ } else if (_musicType == MDT_PCSPK) {
+ adlibMidiDriver = new PcSpkDriver(_mixer);
}
_imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
@@ -1893,6 +1909,8 @@ void ScummEngine::setupMusic(int midi) {
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
}
+ if (_musicType == MDT_PCSPK)
+ _imuse->property(IMuse::PROP_PC_SPEAKER, 1);
}
}
}
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/scumm/sound.cpp b/engines/scumm/sound.cpp
index 27e43b3740..544abe6b1d 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1137,8 +1137,8 @@ int ScummEngine::readSoundResource(ResId idx) {
break;
case MKTAG('S','P','K',' '):
pri = -1;
-// if (_musicType == MDT_PCSPK || _musicType == MDT_PCJR)
-// pri = 11;
+ if (_musicType == MDT_PCSPK || _musicType == MDT_PCJR)
+ pri = 11;
break;
}
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..cff6c24469 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);
@@ -1547,7 +1552,7 @@ void ToonEngine::clickEvent() {
return;
}
} else {
- if (!_drew->walkTo(_mouseX, _mouseY)) {
+ if (!_drew->walkTo(_mouseX + _gameState->_currentScrollValue, _mouseY)) {
// walk was canceled ?
return;
}
@@ -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/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;