diff options
272 files changed, 14703 insertions, 5042 deletions
@@ -3,10 +3,50 @@ For a more comprehensive changelog of the latest experimental code, see: 1.9.0 (XXXX-XX-XX) AGI: - - Added support for Hercules rendering (green + amber) - - Added support for the Hercules hires font (also usable outside of Hercules rendering) - - Added optional "pause, when entering commands" feature, that was only available - in the original interpreter for Hercules rendering. + - Added support for Hercules rendering. Both green and amber modes are + supported. + - Added support for the Hercules high resolution font. The font is also + usable outside of Hercules rendering. + - Added optional "pause, when entering commands" feature, that was only + available in the original interpreter for Hercules rendering. + +1.8.1 (XXXX-XX-XX) + General: + - Removed TESTING flag from several supported games. + - Added Chinese Pinyin translation. + + BBVS: + - Fixed game restart. + + CinE: + - Fixed sound effect loading. + + Gob: + - Fixed lock up for some games during sound initialization. + + Lab: + - Fixed lock-up during ending sequence. + - Improved internal game controls. + + SAGA: + - Fixed user interface colors in the French and German versions of I Have No + Mouth and I Must Scream. + + SCUMM: + - Fixed detection of Maniac Mansion from Day of the Tentacle in the Windows + version of ScummVM. + - Fixed a sound effect not stopping in Loom EGA with AdLib. + + Broken Sword 2.5: + - Added option to use English speech instead of German one when no speech is + available for the selected language. + - Fixed resource releasing on game exit. + - Fixed game restart after language change in-game. + - Fixed flickering in main Menu. + + Windows port: + - Fixed bug in MIDI device listing affecting cases where MIDI devices were + not usable. 1.8.0 (2016-03-04) New Games: diff --git a/audio/softsynth/mt32.cpp b/audio/softsynth/mt32.cpp index 4420657854..d514e64fe9 100644 --- a/audio/softsynth/mt32.cpp +++ b/audio/softsynth/mt32.cpp @@ -140,10 +140,7 @@ MidiDriver_MT32::MidiDriver_MT32(Audio::Mixer *mixer) : MidiDriver_Emulated(mixe } _reportHandler = NULL; _synth = NULL; - // Unfortunately bugs in the emulator cause inaccurate tuning - // at rates other than 32KHz, thus we produce data at 32KHz and - // rely on Mixer to convert. - _outputRate = 32000; //_mixer->getOutputRate(); + _outputRate = 0; _initializing = false; // Initialized in open() @@ -180,7 +177,6 @@ int MidiDriver_MT32::open() { if (_isOpen) return MERR_ALREADY_OPEN; - MidiDriver_Emulated::open(); _reportHandler = new MT32Emu::ReportHandlerScummVM(); _synth = new MT32Emu::Synth(_reportHandler); @@ -212,6 +208,18 @@ int MidiDriver_MT32::open() { double gain = (double)ConfMan.getInt("midi_gain") / 100.0; _synth->setOutputGain(1.0f * gain); _synth->setReverbOutputGain(0.68f * gain); + // We let the synthesizer play MIDI messages immediately. Our MIDI + // handling is synchronous to sample generation. This makes delaying MIDI + // events result in odd sound output in some cases. For example, the + // shattering window in the Indiana Jones and the Fate of Atlantis intro + // will sound like a bell if we use any delay here. + // Bug #6242 "AUDIO: Built-In MT-32 MUNT Produces Wrong Sounds". + _synth->setMIDIDelayMode(MT32Emu::MIDIDelayMode_IMMEDIATE); + + // We need to report the sample rate MUNT renders at as sample rate of our + // AudioStream. + _outputRate = _synth->getStereoOutputSampleRate(); + MidiDriver_Emulated::open(); _initializing = false; diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp new file mode 100644 index 0000000000..3c0d0957da --- /dev/null +++ b/backends/audiocd/audiocd-stream.cpp @@ -0,0 +1,199 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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 "backends/audiocd/audiocd-stream.h" +#include "common/textconsole.h" + +AudioCDStream::AudioCDStream() : _buffer(), _frame(0), _bufferPos(0), _bufferFrame(0), _forceStop(false) { +} + +AudioCDStream::~AudioCDStream() { + // Stop the timer; the subclass needs to do this, + // so this is just a last resort. + stopTimer(); + + // Clear any buffered frames + emptyQueue(); +} + +int AudioCDStream::readBuffer(int16 *buffer, const int numSamples) { + int samples = 0; + + // See if any data is left first + while (_bufferPos < kSamplesPerFrame && samples < numSamples) + buffer[samples++] = _buffer[_bufferPos++]; + + // Bail out if done + if (endOfData()) + return samples; + + while (samples < numSamples && !endOfData()) { + if (!readNextFrame()) + return samples; + + // Copy the samples over + for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples;) + buffer[samples++] = _buffer[_bufferPos++]; + } + + return samples; +} + +bool AudioCDStream::readNextFrame() { + // Fetch a frame from the queue + int16 *buffer; + + { + Common::StackLock lock(_mutex); + + // Nothing we can do if it's empty + if (_bufferQueue.empty()) + return false; + + buffer = _bufferQueue.pop(); + } + + memcpy(_buffer, buffer, kSamplesPerFrame * 2); + delete[] buffer; + _frame++; + return true; +} + +bool AudioCDStream::endOfData() const { + return !shouldForceStop() && getStartFrame() + _frame >= getEndFrame() && _bufferPos == kSamplesPerFrame; +} + +bool AudioCDStream::seek(const Audio::Timestamp &where) { + // Stop the timer + stopTimer(); + + // Clear anything out of the queue + emptyQueue(); + + // Convert to the frame number + // Really not much else needed + _bufferPos = kSamplesPerFrame; + _frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames(); + _bufferFrame = _frame; + + // Start the timer again + startTimer(); + return true; +} + +Audio::Timestamp AudioCDStream::getLength() const { + return Audio::Timestamp(0, getEndFrame() - getStartFrame(), kFramesPerSecond); +} + +void AudioCDStream::timerProc(void *refCon) { + static_cast<AudioCDStream *>(refCon)->onTimer(); +} + +void AudioCDStream::onTimer() { + // The goal here is to do as much work in this timer instead + // of doing it in the readBuffer() call, which is the mixer. + + // If we're done, bail. + if (shouldForceStop() || getStartFrame() + _bufferFrame >= getEndFrame()) + return; + + // Get a quick count of the number of items in the queue + // We don't care that much; we only need a quick estimate + _mutex.lock(); + uint32 queueCount = _bufferQueue.size(); + _mutex.unlock(); + + // If we have enough audio buffered, bail out + if (queueCount >= kBufferThreshold) + return; + + while (!shouldForceStop() && queueCount < kBufferThreshold && getStartFrame() + _bufferFrame < getEndFrame()) { + int16 *buffer = new int16[kSamplesPerFrame]; + + // Figure out the MSF of the frame we're looking for + int frame = _bufferFrame + getStartFrame(); + + // Request to read that frame + if (!readFrame(frame, buffer)) { + warning("Failed to read CD audio"); + forceStop(); + return; + } + + _bufferFrame++; + + // Now push the buffer onto the queue + Common::StackLock lock(_mutex); + _bufferQueue.push(buffer); + queueCount = _bufferQueue.size(); + } +} + +void AudioCDStream::startTimer(bool fillBuffer) { + _forceStop = false; + if (fillBuffer) { + onTimer(); + } + g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "AudioCDStream"); +} + +void AudioCDStream::stopTimer() { + forceStop(); + g_system->getTimerManager()->removeTimerProc(timerProc); +} + +void AudioCDStream::emptyQueue() { + while (!_bufferQueue.empty()) + delete[] _bufferQueue.pop(); +} + +bool AudioCDStream::shouldForceStop() const { + Common::StackLock lock(_forceStopMutex); + return _forceStop; +} + +void AudioCDStream::forceStop() { + Common::StackLock lock(_forceStopMutex); + _forceStop = true; +} diff --git a/backends/audiocd/audiocd-stream.h b/backends/audiocd/audiocd-stream.h new file mode 100644 index 0000000000..dbc6a6321b --- /dev/null +++ b/backends/audiocd/audiocd-stream.h @@ -0,0 +1,108 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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 BACKENDS_AUDIOCD_AUDIOCD_STREAM_H +#define BACKENDS_AUDIOCD_AUDIOCD_STREAM_H + +#include "audio/audiostream.h" +#include "common/mutex.h" +#include "common/queue.h" +#include "common/timer.h" + +class AudioCDStream : public Audio::SeekableAudioStream { +public: + AudioCDStream(); + ~AudioCDStream(); + + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return true; } + int getRate() const { return 44100; } + bool endOfData() const; + bool seek(const Audio::Timestamp &where); + Audio::Timestamp getLength() const; + +protected: + virtual uint getStartFrame() const = 0; + virtual uint getEndFrame() const = 0; + virtual bool readFrame(int frame, int16 *buffer) = 0; + + void startTimer(bool fillBuffer = false); + void stopTimer(); + + enum { + kBytesPerFrame = 2352, + kSamplesPerFrame = kBytesPerFrame / 2 + }; + + enum { + kSecondsPerMinute = 60, + kFramesPerSecond = 75 + }; + + enum { + // Keep about a second's worth of audio in the buffer + kBufferThreshold = kFramesPerSecond + }; + +private: + int16 _buffer[kSamplesPerFrame]; + int _frame; + uint _bufferPos; + + Common::Queue<int16 *> _bufferQueue; + int _bufferFrame; + Common::Mutex _mutex; + + bool _forceStop; + bool shouldForceStop() const; + void forceStop(); + Common::Mutex _forceStopMutex; + + bool readNextFrame(); + static void timerProc(void *refCon); + void onTimer(); + void emptyQueue(); +}; + +#endif diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h index 6eae8e096b..b3674f2570 100644 --- a/backends/audiocd/audiocd.h +++ b/backends/audiocd/audiocd.h @@ -48,26 +48,31 @@ public: }; /** - * @name Emulated playback functions - * Engines should call these functions. Not all platforms - * support cd playback, and these functions should try to - * emulate it. + * Initialize the specified CD drive for audio playback. + * @return true if the CD drive was inited successfully + */ + virtual bool open() = 0; + + /** + * Close the currently open CD drive */ - //@{ + virtual void close() = 0; /** * Start audio CD playback - * @param track the track to play. - * @param numLoops how often playback should be repeated (-1 = infinitely often). - * @param startFrame the frame at which playback should start (75 frames = 1 second). - * @param duration the number of frames to play. - * @param only_emulate determines if the track should be emulated only + * @param track the track to play. + * @param numLoops how often playback should be repeated (<=0 means infinitely often). + * @param startFrame the frame at which playback should start (75 frames = 1 second). + * @param duration the number of frames to play. + * @param onlyEmulate determines if the track should be emulated only + * @note The @c onlyEmulate parameter is deprecated. + * @return @c true if the track started playing, @c false otherwise */ - virtual void play(int track, int numLoops, int startFrame, int duration, bool only_emulate = false) = 0; + virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false) = 0; /** * Get if audio is being played. - * @return true if CD or emulated audio is playing + * @return true if CD audio is playing */ virtual bool isPlaying() const = 0; @@ -82,12 +87,12 @@ public: virtual void setBalance(int8 balance) = 0; /** - * Stop CD or emulated audio playback. + * Stop audio playback. */ virtual void stop() = 0; /** - * Update CD or emulated audio status. + * Update audio status. */ virtual void update() = 0; @@ -96,50 +101,6 @@ public: * @return a Status struct with playback data. */ virtual Status getStatus() const = 0; - - //@} - - - /** - * @name Real CD audio methods - * These functions should be called from the emulated - * ones if they can't emulate the audio playback. - */ - //@{ - - /** - * Initialize the specified CD drive for audio playback. - * @param drive the drive id - * @return true if the CD drive was inited successfully - */ - virtual bool openCD(int drive) = 0; - - /** - * Poll CD status. - * @return true if CD audio is playing - */ - virtual bool pollCD() const = 0; - - /** - * Start CD audio playback. - * @param track the track to play. - * @param num_loops how often playback should be repeated (-1 = infinitely often). - * @param start_frame the frame at which playback should start (75 frames = 1 second). - * @param duration the number of frames to play. - */ - virtual void playCD(int track, int num_loops, int start_frame, int duration) = 0; - - /** - * Stop CD audio playback. - */ - virtual void stopCD() = 0; - - /** - * Update CD audio status. - */ - virtual void updateCD() = 0; - - //@} }; #endif diff --git a/backends/audiocd/default/default-audiocd.cpp b/backends/audiocd/default/default-audiocd.cpp index abf80ac4cd..c2ce7cedcc 100644 --- a/backends/audiocd/default/default-audiocd.cpp +++ b/backends/audiocd/default/default-audiocd.cpp @@ -22,6 +22,7 @@ #include "backends/audiocd/default/default-audiocd.h" #include "audio/audiostream.h" +#include "common/config-manager.h" #include "common/system.h" DefaultAudioCDManager::DefaultAudioCDManager() { @@ -37,7 +38,25 @@ DefaultAudioCDManager::DefaultAudioCDManager() { assert(_mixer); } -void DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool only_emulate) { +DefaultAudioCDManager::~DefaultAudioCDManager() { + // Subclasses should call close as well + close(); +} + +bool DefaultAudioCDManager::open() { + // For emulation, opening is always valid + close(); + return true; +} + +void DefaultAudioCDManager::close() { + // Only need to stop for emulation + stop(); +} + +bool DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) { + stop(); + if (numLoops != 0 || startFrame != 0) { _cd.track = track; _cd.numLoops = numLoops; @@ -55,9 +74,6 @@ void DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int du for (int i = 0; !stream && i < 2; ++i) stream = Audio::SeekableAudioStream::openStreamFile(trackName[i]); - // Stop any currently playing emulated track - _mixer->stopHandle(_handle); - if (stream != 0) { Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75); Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : stream->getLength(); @@ -70,12 +86,11 @@ void DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int du _emulating = true; _mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle, Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance); - } else { - _emulating = false; - if (!only_emulate) - playCD(track, numLoops, startFrame, duration); + return true; } } + + return false; } void DefaultAudioCDManager::stop() { @@ -83,52 +98,32 @@ void DefaultAudioCDManager::stop() { // Audio CD emulation _mixer->stopHandle(_handle); _emulating = false; - } else { - // Real Audio CD - stopCD(); } } bool DefaultAudioCDManager::isPlaying() const { - if (_emulating) { - // Audio CD emulation + // Audio CD emulation + if (_emulating) return _mixer->isSoundHandleActive(_handle); - } else { - // Real Audio CD - return pollCD(); - } + + // The default class only handles emulation + return false; } void DefaultAudioCDManager::setVolume(byte volume) { _cd.volume = volume; - if (_emulating) { - // Audio CD emulation - if (_mixer->isSoundHandleActive(_handle)) - _mixer->setChannelVolume(_handle, _cd.volume); - } else { - // Real Audio CD - // Unfortunately I can't implement this atm - // since SDL doesn't seem to offer an interface method for this. - - // g_system->setVolumeCD(_cd.volume); - } + // Audio CD emulation + if (_emulating && isPlaying()) + _mixer->setChannelVolume(_handle, _cd.volume); } void DefaultAudioCDManager::setBalance(int8 balance) { _cd.balance = balance; - if (_emulating) { - // Audio CD emulation - if (isPlaying()) - _mixer->setChannelBalance(_handle, _cd.balance); - } else { - // Real Audio CD - // Unfortunately I can't implement this atm - // since SDL doesn't seem to offer an interface method for this. - - // g_system->setBalanceCD(_cd.balance); - } + // Audio CD emulation + if (_emulating && isPlaying()) + _mixer->setChannelBalance(_handle, _cd.balance); } void DefaultAudioCDManager::update() { @@ -142,8 +137,6 @@ void DefaultAudioCDManager::update() { // or not. _emulating = false; } - } else { - updateCD(); } } @@ -152,3 +145,21 @@ DefaultAudioCDManager::Status DefaultAudioCDManager::getStatus() const { info.playing = isPlaying(); return info; } + +bool DefaultAudioCDManager::openRealCD() { + Common::String cdrom = ConfMan.get("cdrom"); + + // Try to parse it as an int + char *endPos; + int drive = strtol(cdrom.c_str(), &endPos, 0); + + // If not an integer, treat as a drive path + if (endPos == cdrom.c_str()) + return openCD(cdrom); + + if (drive < 0) + return false; + + return openCD(drive); +} + diff --git a/backends/audiocd/default/default-audiocd.h b/backends/audiocd/default/default-audiocd.h index 9e4ba6b33e..e3fbb4b5a1 100644 --- a/backends/audiocd/default/default-audiocd.h +++ b/backends/audiocd/default/default-audiocd.h @@ -26,29 +26,48 @@ #include "backends/audiocd/audiocd.h" #include "audio/mixer.h" +namespace Common { +class String; +} // End of namespace Common + /** * The default audio cd manager. Implements emulation of audio cd playback. */ class DefaultAudioCDManager : public AudioCDManager { public: DefaultAudioCDManager(); - virtual ~DefaultAudioCDManager() {} - - void play(int track, int numLoops, int startFrame, int duration, bool only_emulate = false); - void stop(); - bool isPlaying() const; - void setVolume(byte volume); - void setBalance(int8 balance); - void update(); + virtual ~DefaultAudioCDManager(); + + virtual bool open(); + virtual void close(); + virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); + virtual void stop(); + virtual bool isPlaying() const; + virtual void setVolume(byte volume); + virtual void setBalance(int8 balance); + virtual void update(); virtual Status getStatus() const; // Subclasses should override for better status results +protected: + /** + * Open a CD using the cdrom config variable + */ + bool openRealCD(); + + /** + * Open a CD using the specified drive index + * @param drive The index of the drive + * @note The index is implementation-defined, but 0 is always the best choice + */ virtual bool openCD(int drive) { return false; } - virtual void updateCD() {} - virtual bool pollCD() const { return false; } - virtual void playCD(int track, int num_loops, int start_frame, int duration) {} - virtual void stopCD() {} -protected: + /** + * Open a CD from a specific drive + * @param drive The name of the drive/path + * @note The drive parameter is platform-specific + */ + virtual bool openCD(const Common::String &drive) { return false; } + Audio::SoundHandle _handle; bool _emulating; diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp new file mode 100644 index 0000000000..caa0265637 --- /dev/null +++ b/backends/audiocd/linux/linux-audiocd.cpp @@ -0,0 +1,471 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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. + * + */ + +// Enable all forbidden symbols to allow us to include and use necessary APIs. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "backends/audiocd/linux/linux-audiocd.h" + +#ifdef USE_LINUXCD + +#include "backends/audiocd/audiocd-stream.h" +#include "backends/audiocd/default/default-audiocd.h" +#include "common/array.h" +#include "common/config-manager.h" +#include "common/str.h" +#include "common/debug.h" + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <linux/cdrom.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +enum { + kLeadoutTrack = 0xAA +}; + +enum { + kBytesPerFrame = 2352, + kSamplesPerFrame = kBytesPerFrame / 2 +}; + +enum { + kSecondsPerMinute = 60, + kFramesPerSecond = 75 +}; + +enum { + // Keep about a second's worth of audio in the buffer + kBufferThreshold = kFramesPerSecond +}; + +static int getFrameCount(const cdrom_msf0 &msf) { + int time = msf.minute; + time *= kSecondsPerMinute; + time += msf.second; + time *= kFramesPerSecond; + time += msf.frame; + return time; +} + +// Helper function to convert an error code into a human-readable message +static Common::String getErrorMessage(int errorCode) { + char buf[256]; + buf[0] = 0; + +#ifdef _GNU_SOURCE + // glibc sucks + return Common::String(strerror_r(errorCode, buf, sizeof(buf))); +#else + strerror_r(errorCode, buf, sizeof(buf)); + return Common::String(buf); +#endif +} + +class LinuxAudioCDStream : public AudioCDStream { +public: + LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry); + ~LinuxAudioCDStream(); + +protected: + uint getStartFrame() const; + uint getEndFrame() const; + bool readFrame(int frame, int16 *buffer); + +private: + int _fd; + const cdrom_tocentry &_startEntry, &_endEntry; +}; + +LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) : + _fd(fd), _startEntry(startEntry), _endEntry(endEntry) { + // We fill the buffer here already to prevent any out of sync issues due + // to the CD not yet having spun up. + startTimer(true); +} + +LinuxAudioCDStream::~LinuxAudioCDStream() { + stopTimer(); +} + +bool LinuxAudioCDStream::readFrame(int frame, int16 *buffer) { + // Create the argument + union { + cdrom_msf msf; + char buffer[kBytesPerFrame]; + } arg; + + int seconds = frame / kFramesPerSecond; + frame %= kFramesPerSecond; + int minutes = seconds / kSecondsPerMinute; + seconds %= kSecondsPerMinute; + + // Request to read that frame + // We don't use CDROMREADAUDIO, as it seems to cause kernel + // panics on ejecting discs. Probably bad to eject the disc + // while playing, but at least let's try to prevent that case. + arg.msf.cdmsf_min0 = minutes; + arg.msf.cdmsf_sec0 = seconds; + arg.msf.cdmsf_frame0 = frame; + // The "end" part is irrelevant (why isn't cdrom_msf0 the type + // instead?) + + if (ioctl(_fd, CDROMREADRAW, &arg) < 0) { + warning("Failed to CD read audio: %s", getErrorMessage(errno).c_str()); + return false; + } + + memcpy(buffer, arg.buffer, kBytesPerFrame); + return true; +} + +uint LinuxAudioCDStream::getStartFrame() const { + return getFrameCount(_startEntry.cdte_addr.msf); +} + +uint LinuxAudioCDStream::getEndFrame() const { + return getFrameCount(_endEntry.cdte_addr.msf); +} + + +class LinuxAudioCDManager : public DefaultAudioCDManager { +public: + LinuxAudioCDManager(); + ~LinuxAudioCDManager(); + + bool open(); + void close(); + bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); + +protected: + bool openCD(int drive); + bool openCD(const Common::String &drive); + +private: + struct Device { + Device(const Common::String &n, dev_t d) : name(n), device(d) {} + Common::String name; + dev_t device; + }; + + typedef Common::Array<Device> DeviceList; + DeviceList scanDevices(); + bool tryAddDrive(DeviceList &devices, const Common::String &drive); + bool tryAddDrive(DeviceList &devices, const Common::String &drive, dev_t device); + bool tryAddDrive(DeviceList &devices, dev_t device); + bool tryAddPath(DeviceList &devices, const Common::String &path); + bool tryAddGamePath(DeviceList &devices); + bool loadTOC(); + static bool hasDevice(const DeviceList &devices, dev_t device); + + int _fd; + cdrom_tochdr _tocHeader; + Common::Array<cdrom_tocentry> _tocEntries; +}; + +static bool isTrayEmpty(int errorNumber) { + switch (errorNumber) { + case EIO: + case ENOENT: + case EINVAL: +#ifdef ENOMEDIUM + case ENOMEDIUM: +#endif + return true; + } + + return false; +} + +LinuxAudioCDManager::LinuxAudioCDManager() { + _fd = -1; + memset(&_tocHeader, 0, sizeof(_tocHeader)); +} + +LinuxAudioCDManager::~LinuxAudioCDManager() { + close(); +} + +bool LinuxAudioCDManager::open() { + close(); + + if (openRealCD()) + return true; + + return DefaultAudioCDManager::open(); +} + +void LinuxAudioCDManager::close() { + DefaultAudioCDManager::close(); + + if (_fd < 0) + return; + + ::close(_fd); + memset(&_tocHeader, 0, sizeof(_tocHeader)); + _tocEntries.clear(); +} + +bool LinuxAudioCDManager::openCD(int drive) { + DeviceList devices = scanDevices(); + if (drive >= (int)devices.size()) + return false; + + _fd = ::open(devices[drive].name.c_str(), O_RDONLY | O_NONBLOCK, 0); + if (_fd < 0) + return false; + + if (!loadTOC()) { + close(); + return false; + } + + return true; +} + +bool LinuxAudioCDManager::openCD(const Common::String &drive) { + DeviceList devices; + if (!tryAddDrive(devices, drive) && !tryAddPath(devices, drive)) + return false; + + _fd = ::open(devices[0].name.c_str(), O_RDONLY | O_NONBLOCK, 0); + if (_fd < 0) + return false; + + if (!loadTOC()) { + close(); + return false; + } + + return true; +} + +bool LinuxAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) { + // Prefer emulation + if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate)) + return true; + + // If we're set to only emulate, or have no CD drive, return here + if (onlyEmulate || _fd < 0) + return false; + + // HACK: For now, just assume that track number is right + // That only works because ScummVM uses the wrong track number anyway + + if (track >= (int)_tocEntries.size() - 1) { + warning("No such track %d", track); + return false; + } + + // Bail if the track isn't an audio track + if ((_tocEntries[track].cdte_ctrl & 0x04) != 0) { + warning("Track %d is not audio", track); + return false; + } + + // Create the AudioStream and play it + debug(1, "Playing CD track %d", track); + + Audio::SeekableAudioStream *audioStream = new LinuxAudioCDStream(_fd, _tocEntries[track], _tocEntries[track + 1]); + + Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75); + Audio::Timestamp end = (duration == 0) ? audioStream->getLength() : Audio::Timestamp(0, startFrame + duration, 75); + + // Fake emulation since we're really playing an AudioStream + _emulating = true; + + _mixer->playStream( + Audio::Mixer::kMusicSoundType, + &_handle, + Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), + -1, + _cd.volume, + _cd.balance, + DisposeAfterUse::YES, + true); + + return true; +} + +LinuxAudioCDManager::DeviceList LinuxAudioCDManager::scanDevices() { + DeviceList devices; + + // Try to use the game's path first as the device + tryAddGamePath(devices); + + // Try adding the default CD-ROM + tryAddDrive(devices, "/dev/cdrom"); + + // TODO: Try others? + + return devices; +} + +bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::String &drive) { + struct stat stbuf; + if (stat(drive.c_str(), &stbuf) < 0) + return false; + + // Must be a character or block device + if (!S_ISCHR(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode)) + return false; + + return tryAddDrive(devices, drive, stbuf.st_rdev); +} + +bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::String &drive, dev_t device) { + if (hasDevice(devices, device)) + return true; + + // Try opening the device and seeing if it is a CD-ROM drve + int fd = ::open(drive.c_str(), O_RDONLY | O_NONBLOCK, 0); + if (fd >= 0) { + cdrom_subchnl info; + info.cdsc_format = CDROM_MSF; + + bool isCD = ioctl(fd, CDROMSUBCHNL, &info) == 0 || isTrayEmpty(errno); + ::close(fd); + if (isCD) { + devices.push_back(Device(drive, device)); + return true; + } + } + + return false; +} + +bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, dev_t device) { + // Construct the block name + // TODO: libblkid's blkid_devno_to_devname is exactly what we look for. + // This requires an external dependency though. + Common::String name = Common::String::format("/dev/block/%d:%d", major(device), minor(device)); + + return tryAddDrive(devices, name, device); +} + +bool LinuxAudioCDManager::tryAddPath(DeviceList &devices, const Common::String &path) { + struct stat stbuf; + if (stat(path.c_str(), &stbuf) < 0) + return false; + + return tryAddDrive(devices, stbuf.st_dev); +} + +bool LinuxAudioCDManager::tryAddGamePath(DeviceList &devices) { + if (!ConfMan.hasKey("path")) + return false; + + return tryAddPath(devices, ConfMan.get("path")); +} + +bool LinuxAudioCDManager::loadTOC() { + if (_fd < 0) + return false; + + if (ioctl(_fd, CDROMREADTOCHDR, &_tocHeader) < 0) + return false; + + debug(4, "CD: Start Track: %d, End Track %d", _tocHeader.cdth_trk0, _tocHeader.cdth_trk1); + + for (int i = _tocHeader.cdth_trk0; i <= _tocHeader.cdth_trk1; i++) { + cdrom_tocentry entry; + memset(&entry, 0, sizeof(entry)); + entry.cdte_track = i; + entry.cdte_format = CDROM_MSF; + + if (ioctl(_fd, CDROMREADTOCENTRY, &entry) < 0) + return false; + +#if 0 + debug("Entry:"); + debug("\tTrack: %d", entry.cdte_track); + debug("\tAdr: %d", entry.cdte_adr); + debug("\tCtrl: %d", entry.cdte_ctrl); + debug("\tFormat: %d", entry.cdte_format); + debug("\tMSF: %d:%d:%d", entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame); + debug("\tMode: %d\n", entry.cdte_datamode); +#endif + + _tocEntries.push_back(entry); + } + + // Fetch the leadout so we can get the length of the last frame + cdrom_tocentry entry; + memset(&entry, 0, sizeof(entry)); + entry.cdte_track = kLeadoutTrack; + entry.cdte_format = CDROM_MSF; + + if (ioctl(_fd, CDROMREADTOCENTRY, &entry) < 0) + return false; + +#if 0 + debug("Lead out:"); + debug("\tTrack: %d", entry.cdte_track); + debug("\tAdr: %d", entry.cdte_adr); + debug("\tCtrl: %d", entry.cdte_ctrl); + debug("\tFormat: %d", entry.cdte_format); + debug("\tMSF: %d:%d:%d", entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame); + debug("\tMode: %d\n", entry.cdte_datamode); +#endif + + _tocEntries.push_back(entry); + return true; +} + +bool LinuxAudioCDManager::hasDevice(const DeviceList &devices, dev_t device) { + for (DeviceList::const_iterator it = devices.begin(); it != devices.end(); it++) + if (it->device == device) + return true; + + return false; +} + +AudioCDManager *createLinuxAudioCDManager() { + return new LinuxAudioCDManager(); +} + +#endif // USE_LINUXCD diff --git a/backends/audiocd/linux/linux-audiocd.h b/backends/audiocd/linux/linux-audiocd.h new file mode 100644 index 0000000000..09d6353991 --- /dev/null +++ b/backends/audiocd/linux/linux-audiocd.h @@ -0,0 +1,62 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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 BACKENDS_AUDIOCD_LINUX_H +#define BACKENDS_AUDIOCD_LINUX_H + +#include "common/scummsys.h" + +#ifdef USE_LINUXCD + +class AudioCDManager; + +/** + * Create an audio CD manager using the Linux CDROM API + */ +AudioCDManager *createLinuxAudioCDManager(); + +#endif + +#endif + diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp new file mode 100644 index 0000000000..e8d41c3e10 --- /dev/null +++ b/backends/audiocd/macosx/macosx-audiocd.cpp @@ -0,0 +1,306 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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. + * + */ + +#ifdef MACOSX + +#include <sys/stat.h> +#include <sys/mount.h> +#include <limits.h> + +#include "common/scummsys.h" + +#include "audio/audiostream.h" +#include "audio/decoders/aiff.h" +#include "audio/timestamp.h" +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/fs.h" +#include "common/hashmap.h" +#include "common/textconsole.h" +#include "backends/audiocd/default/default-audiocd.h" +#include "backends/audiocd/macosx/macosx-audiocd.h" +#include "backends/fs/stdiostream.h" + +// Partially based on SDL's code + +/** + * The Mac OS X audio cd manager. Implements real audio cd playback. + */ +class MacOSXAudioCDManager : public DefaultAudioCDManager { +public: + MacOSXAudioCDManager() {} + ~MacOSXAudioCDManager(); + + bool open(); + void close(); + bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); + +protected: + bool openCD(int drive); + bool openCD(const Common::String &drive); + +private: + struct Drive { + Drive(const Common::String &m, const Common::String &d, const Common::String &f) : + mountPoint(m), deviceName(d), fsType(f) {} + + Common::String mountPoint; + Common::String deviceName; + Common::String fsType; + }; + + typedef Common::Array<Drive> DriveList; + DriveList detectAllDrives(); + DriveList detectCDDADrives(); + + bool findTrackNames(const Common::String &drivePath); + + Common::HashMap<uint, Common::String> _trackMap; +}; + +MacOSXAudioCDManager::~MacOSXAudioCDManager() { + close(); +} + +bool MacOSXAudioCDManager::open() { + close(); + + if (openRealCD()) + return true; + + return DefaultAudioCDManager::open(); +} + +/** + * Find the base disk number of device name. + * Returns -1 if mount point is not /dev/disk* + */ +static int findBaseDiskNumber(const Common::String &diskName) { + if (!diskName.hasPrefix("/dev/disk")) + return -1; + + const char *startPtr = diskName.c_str() + 9; + char *endPtr; + int baseDiskNumber = strtol(startPtr, &endPtr, 10); + if (startPtr == endPtr) + return -1; + + return baseDiskNumber; +} + +bool MacOSXAudioCDManager::openCD(int drive) { + DriveList allDrives = detectAllDrives(); + if (allDrives.empty()) + return false; + + DriveList cddaDrives; + + // Try to get the volume related to the game's path + if (ConfMan.hasKey("path")) { + Common::String gamePath = ConfMan.get("path"); + struct statfs gamePathStat; + if (statfs(gamePath.c_str(), &gamePathStat) == 0) { + int baseDiskNumber = findBaseDiskNumber(gamePathStat.f_mntfromname); + if (baseDiskNumber >= 0) { + // Look for a CDDA drive with the same base disk number + for (uint32 i = 0; i < allDrives.size(); i++) { + if (allDrives[i].fsType == "cddafs" && findBaseDiskNumber(allDrives[i].deviceName) == baseDiskNumber) { + debug(1, "Preferring drive '%s'", allDrives[i].mountPoint.c_str()); + cddaDrives.push_back(allDrives[i]); + allDrives.remove_at(i); + break; + } + } + } + } + } + + // Add the remaining CDDA drives to the CDDA list + for (uint32 i = 0; i < allDrives.size(); i++) + if (allDrives[i].fsType == "cddafs") + cddaDrives.push_back(allDrives[i]); + + if (drive >= (int)cddaDrives.size()) + return false; + + debug(1, "Using '%s' as the CD drive", cddaDrives[drive].mountPoint.c_str()); + + return findTrackNames(cddaDrives[drive].mountPoint); +} + +bool MacOSXAudioCDManager::openCD(const Common::String &drive) { + DriveList drives = detectAllDrives(); + + for (uint32 i = 0; i < drives.size(); i++) { + if (drives[i].fsType != "cddafs") + continue; + + if (drives[i].mountPoint == drive || drives[i].deviceName == drive) { + debug(1, "Using '%s' as the CD drive", drives[i].mountPoint.c_str()); + return findTrackNames(drives[i].mountPoint); + } + } + + return false; +} + +void MacOSXAudioCDManager::close() { + DefaultAudioCDManager::close(); + _trackMap.clear(); +} + +enum { + // Some crazy high number that we'll never actually hit + kMaxDriveCount = 256 +}; + +MacOSXAudioCDManager::DriveList MacOSXAudioCDManager::detectAllDrives() { + // Fetch the lists of drives + struct statfs driveStats[kMaxDriveCount]; + int foundDrives = getfsstat(driveStats, sizeof(driveStats), MNT_WAIT); + if (foundDrives <= 0) + return DriveList(); + + DriveList drives; + for (int i = 0; i < foundDrives; i++) + drives.push_back(Drive(driveStats[i].f_mntonname, driveStats[i].f_mntfromname, driveStats[i].f_fstypename)); + + return drives; +} + +bool MacOSXAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) { + // Prefer emulation + if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate)) + return true; + + // If we're set to only emulate, or have no CD drive, return here + if (onlyEmulate || !_trackMap.contains(track)) + return false; + + if (!numLoops && !startFrame) + return false; + + // Now load the AIFF track from the name + Common::String fileName = _trackMap[track]; + Common::SeekableReadStream *stream = StdioStream::makeFromPath(fileName.c_str(), false); + + if (!stream) { + warning("Failed to open track '%s'", fileName.c_str()); + return false; + } + + Audio::AudioStream *audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES); + if (!audioStream) { + warning("Track '%s' is not an AIFF track", fileName.c_str()); + return false; + } + + Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(audioStream); + if (!seekStream) { + warning("Track '%s' is not seekable", fileName.c_str()); + return false; + } + + Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75); + Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : seekStream->getLength(); + + // Fake emulation since we're really playing an AIFF file + _emulating = true; + + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle, + Audio::makeLoopingAudioStream(seekStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance); + return true; +} + +bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) { + Common::FSNode directory(drivePath); + + if (!directory.exists()) { + warning("Directory '%s' does not exist", drivePath.c_str()); + return false; + } + + if (!directory.isDirectory()) { + warning("'%s' is not a directory", drivePath.c_str()); + return false; + } + + Common::FSList children; + if (!directory.getChildren(children, Common::FSNode::kListFilesOnly)) { + warning("Failed to find children for '%s'", drivePath.c_str()); + return false; + } + + for (uint32 i = 0; i < children.size(); i++) { + if (!children[i].isDirectory()) { + Common::String fileName = children[i].getName(); + + if (fileName.hasSuffix(".aiff") || fileName.hasSuffix(".cdda")) { + uint j = 0; + + // Search for the track ID in the file name. + for (; j < fileName.size() && !Common::isDigit(fileName[j]); j++) + ; + + const char *trackIDString = fileName.c_str() + j; + char *endPtr = nullptr; + long trackID = strtol(trackIDString, &endPtr, 10); + + if (trackIDString != endPtr && trackID > 0 && trackID < UINT_MAX) { + _trackMap[trackID - 1] = drivePath + '/' + fileName; + } else { + warning("Invalid track file name: '%s'", fileName.c_str()); + } + } + } + } + + return true; +} + +AudioCDManager *createMacOSXAudioCDManager() { + return new MacOSXAudioCDManager(); +} + +#endif // MACOSX diff --git a/backends/audiocd/macosx/macosx-audiocd.h b/backends/audiocd/macosx/macosx-audiocd.h new file mode 100644 index 0000000000..55b8c7b8c6 --- /dev/null +++ b/backends/audiocd/macosx/macosx-audiocd.h @@ -0,0 +1,61 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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 BACKENDS_AUDIOCD_MACOSX_H +#define BACKENDS_AUDIOCD_MACOSX_H + +#include "common/scummsys.h" + +#ifdef MACOSX + +class AudioCDManager; + +/** + * Create an audio CD manager for Mac OS X + */ +AudioCDManager *createMacOSXAudioCDManager(); + +#endif + +#endif // diff --git a/backends/audiocd/sdl/sdl-audiocd.cpp b/backends/audiocd/sdl/sdl-audiocd.cpp index ff50c56af3..3558fb5671 100644 --- a/backends/audiocd/sdl/sdl-audiocd.cpp +++ b/backends/audiocd/sdl/sdl-audiocd.cpp @@ -43,10 +43,16 @@ SdlAudioCDManager::SdlAudioCDManager() } SdlAudioCDManager::~SdlAudioCDManager() { - if (_cdrom) { - SDL_CDStop(_cdrom); - SDL_CDClose(_cdrom); - } + close(); +} + +bool SdlAudioCDManager::open() { + close(); + + if (openRealCD()) + return true; + + return DefaultAudioCDManager::open(); } bool SdlAudioCDManager::openCD(int drive) { @@ -67,44 +73,69 @@ bool SdlAudioCDManager::openCD(int drive) { return (_cdrom != NULL); } -void SdlAudioCDManager::stopCD() { +void SdlAudioCDManager::close() { + DefaultAudioCDManager::close(); + + if (_cdrom) { + SDL_CDStop(_cdrom); + SDL_CDClose(_cdrom); + _cdrom = 0; + } +} + +void SdlAudioCDManager::stop() { + DefaultAudioCDManager::stop(); + // Stop CD Audio in 1/10th of a second _cdStopTime = SDL_GetTicks() + 100; _cdNumLoops = 0; } -void SdlAudioCDManager::playCD(int track, int num_loops, int start_frame, int duration) { - if (!num_loops && !start_frame) - return; +bool SdlAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) { + // Prefer emulation + if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate)) + return true; - if (!_cdrom) - return; + // If we're set to only emulate, or have no CD, return here + if (onlyEmulate || !_cdrom) + return false; + if (!numLoops && !startFrame) + return false; + + // FIXME: Explain this. if (duration > 0) duration += 5; _cdTrack = track; - _cdNumLoops = num_loops; - _cdStartFrame = start_frame; + _cdNumLoops = numLoops; + _cdStartFrame = startFrame; SDL_CDStatus(_cdrom); - if (start_frame == 0 && duration == 0) + if (startFrame == 0 && duration == 0) SDL_CDPlayTracks(_cdrom, track, 0, 1, 0); else - SDL_CDPlayTracks(_cdrom, track, start_frame, 0, duration); + SDL_CDPlayTracks(_cdrom, track, startFrame, 0, duration); _cdDuration = duration; _cdStopTime = 0; _cdEndTime = SDL_GetTicks() + _cdrom->track[track].length * 1000 / CD_FPS; + + return true; } -bool SdlAudioCDManager::pollCD() const { +bool SdlAudioCDManager::isPlaying() const { + if (DefaultAudioCDManager::isPlaying()) + return true; + if (!_cdrom) return false; return (_cdNumLoops != 0 && (SDL_GetTicks() < _cdEndTime || SDL_CDStatus(_cdrom) == CD_PLAYING)); } -void SdlAudioCDManager::updateCD() { +void SdlAudioCDManager::update() { + DefaultAudioCDManager::update(); + if (!_cdrom) return; diff --git a/backends/audiocd/sdl/sdl-audiocd.h b/backends/audiocd/sdl/sdl-audiocd.h index bfad7b6805..91895dac99 100644 --- a/backends/audiocd/sdl/sdl-audiocd.h +++ b/backends/audiocd/sdl/sdl-audiocd.h @@ -37,12 +37,15 @@ public: SdlAudioCDManager(); virtual ~SdlAudioCDManager(); + virtual bool open(); + virtual void close(); + virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); + virtual void stop(); + virtual bool isPlaying() const; + virtual void update(); + protected: virtual bool openCD(int drive); - virtual void updateCD(); - virtual bool pollCD() const; - virtual void playCD(int track, int num_loops, int start_frame, int duration); - virtual void stopCD(); SDL_CD *_cdrom; int _cdTrack, _cdNumLoops, _cdStartFrame, _cdDuration; diff --git a/backends/audiocd/win32/msvc/ntddcdrm.h b/backends/audiocd/win32/msvc/ntddcdrm.h new file mode 100644 index 0000000000..18527e2675 --- /dev/null +++ b/backends/audiocd/win32/msvc/ntddcdrm.h @@ -0,0 +1,362 @@ +/** + * @file ntddcdrm.h + * Copyright 2012, 2013 MinGW.org project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* Created by Casper S. Hornstrup <chorns@users.sourceforge.net> */ +#ifndef __NTDDCDRM_H +#define __NTDDCDRM_H +#if 0 // Added to make MSVC happy. +#pragma GCC system_header +#include <_mingw.h> +#endif + +/* + * CDROM IOCTL interface. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 // Added to make MSVC happy. +#include "ntddk.h" +#include "ntddstor.h" +#endif + +#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM + +#define IOCTL_CDROM_CHECK_VERIFY \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_FIND_NEW_DEVICES \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_CONTROL \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000D, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_DRIVE_GEOMETRY \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_LAST_SESSION \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000E, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_GET_VOLUME \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_PAUSE_AUDIO \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_PLAY_AUDIO_MSF \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_RAW_READ \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS) + +#define IOCTL_CDROM_READ_Q_CHANNEL \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_READ_TOC \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_READ_TOC_EX \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_RESUME_AUDIO \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_SEEK_AUDIO_MSF \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_SET_VOLUME \ + CTL_CODE(IOCTL_CDROM_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_SIMBAD \ + CTL_CODE(IOCTL_CDROM_BASE, 0x1003, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_CDROM_STOP_AUDIO \ + CTL_CODE(IOCTL_CDROM_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS) + + +#define MAXIMUM_NUMBER_TRACKS 100 +#define MAXIMUM_CDROM_SIZE 804 +#define MINIMUM_CDROM_READ_TOC_EX_SIZE 2 + +typedef struct _TRACK_DATA { + UCHAR Reserved; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TrackNumber; + UCHAR Reserved1; + UCHAR Address[4]; +} TRACK_DATA, *PTRACK_DATA; + +/* CDROM_DISK_DATA.DiskData flags */ +#define CDROM_DISK_AUDIO_TRACK 0x00000001 +#define CDROM_DISK_DATA_TRACK 0x00000002 + +typedef struct _CDROM_DISK_DATA { + ULONG DiskData; +} CDROM_DISK_DATA, *PCDROM_DISK_DATA; + +typedef struct _CDROM_PLAY_AUDIO_MSF { + UCHAR StartingM; + UCHAR StartingS; + UCHAR StartingF; + UCHAR EndingM; + UCHAR EndingS; + UCHAR EndingF; +} CDROM_PLAY_AUDIO_MSF, *PCDROM_PLAY_AUDIO_MSF; + +/* CDROM_READ_TOC_EX.Format constants */ +#define CDROM_READ_TOC_EX_FORMAT_TOC 0x00 +#define CDROM_READ_TOC_EX_FORMAT_SESSION 0x01 +#define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02 +#define CDROM_READ_TOC_EX_FORMAT_PMA 0x03 +#define CDROM_READ_TOC_EX_FORMAT_ATIP 0x04 +#define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05 + +typedef struct _CDROM_READ_TOC_EX { + UCHAR Format : 4; + UCHAR Reserved1 : 3; + UCHAR Msf : 1; + UCHAR SessionTrack; + UCHAR Reserved2; + UCHAR Reserved3; +} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX; + +typedef struct _CDROM_SEEK_AUDIO_MSF { + UCHAR M; + UCHAR S; + UCHAR F; +} CDROM_SEEK_AUDIO_MSF, *PCDROM_SEEK_AUDIO_MSF; + +/* CDROM_SUB_Q_DATA_FORMAT.Format constants */ +#define IOCTL_CDROM_SUB_Q_CHANNEL 0x00 +#define IOCTL_CDROM_CURRENT_POSITION 0x01 +#define IOCTL_CDROM_MEDIA_CATALOG 0x02 +#define IOCTL_CDROM_TRACK_ISRC 0x03 + +typedef struct _CDROM_SUB_Q_DATA_FORMAT { + UCHAR Format; + UCHAR Track; +} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT; + +typedef struct _CDROM_TOC { + UCHAR Length[2]; + UCHAR FirstTrack; + UCHAR LastTrack; + TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS]; +} CDROM_TOC, *PCDROM_TOC; + +#define CDROM_TOC_SIZE sizeof(CDROM_TOC) + +typedef struct _CDROM_TOC_ATIP_DATA_BLOCK { + UCHAR CdrwReferenceSpeed : 3; + UCHAR Reserved3 : 1; + UCHAR WritePower : 3; + UCHAR True1 : 1; + UCHAR Reserved4 : 6; + UCHAR UnrestrictedUse : 1; + UCHAR Reserved5 : 1; + UCHAR A3Valid : 1; + UCHAR A2Valid : 1; + UCHAR A1Valid : 1; + UCHAR Reserved6 : 3; + UCHAR IsCdrw : 1; + UCHAR True2 : 1; + UCHAR Reserved7; + UCHAR LeadInMsf[3]; + UCHAR Reserved8; + UCHAR LeadOutMsf[3]; + UCHAR Reserved9; + UCHAR A1Values[3]; + UCHAR Reserved10; + UCHAR A2Values[3]; + UCHAR Reserved11; + UCHAR A3Values[3]; + UCHAR Reserved12; +} CDROM_TOC_ATIP_DATA_BLOCK, *PCDROM_TOC_ATIP_DATA_BLOCK; + +#if 0 // Added to make MSVC happy. +typedef struct _CDROM_TOC_ATIP_DATA { + UCHAR Length[2]; + UCHAR Reserved1; + UCHAR Reserved2; + CDROM_TOC_ATIP_DATA_BLOCK Descriptors[0]; + CDROM_TOC_ATIP_DATA_BLOCK Descriptors[1]; +} CDROM_TOC_ATIP_DATA, *PCDROM_TOC_ATIP_DATA; +#endif + +/* CDROM_TOC_CD_TEXT_DATA_BLOCK.PackType constants */ +#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80 +#define CDROM_CD_TEXT_PACK_PERFORMER 0x81 +#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82 +#define CDROM_CD_TEXT_PACK_COMPOSER 0x83 +#define CDROM_CD_TEXT_PACK_ARRANGER 0x84 +#define CDROM_CD_TEXT_PACK_MESSAGES 0x85 +#define CDROM_CD_TEXT_PACK_DISC_ID 0x86 +#define CDROM_CD_TEXT_PACK_GENRE 0x87 +#define CDROM_CD_TEXT_PACK_TOC_INFO 0x88 +#define CDROM_CD_TEXT_PACK_TOC_INFO2 0x89 +#define CDROM_CD_TEXT_PACK_UPC_EAN 0x8e +#define CDROM_CD_TEXT_PACK_SIZE_INFO 0x8f + +#if 0 // Added to make MSVC happy. +typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK { + UCHAR PackType; + UCHAR TrackNumber : 7; + UCHAR ExtensionFlag : 1; + UCHAR SequenceNumber; + UCHAR CharacterPosition : 4; + UCHAR BlockNumber : 3; + UCHAR Unicode : 1; + _ANONYMOUS_UNION union { + UCHAR Text[12]; + WCHAR WText[6]; + } DUMMYUNIONNAME; + UCHAR CRC[2]; +} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK; + +typedef struct _CDROM_TOC_CD_TEXT_DATA { + UCHAR Length[2]; + UCHAR Reserved1; + UCHAR Reserved2; + CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA; +#endif + +/* CDROM_TOC_FULL_TOC_DATA_BLOCK.Adr constants */ +#define ADR_NO_MODE_INFORMATION 0x0 +#define ADR_ENCODES_CURRENT_POSITION 0x1 +#define ADR_ENCODES_MEDIA_CATALOG 0x2 +#define ADR_ENCODES_ISRC 0x3 + +typedef struct _CDROM_TOC_FULL_TOC_DATA_BLOCK { + UCHAR SessionNumber; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR Reserved1; + UCHAR Point; + UCHAR MsfExtra[3]; + UCHAR Zero; + UCHAR Msf[3]; +} CDROM_TOC_FULL_TOC_DATA_BLOCK, *PCDROM_TOC_FULL_TOC_DATA_BLOCK; + +#if 0 // Added to make MSVC happy. +typedef struct _CDROM_TOC_FULL_TOC_DATA { + UCHAR Length[2]; + UCHAR FirstCompleteSession; + UCHAR LastCompleteSession; + CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_FULL_TOC_DATA, *PCDROM_TOC_FULL_TOC_DATA; + +typedef struct _CDROM_TOC_PMA_DATA { + UCHAR Length[2]; + UCHAR Reserved1; + UCHAR Reserved2; + CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0]; +} CDROM_TOC_PMA_DATA, *PCDROM_TOC_PMA_DATA; +#endif + +/* SUB_Q_HEADER.AudioStatus constants */ +#define AUDIO_STATUS_NOT_SUPPORTED 0x00 +#define AUDIO_STATUS_IN_PROGRESS 0x11 +#define AUDIO_STATUS_PAUSED 0x12 +#define AUDIO_STATUS_PLAY_COMPLETE 0x13 +#define AUDIO_STATUS_PLAY_ERROR 0x14 +#define AUDIO_STATUS_NO_STATUS 0x15 + +typedef struct _SUB_Q_HEADER { + UCHAR Reserved; + UCHAR AudioStatus; + UCHAR DataLength[2]; +} SUB_Q_HEADER, *PSUB_Q_HEADER; + +typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Reserved[3]; + UCHAR Reserved1 : 7; + UCHAR Mcval :1; + UCHAR MediaCatalog[15]; +} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER; + +typedef struct _SUB_Q_TRACK_ISRC { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Reserved0; + UCHAR Track; + UCHAR Reserved1; + UCHAR Reserved2 : 7; + UCHAR Tcval : 1; + UCHAR TrackIsrc[15]; +} SUB_Q_TRACK_ISRC, *PSUB_Q_TRACK_ISRC; + +typedef struct _SUB_Q_CURRENT_POSITION { + SUB_Q_HEADER Header; + UCHAR FormatCode; + UCHAR Control : 4; + UCHAR ADR : 4; + UCHAR TrackNumber; + UCHAR IndexNumber; + UCHAR AbsoluteAddress[4]; + UCHAR TrackRelativeAddress[4]; +} SUB_Q_CURRENT_POSITION, *PSUB_Q_CURRENT_POSITION; + +typedef union _SUB_Q_CHANNEL_DATA { + SUB_Q_CURRENT_POSITION CurrentPosition; + SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog; + SUB_Q_TRACK_ISRC TrackIsrc; +} SUB_Q_CHANNEL_DATA, *PSUB_Q_CHANNEL_DATA; + +/* CDROM_AUDIO_CONTROL.LbaFormat constants */ +#define AUDIO_WITH_PREEMPHASIS 0x1 +#define DIGITAL_COPY_PERMITTED 0x2 +#define AUDIO_DATA_TRACK 0x4 +#define TWO_FOUR_CHANNEL_AUDIO 0x8 + +typedef struct _CDROM_AUDIO_CONTROL { + UCHAR LbaFormat; + USHORT LogicalBlocksPerSecond; +} CDROM_AUDIO_CONTROL, *PCDROM_AUDIO_CONTROL; + +typedef struct _VOLUME_CONTROL { + UCHAR PortVolume[4]; +} VOLUME_CONTROL, *PVOLUME_CONTROL; + +typedef enum _TRACK_MODE_TYPE { + YellowMode2, + XAForm2, + CDDA +} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE; + +typedef struct __RAW_READ_INFO { + LARGE_INTEGER DiskOffset; + ULONG SectorCount; + TRACK_MODE_TYPE TrackMode; +} RAW_READ_INFO, *PRAW_READ_INFO; + +#ifdef __cplusplus +} +#endif + +#endif /* __NTDDCDRM_H */ diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp new file mode 100644 index 0000000000..6c057efdb7 --- /dev/null +++ b/backends/audiocd/win32/win32-audiocd.cpp @@ -0,0 +1,388 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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. + * + */ + +#ifdef WIN32 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one... + +#include "backends/audiocd/win32/win32-audiocd.h" + +#include "audio/audiostream.h" +#include "backends/audiocd/audiocd-stream.h" +#include "backends/audiocd/default/default-audiocd.h" +#include "common/array.h" +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/mutex.h" +#include "common/queue.h" +#include "common/str.h" +#include "common/timer.h" + +#include <winioctl.h> +#if _MSC_VER < 1900 +// WORKAROUND: Older versions of MSVC might not supply DDK headers by default. +// Visual Studio 2015 contains the required headers. We use a compatability +// header from MinGW's w32api for all older versions. +// TODO: Limit this to the Visual Studio versions which actually require this. +#include "msvc/ntddcdrm.h" +#elif defined(__MINGW32__) && !defined(__MINGW64__) +// Classic MinGW uses non standard paths for DDK headers. +#include <ddk/ntddcdrm.h> +#else +#include <ntddcdrm.h> +#endif + +class Win32AudioCDStream : public AudioCDStream { +public: + Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry); + ~Win32AudioCDStream(); + +protected: + uint getStartFrame() const; + uint getEndFrame() const; + bool readFrame(int frame, int16 *buffer); + +private: + HANDLE _driveHandle; + const TRACK_DATA &_startEntry, &_endEntry; + + enum { + // The CD-ROM pre-gap is 2s + kPreGapFrames = kFramesPerSecond * 2 + }; + + static int getFrameCount(const TRACK_DATA &data) { + int time = data.Address[1]; + time *= kSecondsPerMinute; + time += data.Address[2]; + time *= kFramesPerSecond; + time += data.Address[3]; + return time; + } +}; + +Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) : + _driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry) { + // We fill the buffer here already to prevent any out of sync issues due + // to the CD not yet having spun up. + startTimer(true); +} + +Win32AudioCDStream::~Win32AudioCDStream() { + stopTimer(); +} + +uint Win32AudioCDStream::getStartFrame() const { + return getFrameCount(_startEntry); +} + +uint Win32AudioCDStream::getEndFrame() const { + return getFrameCount(_endEntry); +} + +bool Win32AudioCDStream::readFrame(int frame, int16 *buffer) { + // Request to read that frame + RAW_READ_INFO readAudio; + memset(&readAudio, 0, sizeof(readAudio)); + readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048; + readAudio.SectorCount = 1; + readAudio.TrackMode = CDDA; + + DWORD bytesReturned; + return DeviceIoControl( + _driveHandle, + IOCTL_CDROM_RAW_READ, + &readAudio, + sizeof(readAudio), + buffer, + kBytesPerFrame, + &bytesReturned, + NULL); +} + + +class Win32AudioCDManager : public DefaultAudioCDManager { +public: + Win32AudioCDManager(); + ~Win32AudioCDManager(); + + bool open(); + void close(); + bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); + +protected: + bool openCD(int drive); + bool openCD(const Common::String &drive); + +private: + bool loadTOC(); + + typedef Common::Array<char> DriveList; + DriveList detectDrives(); + bool tryAddDrive(char drive, DriveList &drives); + + HANDLE _driveHandle; + int _firstTrack, _lastTrack; + Common::Array<TRACK_DATA> _tocEntries; +}; + +Win32AudioCDManager::Win32AudioCDManager() { + _driveHandle = INVALID_HANDLE_VALUE; + _firstTrack = _lastTrack = 0; +} + +Win32AudioCDManager::~Win32AudioCDManager() { + close(); +} + +bool Win32AudioCDManager::open() { + close(); + + if (openRealCD()) + return true; + + return DefaultAudioCDManager::open(); +} + +bool Win32AudioCDManager::openCD(int drive) { + // Fetch the drive list + DriveList drives = detectDrives(); + if (drive >= (int)drives.size()) + return false; + + debug(1, "Opening CD drive %c:\\", drives[drive]); + + // Construct the drive path and try to open it + Common::String drivePath = Common::String::format("\\\\.\\%c:", drives[drive]); + _driveHandle = CreateFileA(drivePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (_driveHandle == INVALID_HANDLE_VALUE) { + warning("Failed to open drive %c:\\, error %d", drives[drive], (int)GetLastError()); + return false; + } + + if (!loadTOC()) { + close(); + return false; + } + + return true; +} + +bool Win32AudioCDManager::openCD(const Common::String &drive) { + // Just some bounds checking + if (drive.empty() || drive.size() > 3) + return false; + + if (!Common::isAlpha(drive[0]) || drive[1] != ':') + return false; + + if (drive[2] != 0 && drive[2] != '\\') + return false; + + DriveList drives; + if (!tryAddDrive(toupper(drive[0]), drives)) + return false; + + // Construct the drive path and try to open it + Common::String drivePath = Common::String::format("\\\\.\\%c:", drives[0]); + _driveHandle = CreateFileA(drivePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (_driveHandle == INVALID_HANDLE_VALUE) { + warning("Failed to open drive %c:\\, error %d", drives[0], (int)GetLastError()); + return false; + } + + if (!loadTOC()) { + close(); + return false; + } + + return true; +} + +void Win32AudioCDManager::close() { + DefaultAudioCDManager::close(); + + if (_driveHandle != INVALID_HANDLE_VALUE) { + CloseHandle(_driveHandle); + _driveHandle = INVALID_HANDLE_VALUE; + } + + _firstTrack = _lastTrack = 0; + _tocEntries.clear(); +} + +bool Win32AudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) { + // Prefer emulation + if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate)) + return true; + + // If we're set to only emulate, or have no CD drive, return here + if (onlyEmulate || _driveHandle == INVALID_HANDLE_VALUE) + return false; + + // HACK: For now, just assume that track number is right + // That only works because ScummVM uses the wrong track number anyway + + if (track >= (int)_tocEntries.size() - 1) { + warning("No such track %d", track); + return false; + } + + // Bail if the track isn't an audio track + if ((_tocEntries[track].Control & 0x04) != 0) { + warning("Track %d is not audio", track); + return false; + } + + // Create the AudioStream and play it + debug(1, "Playing CD track %d", track); + + Audio::SeekableAudioStream *audioStream = new Win32AudioCDStream(_driveHandle, _tocEntries[track], _tocEntries[track + 1]); + + Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75); + Audio::Timestamp end = (duration == 0) ? audioStream->getLength() : Audio::Timestamp(0, startFrame + duration, 75); + + // Fake emulation since we're really playing an AudioStream + _emulating = true; + + _mixer->playStream( + Audio::Mixer::kMusicSoundType, + &_handle, + Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), + -1, + _cd.volume, + _cd.balance, + DisposeAfterUse::YES, + true); + return true; +} + +bool Win32AudioCDManager::loadTOC() { + CDROM_READ_TOC_EX tocRequest; + memset(&tocRequest, 0, sizeof(tocRequest)); + tocRequest.Format = CDROM_READ_TOC_EX_FORMAT_TOC; + tocRequest.Msf = 1; + tocRequest.SessionTrack = 0; + + DWORD bytesReturned; + CDROM_TOC tocData; + bool result = DeviceIoControl( + _driveHandle, + IOCTL_CDROM_READ_TOC_EX, + &tocRequest, + sizeof(tocRequest), + &tocData, + sizeof(tocData), + &bytesReturned, + NULL); + if (!result) { + debug("Failed to query the CD TOC: %d", (int)GetLastError()); + return false; + } + + _firstTrack = tocData.FirstTrack; + _lastTrack = tocData.LastTrack; +#if 0 + debug("First Track: %d", tocData.FirstTrack); + debug("Last Track: %d", tocData.LastTrack); +#endif + + for (uint32 i = 0; i < (bytesReturned - 4) / sizeof(TRACK_DATA); i++) + _tocEntries.push_back(tocData.TrackData[i]); + +#if 0 + for (uint32 i = 0; i < _tocEntries.size(); i++) { + const TRACK_DATA &entry = _tocEntries[i]; + debug("Entry:"); + debug("\tTrack: %d", entry.TrackNumber); + debug("\tAdr: %d", entry.Adr); + debug("\tCtrl: %d", entry.Control); + debug("\tMSF: %d:%d:%d\n", entry.Address[1], entry.Address[2], entry.Address[3]); + } +#endif + + return true; +} + +Win32AudioCDManager::DriveList Win32AudioCDManager::detectDrives() { + DriveList drives; + + // Try to get the game path's drive + char gameDrive = 0; + if (ConfMan.hasKey("path")) { + Common::String gamePath = ConfMan.get("path"); + char fullPath[MAX_PATH]; + DWORD result = GetFullPathNameA(gamePath.c_str(), sizeof(fullPath), fullPath, 0); + + if (result > 0 && result < sizeof(fullPath) && Common::isAlpha(fullPath[0]) && fullPath[1] == ':' && tryAddDrive(toupper(fullPath[0]), drives)) + gameDrive = drives[0]; + } + + // Try adding the rest of the drives + for (char drive = 'A'; drive <= 'Z'; drive++) + if (drive != gameDrive) + tryAddDrive(drive, drives); + + return drives; +} + +bool Win32AudioCDManager::tryAddDrive(char drive, DriveList &drives) { + Common::String drivePath = Common::String::format("%c:\\", drive); + + // Ensure it's an actual CD drive + if (GetDriveTypeA(drivePath.c_str()) != DRIVE_CDROM) + return false; + + debug(2, "Detected drive %c:\\ as a CD drive", drive); + drives.push_back(drive); + return true; +} + +AudioCDManager *createWin32AudioCDManager() { + return new Win32AudioCDManager(); +} + +#endif // WIN32
\ No newline at end of file diff --git a/backends/audiocd/win32/win32-audiocd.h b/backends/audiocd/win32/win32-audiocd.h new file mode 100644 index 0000000000..0c103641ef --- /dev/null +++ b/backends/audiocd/win32/win32-audiocd.h @@ -0,0 +1,60 @@ +/* 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. + * + * Original license header: + * + * Cabal - Legacy Game Implementations + * + * Cabal 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 BACKENDS_AUDIOCD_WIN32_H +#define BACKENDS_AUDIOCD_WIN32_H + +#ifdef WIN32 + +class AudioCDManager; + +/** + * Create an AudioCDManager using the Win32 API + */ +AudioCDManager *createWin32AudioCDManager(); + +#endif + +#endif + diff --git a/backends/graphics/opengl/context.cpp b/backends/graphics/opengl/context.cpp new file mode 100644 index 0000000000..a7f640d37e --- /dev/null +++ b/backends/graphics/opengl/context.cpp @@ -0,0 +1,182 @@ +/* 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 "backends/graphics/opengl/opengl-sys.h" +#include "backends/graphics/opengl/opengl-graphics.h" +#include "backends/graphics/opengl/shader.h" +#include "backends/graphics/opengl/pipelines/pipeline.h" +#include "backends/graphics/opengl/framebuffer.h" + +#include "common/tokenizer.h" +#include "common/debug.h" + +namespace OpenGL { + +void Context::reset() { + maxTextureSize = 0; + + NPOTSupported = false; + shadersSupported = false; + multitextureSupported = false; + framebufferObjectSupported = false; + +#define GL_FUNC_DEF(ret, name, param) name = nullptr; +#include "backends/graphics/opengl/opengl-func.h" +#undef GL_FUNC_DEF + + activePipeline = nullptr; +} + +Pipeline *Context::setPipeline(Pipeline *pipeline) { + Pipeline *oldPipeline = activePipeline; + if (oldPipeline) { + oldPipeline->deactivate(); + } + + activePipeline = pipeline; + if (activePipeline) { + activePipeline->activate(); + } + + return oldPipeline; +} + +Context g_context; + +void OpenGLGraphicsManager::setContextType(ContextType type) { +#if USE_FORCED_GL + type = kContextGL; +#elif USE_FORCED_GLES + type = kContextGLES; +#elif USE_FORCED_GLES2 + type = kContextGLES2; +#endif + + g_context.type = type; +} + +void OpenGLGraphicsManager::initializeGLContext() { + // Initialize default state. + g_context.reset(); + + // Load all functions. + // We use horrible trickery to silence C++ compilers. + // See backends/plugins/sdl/sdl-provider.cpp for more information. + assert(sizeof(void (*)()) == sizeof(void *)); + void *fn = nullptr; + +#define LOAD_FUNC(name, loadName) \ + fn = getProcAddress(#loadName); \ + memcpy(&g_context.name, &fn, sizeof(fn)) + +#define GL_EXT_FUNC_DEF(ret, name, param) LOAD_FUNC(name, name) + +#ifdef USE_BUILTIN_OPENGL +#define GL_FUNC_DEF(ret, name, param) g_context.name = &name +#define GL_FUNC_2_DEF GL_FUNC_DEF +#else +#define GL_FUNC_DEF GL_EXT_FUNC_DEF +#define GL_FUNC_2_DEF(ret, name, extName, param) \ + if (g_context.type == kContextGL) { \ + LOAD_FUNC(name, extName); \ + } else { \ + LOAD_FUNC(name, name); \ + } +#endif +#include "backends/graphics/opengl/opengl-func.h" +#undef GL_FUNC_2_DEF +#undef GL_FUNC_DEF +#undef GL_EXT_FUNC_DEF +#undef LOAD_FUNC + + // Obtain maximum texture size. + GL_CALL(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &g_context.maxTextureSize)); + debug(5, "OpenGL maximum texture size: %d", g_context.maxTextureSize); + + const char *extString = (const char *)g_context.glGetString(GL_EXTENSIONS); + debug(5, "OpenGL extensions: %s", extString); + + bool ARBShaderObjects = false; + bool ARBShadingLanguage100 = false; + bool ARBVertexShader = false; + bool ARBFragmentShader = false; + + Common::StringTokenizer tokenizer(extString, " "); + while (!tokenizer.empty()) { + Common::String token = tokenizer.nextToken(); + + if (token == "GL_ARB_texture_non_power_of_two" || token == "GL_OES_texture_npot") { + g_context.NPOTSupported = true; + } else if (token == "GL_ARB_shader_objects") { + ARBShaderObjects = true; + } else if (token == "GL_ARB_shading_language_100") { + ARBShadingLanguage100 = true; + } else if (token == "GL_ARB_vertex_shader") { + ARBVertexShader = true; + } else if (token == "GL_ARB_fragment_shader") { + ARBFragmentShader = true; + } else if (token == "GL_ARB_multitexture") { + g_context.multitextureSupported = true; + } else if (token == "GL_EXT_framebuffer_object") { + g_context.framebufferObjectSupported = true; + } + } + + if (g_context.type == kContextGLES2) { + // GLES2 always has (limited) NPOT support. + g_context.NPOTSupported = true; + + // GLES2 always has shader support. + g_context.shadersSupported = true; + + // GLES2 always has multi texture support. + g_context.multitextureSupported = true; + + // GLES2 always has FBO support. + g_context.framebufferObjectSupported = true; + } else { + g_context.shadersSupported = ARBShaderObjects & ARBShadingLanguage100 & ARBVertexShader & ARBFragmentShader; + } + + // Log context type. + switch (g_context.type) { + case kContextGL: + debug(5, "OpenGL: GL context initialized"); + break; + + case kContextGLES: + debug(5, "OpenGL: GLES context initialized"); + break; + + case kContextGLES2: + debug(5, "OpenGL: GLES2 context initialized"); + break; + } + + // Log features supported by GL context. + debug(5, "OpenGL: NPOT texture support: %d", g_context.NPOTSupported); + debug(5, "OpenGL: Shader support: %d", g_context.shadersSupported); + debug(5, "OpenGL: Multitexture support: %d", g_context.multitextureSupported); + debug(5, "OpenGL: FBO support: %d", g_context.framebufferObjectSupported); +} + +} // End of namespace OpenGL diff --git a/backends/graphics/opengl/debug.cpp b/backends/graphics/opengl/debug.cpp index d5d73fb5ec..c4319f5e36 100644 --- a/backends/graphics/opengl/debug.cpp +++ b/backends/graphics/opengl/debug.cpp @@ -54,7 +54,7 @@ Common::String getGLErrStr(GLenum error) { void checkGLError(const char *expr, const char *file, int line) { GLenum error; - while ((error = glGetError()) != GL_NO_ERROR) { + while ((error = g_context.glGetError()) != GL_NO_ERROR) { // We cannot use error here because we do not know whether we have a // working screen or not. warning("GL ERROR: %s on %s (%s:%d)", getGLErrStr(error).c_str(), expr, file, line); diff --git a/backends/graphics/opengl/debug.h b/backends/graphics/opengl/debug.h index ff6b678870..abaa6544dc 100644 --- a/backends/graphics/opengl/debug.h +++ b/backends/graphics/opengl/debug.h @@ -31,9 +31,9 @@ namespace OpenGL { void checkGLError(const char *expr, const char *file, int line); } // End of namespace OpenGL -#define GLCALL(x) do { (x); OpenGL::checkGLError(#x, __FILE__, __LINE__); } while (false) +#define GL_WRAP_DEBUG(call, name) do { (call); OpenGL::checkGLError(#name, __FILE__, __LINE__); } while (false) #else -#define GLCALL(x) do { (x); } while (false) +#define GL_WRAP_DEBUG(call, name) do { (call); } while (false) #endif #endif diff --git a/backends/graphics/opengl/framebuffer.cpp b/backends/graphics/opengl/framebuffer.cpp new file mode 100644 index 0000000000..7191aab8bc --- /dev/null +++ b/backends/graphics/opengl/framebuffer.cpp @@ -0,0 +1,259 @@ +/* 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 "backends/graphics/opengl/framebuffer.h" +#include "backends/graphics/opengl/texture.h" +#include "backends/graphics/opengl/pipelines/pipeline.h" + +namespace OpenGL { + +Framebuffer::Framebuffer() + : _viewport(), _projectionMatrix(), _isActive(false), _clearColor(), + _blendState(false), _scissorTestState(false), _scissorBox() { +} + +void Framebuffer::activate() { + _isActive = true; + + applyViewport(); + applyProjectionMatrix(); + applyClearColor(); + applyBlendState(); + applyScissorTestState(); + applyScissorBox(); + + activateInternal(); +} + +void Framebuffer::deactivate() { + _isActive = false; + + deactivateInternal(); +} + +void Framebuffer::setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { + _clearColor[0] = r; + _clearColor[1] = g; + _clearColor[2] = b; + _clearColor[3] = a; + + // Directly apply changes when we are active. + if (isActive()) { + applyClearColor(); + } +} + +void Framebuffer::enableBlend(bool enable) { + _blendState = enable; + + // Directly apply changes when we are active. + if (isActive()) { + applyBlendState(); + } +} + +void Framebuffer::enableScissorTest(bool enable) { + _scissorTestState = enable; + + // Directly apply changes when we are active. + if (isActive()) { + applyScissorTestState(); + } +} + +void Framebuffer::setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h) { + _scissorBox[0] = x; + _scissorBox[1] = y; + _scissorBox[2] = w; + _scissorBox[3] = h; + + // Directly apply changes when we are active. + if (isActive()) { + applyScissorBox(); + } +} + +void Framebuffer::applyViewport() { + GL_CALL(glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3])); +} + +void Framebuffer::applyProjectionMatrix() { + g_context.getActivePipeline()->setProjectionMatrix(_projectionMatrix); +} + +void Framebuffer::applyClearColor() { + GL_CALL(glClearColor(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3])); +} + +void Framebuffer::applyBlendState() { + if (_blendState) { + GL_CALL(glEnable(GL_BLEND)); + } else { + GL_CALL(glDisable(GL_BLEND)); + } +} + +void Framebuffer::applyScissorTestState() { + if (_scissorTestState) { + GL_CALL(glEnable(GL_SCISSOR_TEST)); + } else { + GL_CALL(glDisable(GL_SCISSOR_TEST)); + } +} + +void Framebuffer::applyScissorBox() { + GL_CALL(glScissor(_scissorBox[0], _scissorBox[1], _scissorBox[2], _scissorBox[3])); +} + +// +// Backbuffer implementation +// + +void Backbuffer::activateInternal() { +#if !USE_FORCED_GLES + if (g_context.framebufferObjectSupported) { + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } +#endif +} + +void Backbuffer::setDimensions(uint width, uint height) { + // Set viewport dimensions. + _viewport[0] = 0; + _viewport[1] = 0; + _viewport[2] = width; + _viewport[3] = height; + + // Setup orthogonal projection matrix. + _projectionMatrix[ 0] = 2.0f / width; + _projectionMatrix[ 1] = 0.0f; + _projectionMatrix[ 2] = 0.0f; + _projectionMatrix[ 3] = 0.0f; + + _projectionMatrix[ 4] = 0.0f; + _projectionMatrix[ 5] = -2.0f / height; + _projectionMatrix[ 6] = 0.0f; + _projectionMatrix[ 7] = 0.0f; + + _projectionMatrix[ 8] = 0.0f; + _projectionMatrix[ 9] = 0.0f; + _projectionMatrix[10] = 0.0f; + _projectionMatrix[11] = 0.0f; + + _projectionMatrix[12] = -1.0f; + _projectionMatrix[13] = 1.0f; + _projectionMatrix[14] = 0.0f; + _projectionMatrix[15] = 1.0f; + + // Directly apply changes when we are active. + if (isActive()) { + applyViewport(); + applyProjectionMatrix(); + } +} + +// +// Render to texture target implementation +// + +#if !USE_FORCED_GLES +TextureTarget::TextureTarget() + : _texture(new GLTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE)), _glFBO(0), _needUpdate(true) { +} + +TextureTarget::~TextureTarget() { + delete _texture; + GL_CALL_SAFE(glDeleteFramebuffers, (1, &_glFBO)); +} + +void TextureTarget::activateInternal() { + // Allocate framebuffer object if necessary. + if (!_glFBO) { + GL_CALL(glGenFramebuffers(1, &_glFBO)); + _needUpdate = true; + } + + // Attach destination texture to FBO. + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO)); + + // If required attach texture to FBO. + if (_needUpdate) { + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture->getGLTexture(), 0)); + _needUpdate = false; + } +} + +void TextureTarget::destroy() { + GL_CALL(glDeleteFramebuffers(1, &_glFBO)); + _glFBO = 0; + + _texture->destroy(); +} + +void TextureTarget::create() { + _texture->create(); + + _needUpdate = true; +} + +void TextureTarget::setSize(uint width, uint height) { + _texture->setSize(width, height); + + const uint texWidth = _texture->getWidth(); + const uint texHeight = _texture->getHeight(); + + // Set viewport dimensions. + _viewport[0] = 0; + _viewport[1] = 0; + _viewport[2] = texWidth; + _viewport[3] = texHeight; + + // Setup orthogonal projection matrix. + _projectionMatrix[ 0] = 2.0f / texWidth; + _projectionMatrix[ 1] = 0.0f; + _projectionMatrix[ 2] = 0.0f; + _projectionMatrix[ 3] = 0.0f; + + _projectionMatrix[ 4] = 0.0f; + _projectionMatrix[ 5] = 2.0f / texHeight; + _projectionMatrix[ 6] = 0.0f; + _projectionMatrix[ 7] = 0.0f; + + _projectionMatrix[ 8] = 0.0f; + _projectionMatrix[ 9] = 0.0f; + _projectionMatrix[10] = 0.0f; + _projectionMatrix[11] = 0.0f; + + _projectionMatrix[12] = -1.0f; + _projectionMatrix[13] = -1.0f; + _projectionMatrix[14] = 0.0f; + _projectionMatrix[15] = 1.0f; + + // Directly apply changes when we are active. + if (isActive()) { + applyViewport(); + applyProjectionMatrix(); + } +} +#endif // !USE_FORCED_GLES + +} // End of namespace OpenGL diff --git a/backends/graphics/opengl/framebuffer.h b/backends/graphics/opengl/framebuffer.h new file mode 100644 index 0000000000..c44c98ddc4 --- /dev/null +++ b/backends/graphics/opengl/framebuffer.h @@ -0,0 +1,175 @@ +/* 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 BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H +#define BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H + +#include "backends/graphics/opengl/opengl-sys.h" + +namespace OpenGL { + +/** + * Object describing a framebuffer OpenGL can render to. + */ +class Framebuffer { + friend class Pipeline; +public: + Framebuffer(); + virtual ~Framebuffer() {}; + +public: + /** + * Set the clear color of the framebuffer. + */ + void setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); + + /** + * Enable/disable GL_BLEND. + */ + void enableBlend(bool enable); + + /** + * Enable/disable GL_SCISSOR_TEST. + */ + void enableScissorTest(bool enable); + + /** + * Set scissor box dimensions. + */ + void setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h); + + /** + * Obtain projection matrix of the framebuffer. + */ + const GLfloat *getProjectionMatrix() const { return _projectionMatrix; } +protected: + bool isActive() const { return _isActive; } + + GLint _viewport[4]; + void applyViewport(); + + GLfloat _projectionMatrix[4*4]; + void applyProjectionMatrix(); + + /** + * Activate framebuffer. + * + * This is supposed to set all state associated with the framebuffer. + */ + virtual void activateInternal() = 0; + + /** + * Deactivate framebuffer. + * + * This is supposed to make any cleanup required when unbinding the + * framebuffer. + */ + virtual void deactivateInternal() {} + +private: + /** + * Accessor to activate framebuffer for pipeline. + */ + void activate(); + + /** + * Accessor to deactivate framebuffer from pipeline. + */ + void deactivate(); + +private: + bool _isActive; + + GLfloat _clearColor[4]; + void applyClearColor(); + + bool _blendState; + void applyBlendState(); + + bool _scissorTestState; + void applyScissorTestState(); + + GLint _scissorBox[4]; + void applyScissorBox(); +}; + +/** + * Default back buffer implementation. + */ +class Backbuffer : public Framebuffer { +public: + /** + * Set the dimensions (a.k.a. size) of the back buffer. + */ + void setDimensions(uint width, uint height); + +protected: + virtual void activateInternal(); +}; + +#if !USE_FORCED_GLES +class GLTexture; + +/** + * Render to texture framebuffer implementation. + * + * This target allows to render to a texture, which can then be used for + * further rendering. + */ +class TextureTarget : public Framebuffer { +public: + TextureTarget(); + virtual ~TextureTarget(); + + /** + * Notify that the GL context is about to be destroyed. + */ + void destroy(); + + /** + * Notify that the GL context has been created. + */ + void create(); + + /** + * Set size of the texture target. + */ + void setSize(uint width, uint height); + + /** + * Query pointer to underlying GL texture. + */ + GLTexture *getTexture() const { return _texture; } + +protected: + virtual void activateInternal(); + +private: + GLTexture *_texture; + GLuint _glFBO; + bool _needUpdate; +}; +#endif + +} // End of namespace OpenGL + +#endif diff --git a/backends/graphics/opengl/opengl-defs.h b/backends/graphics/opengl/opengl-defs.h new file mode 100644 index 0000000000..733fc2933c --- /dev/null +++ b/backends/graphics/opengl/opengl-defs.h @@ -0,0 +1,262 @@ +/* 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. + * + */ + +/* This file is based on Mesa 3-D's gl.h and GLES/gl.h from Khronos Registry. + * + * Mesa 3-D's gl.h file is distributed under the following license: + * Mesa 3-D graphics library + * + * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. + * Copyright (C) 2009 VMware, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * + * GLES/gl.h from Khronos Registry is distributed under the following license: + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_DEFS_H +#define BACKENDS_GRAPHICS_OPENGL_OPENGL_DEFS_H + +#include "common/scummsys.h" + +/* + * Datatypes + */ +typedef uint GLenum; +typedef uint8 GLboolean; +typedef uint GLbitfield; +typedef void GLvoid; +typedef int8 GLbyte; /* 1-byte signed */ +typedef int16 GLshort; /* 2-byte signed */ +typedef int32 GLint; /* 4-byte signed */ +typedef uint8 GLubyte; /* 1-byte unsigned */ +typedef uint16 GLushort; /* 2-byte unsigned */ +typedef uint32 GLuint; /* 4-byte unsigned */ +typedef int32 GLsizei; /* 4-byte signed */ +typedef float GLfloat; /* single precision float */ +typedef float GLclampf; /* single precision float in [0,1] */ +typedef double GLdouble; /* double precision float */ +typedef double GLclampd; /* double precision float in [0,1] */ +typedef char GLchar; +#if defined(MACOSX) +typedef void *GLhandleARB; +#else +typedef uint GLhandleARB; +#endif + +// This is an addition from us to alias ARB shader object extensions to +// OpenGL (ES) 2.0 style functions. It only works when GLhandleARB and GLuint +// are type compatible. +typedef GLhandleARB GLprogram; +typedef GLhandleARB GLshader; + +/* + * Constants + */ + +/* Boolean constants */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Scissor box */ +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 + +/* MatrixMode */ +#define GL_MATRIX_MODE 0x0BA0 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* EnableCap */ +#define GL_FOG 0x0B60 +#define GL_LIGHTING 0x0B50 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_BLEND 0x0BE2 +#define GL_DITHER 0x0BD0 +#define GL_DEPTH_TEST 0x0B71 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_COLOR_ARRAY 0x8076 +#define GL_TEXTURE_COORD_ARRAY 0x8078 + +/* ShadingModel */ +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_FOG_HINT 0x0C54 +#define GL_GENERATE_MIPMAP_HINT 0x8192 + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* PixelFormat */ +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 + +#define GL_RED 0x1903 +#define GL_R8 0x8229 + +/* PixelStoreParameter */ +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 + +/* Implementation limits */ +#define GL_MAX_TEXTURE_SIZE 0x0D33 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* Shaders */ +#define GL_FRAGMENT_SHADER 0x8B30 + +#define GL_VERTEX_SHADER 0x8B31 + +/* Programs */ +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_CURRENT_PROGRAM 0x8B8D + +/* Textures */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 + +/* GetPName */ +#define GL_VIEWPORT 0x0BA2 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 + +/* Framebuffer objects */ +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_FRAMEBUFFER 0x8D40 + +#endif diff --git a/backends/graphics/opengl/opengl-func.h b/backends/graphics/opengl/opengl-func.h new file mode 100644 index 0000000000..4e44c13d0f --- /dev/null +++ b/backends/graphics/opengl/opengl-func.h @@ -0,0 +1,153 @@ +/* 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. + * + */ + +/* This file is based on Mesa 3-D's gl.h and GLES/gl.h from Khronos Registry. + * + * Mesa 3-D's gl.h file is distributed under the following license: + * Mesa 3-D graphics library + * + * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. + * Copyright (C) 2009 VMware, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * + * GLES/gl.h from Khronos Registry is distributed under the following license: + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/* + * This file is a template file to be used inside specific locations in the + * OpenGL graphics code. It is not to be included otherwise. It intentionally + * does not contain include guards because it can be required to include it + * multiple times in a source file. + * + * Functions are defined by three different user supplied macros: + * GL_FUNC_DEF: Define a (builtin) OpenGL (ES) function. + * GL_FUNC_2_DEF: Define a OpenGL (ES) 2.0 function which can be provided by + * extensions in OpenGL 1.x contexts. + * GL_EXT_FUNC_DEF: Define an OpenGL (ES) extension function. + */ + +#if !defined(GL_FUNC_2_DEF) +#define GL_FUNC_2_DEF(ret, name, extName, param) GL_FUNC_DEF(ret, name, param) +#define DEFINED_GL_FUNC_2_DEF +#endif + +#if !defined(GL_EXT_FUNC_DEF) +#define GL_EXT_FUNC_DEF(ret, name, param) GL_FUNC_DEF(ret, name, param) +#define DEFINED_GL_EXT_FUNC_DEF +#endif + +GL_FUNC_DEF(void, glEnable, (GLenum cap)); +GL_FUNC_DEF(void, glDisable, (GLenum cap)); +GL_FUNC_DEF(GLboolean, glIsEnabled, (GLenum cap)); +GL_FUNC_DEF(void, glClear, (GLbitfield mask)); +GL_FUNC_DEF(void, glColor4f, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)); +GL_FUNC_DEF(void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height)); +GL_FUNC_DEF(void, glMatrixMode, (GLenum mode)); +GL_FUNC_DEF(void, glLoadIdentity, ()); +GL_FUNC_DEF(void, glLoadMatrixf, (const GLfloat *m)); +GL_FUNC_DEF(void, glShadeModel, (GLenum mode)); +GL_FUNC_DEF(void, glHint, (GLenum target, GLenum mode)); +GL_FUNC_DEF(void, glClearColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)); +GL_FUNC_DEF(void, glBlendFunc, (GLenum sfactor, GLenum dfactor)); +GL_FUNC_DEF(void, glEnableClientState, (GLenum array)); +GL_FUNC_DEF(void, glPixelStorei, (GLenum pname, GLint param)); +GL_FUNC_DEF(void, glScissor, (GLint x, GLint y, GLsizei width, GLsizei height)); +GL_FUNC_DEF(void, glReadPixels, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)); +GL_FUNC_DEF(void, glGetIntegerv, (GLenum pname, GLint *params)); +GL_FUNC_DEF(void, glDeleteTextures, (GLsizei n, const GLuint *textures)); +GL_FUNC_DEF(void, glGenTextures, (GLsizei n, GLuint *textures)); +GL_FUNC_DEF(void, glBindTexture, (GLenum target, GLuint texture)); +GL_FUNC_DEF(void, glTexParameteri, (GLenum target, GLenum pname, GLint param)); +GL_FUNC_DEF(void, glTexImage2D, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)); +GL_FUNC_DEF(void, glTexCoordPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)); +GL_FUNC_DEF(void, glVertexPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)); +GL_FUNC_DEF(void, glDrawArrays, (GLenum mode, GLint first, GLsizei count)); +GL_FUNC_DEF(void, glTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)); +GL_FUNC_DEF(const GLubyte *, glGetString, (GLenum name)); +GL_FUNC_DEF(GLenum, glGetError, ()); + +#if !USE_FORCED_GLES +GL_FUNC_2_DEF(void, glEnableVertexAttribArray, glEnableVertexAttribArrayARB, (GLuint index)); +GL_FUNC_2_DEF(void, glDisableVertexAttribArray, glDisableVertexAttribArrayARB, (GLuint index)); +GL_FUNC_2_DEF(void, glUniform1i, glUniform1iARB, (GLint location, GLint v0)); +GL_FUNC_2_DEF(void, glUniform1f, glUniform1fARB, (GLint location, GLfloat v0)); +GL_FUNC_2_DEF(void, glUniformMatrix4fv, glUniformMatrix4fvARB, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)); +GL_FUNC_2_DEF(void, glVertexAttrib4f, glVertexAttrib4fARB, (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)); +GL_FUNC_2_DEF(void, glVertexAttribPointer, glVertexAttribPointerARB, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)); + +GL_FUNC_2_DEF(GLprogram, glCreateProgram, glCreateProgramObjectARB, ()); +GL_FUNC_2_DEF(void, glDeleteProgram, glDeleteObjectARB, (GLprogram program)); +GL_FUNC_2_DEF(void, glAttachShader, glAttachObjectARB, (GLprogram program, GLshader shader)); +GL_FUNC_2_DEF(void, glDetachShader, glDetachObjectARB, (GLprogram program, GLshader shader)); +GL_FUNC_2_DEF(void, glLinkProgram, glLinkProgramARB, (GLprogram program)); +GL_FUNC_2_DEF(void, glUseProgram, glUseProgramObjectARB, (GLprogram program)); +GL_FUNC_2_DEF(void, glGetProgramiv, glGetObjectParameterivARB, (GLprogram program, GLenum pname, GLint *params)); +GL_FUNC_2_DEF(void, glGetProgramInfoLog, glGetInfoLogARB, (GLprogram program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)); +GL_FUNC_2_DEF(void, glBindAttribLocation, glBindAttribLocationARB, (GLprogram program, GLuint index, const GLchar *name)); +GL_FUNC_2_DEF(GLint, glGetAttribLocation, glGetAttribLocationARB, (GLprogram program, const GLchar *name)); +GL_FUNC_2_DEF(GLint, glGetUniformLocation, glGetUniformLocationARB, (GLprogram program, const GLchar *name)); + +GL_FUNC_2_DEF(GLshader, glCreateShader, glCreateShaderObjectARB, (GLenum type)); +GL_FUNC_2_DEF(void, glDeleteShader, glDeleteObjectARB, (GLshader shader)); +GL_FUNC_2_DEF(void, glGetShaderiv, glGetObjectParameterivARB, (GLshader shader, GLenum pname, GLint *params)); +GL_FUNC_2_DEF(void, glGetShaderInfoLog, glGetInfoLogARB, (GLshader shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)); +GL_FUNC_2_DEF(void, glShaderSource, glShaderSourceARB, (GLshader shader, GLsizei count, const GLchar *const *string, const GLint *length)); +GL_FUNC_2_DEF(void, glCompileShader, glCompileShaderARB, (GLshader shader)); + +GL_FUNC_2_DEF(void, glBindFramebuffer, glBindFramebufferEXT, (GLenum target, GLuint renderbuffer)); +GL_FUNC_2_DEF(void, glDeleteFramebuffers, glDeleteFramebuffersEXT, (GLsizei n, const GLuint *framebuffers)); +GL_FUNC_2_DEF(void, glGenFramebuffers, glGenFramebuffersEXT, (GLsizei n, GLuint *renderbuffers)); +GL_FUNC_2_DEF(void, glFramebufferTexture2D, glFramebufferTexture2DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)); +GL_FUNC_2_DEF(GLenum, glCheckFramebufferStatus, glCheckFramebufferStatusEXT, (GLenum target)); + +GL_FUNC_2_DEF(void, glActiveTexture, glActiveTextureARB, (GLenum texture)); +#endif + +#ifdef DEFINED_GL_EXT_FUNC_DEF +#undef DEFINED_GL_EXT_FUNC_DEF +#undef GL_EXT_FUNC_DEF +#endif + +#ifdef DEFINED_GL_FUNC_2_DEF +#undef DEFINED_GL_FUNC_2_DEF +#undef GL_FUNC_2_DEF +#endif diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index ac6d41d47d..4d6a00a3b3 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -23,8 +23,10 @@ #include "backends/graphics/opengl/opengl-graphics.h" #include "backends/graphics/opengl/texture.h" -#include "backends/graphics/opengl/debug.h" -#include "backends/graphics/opengl/extensions.h" +#include "backends/graphics/opengl/pipelines/pipeline.h" +#include "backends/graphics/opengl/pipelines/fixed.h" +#include "backends/graphics/opengl/pipelines/shader.h" +#include "backends/graphics/opengl/shader.h" #include "common/textconsole.h" #include "common/translation.h" @@ -45,6 +47,7 @@ namespace OpenGL { OpenGLGraphicsManager::OpenGLGraphicsManager() : _currentState(), _oldState(), _transactionMode(kTransactionNone), _screenChangeID(1 << (sizeof(int) * 8 - 2)), + _pipeline(nullptr), _outputScreenWidth(0), _outputScreenHeight(0), _displayX(0), _displayY(0), _displayWidth(0), _displayHeight(0), _defaultFormat(), _defaultFormatAlpha(), _gameScreen(nullptr), _gameScreenShakeOffset(0), _overlay(nullptr), @@ -58,6 +61,7 @@ OpenGLGraphicsManager::OpenGLGraphicsManager() #endif { memset(_gamePalette, 0, sizeof(_gamePalette)); + g_context.reset(); } OpenGLGraphicsManager::~OpenGLGraphicsManager() { @@ -67,6 +71,9 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() { #ifdef USE_OSD delete _osd; #endif +#if !USE_FORCED_GLES + ShaderManager::destroy(); +#endif } bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) { @@ -215,8 +222,8 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() { // a context existing before, which means we don't know the maximum // supported texture size before this. Thus, we check whether the // requested game resolution is supported over here. - || ( _currentState.gameWidth > (uint)Texture::getMaximumTextureSize() - || _currentState.gameHeight > (uint)Texture::getMaximumTextureSize())) { + || ( _currentState.gameWidth > (uint)g_context.maxTextureSize + || _currentState.gameHeight > (uint)g_context.maxTextureSize)) { if (_transactionMode == kTransactionActive) { // Try to setup the old state in case its valid and is // actually different from the new one. @@ -267,9 +274,9 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() { _gameScreen = nullptr; #ifdef USE_RGB_COLOR - _gameScreen = createTexture(_currentState.gameFormat); + _gameScreen = createSurface(_currentState.gameFormat); #else - _gameScreen = createTexture(Graphics::PixelFormat::createFormatCLUT8()); + _gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8()); #endif assert(_gameScreen); if (_gameScreen->hasPalette()) { @@ -365,29 +372,37 @@ void OpenGLGraphicsManager::updateScreen() { } _forceRedraw = false; + // Update changes to textures. + _gameScreen->updateGLTexture(); + if (_cursor) { + _cursor->updateGLTexture(); + } + _overlay->updateGLTexture(); + _osd->updateGLTexture(); + // Clear the screen buffer. if (_scissorOverride && !_overlayVisible) { // In certain cases we need to assure that the whole screen area is // cleared. For example, when switching from overlay visible to // invisible, we need to assure that all contents are cleared to // properly remove all overlay contents. - GLCALL(glDisable(GL_SCISSOR_TEST)); - GLCALL(glClear(GL_COLOR_BUFFER_BIT)); - GLCALL(glEnable(GL_SCISSOR_TEST)); + _backBuffer.enableScissorTest(false); + GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); + _backBuffer.enableScissorTest(true); --_scissorOverride; } else { - GLCALL(glClear(GL_COLOR_BUFFER_BIT)); + GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); } const GLfloat shakeOffset = _gameScreenShakeOffset * (GLfloat)_displayHeight / _gameScreen->getHeight(); // First step: Draw the (virtual) game screen. - _gameScreen->draw(_displayX, _displayY + shakeOffset, _displayWidth, _displayHeight); + g_context.getActivePipeline()->drawTexture(_gameScreen->getGLTexture(), _displayX, _displayY + shakeOffset, _displayWidth, _displayHeight); // Second step: Draw the overlay if visible. if (_overlayVisible) { - _overlay->draw(0, 0, _outputScreenWidth, _outputScreenHeight); + g_context.getActivePipeline()->drawTexture(_overlay->getGLTexture(), 0, 0, _outputScreenWidth, _outputScreenHeight); } // Third step: Draw the cursor if visible. @@ -396,9 +411,10 @@ void OpenGLGraphicsManager::updateScreen() { // visible. const GLfloat cursorOffset = _overlayVisible ? 0 : shakeOffset; - _cursor->draw(_cursorDisplayX - _cursorHotspotXScaled, - _cursorDisplayY - _cursorHotspotYScaled + cursorOffset, - _cursorWidthScaled, _cursorHeightScaled); + g_context.getActivePipeline()->drawTexture(_cursor->getGLTexture(), + _cursorDisplayX - _cursorHotspotXScaled, + _cursorDisplayY - _cursorHotspotYScaled + cursorOffset, + _cursorWidthScaled, _cursorHeightScaled); } #ifdef USE_OSD @@ -419,13 +435,13 @@ void OpenGLGraphicsManager::updateScreen() { } // Set the OSD transparency. - GLCALL(glColor4f(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f)); + g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f); // Draw the OSD texture. - _osd->draw(0, 0, _outputScreenWidth, _outputScreenHeight); + g_context.getActivePipeline()->drawTexture(_osd->getGLTexture(), 0, 0, _outputScreenWidth, _outputScreenHeight); // Reset color. - GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f)); + g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f); } #endif @@ -467,7 +483,7 @@ void OpenGLGraphicsManager::showOverlay() { _forceRedraw = true; // Allow drawing inside full screen area. - GLCALL(glDisable(GL_SCISSOR_TEST)); + _backBuffer.enableScissorTest(false); // Update cursor position. setMousePosition(_cursorX, _cursorY); @@ -478,7 +494,7 @@ void OpenGLGraphicsManager::hideOverlay() { _forceRedraw = true; // Limit drawing to screen area. - GLCALL(glEnable(GL_SCISSOR_TEST)); + _backBuffer.enableScissorTest(true); _scissorOverride = 3; // Update cursor position. @@ -609,7 +625,7 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int } else { textureFormat = _defaultFormatAlpha; } - _cursor = createTexture(textureFormat, true); + _cursor = createSurface(textureFormat, true); assert(_cursor); _cursor->enableLinearFiltering(_currentState.graphicsMode == GFX_LINEAR); } @@ -755,18 +771,8 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { _outputScreenWidth = width; _outputScreenHeight = height; - // Setup coordinates system. - GLCALL(glViewport(0, 0, _outputScreenWidth, _outputScreenHeight)); - - GLCALL(glMatrixMode(GL_PROJECTION)); - GLCALL(glLoadIdentity()); -#ifdef USE_GLES - GLCALL(glOrthof(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1)); -#else - GLCALL(glOrtho(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1)); -#endif - GLCALL(glMatrixMode(GL_MODELVIEW)); - GLCALL(glLoadIdentity()); + // Setup backbuffer size. + _backBuffer.setDimensions(width, height); uint overlayWidth = width; uint overlayHeight = height; @@ -777,15 +783,15 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { // possible and then scale it to the physical display size. This sounds // bad but actually all recent chips should support full HD resolution // anyway. Thus, it should not be a real issue for modern hardware. - if ( overlayWidth > (uint)Texture::getMaximumTextureSize() - || overlayHeight > (uint)Texture::getMaximumTextureSize()) { + if ( overlayWidth > (uint)g_context.maxTextureSize + || overlayHeight > (uint)g_context.maxTextureSize) { const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight; if (outputAspect > (frac_t)FRAC_ONE) { - overlayWidth = Texture::getMaximumTextureSize(); + overlayWidth = g_context.maxTextureSize; overlayHeight = intToFrac(overlayWidth) / outputAspect; } else { - overlayHeight = Texture::getMaximumTextureSize(); + overlayHeight = g_context.maxTextureSize; overlayWidth = fracToInt(overlayHeight * outputAspect); } } @@ -801,7 +807,7 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { delete _overlay; _overlay = nullptr; - _overlay = createTexture(_defaultFormatAlpha); + _overlay = createSurface(_defaultFormatAlpha); assert(_overlay); // We always filter the overlay with GL_LINEAR. This assures it's // readable in case it needs to be scaled and does not affect it @@ -816,7 +822,7 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { delete _osd; _osd = nullptr; - _osd = createTexture(_defaultFormatAlpha); + _osd = createSurface(_defaultFormatAlpha); assert(_osd); // We always filter the osd with GL_LINEAR. This assures it's // readable in case it needs to be scaled and does not affect it @@ -836,38 +842,48 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) { } void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha) { - // Initialize all extensions. - initializeGLExtensions(); + // Initialize context for use. + initializeGLContext(); - // Disable 3D properties. - GLCALL(glDisable(GL_CULL_FACE)); - GLCALL(glDisable(GL_DEPTH_TEST)); - GLCALL(glDisable(GL_LIGHTING)); - GLCALL(glDisable(GL_FOG)); - GLCALL(glDisable(GL_DITHER)); - GLCALL(glShadeModel(GL_FLAT)); - GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)); + // Initialize pipeline. + delete _pipeline; + _pipeline = nullptr; - // Default to black as clear color. - GLCALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f)); +#if !USE_FORCED_GLES + if (g_context.shadersSupported) { + ShaderMan.notifyCreate(); + _pipeline = new ShaderPipeline(ShaderMan.query(ShaderManager::kDefault)); + } +#endif - // Setup alpha blend (for overlay and cursor). - GLCALL(glEnable(GL_BLEND)); - GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); +#if !USE_FORCED_GLES2 + if (_pipeline == nullptr) { + _pipeline = new FixedPipeline(); + } +#endif + + g_context.setPipeline(_pipeline); + + // Disable 3D properties. + GL_CALL(glDisable(GL_CULL_FACE)); + GL_CALL(glDisable(GL_DEPTH_TEST)); + GL_CALL(glDisable(GL_DITHER)); - // Enable rendering with vertex and coord arrays. - GLCALL(glEnableClientState(GL_VERTEX_ARRAY)); - GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f); - GLCALL(glEnable(GL_TEXTURE_2D)); + GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + // Setup backbuffer state. + + // Default to black as clear color. + _backBuffer.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); + // Setup alpha blend (for overlay and cursor). + _backBuffer.enableBlend(true); // Setup scissor state accordingly. - if (_overlayVisible) { - GLCALL(glDisable(GL_SCISSOR_TEST)); - } else { - GLCALL(glEnable(GL_SCISSOR_TEST)); - } + _backBuffer.enableScissorTest(!_overlayVisible); + + g_context.getActivePipeline()->setFramebuffer(&_backBuffer); + // Clear the whole screen for the first three frames to assure any // leftovers are cleared. _scissorOverride = 3; @@ -875,10 +891,7 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def // We use a "pack" alignment (when reading from textures) to 4 here, // since the only place where we really use it is the BMP screenshot // code and that requires the same alignment too. - GLCALL(glPixelStorei(GL_PACK_ALIGNMENT, 4)); - - // Query information needed by textures. - Texture::queryTextureInformation(); + GL_CALL(glPixelStorei(GL_PACK_ALIGNMENT, 4)); // Refresh the output screen dimensions if some are set up. if (_outputScreenWidth != 0 && _outputScreenHeight != 0) { @@ -892,42 +905,56 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def _defaultFormatAlpha = defaultFormatAlpha; if (_gameScreen) { - _gameScreen->recreateInternalTexture(); + _gameScreen->recreate(); } if (_overlay) { - _overlay->recreateInternalTexture(); + _overlay->recreate(); } if (_cursor) { - _cursor->recreateInternalTexture(); + _cursor->recreate(); } #ifdef USE_OSD if (_osd) { - _osd->recreateInternalTexture(); + _osd->recreate(); } #endif } void OpenGLGraphicsManager::notifyContextDestroy() { if (_gameScreen) { - _gameScreen->releaseInternalTexture(); + _gameScreen->destroy(); } if (_overlay) { - _overlay->releaseInternalTexture(); + _overlay->destroy(); } if (_cursor) { - _cursor->releaseInternalTexture(); + _cursor->destroy(); } #ifdef USE_OSD if (_osd) { - _osd->releaseInternalTexture(); + _osd->destroy(); } #endif + +#if !USE_FORCED_GLES + if (g_context.shadersSupported) { + ShaderMan.notifyDestroy(); + } +#endif + + // Destroy rendering pipeline. + g_context.setPipeline(nullptr); + delete _pipeline; + _pipeline = nullptr; + + // Rest our context description since the context is gone soon. + g_context.reset(); } void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) { @@ -970,9 +997,15 @@ void OpenGLGraphicsManager::setMousePosition(int x, int y) { } } -Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &format, bool wantAlpha) { +Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha) { GLenum glIntFormat, glFormat, glType; if (format.bytesPerPixel == 1) { +#if !USE_FORCED_GLES + if (TextureCLUT8GPU::isSupportedByContext()) { + return new TextureCLUT8GPU(); + } +#endif + const Graphics::PixelFormat &virtFormat = wantAlpha ? _defaultFormatAlpha : _defaultFormat; const bool supported = getGLPixelFormat(virtFormat, glIntFormat, glFormat, glType); if (!supported) { @@ -980,6 +1013,15 @@ Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &forma } else { return new TextureCLUT8(glIntFormat, glFormat, glType, virtFormat); } +#if !USE_FORCED_GL + } else if (isGLESContext() && format == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { + // OpenGL ES does not support a texture format usable for RGB555. + // Since SCUMM uses this pixel format for some games (and there is no + // hope for this to change anytime soon) we use pixel format + // conversion to a supported texture format. However, this is a one + // time exception. + return new TextureRGB555(); +#endif // !USE_FORCED_GL } else { const bool supported = getGLPixelFormat(format, glIntFormat, glFormat, glType); if (!supported) { @@ -1015,7 +1057,11 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4; return true; -#ifndef USE_GLES +#if !USE_FORCED_GLES && !USE_FORCED_GLES2 + // The formats below are not supported by every GLES implementation. + // Thus, we do not mark them as supported when a GLES context is setup. + } else if (isGLESContext()) { + return false; #ifdef SCUMM_LITTLE_ENDIAN } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888 glIntFormat = GL_RGBA; @@ -1024,17 +1070,10 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF return true; #endif } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555 - // GL_BGRA does not exist in every GLES implementation so should not be configured if - // USE_GLES is set. glIntFormat = GL_RGB; glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_1_5_5_5_REV; return true; - } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888 - glIntFormat = GL_RGBA; - glFormat = GL_BGRA; - glType = GL_UNSIGNED_INT_8_8_8_8_REV; - return true; } else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444 glIntFormat = GL_RGBA; glFormat = GL_BGRA; @@ -1054,8 +1093,8 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565 glIntFormat = GL_RGB; - glFormat = GL_BGR; - glType = GL_UNSIGNED_SHORT_5_6_5; + glFormat = GL_RGB; + glType = GL_UNSIGNED_SHORT_5_6_5_REV; return true; } else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551 glIntFormat = GL_RGBA; @@ -1072,7 +1111,7 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF glFormat = GL_BGRA; glType = GL_UNSIGNED_SHORT_4_4_4_4; return true; -#endif +#endif // !USE_FORCED_GLES && !USE_FORCED_GLES2 } else { return false; } @@ -1119,10 +1158,10 @@ void OpenGLGraphicsManager::recalculateDisplayArea() { // Setup drawing limitation for game graphics. // This invovles some trickery because OpenGL's viewport coordinate system // is upside down compared to ours. - GLCALL(glScissor(_displayX, - _outputScreenHeight - _displayHeight - _displayY, - _displayWidth, - _displayHeight)); + _backBuffer.setScissorBox(_displayX, + _outputScreenHeight - _displayHeight - _displayY, + _displayWidth, + _displayHeight); // Clear the whole screen for the first three frames to remove leftovers. _scissorOverride = 3; @@ -1144,20 +1183,7 @@ void OpenGLGraphicsManager::updateCursorPalette() { _cursor->setPalette(0, 256, _gamePalette); } - // We remove all alpha bits from the palette entry of the color key. - // This makes sure its properly handled as color key. - const Graphics::PixelFormat &hardwareFormat = _cursor->getHardwareFormat(); - const uint32 aMask = (0xFF >> hardwareFormat.aLoss) << hardwareFormat.aShift; - - if (hardwareFormat.bytesPerPixel == 2) { - uint16 *palette = (uint16 *)_cursor->getPalette() + _cursorKeyColor; - *palette &= ~aMask; - } else if (hardwareFormat.bytesPerPixel == 4) { - uint32 *palette = (uint32 *)_cursor->getPalette() + _cursorKeyColor; - *palette &= ~aMask; - } else { - warning("OpenGLGraphicsManager::updateCursorPalette: Unsupported pixel depth %d", hardwareFormat.bytesPerPixel); - } + _cursor->setColorKey(_cursorKeyColor); } void OpenGLGraphicsManager::recalculateCursorScaling() { @@ -1207,7 +1233,7 @@ void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const uint8 *pixels = new uint8[lineSize * height]; // Get pixel data from OpenGL buffer - GLCALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels)); + GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels)); // BMP stores as BGR. Since we can't assume that GL_BGR is supported we // will swap the components from the RGB we read to BGR on our own. diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h index 9578839383..35435c156e 100644 --- a/backends/graphics/opengl/opengl-graphics.h +++ b/backends/graphics/opengl/opengl-graphics.h @@ -24,6 +24,7 @@ #define BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H #include "backends/graphics/opengl/opengl-sys.h" +#include "backends/graphics/opengl/framebuffer.h" #include "backends/graphics/graphics.h" #include "common/frac.h" @@ -40,7 +41,11 @@ namespace OpenGL { // SurfaceSDL backend enables it and disabling it can cause issues in sdl.cpp. #define USE_OSD 1 -class Texture; +class Surface; +class Pipeline; +#if !USE_FORCED_GLES +class Shader; +#endif enum { GFX_LINEAR = 0, @@ -117,6 +122,11 @@ public: protected: /** + * Whether an GLES or GLES2 context is active. + */ + bool isGLESContext() const { return g_context.type == kContextGLES || g_context.type == kContextGLES2; } + + /** * Set up the actual screen size available for the OpenGL code to do any * drawing. * @@ -126,6 +136,16 @@ protected: void setActualScreenSize(uint width, uint height); /** + * Sets the OpenGL (ES) type the graphics manager shall work with. + * + * This needs to be called at least once (and before ever calling + * notifyContextCreate). + * + * @param type Type of the OpenGL (ES) contexts to be created. + */ + void setContextType(ContextType type); + + /** * Notify the manager of a OpenGL context change. This should be the first * thing to call after you created an OpenGL (ES) context! * @@ -172,15 +192,15 @@ protected: private: /** - * Create a texture with the specified pixel format. + * Create a surface with the specified pixel format. * - * @param format The pixel format the Texture object should accept as + * @param format The pixel format the Surface object should accept as * input. - * @param wantAlpha For CLUT8 textures this marks whether an alpha + * @param wantAlpha For CLUT8 surfaces this marks whether an alpha * channel should be used. - * @return A pointer to the texture or nullptr on failure. + * @return A pointer to the surface or nullptr on failure. */ - Texture *createTexture(const Graphics::PixelFormat &format, bool wantAlpha = false); + Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false); // // Transaction support @@ -281,6 +301,36 @@ private: // /** + * Initialize the active context for use. + */ + void initializeGLContext(); + + /** + * Render back buffer. + */ + Backbuffer _backBuffer; + + /** + * OpenGL pipeline used for rendering. + */ + Pipeline *_pipeline; + +protected: + /** + * Query the address of an OpenGL function by name. + * + * This can only be used after a context has been created. + * Please note that this function can return valid addresses even if the + * OpenGL context does not support the function. + * + * @param name The name of the OpenGL function. + * @return An function pointer for the requested OpenGL function or + * nullptr in case of failure. + */ + virtual void *getProcAddress(const char *name) const = 0; + +private: + /** * Try to determine the internal parameters for a given pixel format. * * @return true when the format can be used, false otherwise. @@ -348,7 +398,7 @@ private: /** * The virtual game screen. */ - Texture *_gameScreen; + Surface *_gameScreen; /** * The game palette if in CLUT8 mode. @@ -367,7 +417,7 @@ private: /** * The overlay screen. */ - Texture *_overlay; + Surface *_overlay; /** * Whether the overlay is visible or not. @@ -386,7 +436,7 @@ private: /** * The cursor image. */ - Texture *_cursor; + Surface *_cursor; /** * X coordinate of the cursor in phyiscal coordinates. @@ -497,7 +547,7 @@ private: /** * The OSD's contents. */ - Texture *_osd; + Surface *_osd; /** * Current opacity level of the OSD. diff --git a/backends/graphics/opengl/opengl-sys.h b/backends/graphics/opengl/opengl-sys.h index 4e21894380..4495128f32 100644 --- a/backends/graphics/opengl/opengl-sys.h +++ b/backends/graphics/opengl/opengl-sys.h @@ -23,35 +23,147 @@ #ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_SYS_H #define BACKENDS_GRAPHICS_OPENGL_OPENGL_SYS_H -// The purpose of this header is to include the OpenGL headers in an uniform -// fashion. A notable example for a non standard port is the Tizen port. - #include "common/scummsys.h" -#ifdef WIN32 -#if defined(ARRAYSIZE) && !defined(_WINDOWS_) -#undef ARRAYSIZE -#endif -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#undef ARRAYSIZE +#include "backends/graphics/opengl/debug.h" +#ifdef SDL_BACKEND +#include "backends/platform/sdl/sdl-sys.h" #endif -// HACK: In case common/util.h has been included already we need to make sure -// to define ARRAYSIZE again in case of Windows. -#if !defined(ARRAYSIZE) && defined(COMMON_UTIL_H) -#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0]))) +// On OS X we only support GL contexts. The reason is that Apple's GL interface +// uses "void *" for GLhandleARB which is not type compatible with GLint. This +// kills our aliasing trick for extension functions and thus would force us to +// supply two different Shader class implementations or introduce other +// wrappers. OS X only supports GL contexts right now anyway (at least +// according to SDL2 sources), thus it is not much of an issue. +#if defined(MACOSX) && (!defined(USE_GLES_MODE) || USE_GLES_MODE != 0) +//#warning "Only forced OpenGL mode is supported on Mac OS X. Overriding settings." +#undef USE_GLES_MODE +#define USE_GLES_MODE 0 #endif +// We allow to force GL or GLES modes on compile time. +// For this the USE_GLES_MODE define is used. The following values represent +// the given selection choices: +// 0 - Force OpenGL context +// 1 - Force OpenGL ES context +// 2 - Force OpenGL ES 2.0 context +#define USE_FORCED_GL (defined(USE_GLES_MODE) && USE_GLES_MODE == 0) +#define USE_FORCED_GLES (defined(USE_GLES_MODE) && USE_GLES_MODE == 1) +#define USE_FORCED_GLES2 (defined(USE_GLES_MODE) && USE_GLES_MODE == 2) + +// On Tizen we include the toolchain's OpenGL file. This is something we +// actually want to avoid. However, since Tizen uses eglGetProcAddress which +// is not required to return valid function pointers to non OpenGL extension +// functions, we need the system's definitions to resolve all OpenGL +// functions. +// TODO: See if there is an alternative which allows us to avoid including +// Tizen's OpenGL header here. #if defined(TIZEN) -#include <FGraphicsOpengl.h> -using namespace Tizen::Graphics::Opengl; -#elif defined(USE_GLES) -#include <GLES/gl.h> -#elif defined(SDL_BACKEND) -#include <SDL_opengl.h> + #include <FGraphicsOpengl.h> + using namespace Tizen::Graphics::Opengl; + #define USE_BUILTIN_OPENGL #else -#include <GL/gl.h> + #include "backends/graphics/opengl/opengl-defs.h" #endif +#ifdef SDL_BACKEND + // Win32 needs OpenGL functions declared with APIENTRY. + // However, SDL does not define APIENTRY in it's SDL.h file on non-Windows + // targets, thus if it is not available, we just dummy define it. + #ifndef APIENTRY + #define APIENTRY + #endif + #define GL_CALL_CONV APIENTRY +#else + #define GL_CALL_CONV +#endif + +namespace OpenGL { + +enum ContextType { + kContextGL, + kContextGLES, + kContextGLES2 +}; + +class Pipeline; +class Framebuffer; + +/** + * Description structure of the OpenGL (ES) context. + */ +struct Context { + /** The type of the active context. */ + ContextType type; + + /** + * Reset context. + * + * This marks all extensions as unavailable and clears all function + * pointers. + */ + void reset(); + + /** The maximum texture size supported by the context. */ + GLint maxTextureSize; + + /** Whether GL_ARB_texture_non_power_of_two is available or not. */ + bool NPOTSupported; + + /** Whether shader support is available or not. */ + bool shadersSupported; + + /** Whether multi texture support is available or not. */ + bool multitextureSupported; + + /** Whether FBO support is available or not. */ + bool framebufferObjectSupported; + +#define GL_FUNC_DEF(ret, name, param) ret (GL_CALL_CONV *name)param +#include "backends/graphics/opengl/opengl-func.h" +#undef GL_FUNC_DEF + + // + // Wrapper functionality to handle fixed-function pipelines and + // programmable pipelines in the same fashion. + // + +private: + /** Currently active rendering pipeline. */ + Pipeline *activePipeline; + +public: + /** + * Set new pipeline. + * + * Client is responsible for any memory management related to pipelines. + * + * @param pipeline Pipeline to activate. + * @return Formerly active pipeline. + */ + Pipeline *setPipeline(Pipeline *pipeline); + + /** + * Query the currently active rendering pipeline. + */ + Pipeline *getActivePipeline() const { return activePipeline; } +}; + +/** + * The (active) OpenGL context. + */ +extern Context g_context; + +} // End of namespace OpenGL + +#define GL_CALL(x) GL_WRAP_DEBUG(g_context.x, x) +#define GL_CALL_SAFE(func, params) \ + do { \ + if (g_context.func) { \ + GL_CALL(func params); \ + } \ + } while (0) +#define GL_ASSIGN(var, x) GL_WRAP_DEBUG(var = g_context.x, x) + #endif diff --git a/backends/graphics/opengl/pipelines/clut8.cpp b/backends/graphics/opengl/pipelines/clut8.cpp new file mode 100644 index 0000000000..fca40074f0 --- /dev/null +++ b/backends/graphics/opengl/pipelines/clut8.cpp @@ -0,0 +1,46 @@ +/* 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 "backends/graphics/opengl/pipelines/clut8.h" +#include "backends/graphics/opengl/shader.h" +#include "backends/graphics/opengl/framebuffer.h" + +namespace OpenGL { + +#if !USE_FORCED_GLES +CLUT8LookUpPipeline::CLUT8LookUpPipeline() + : ShaderPipeline(ShaderMan.query(ShaderManager::kCLUT8LookUp)), _paletteTexture(nullptr) { +} + +void CLUT8LookUpPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) { + // Set the palette texture. + GL_CALL(glActiveTexture(GL_TEXTURE1)); + if (_paletteTexture) { + _paletteTexture->bind(); + } + + GL_CALL(glActiveTexture(GL_TEXTURE0)); + ShaderPipeline::drawTexture(texture, coordinates); +} +#endif // !USE_FORCED_GLES + +} // End of namespace OpenGL diff --git a/backends/graphics/opengl/extensions.cpp b/backends/graphics/opengl/pipelines/clut8.h index 4482ef82b5..16724e4652 100644 --- a/backends/graphics/opengl/extensions.cpp +++ b/backends/graphics/opengl/pipelines/clut8.h @@ -20,29 +20,27 @@ * */ -#include "backends/graphics/opengl/extensions.h" -#include "backends/graphics/opengl/opengl-sys.h" +#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H +#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H -#include "common/tokenizer.h" +#include "backends/graphics/opengl/pipelines/shader.h" namespace OpenGL { -bool g_extNPOTSupported = false; +#if !USE_FORCED_GLES +class CLUT8LookUpPipeline : public ShaderPipeline { +public: + CLUT8LookUpPipeline(); -void initializeGLExtensions() { - const char *extString = (const char *)glGetString(GL_EXTENSIONS); + void setPaletteTexture(const GLTexture *paletteTexture) { _paletteTexture = paletteTexture; } - // Initialize default state. - g_extNPOTSupported = false; + virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates); - Common::StringTokenizer tokenizer(extString, " "); - while (!tokenizer.empty()) { - Common::String token = tokenizer.nextToken(); - - if (token == "GL_ARB_texture_non_power_of_two") { - g_extNPOTSupported = true; - } - } -} +private: + const GLTexture *_paletteTexture; +}; +#endif // !USE_FORCED_GLES } // End of namespace OpenGL + +#endif diff --git a/backends/graphics/opengl/pipelines/fixed.cpp b/backends/graphics/opengl/pipelines/fixed.cpp new file mode 100644 index 0000000000..8e3bd7eaee --- /dev/null +++ b/backends/graphics/opengl/pipelines/fixed.cpp @@ -0,0 +1,70 @@ +/* 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 "backends/graphics/opengl/pipelines/fixed.h" + +namespace OpenGL { + +#if !USE_FORCED_GLES2 +void FixedPipeline::activateInternal() { + GL_CALL(glDisable(GL_LIGHTING)); + GL_CALL(glDisable(GL_FOG)); + GL_CALL(glShadeModel(GL_FLAT)); + GL_CALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)); + + GL_CALL(glEnableClientState(GL_VERTEX_ARRAY)); + GL_CALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + +#if !USE_FORCED_GLES + if (g_context.multitextureSupported) { + GL_CALL(glActiveTexture(GL_TEXTURE0)); + } +#endif + GL_CALL(glEnable(GL_TEXTURE_2D)); +} + +void FixedPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { + GL_CALL(glColor4f(r, g, b, a)); +} + +void FixedPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) { + texture.bind(); + + GL_CALL(glTexCoordPointer(2, GL_FLOAT, 0, texture.getTexCoords())); + GL_CALL(glVertexPointer(2, GL_FLOAT, 0, coordinates)); + GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +} + +void FixedPipeline::setProjectionMatrix(const GLfloat *projectionMatrix) { + if (!isActive()) { + return; + } + + GL_CALL(glMatrixMode(GL_PROJECTION)); + GL_CALL(glLoadMatrixf(projectionMatrix)); + + GL_CALL(glMatrixMode(GL_MODELVIEW)); + GL_CALL(glLoadIdentity()); +} +#endif // !USE_FORCED_GLES2 + +} // End of namespace OpenGL diff --git a/backends/graphics/opengl/pipelines/fixed.h b/backends/graphics/opengl/pipelines/fixed.h new file mode 100644 index 0000000000..6bfe140c19 --- /dev/null +++ b/backends/graphics/opengl/pipelines/fixed.h @@ -0,0 +1,46 @@ +/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H +#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H + +#include "backends/graphics/opengl/pipelines/pipeline.h" + +namespace OpenGL { + +#if !USE_FORCED_GLES2 +class FixedPipeline : public Pipeline { +public: + virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); + + virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates); + + virtual void setProjectionMatrix(const GLfloat *projectionMatrix); + +protected: + virtual void activateInternal(); +}; +#endif // !USE_FORCED_GLES2 + +} // End of namespace OpenGL + +#endif diff --git a/backends/graphics/opengl/pipelines/pipeline.cpp b/backends/graphics/opengl/pipelines/pipeline.cpp new file mode 100644 index 0000000000..6a59cd28e7 --- /dev/null +++ b/backends/graphics/opengl/pipelines/pipeline.cpp @@ -0,0 +1,66 @@ +/* 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 "backends/graphics/opengl/pipelines/pipeline.h" +#include "backends/graphics/opengl/framebuffer.h" + +namespace OpenGL { + +Pipeline::Pipeline() + : _activeFramebuffer(nullptr), _isActive(false) { +} + +void Pipeline::activate() { + _isActive = true; + + if (_activeFramebuffer) { + _activeFramebuffer->activate(); + } + + activateInternal(); +} + +void Pipeline::deactivate() { + deactivateInternal(); + + if (_activeFramebuffer) { + _activeFramebuffer->deactivate(); + } + + _isActive = false; +} + +Framebuffer *Pipeline::setFramebuffer(Framebuffer *framebuffer) { + Framebuffer *oldFramebuffer = _activeFramebuffer; + if (_isActive && oldFramebuffer) { + oldFramebuffer->deactivate(); + } + + _activeFramebuffer = framebuffer; + if (_isActive && _activeFramebuffer) { + _activeFramebuffer->activate(); + } + + return oldFramebuffer; +} + +} // End of namespace OpenGL diff --git a/backends/graphics/opengl/pipelines/pipeline.h b/backends/graphics/opengl/pipelines/pipeline.h new file mode 100644 index 0000000000..9f32d33b95 --- /dev/null +++ b/backends/graphics/opengl/pipelines/pipeline.h @@ -0,0 +1,126 @@ +/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H +#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H + +#include "backends/graphics/opengl/opengl-sys.h" +#include "backends/graphics/opengl/texture.h" + +namespace OpenGL { + +class Framebuffer; + +/** + * Interface for OpenGL pipeline functionality. + * + * This encapsulates differences in various rendering pipelines used for + * OpenGL, OpenGL ES 1, and OpenGL ES 2. + */ +class Pipeline { +public: + Pipeline(); + virtual ~Pipeline() {} + + /** + * Activate the pipeline. + * + * This sets the OpenGL state to make use of drawing with the given + * OpenGL pipeline. + */ + void activate(); + + /** + * Deactivate the pipeline. + */ + void deactivate(); + + /** + * Set framebuffer to render to. + * + * Client is responsible for any memory management related to framebuffer. + * + * @param framebuffer Framebuffer to activate. + * @return Formerly active framebuffer. + */ + Framebuffer *setFramebuffer(Framebuffer *framebuffer); + + /** + * Set modulation color. + * + * @param r Red component in [0,1]. + * @param g Green component in [0,1]. + * @param b Blue component in [0,1]. + * @param a Alpha component in [0,1]. + */ + virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) = 0; + + /** + * Draw a texture rectangle to the currently active framebuffer. + * + * @param texture Texture to use for drawing. + * @param coordinates x1, y1, x2, y2 coordinates where to draw the texture. + */ + virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates) = 0; + + void drawTexture(const GLTexture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h) { + const GLfloat coordinates[4*2] = { + x, y, + x + w, y, + x, y + h, + x + w, y + h + }; + drawTexture(texture, coordinates); + } + + /** + * Set the projection matrix. + * + * This is intended to be only ever be used by Framebuffer subclasses. + */ + virtual void setProjectionMatrix(const GLfloat *projectionMatrix) = 0; + +protected: + /** + * Activate the pipeline. + * + * This sets the OpenGL state to make use of drawing with the given + * OpenGL pipeline. + */ + virtual void activateInternal() = 0; + + /** + * Deactivate the pipeline. + */ + virtual void deactivateInternal() {} + + bool isActive() const { return _isActive; } + + Framebuffer *_activeFramebuffer; + +private: + bool _isActive; +}; + +} // End of namespace OpenGL + +#endif diff --git a/backends/graphics/opengl/pipelines/shader.cpp b/backends/graphics/opengl/pipelines/shader.cpp new file mode 100644 index 0000000000..8e38458f73 --- /dev/null +++ b/backends/graphics/opengl/pipelines/shader.cpp @@ -0,0 +1,94 @@ +/* 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 "backends/graphics/opengl/pipelines/shader.h" +#include "backends/graphics/opengl/shader.h" +#include "backends/graphics/opengl/framebuffer.h" + +namespace OpenGL { + +#if !USE_FORCED_GLES +ShaderPipeline::ShaderPipeline(Shader *shader) + : _activeShader(shader), _colorAttributes() { + _vertexAttribLocation = shader->getAttributeLocation("position"); + _texCoordAttribLocation = shader->getAttributeLocation("texCoordIn"); + _colorAttribLocation = shader->getAttributeLocation("blendColorIn"); + + assert(_vertexAttribLocation != -1); + assert(_texCoordAttribLocation != -1); + assert(_colorAttribLocation != -1); + + // One of the attributes needs to be passed through location 0, otherwise + // we get no output for GL contexts due to GL compatibility reasons. Let's + // check whether this ever happens. If this ever gets hit, we need to + // enable location 0 and pass some dummy values through it to fix output. + assert( _vertexAttribLocation == 0 + || _texCoordAttribLocation == 0 + || _colorAttribLocation == 0); +} + +void ShaderPipeline::activateInternal() { + GL_CALL(glEnableVertexAttribArray(_vertexAttribLocation)); + GL_CALL(glEnableVertexAttribArray(_texCoordAttribLocation)); + GL_CALL(glEnableVertexAttribArray(_colorAttribLocation)); + + if (g_context.multitextureSupported) { + GL_CALL(glActiveTexture(GL_TEXTURE0)); + } + + _activeShader->activate(); +} + +void ShaderPipeline::deactivateInternal() { + GL_CALL(glDisableVertexAttribArray(_vertexAttribLocation)); + GL_CALL(glDisableVertexAttribArray(_texCoordAttribLocation)); + GL_CALL(glDisableVertexAttribArray(_colorAttribLocation)); + + _activeShader->deactivate(); +} + +void ShaderPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { + GLfloat *dst = _colorAttributes; + for (uint i = 0; i < 4; ++i) { + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } + + GL_CALL(glVertexAttribPointer(_colorAttribLocation, 4, GL_FLOAT, GL_FALSE, 0, _colorAttributes)); +} + +void ShaderPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) { + texture.bind(); + + GL_CALL(glVertexAttribPointer(_texCoordAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, texture.getTexCoords())); + GL_CALL(glVertexAttribPointer(_vertexAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, coordinates)); + GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +} + +void ShaderPipeline::setProjectionMatrix(const GLfloat *projectionMatrix) { + _activeShader->setUniform("projection", new ShaderUniformMatrix44(projectionMatrix)); +} +#endif // !USE_FORCED_GLES + +} // End of namespace OpenGL diff --git a/backends/graphics/opengl/pipelines/shader.h b/backends/graphics/opengl/pipelines/shader.h new file mode 100644 index 0000000000..6159607099 --- /dev/null +++ b/backends/graphics/opengl/pipelines/shader.h @@ -0,0 +1,59 @@ +/* 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 BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H +#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H + +#include "backends/graphics/opengl/pipelines/pipeline.h" + +namespace OpenGL { + +#if !USE_FORCED_GLES +class Shader; + +class ShaderPipeline : public Pipeline { +public: + ShaderPipeline(Shader *shader); + + virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); + + virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates); + + virtual void setProjectionMatrix(const GLfloat *projectionMatrix); + +protected: + virtual void activateInternal(); + virtual void deactivateInternal(); + + GLint _vertexAttribLocation; + GLint _texCoordAttribLocation; + GLint _colorAttribLocation; + + GLfloat _colorAttributes[4*4]; + + Shader *const _activeShader; +}; +#endif // !USE_FORCED_GLES + +} // End of namespace OpenGL + +#endif diff --git a/backends/graphics/opengl/shader.cpp b/backends/graphics/opengl/shader.cpp new file mode 100644 index 0000000000..27981f25dc --- /dev/null +++ b/backends/graphics/opengl/shader.cpp @@ -0,0 +1,332 @@ +/* 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 "backends/graphics/opengl/shader.h" + +#if !USE_FORCED_GLES + +#include "common/textconsole.h" +#include "common/util.h" + +namespace Common { +DECLARE_SINGLETON(OpenGL::ShaderManager); +} + +namespace OpenGL { + +namespace { + +#pragma mark - Builtin Shader Sources - + +const char *const g_defaultVertexShader = + "attribute vec4 position;\n" + "attribute vec2 texCoordIn;\n" + "attribute vec4 blendColorIn;\n" + "\n" + "uniform mat4 projection;\n" + "\n" + "varying vec2 texCoord;\n" + "varying vec4 blendColor;\n" + "\n" + "void main(void) {\n" + "\ttexCoord = texCoordIn;\n" + "\tblendColor = blendColorIn;\n" + "\tgl_Position = projection * position;\n" + "}\n"; + +const char *const g_defaultFragmentShader = + "varying vec2 texCoord;\n" + "varying vec4 blendColor;\n" + "\n" + "uniform sampler2D texture;\n" + "\n" + "void main(void) {\n" + "\tgl_FragColor = blendColor * texture2D(texture, texCoord);\n" + "}\n"; + +const char *const g_lookUpFragmentShader = + "varying vec2 texCoord;\n" + "varying vec4 blendColor;\n" + "\n" + "uniform sampler2D texture;\n" + "uniform sampler2D palette;\n" + "\n" + "const float adjustFactor = 255.0 / 256.0 + 1.0 / (2.0 * 256.0);" + "\n" + "void main(void) {\n" + "\tvec4 index = texture2D(texture, texCoord);\n" + "\tgl_FragColor = blendColor * texture2D(palette, vec2(index.a * adjustFactor, 0.0));\n" + "}\n"; + + +// Taken from: https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_03#OpenGL_ES_2_portability +const char *const g_precisionDefines = + "#ifdef GL_ES\n" + "\t#if defined(GL_FRAGMENT_PRECISION_HIGH) && GL_FRAGMENT_PRECISION_HIGH == 1\n" + "\t\tprecision highp float;\n" + "\t#else\n" + "\t\tprecision mediump float;\n" + "\t#endif\n" + "#else\n" + "\t#define highp\n" + "\t#define mediump\n" + "\t#define lowp\n" + "#endif\n"; + +} // End of anonymous namespace + +#pragma mark - Uniform Values - + +void ShaderUniformInteger::set(GLint location) const { + GL_CALL(glUniform1i(location, _value)); +} + +void ShaderUniformFloat::set(GLint location) const { + GL_CALL(glUniform1f(location, _value)); +} + +void ShaderUniformMatrix44::set(GLint location) const { + GL_CALL(glUniformMatrix4fv(location, 1, GL_FALSE, _matrix)); +} + +#pragma mark - Shader Implementation - + +Shader::Shader(const Common::String &vertex, const Common::String &fragment) + : _vertex(vertex), _fragment(fragment), _isActive(false), _program(0), _uniforms() { + recreate(); +} + +Shader::~Shader() { + // According to extension specification glDeleteObjectARB silently ignores + // 0. However, with nVidia drivers this can cause GL_INVALID_VALUE, thus + // we do not call it with 0 as parameter to avoid warnings. + if (_program) { + GL_CALL_SAFE(glDeleteProgram, (_program)); + } +} + +void Shader::destroy() { + // According to extension specification glDeleteObjectARB silently ignores + // 0. However, with nVidia drivers this can cause GL_INVALID_VALUE, thus + // we do not call it with 0 as parameter to avoid warnings. + if (_program) { + GL_CALL(glDeleteProgram(_program)); + _program = 0; + } +} + +bool Shader::recreate() { + // Make sure any old programs are destroyed properly. + destroy(); + + GLshader vertexShader = compileShader(_vertex.c_str(), GL_VERTEX_SHADER); + if (!vertexShader) { + return false; + } + + GLshader fragmentShader = compileShader(_fragment.c_str(), GL_FRAGMENT_SHADER); + if (!fragmentShader) { + GL_CALL(glDeleteShader(vertexShader)); + return false; + } + + GL_ASSIGN(_program, glCreateProgram()); + if (!_program) { + GL_CALL(glDeleteShader(vertexShader)); + GL_CALL(glDeleteShader(fragmentShader)); + return false; + } + + GL_CALL(glAttachShader(_program, vertexShader)); + GL_CALL(glAttachShader(_program, fragmentShader)); + + GL_CALL(glLinkProgram(_program)); + + GL_CALL(glDetachShader(_program, fragmentShader)); + GL_CALL(glDeleteShader(fragmentShader)); + + GL_CALL(glDetachShader(_program, vertexShader)); + GL_CALL(glDeleteShader(vertexShader)); + + GLint result; + GL_CALL(glGetProgramiv(_program, GL_LINK_STATUS, &result)); + if (result == GL_FALSE) { + GLint logSize; + GL_CALL(glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &logSize)); + + GLchar *log = new GLchar[logSize]; + GL_CALL(glGetProgramInfoLog(_program, logSize, nullptr, log)); + warning("Could not link shader: \"%s\"", log); + delete[] log; + + destroy(); + return false; + } + + // Set program object in case shader is active during recreation. + if (_isActive) { + GL_CALL(glUseProgram(_program)); + } + + for (UniformMap::iterator i = _uniforms.begin(), end = _uniforms.end(); i != end; ++i) { + i->_value.location = getUniformLocation(i->_key.c_str()); + i->_value.altered = true; + if (_isActive) { + i->_value.set(); + } + } + + return true; +} + +void Shader::activate() { + // Activate program. + GL_CALL(glUseProgram(_program)); + + // Reset changed uniform values. + for (UniformMap::iterator i = _uniforms.begin(), end = _uniforms.end(); i != end; ++i) { + i->_value.set(); + } + + _isActive = true; +} + +void Shader::deactivate() { + _isActive = false; +} + +GLint Shader::getAttributeLocation(const char *name) const { + GLint result = -1; + GL_ASSIGN(result, glGetAttribLocation(_program, name)); + return result; +} + +GLint Shader::getUniformLocation(const char *name) const { + GLint result = -1; + GL_ASSIGN(result, glGetUniformLocation(_program, name)); + return result; +} + +bool Shader::setUniform(const Common::String &name, ShaderUniformValue *value) { + UniformMap::iterator uniformIter = _uniforms.find(name); + Uniform *uniform; + + if (uniformIter == _uniforms.end()) { + const GLint location = getUniformLocation(name.c_str()); + if (location == -1) { + delete value; + return false; + } + + uniform = &_uniforms[name]; + uniform->location = location; + } else { + uniform = &uniformIter->_value; + } + + uniform->value = Common::SharedPtr<ShaderUniformValue>(value); + uniform->altered = true; + if (_isActive) { + uniform->set(); + } + + return true; +} + +GLshader Shader::compileShader(const char *source, GLenum shaderType) { + GLshader handle; + GL_ASSIGN(handle, glCreateShader(shaderType)); + if (!handle) { + return 0; + } + + const char *const sources[2] = { + g_precisionDefines, + source + }; + + GL_CALL(glShaderSource(handle, ARRAYSIZE(sources), sources, nullptr)); + GL_CALL(glCompileShader(handle)); + + GLint result; + GL_CALL(glGetShaderiv(handle, GL_COMPILE_STATUS, &result)); + if (result == GL_FALSE) { + GLint logSize; + GL_CALL(glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &logSize)); + + GLchar *log = new GLchar[logSize]; + GL_CALL(glGetShaderInfoLog(handle, logSize, nullptr, log)); + warning("Could not compile shader \"%s\": \"%s\"", source, log); + delete[] log; + + GL_CALL(glDeleteShader(handle)); + return 0; + } + + return handle; +} + +ShaderManager::ShaderManager() : _initializeShaders(true) { +} + +ShaderManager::~ShaderManager() { + for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) { + delete _builtIn[i]; + } +} + +void ShaderManager::notifyDestroy() { + for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) { + _builtIn[i]->destroy(); + } +} + +void ShaderManager::notifyCreate() { + if (_initializeShaders) { + _initializeShaders = false; + + _builtIn[kDefault] = new Shader(g_defaultVertexShader, g_defaultFragmentShader); + _builtIn[kCLUT8LookUp] = new Shader(g_defaultVertexShader, g_lookUpFragmentShader); + _builtIn[kCLUT8LookUp]->setUniform1I("palette", 1); + + for (uint i = 0; i < kMaxUsages; ++i) { + _builtIn[i]->setUniform1I("texture", 0); + } + } else { + for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) { + _builtIn[i]->recreate(); + } + } +} + +Shader *ShaderManager::query(ShaderUsage shader) const { + if (shader == kMaxUsages) { + warning("OpenGL: ShaderManager::query used with kMaxUsages"); + return nullptr; + } + + return _builtIn[shader]; +} + +} // End of namespace OpenGL + +#endif // !USE_FORCED_GLES diff --git a/backends/graphics/opengl/shader.h b/backends/graphics/opengl/shader.h new file mode 100644 index 0000000000..ec1e516d14 --- /dev/null +++ b/backends/graphics/opengl/shader.h @@ -0,0 +1,288 @@ +/* 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 BACKENDS_GRAPHICS_OPENGL_SHADER_H +#define BACKENDS_GRAPHICS_OPENGL_SHADER_H + +#include "backends/graphics/opengl/opengl-sys.h" + +#if !USE_FORCED_GLES + +#include "common/singleton.h" +#include "common/hash-str.h" +#include "common/ptr.h" + +namespace OpenGL { + +/** + * A generic uniform value interface for a shader program. + */ +class ShaderUniformValue { +public: + virtual ~ShaderUniformValue() {} + + /** + * Setup the the value to the given location. + * + * @param location Location of the uniform. + */ + virtual void set(GLint location) const = 0; +}; + +/** + * Integer value for a shader uniform. + */ +class ShaderUniformInteger : public ShaderUniformValue { +public: + ShaderUniformInteger(GLint value) : _value(value) {} + + virtual void set(GLint location) const override; + +private: + const GLint _value; +}; + +/** + * Float value for a shader uniform. + */ +class ShaderUniformFloat : public ShaderUniformValue { +public: + ShaderUniformFloat(GLfloat value) : _value(value) {} + + virtual void set(GLint location) const override; + +private: + const GLfloat _value; +}; + +/** + * 4x4 Matrix value for a shader uniform. + */ +class ShaderUniformMatrix44 : public ShaderUniformValue { +public: + ShaderUniformMatrix44(const GLfloat *mat44) { + memcpy(_matrix, mat44, sizeof(_matrix)); + } + + virtual void set(GLint location) const override; + +private: + GLfloat _matrix[4*4]; +}; + +class Shader { +public: + Shader(const Common::String &vertex, const Common::String &fragment); + ~Shader(); + + /** + * Destroy the shader program. + * + * This keeps the vertex and fragment shader sources around and thus + * allows for recreating the shader on context recreation. It also keeps + * the uniform state around. + */ + void destroy(); + + /** + * Recreate shader program. + * + * @return true on success, false on failure. + */ + bool recreate(); + + /** + * Make shader active. + */ + void activate(); + + /** + * Make shader inactive. + */ + void deactivate(); + + /** + * Return location for attribute with given name. + * + * @param name Name of the attribute to look up in the shader. + * @return The loctaion of -1 if attribute was not found. + */ + GLint getAttributeLocation(const char *name) const; + GLint getAttributeLocation(const Common::String &name) const { + return getAttributeLocation(name.c_str()); + } + + /** + * Return location for uniform with given name. + * + * @param name Name of the uniform to look up in the shader. + * @return The location or -1 if uniform was not found. + */ + GLint getUniformLocation(const char *name) const; + GLint getUniformLocation(const Common::String &name) const { + return getUniformLocation(name.c_str()); + } + + /** + * Bind value to uniform. + * + * @param name The name of the uniform to be set. + * @param value The value to be set. + * @return 'false' on error (i.e. uniform unknown or otherwise), + * 'true' otherwise. + */ + bool setUniform(const Common::String &name, ShaderUniformValue *value); + + /** + * Bind integer value to uniform. + * + * @param name The name of the uniform to be set. + * @param value The value to be set. + * @return 'false' on error (i.e. uniform unknown or otherwise), + * 'true' otherwise. + */ + bool setUniform1I(const Common::String &name, GLint value) { + return setUniform(name, new ShaderUniformInteger(value)); + } +protected: + /** + * Vertex shader sources. + */ + const Common::String _vertex; + + /** + * Fragment shader sources. + */ + const Common::String _fragment; + + /** + * Whether the shader is active or not. + */ + bool _isActive; + + /** + * Shader program handle. + */ + GLprogram _program; + + /** + * A uniform descriptor. + * + * This stores the state of a shader uniform. The state is made up of the + * uniform location, whether the state was altered since last set, and the + * value of the uniform. + */ + struct Uniform { + Uniform() : location(-1), altered(false), value() {} + Uniform(GLint loc, ShaderUniformValue *val) + : location(loc), altered(true), value(val) {} + + /** + * Write uniform value into currently active shader. + */ + void set() { + if (altered && value) { + value->set(location); + altered = false; + } + } + + /** + * The location of the uniform or -1 in case it does not exist. + */ + GLint location; + + /** + * Whether the uniform state was aletered since last 'set'. + */ + bool altered; + + /** + * The value of the uniform. + */ + Common::SharedPtr<ShaderUniformValue> value; + }; + + typedef Common::HashMap<Common::String, Uniform> UniformMap; + + /** + * Map from uniform name to associated uniform description. + */ + UniformMap _uniforms; + + /** + * Compile a vertex or fragment shader. + * + * @param source Sources to the shader. + * @param shaderType Type of shader to compile (GL_FRAGMENT_SHADER_ARB or + * GL_VERTEX_SHADER_ARB) + * @return The shader object or 0 on failure. + */ + static GLshader compileShader(const char *source, GLenum shaderType); +}; + +class ShaderManager : public Common::Singleton<ShaderManager> { +public: + enum ShaderUsage { + /** Default shader implementing the GL fixed-function pipeline. */ + kDefault = 0, + + /** CLUT8 look up shader. */ + kCLUT8LookUp, + + /** Number of built-in shaders. Should not be used for query. */ + kMaxUsages + }; + + /** + * Notify shader manager about context destruction. + */ + void notifyDestroy(); + + /** + * Notify shader manager about context creation. + */ + void notifyCreate(); + + /** + * Query a built-in shader. + */ + Shader *query(ShaderUsage shader) const; + +private: + friend class Common::Singleton<SingletonBaseType>; + ShaderManager(); + ~ShaderManager(); + + bool _initializeShaders; + + Shader *_builtIn[kMaxUsages]; +}; + +} // End of namespace OpenGL + +/** Shortcut for accessing the font manager. */ +#define ShaderMan (OpenGL::ShaderManager::instance()) + +#endif // !USE_FORCED_GLES + +#endif diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp index 7b0b22d630..8b94549971 100644 --- a/backends/graphics/opengl/texture.cpp +++ b/backends/graphics/opengl/texture.cpp @@ -21,8 +21,10 @@ */ #include "backends/graphics/opengl/texture.h" -#include "backends/graphics/opengl/extensions.h" -#include "backends/graphics/opengl/debug.h" +#include "backends/graphics/opengl/shader.h" +#include "backends/graphics/opengl/pipelines/pipeline.h" +#include "backends/graphics/opengl/pipelines/clut8.h" +#include "backends/graphics/opengl/framebuffer.h" #include "common/rect.h" #include "common/textconsole.h" @@ -41,94 +43,140 @@ static GLuint nextHigher2(GLuint v) { return ++v; } -GLint Texture::_maxTextureSize = 0; -void Texture::queryTextureInformation() { - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize); - debug(5, "OpenGL maximum texture size: %d", _maxTextureSize); +GLTexture::GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType) + : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), + _width(0), _height(0), _logicalWidth(0), _logicalHeight(0), + _texCoords(), _glFilter(GL_NEAREST), + _glTexture(0) { + create(); } -Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format) - : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), _format(format), _glFilter(GL_NEAREST), - _glTexture(0), _textureData(), _userPixelData(), _allDirty(false) { - recreateInternalTexture(); +GLTexture::~GLTexture() { + GL_CALL_SAFE(glDeleteTextures, (1, &_glTexture)); } -Texture::~Texture() { - releaseInternalTexture(); - _textureData.free(); +void GLTexture::enableLinearFiltering(bool enable) { + if (enable) { + _glFilter = GL_LINEAR; + } else { + _glFilter = GL_NEAREST; + } + + bind(); + + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); } -void Texture::releaseInternalTexture() { - GLCALL(glDeleteTextures(1, &_glTexture)); +void GLTexture::destroy() { + GL_CALL(glDeleteTextures(1, &_glTexture)); _glTexture = 0; } -void Texture::recreateInternalTexture() { +void GLTexture::create() { // Release old texture name in case it exists. - releaseInternalTexture(); + destroy(); // Get a new texture name. - GLCALL(glGenTextures(1, &_glTexture)); + GL_CALL(glGenTextures(1, &_glTexture)); // Set up all texture parameters. - GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - // In case there is an actual texture setup we reinitialize it. - if (_textureData.getPixels()) { + bind(); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + + // If a size is specified, allocate memory for it. + if (_width != 0 && _height != 0) { // Allocate storage for OpenGL texture. - GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w, - _textureData.h, 0, _glFormat, _glType, NULL)); - - // Mark dirts such that it will be completely refreshed the next time. - flagDirty(); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, _height, + 0, _glFormat, _glType, NULL)); } } -void Texture::enableLinearFiltering(bool enable) { - if (enable) { - _glFilter = GL_LINEAR; +void GLTexture::bind() const { + GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); +} + +void GLTexture::setSize(uint width, uint height) { + const uint oldWidth = _width; + const uint oldHeight = _height; + + if (!g_context.NPOTSupported) { + _width = nextHigher2(width); + _height = nextHigher2(height); } else { - _glFilter = GL_NEAREST; + _width = width; + _height = height; } - GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); + _logicalWidth = width; + _logicalHeight = height; - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); - GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); -} + // If a size is specified, allocate memory for it. + if (width != 0 && height != 0) { + const GLfloat texWidth = (GLfloat)width / _width; + const GLfloat texHeight = (GLfloat)height / _height; -void Texture::allocate(uint width, uint height) { - uint texWidth = width, texHeight = height; - if (!g_extNPOTSupported) { - texWidth = nextHigher2(texWidth); - texHeight = nextHigher2(texHeight); - } + _texCoords[0] = 0; + _texCoords[1] = 0; - // In case the needed texture dimension changed we will reinitialize the - // texture. - if (texWidth != _textureData.w || texHeight != _textureData.h) { - // Create a buffer for the texture data. - _textureData.create(texWidth, texHeight, _format); + _texCoords[2] = texWidth; + _texCoords[3] = 0; - // Set the texture. - GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); + _texCoords[4] = 0; + _texCoords[5] = texHeight; - // Allocate storage for OpenGL texture. - GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w, - _textureData.h, 0, _glFormat, _glType, NULL)); + _texCoords[6] = texWidth; + _texCoords[7] = texHeight; + + // Allocate storage for OpenGL texture if necessary. + if (oldWidth != _width || oldHeight != _height) { + bind(); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, + _height, 0, _glFormat, _glType, NULL)); + } } +} - // Create a sub-buffer for raw access. - _userPixelData = _textureData.getSubArea(Common::Rect(width, height)); +void GLTexture::updateArea(const Common::Rect &area, const Graphics::Surface &src) { + // Set the texture on the active texture unit. + bind(); + + // Update the actual texture. + // Although we have the area of the texture buffer we want to update we + // cannot take advantage of the left/right boundries here because it is + // not possible to specify a pitch to glTexSubImage2D. To be precise, with + // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However, + // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left + // with the following options: + // + // 1) (As we do right now) Simply always update the whole texture lines of + // rect changed. This is simplest to implement. In case performance is + // really an issue we can think of switching to another method. + // + // 2) Copy the dirty rect to a temporary buffer and upload that by using + // glTexSubImage2D. This is what the Android backend does. It is more + // complicated though. + // + // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL + // graphics manager did but it is much slower! Thus, we do not use it. + GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, area.top, src.w, area.height(), + _glFormat, _glType, src.getBasePtr(0, area.top))); +} + +// +// Surface +// + +Surface::Surface() + : _allDirty(false), _dirtyArea() { } -void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) { +void Surface::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) { Graphics::Surface *dstSurf = getSurface(); assert(x + w <= dstSurf->w); assert(y + h <= dstSurf->h); @@ -159,50 +207,74 @@ void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcP } } -void Texture::fill(uint32 color) { +void Surface::fill(uint32 color) { Graphics::Surface *dst = getSurface(); dst->fillRect(Common::Rect(dst->w, dst->h), color); flagDirty(); } -void Texture::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) { - // Only do any processing when the Texture is initialized. - if (!_textureData.getPixels()) { - return; +Common::Rect Surface::getDirtyArea() const { + if (_allDirty) { + return Common::Rect(getWidth(), getHeight()); + } else { + return _dirtyArea; + } +} + +// +// Surface implementations +// + +Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format) + : Surface(), _format(format), _glTexture(glIntFormat, glFormat, glType), + _textureData(), _userPixelData() { +} + +Texture::~Texture() { + _textureData.free(); +} + +void Texture::destroy() { + _glTexture.destroy(); +} + +void Texture::recreate() { + _glTexture.create(); + + // In case image date exists assure it will be completely refreshed next + // time. + if (_textureData.getPixels()) { + flagDirty(); + } +} + +void Texture::enableLinearFiltering(bool enable) { + _glTexture.enableLinearFiltering(enable); +} + +void Texture::allocate(uint width, uint height) { + // Assure the texture can contain our user data. + _glTexture.setSize(width, height); + + // In case the needed texture dimension changed we will reinitialize the + // texture data buffer. + if (_glTexture.getWidth() != _textureData.w || _glTexture.getHeight() != _textureData.h) { + // Create a buffer for the texture data. + _textureData.create(_glTexture.getWidth(), _glTexture.getHeight(), _format); } - // First update any potentional changes. - updateTexture(); - - // Set the texture. - GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); - - // Calculate the texture rect that will be drawn. - const GLfloat texWidth = (GLfloat)_userPixelData.w / _textureData.w; - const GLfloat texHeight = (GLfloat)_userPixelData.h / _textureData.h; - const GLfloat texcoords[4*2] = { - 0, 0, - texWidth, 0, - 0, texHeight, - texWidth, texHeight - }; - GLCALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords)); - - // Calculate the screen rect where the texture will be drawn. - const GLfloat vertices[4*2] = { - x, y, - x + w, y, - x, y + h, - x + w, y + h - }; - GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices)); - - // Draw the texture to the screen buffer. - GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); -} - -void Texture::updateTexture() { + // Create a sub-buffer for raw access. + _userPixelData = _textureData.getSubArea(Common::Rect(width, height)); + + // The whole texture is dirty after we changed the size. This fixes + // multiple texture size changes without any actual update in between. + // Without this we might try to write a too big texture into the GL + // texture. + flagDirty(); +} + +void Texture::updateGLTexture() { if (!isDirty()) { return; } @@ -211,7 +283,7 @@ void Texture::updateTexture() { // In case we use linear filtering we might need to duplicate the last // pixel row/column to avoid glitches with filtering. - if (_glFilter == GL_LINEAR) { + if (_glTexture.isLinearFilteringEnabled()) { if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) { uint height = dirtyArea.height(); @@ -238,42 +310,12 @@ void Texture::updateTexture() { } } - // Set the texture. - GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); - - // Update the actual texture. - // Although we keep track of the dirty part of the texture buffer we - // cannot take advantage of the left/right boundries here because it is - // not possible to specify a pitch to glTexSubImage2D. To be precise, with - // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However, - // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left - // with the following options: - // - // 1) (As we do right now) Simply always update the whole texture lines of - // rect changed. This is simplest to implement. In case performance is - // really an issue we can think of switching to another method. - // - // 2) Copy the dirty rect to a temporary buffer and upload that by using - // glTexSubImage2D. This is what the Android backend does. It is more - // complicated though. - // - // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL - // graphics manager did but it is much slower! Thus, we do not use it. - GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(), - _glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top))); + _glTexture.updateArea(dirtyArea, _textureData); // We should have handled everything, thus not dirty anymore. clearDirty(); } -Common::Rect Texture::getDirtyArea() const { - if (_allDirty) { - return Common::Rect(_userPixelData.w, _userPixelData.h); - } else { - return _dirtyArea; - } -} - TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format) : Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) { memset(_palette, 0, sizeof(byte) * format.bytesPerPixel); @@ -301,6 +343,25 @@ Graphics::PixelFormat TextureCLUT8::getFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } +void TextureCLUT8::setColorKey(uint colorKey) { + // We remove all alpha bits from the palette entry of the color key. + // This makes sure its properly handled as color key. + const uint32 aMask = (0xFF >> _format.aLoss) << _format.aShift; + + if (_format.bytesPerPixel == 2) { + uint16 *palette = (uint16 *)_palette + colorKey; + *palette &= ~aMask; + } else if (_format.bytesPerPixel == 4) { + uint32 *palette = (uint32 *)_palette + colorKey; + *palette &= ~aMask; + } else { + warning("TextureCLUT8::setColorKey: Unsupported pixel depth %d", _format.bytesPerPixel); + } + + // A palette changes means we need to refresh the whole surface. + flagDirty(); +} + namespace { template<typename ColorType> inline void convertPalette(ColorType *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) { @@ -312,14 +373,12 @@ inline void convertPalette(ColorType *dst, const byte *src, uint colors, const G } // End of anonymous namespace void TextureCLUT8::setPalette(uint start, uint colors, const byte *palData) { - const Graphics::PixelFormat &hardwareFormat = getHardwareFormat(); - - if (hardwareFormat.bytesPerPixel == 2) { - convertPalette<uint16>((uint16 *)_palette + start, palData, colors, hardwareFormat); - } else if (hardwareFormat.bytesPerPixel == 4) { - convertPalette<uint32>((uint32 *)_palette + start, palData, colors, hardwareFormat); + if (_format.bytesPerPixel == 2) { + convertPalette<uint16>((uint16 *)_palette + start, palData, colors, _format); + } else if (_format.bytesPerPixel == 4) { + convertPalette<uint32>((uint32 *)_palette + start, palData, colors, _format); } else { - warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", hardwareFormat.bytesPerPixel); + warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", _format.bytesPerPixel); } // A palette changes means we need to refresh the whole surface. @@ -343,7 +402,7 @@ inline void doPaletteLookUp(PixelType *dst, const byte *src, uint width, uint he } } // End of anonymous namespace -void TextureCLUT8::updateTexture() { +void TextureCLUT8::updateGLTexture() { if (!isDirty()) { return; } @@ -368,7 +427,222 @@ void TextureCLUT8::updateTexture() { } // Do generic handling of updating the texture. - Texture::updateTexture(); + Texture::updateGLTexture(); +} + +#if !USE_FORCED_GL +TextureRGB555::TextureRGB555() + : Texture(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)), + _rgb555Data() { +} + +TextureRGB555::~TextureRGB555() { + _rgb555Data.free(); +} + +void TextureRGB555::allocate(uint width, uint height) { + Texture::allocate(width, height); + + // We only need to reinitialize our RGB555 surface when the output size + // changed. + if (width == _rgb555Data.w && height == _rgb555Data.h) { + return; + } + + _rgb555Data.create(width, height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); +} + +Graphics::PixelFormat TextureRGB555::getFormat() const { + return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); +} + +void TextureRGB555::updateTexture() { + if (!isDirty()) { + return; + } + + // Convert color space. + Graphics::Surface *outSurf = Texture::getSurface(); + + const Common::Rect dirtyArea = getDirtyArea(); + + uint16 *dst = (uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top); + const uint dstAdd = outSurf->pitch - 2 * dirtyArea.width(); + + const uint16 *src = (const uint16 *)_rgb555Data.getBasePtr(dirtyArea.left, dirtyArea.top); + const uint srcAdd = _rgb555Data.pitch - 2 * dirtyArea.width(); + + for (int height = dirtyArea.height(); height > 0; --height) { + for (int width = dirtyArea.width(); width > 0; --width) { + const uint16 color = *src++; + + *dst++ = ((color & 0x7C00) << 1) // R + | (((color & 0x03E0) << 1) | ((color & 0x0200) >> 4)) // G + | (color & 0x001F); // B + } + + src = (const uint16 *)((const byte *)src + srcAdd); + dst = (uint16 *)((byte *)dst + dstAdd); + } + + // Do generic handling of updating the texture. + Texture::updateGLTexture(); +} +#endif // !USE_FORCED_GL + +#if !USE_FORCED_GLES + +// _clut8Texture needs 8 bits internal precision, otherwise graphics glitches +// can occur. GL_ALPHA does not have any internal precision requirements. +// However, in practice (according to fuzzie) it's 8bit. If we run into +// problems, we need to switch to GL_R8 and GL_RED, but that is only supported +// for ARB_texture_rg and GLES3+ (EXT_rexture_rg does not support GL_R8). +TextureCLUT8GPU::TextureCLUT8GPU() + : _clut8Texture(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE), + _paletteTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE), + _target(new TextureTarget()), _clut8Pipeline(new CLUT8LookUpPipeline()), + _clut8Vertices(), _clut8Data(), _userPixelData(), _palette(), + _paletteDirty(false) { + // Allocate space for 256 colors. + _paletteTexture.setSize(256, 1); + + // Setup pipeline. + _clut8Pipeline->setFramebuffer(_target); + _clut8Pipeline->setPaletteTexture(&_paletteTexture); +} + +TextureCLUT8GPU::~TextureCLUT8GPU() { + delete _clut8Pipeline; + delete _target; + _clut8Data.free(); +} + +void TextureCLUT8GPU::destroy() { + _clut8Texture.destroy(); + _paletteTexture.destroy(); + _target->destroy(); +} + +void TextureCLUT8GPU::recreate() { + _clut8Texture.create(); + _paletteTexture.create(); + _target->create(); + + // In case image date exists assure it will be completely refreshed next + // time. + if (_clut8Data.getPixels()) { + flagDirty(); + _paletteDirty = true; + } +} + +void TextureCLUT8GPU::enableLinearFiltering(bool enable) { + _target->getTexture()->enableLinearFiltering(enable); +} + +void TextureCLUT8GPU::allocate(uint width, uint height) { + // Assure the texture can contain our user data. + _clut8Texture.setSize(width, height); + _target->setSize(width, height); + + // In case the needed texture dimension changed we will reinitialize the + // texture data buffer. + if (_clut8Texture.getWidth() != _clut8Data.w || _clut8Texture.getHeight() != _clut8Data.h) { + // Create a buffer for the texture data. + _clut8Data.create(_clut8Texture.getWidth(), _clut8Texture.getHeight(), Graphics::PixelFormat::createFormatCLUT8()); + } + + // Create a sub-buffer for raw access. + _userPixelData = _clut8Data.getSubArea(Common::Rect(width, height)); + + // Setup structures for internal rendering to _glTexture. + _clut8Vertices[0] = 0; + _clut8Vertices[1] = 0; + + _clut8Vertices[2] = width; + _clut8Vertices[3] = 0; + + _clut8Vertices[4] = 0; + _clut8Vertices[5] = height; + + _clut8Vertices[6] = width; + _clut8Vertices[7] = height; + + // The whole texture is dirty after we changed the size. This fixes + // multiple texture size changes without any actual update in between. + // Without this we might try to write a too big texture into the GL + // texture. + flagDirty(); +} + +Graphics::PixelFormat TextureCLUT8GPU::getFormat() const { + return Graphics::PixelFormat::createFormatCLUT8(); +} + +void TextureCLUT8GPU::setColorKey(uint colorKey) { + _palette[colorKey * 4 + 3] = 0x00; + + _paletteDirty = true; +} + +void TextureCLUT8GPU::setPalette(uint start, uint colors, const byte *palData) { + byte *dst = _palette + start * 4; + + while (colors-- > 0) { + memcpy(dst, palData, 3); + dst[3] = 0xFF; + + dst += 4; + palData += 3; + } + + _paletteDirty = true; +} + +const GLTexture &TextureCLUT8GPU::getGLTexture() const { + return *_target->getTexture(); +} + +void TextureCLUT8GPU::updateGLTexture() { + const bool needLookUp = Surface::isDirty() || _paletteDirty; + + // Update CLUT8 texture if necessary. + if (Surface::isDirty()) { + _clut8Texture.updateArea(getDirtyArea(), _clut8Data); + clearDirty(); + } + + // Update palette if necessary. + if (_paletteDirty) { + Graphics::Surface palSurface; + palSurface.init(256, 1, 256, _palette, +#ifdef SCUMM_LITTLE_ENDIAN + Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24) // ABGR8888 +#else + Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0) // RGBA8888 +#endif + ); + + _paletteTexture.updateArea(Common::Rect(256, 1), palSurface); + _paletteDirty = false; + } + + // In case any data changed, do color look up and store result in _target. + if (needLookUp) { + lookUpColors(); + } +} + +void TextureCLUT8GPU::lookUpColors() { + // Setup pipeline to do color look up. + Pipeline *oldPipeline = g_context.setPipeline(_clut8Pipeline); + + // Do color look up. + g_context.getActivePipeline()->drawTexture(_clut8Texture, _clut8Vertices); + + // Restore old state. + g_context.setPipeline(oldPipeline); } +#endif // !USE_FORCED_GLES } // End of namespace OpenGL diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h index ad70833544..3be09cb9f9 100644 --- a/backends/graphics/opengl/texture.h +++ b/backends/graphics/opengl/texture.h @@ -32,115 +32,267 @@ namespace OpenGL { +class Shader; + /** - * An OpenGL texture wrapper. It automatically takes care of all OpenGL - * texture handling issues and also provides access to the texture data. + * A simple GL texture object abstraction. + * + * This is used for low-level GL texture handling. */ -class Texture { +class GLTexture { public: /** - * Create a new texture with the specific internal format. + * Constrcut a new GL texture object. * * @param glIntFormat The internal format to use. * @param glFormat The input format. * @param glType The input type. - * @param format The format used for the texture input. */ - Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format); - virtual ~Texture(); + GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType); + ~GLTexture(); + + /** + * Enable or disable linear texture filtering. + * + * @param enable true to enable and false to disable. + */ + void enableLinearFiltering(bool enable); + + /** + * Test whether linear filtering is enabled. + */ + bool isLinearFilteringEnabled() const { return (_glFilter == GL_LINEAR); } /** * Destroy the OpenGL texture name. */ - void releaseInternalTexture(); + void destroy(); + + /** + * Create the OpenGL texture name. + */ + void create(); + + /** + * Bind the texture to the active texture unit. + */ + void bind() const; + + /** + * Sets the size of the texture in pixels. + * + * The internal OpenGL texture might have a different size. To query the + * actual size use getWidth()/getHeight(). + * + * @param width The desired logical width. + * @param height The desired logical height. + */ + void setSize(uint width, uint height); + + /** + * Copy image data to the texture. + * + * @param area The area to update. + * @param src Surface for the whole texture containing the pixel data + * to upload. Only the area described by area will be + * uploaded. + */ + void updateArea(const Common::Rect &area, const Graphics::Surface &src); /** - * Create the OpenGL texture name and flag the whole texture as dirty. + * Query the GL texture's width. */ - void recreateInternalTexture(); + uint getWidth() const { return _width; } + + /** + * Query the GL texture's height. + */ + uint getHeight() const { return _height; } + + /** + * Query the logical texture's width. + */ + uint getLogicalWidth() const { return _logicalWidth; } + + /** + * Query the logical texture's height. + */ + uint getLogicalHeight() const { return _logicalHeight; } + + /** + * Obtain texture coordinates for rectangular drawing. + */ + const GLfloat *getTexCoords() const { return _texCoords; } + + /** + * Obtain texture name. + * + * Beware that the texture name changes whenever create is used. + * destroy will invalidate the texture name. + */ + GLuint getGLTexture() const { return _glTexture; } +private: + const GLenum _glIntFormat; + const GLenum _glFormat; + const GLenum _glType; + + uint _width, _height; + uint _logicalWidth, _logicalHeight; + GLfloat _texCoords[4*2]; + + GLint _glFilter; + + GLuint _glTexture; +}; + +/** + * Interface for OpenGL implementations of a 2D surface. + */ +class Surface { +public: + Surface(); + virtual ~Surface() {} + + /** + * Destroy OpenGL description of surface. + */ + virtual void destroy() = 0; + + /** + * Recreate OpenGL description of surface. + */ + virtual void recreate() = 0; /** * Enable or disable linear texture filtering. * * @param enable true to enable and false to disable. */ - void enableLinearFiltering(bool enable); + virtual void enableLinearFiltering(bool enable) = 0; /** - * Allocate texture space for the desired dimensions. This wraps any - * handling of requirements for POT textures. + * Allocate storage for surface. * * @param width The desired logical width. * @param height The desired logical height. */ - virtual void allocate(uint width, uint height); + virtual void allocate(uint width, uint height) = 0; + /** + * Copy image data to the surface. + * + * The format of the input data needs to match the format returned by + * getFormat. + * + * @param x X coordinate of upper left corner to copy data to. + * @param y Y coordinate of upper left corner to copy data to. + * @param w Width of the image data to copy. + * @param h Height of the image data to copy. + * @param src Pointer to image data. + * @param srcPitch The number of bytes in a row of the image data. + */ void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch); + /** + * Fill the surface with a fixed color. + * + * @param color Color value in format returned by getFormat. + */ void fill(uint32 color); - void draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h); - void flagDirty() { _allDirty = true; } - bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); } - - uint getWidth() const { return _userPixelData.w; } - uint getHeight() const { return _userPixelData.h; } + virtual bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); } - /** - * @return The hardware format of the texture data. - */ - const Graphics::PixelFormat &getHardwareFormat() const { return _format; } + virtual uint getWidth() const = 0; + virtual uint getHeight() const = 0; /** * @return The logical format of the texture data. */ - virtual Graphics::PixelFormat getFormat() const { return _format; } + virtual Graphics::PixelFormat getFormat() const = 0; - virtual Graphics::Surface *getSurface() { return &_userPixelData; } - virtual const Graphics::Surface *getSurface() const { return &_userPixelData; } + virtual Graphics::Surface *getSurface() = 0; + virtual const Graphics::Surface *getSurface() const = 0; /** - * @return Whether the texture data is using a palette. + * @return Whether the surface is having a palette. */ virtual bool hasPalette() const { return false; } + /** + * Set color key for paletted textures. + * + * This needs to be called after any palette update affecting the color + * key. Calling this multiple times will result in multiple color indices + * to be treated as color keys. + */ + virtual void setColorKey(uint colorKey) {} virtual void setPalette(uint start, uint colors, const byte *palData) {} - virtual void *getPalette() { return 0; } - virtual const void *getPalette() const { return 0; } - /** - * Query texture related OpenGL information from the context. This only - * queries the maximum texture size for now. + * Update underlying OpenGL texture to reflect current state. */ - static void queryTextureInformation(); + virtual void updateGLTexture() = 0; /** - * @return Return the maximum texture dimensions supported. + * Obtain underlying OpenGL texture. */ - static GLint getMaximumTextureSize() { return _maxTextureSize; } + virtual const GLTexture &getGLTexture() const = 0; protected: - virtual void updateTexture(); + void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); } Common::Rect getDirtyArea() const; private: - const GLenum _glIntFormat; - const GLenum _glFormat; - const GLenum _glType; + bool _allDirty; + Common::Rect _dirtyArea; +}; + +/** + * An OpenGL texture wrapper. It automatically takes care of all OpenGL + * texture handling issues and also provides access to the texture data. + */ +class Texture : public Surface { +public: + /** + * Create a new texture with the specific internal format. + * + * @param glIntFormat The internal format to use. + * @param glFormat The input format. + * @param glType The input type. + * @param format The format used for the texture input. + */ + Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format); + virtual ~Texture(); + + virtual void destroy(); + + virtual void recreate(); + + virtual void enableLinearFiltering(bool enable); + + virtual void allocate(uint width, uint height); + + virtual uint getWidth() const { return _userPixelData.w; } + virtual uint getHeight() const { return _userPixelData.h; } + + /** + * @return The logical format of the texture data. + */ + virtual Graphics::PixelFormat getFormat() const { return _format; } + + virtual Graphics::Surface *getSurface() { return &_userPixelData; } + virtual const Graphics::Surface *getSurface() const { return &_userPixelData; } + + virtual void updateGLTexture(); + virtual const GLTexture &getGLTexture() const { return _glTexture; } +protected: const Graphics::PixelFormat _format; - GLint _glFilter; - GLuint _glTexture; +private: + GLTexture _glTexture; Graphics::Surface _textureData; Graphics::Surface _userPixelData; - - bool _allDirty; - Common::Rect _dirtyArea; - void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); } - - static GLint _maxTextureSize; }; class TextureCLUT8 : public Texture { @@ -154,21 +306,95 @@ public: virtual bool hasPalette() const { return true; } + virtual void setColorKey(uint colorKey); virtual void setPalette(uint start, uint colors, const byte *palData); - virtual void *getPalette() { return _palette; } - virtual const void *getPalette() const { return _palette; } - virtual Graphics::Surface *getSurface() { return &_clut8Data; } virtual const Graphics::Surface *getSurface() const { return &_clut8Data; } -protected: + virtual void updateGLTexture(); +private: + Graphics::Surface _clut8Data; + byte *_palette; +}; + +#if !USE_FORCED_GL +class TextureRGB555 : public Texture { +public: + TextureRGB555(); + virtual ~TextureRGB555(); + + virtual void allocate(uint width, uint height); + + virtual Graphics::PixelFormat getFormat() const; + + virtual Graphics::Surface *getSurface() { return &_rgb555Data; } + virtual const Graphics::Surface *getSurface() const { return &_rgb555Data; } + virtual void updateTexture(); +private: + Graphics::Surface _rgb555Data; +}; +#endif // !USE_FORCED_GL + +#if !USE_FORCED_GLES +class TextureTarget; +class CLUT8LookUpPipeline; + +class TextureCLUT8GPU : public Surface { +public: + TextureCLUT8GPU(); + virtual ~TextureCLUT8GPU(); + + virtual void destroy(); + + virtual void recreate(); + virtual void enableLinearFiltering(bool enable); + + virtual void allocate(uint width, uint height); + + virtual bool isDirty() const { return _paletteDirty || Surface::isDirty(); } + + virtual uint getWidth() const { return _userPixelData.w; } + virtual uint getHeight() const { return _userPixelData.h; } + + virtual Graphics::PixelFormat getFormat() const; + + virtual bool hasPalette() const { return true; } + + virtual void setColorKey(uint colorKey); + virtual void setPalette(uint start, uint colors, const byte *palData); + + virtual Graphics::Surface *getSurface() { return &_userPixelData; } + virtual const Graphics::Surface *getSurface() const { return &_userPixelData; } + + virtual void updateGLTexture(); + virtual const GLTexture &getGLTexture() const; + + static bool isSupportedByContext() { + return g_context.shadersSupported + && g_context.multitextureSupported + && g_context.framebufferObjectSupported; + } private: + void lookUpColors(); + + GLTexture _clut8Texture; + GLTexture _paletteTexture; + + TextureTarget *_target; + CLUT8LookUpPipeline *_clut8Pipeline; + + GLfloat _clut8Vertices[4*2]; + Graphics::Surface _clut8Data; - byte *_palette; + Graphics::Surface _userPixelData; + + byte _palette[4 * 256]; + bool _paletteDirty; }; +#endif // !USE_FORCED_GLES } // End of namespace OpenGL diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index 0d140ee4d7..7ea1860d93 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -45,6 +45,100 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + // Setup proper SDL OpenGL context creation. +#if SDL_VERSION_ATLEAST(2, 0, 0) + OpenGL::ContextType glContextType; + + // Context version 1.4 is choosen arbitrarily based on what most shader + // extensions were written against. +#define DEFAULT_GL_MAJOR 1 +#define DEFAULT_GL_MINOR 4 + +#define DEFAULT_GLES_MAJOR 1 +#define DEFAULT_GLES_MINOR 1 + +#define DEFAULT_GLES2_MAJOR 2 +#define DEFAULT_GLES2_MINOR 0 + +#if USE_FORCED_GL + glContextType = OpenGL::kContextGL; + _glContextProfileMask = 0; + _glContextMajor = DEFAULT_GL_MAJOR; + _glContextMinor = DEFAULT_GL_MINOR; +#elif USE_FORCED_GLES + glContextType = OpenGL::kContextGLES; + _glContextProfileMask = SDL_GL_CONTEXT_PROFILE_ES; + _glContextMajor = DEFAULT_GLES_MAJOR; + _glContextMinor = DEFAULT_GLES_MINOR; +#elif USE_FORCED_GLES2 + glContextType = OpenGL::kContextGLES2; + _glContextProfileMask = SDL_GL_CONTEXT_PROFILE_ES; + _glContextMajor = DEFAULT_GLES2_MAJOR; + _glContextMinor = DEFAULT_GLES2_MINOR; +#else + bool noDefaults = false; + + // Obtain the default GL(ES) context SDL2 tries to setup. + // + // Please note this might not actually be SDL2's defaults when multiple + // instances of this object have been created. But that is no issue + // because then we already set up what we want to use. + // + // In case no defaults are given we prefer OpenGL over OpenGL ES. + if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask) != 0) { + _glContextProfileMask = 0; + noDefaults = true; + } + + if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor) != 0) { + noDefaults = true; + } + + if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor) != 0) { + noDefaults = true; + } + + if (noDefaults) { + if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) { + _glContextMajor = DEFAULT_GLES_MAJOR; + _glContextMinor = DEFAULT_GLES_MINOR; + } else { + _glContextProfileMask = 0; + _glContextMajor = DEFAULT_GL_MAJOR; + _glContextMinor = DEFAULT_GL_MINOR; + } + } + + if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) { + if (_glContextMajor >= 2) { + glContextType = OpenGL::kContextGLES2; + } else { + glContextType = OpenGL::kContextGLES; + } + } else if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_CORE) { + glContextType = OpenGL::kContextGL; + + // Core profile does not allow legacy functionality, which we use. + // Thus we request a standard OpenGL context. + _glContextProfileMask = 0; + _glContextMajor = DEFAULT_GL_MAJOR; + _glContextMinor = DEFAULT_GL_MINOR; + } else { + glContextType = OpenGL::kContextGL; + } +#undef DEFAULT_GL_MAJOR +#undef DEFAULT_GL_MINOR +#undef DEFAULT_GLES_MAJOR +#undef DEFAULT_GLES_MINOR +#undef DEFAULT_GLES2_MAJOR +#undef DEFAULT_GLES2_MINOR +#endif + + setContextType(glContextType); +#else + setContextType(OpenGL::kContextGL); +#endif + // Retrieve a list of working fullscreen modes #if SDL_VERSION_ATLEAST(2, 0, 0) const int numModes = SDL_GetNumDisplayModes(0); @@ -100,6 +194,10 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt } OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + notifyContextDestroy(); + SDL_GL_DeleteContext(_glContext); +#endif } void OpenGLSdlGraphicsManager::activateManager() { @@ -210,20 +308,26 @@ Common::List<Graphics::PixelFormat> OpenGLSdlGraphicsManager::getSupportedFormat // RGBA4444 formats.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)); -#ifndef USE_GLES +#if !USE_FORCED_GLES && !USE_FORCED_GLES2 +#if !USE_FORCED_GL + if (!isGLESContext()) { +#endif #ifdef SCUMM_LITTLE_ENDIAN - // RGBA8888 - formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); + // RGBA8888 + formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); #else - // ABGR8888 - formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)); + // ABGR8888 + formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24)); +#endif +#if !USE_FORCED_GL + } +#endif #endif - // ARGB8888, this should not be here, but Sword25 requires it. :-/ - formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); // RGB555, this is used by SCUMM HE 16 bit games. + // This is not natively supported by OpenGL ES implementations, we convert + // the pixel format internally. formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); -#endif formats.push_back(Graphics::PixelFormat::createFormatCLUT8()); @@ -305,6 +409,10 @@ void OpenGLSdlGraphicsManager::refreshScreen() { #endif } +void *OpenGLSdlGraphicsManager::getProcAddress(const char *name) const { + return SDL_GL_GetProcAddress(name); +} + bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { // In case we request a fullscreen mode we will use the mode the user // has chosen last time or the biggest mode available. @@ -378,6 +486,11 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { flags |= SDL_WINDOW_RESIZABLE; } + // Request a OpenGL (ES) context we can use. + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, _glContextProfileMask); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, _glContextMajor); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, _glContextMinor); + if (!_window->createWindow(width, height, flags)) { // We treat fullscreen requests as a "hint" for now. This means in // case it is not available we simply ignore it. @@ -390,13 +503,6 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) { } } -#ifdef USE_GLES - // SDL2 will create a GLES2 context by default, so this is needed for GLES1-profile - // functions to work. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); -#endif _glContext = SDL_GL_CreateContext(_window->getSDLWindow()); if (!_glContext) { return false; diff --git a/backends/graphics/openglsdl/openglsdl-graphics.h b/backends/graphics/openglsdl/openglsdl-graphics.h index 1552593575..51edcb4363 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.h +++ b/backends/graphics/openglsdl/openglsdl-graphics.h @@ -67,10 +67,13 @@ protected: virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format); virtual void refreshScreen(); + + virtual void *getProcAddress(const char *name) const; private: bool setupMode(uint width, uint height); #if SDL_VERSION_ATLEAST(2, 0, 0) + int _glContextProfileMask, _glContextMajor, _glContextMinor; SDL_GLContext _glContext; #else uint32 _lastVideoModeLoad; diff --git a/backends/module.mk b/backends/module.mk index 3d412c031a..2e100d215d 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -3,6 +3,7 @@ MODULE := backends MODULE_OBJS := \ base-backend.o \ modular-backend.o \ + audiocd/audiocd-stream.o \ audiocd/default/default-audiocd.o \ events/default/default-events.o \ fs/abstract-fs.o \ @@ -52,10 +53,16 @@ endif # OpenGL specific source files. ifdef USE_OPENGL MODULE_OBJS += \ + graphics/opengl/context.o \ graphics/opengl/debug.o \ - graphics/opengl/extensions.o \ + graphics/opengl/framebuffer.o \ graphics/opengl/opengl-graphics.o \ - graphics/opengl/texture.o + graphics/opengl/shader.o \ + graphics/opengl/texture.o \ + graphics/opengl/pipelines/clut8.o \ + graphics/opengl/pipelines/fixed.o \ + graphics/opengl/pipelines/pipeline.o \ + graphics/opengl/pipelines/shader.o endif # SDL specific source files. @@ -97,6 +104,7 @@ endif ifdef MACOSX MODULE_OBJS += \ + audiocd/macosx/macosx-audiocd.o \ midi/coreaudio.o \ midi/coremidi.o \ updates/macosx/macosx-updates.o \ @@ -105,6 +113,7 @@ endif ifdef WIN32 MODULE_OBJS += \ + audiocd/win32/win32-audiocd.o \ fs/windows/windows-fs.o \ fs/windows/windows-fs-factory.o \ midi/windows.o \ @@ -128,6 +137,11 @@ MODULE_OBJS += \ events/ps3sdl/ps3sdl-events.o endif +ifdef USE_LINUXCD +MODULE_OBJS += \ + audiocd/linux/linux-audiocd.o +endif + ifeq ($(BACKEND),tizen) MODULE_OBJS += \ timer/tizen/timer.o diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h index d8ab549c3a..b567142b8f 100644 --- a/backends/platform/dc/dc.h +++ b/backends/platform/dc/dc.h @@ -57,21 +57,16 @@ class DCHardware { }; class DCCDManager : public DefaultAudioCDManager { - // Initialize the specified CD drive for audio playback. - bool openCD(int drive); - - // Poll cdrom status - // Returns true if cd audio is playing - bool pollCD(); - - // Play cdrom audio track - void playCD(int track, int num_loops, int start_frame, int duration); +public: + // Poll cdrom status + // Returns true if cd audio is playing + bool isPlaying() const; - // Stop cdrom audio track - void stopCD(); + // Play cdrom audio track + void play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); - // Update cdrom audio status - void updateCD(); + // Stop cdrom audio track + void stop(); }; class OSystem_Dreamcast : private DCHardware, public EventsBaseBackend, public PaletteManager, public FilesystemFactory diff --git a/backends/platform/dc/dcmain.cpp b/backends/platform/dc/dcmain.cpp index eede796991..bd66b81b35 100644 --- a/backends/platform/dc/dcmain.cpp +++ b/backends/platform/dc/dcmain.cpp @@ -90,43 +90,45 @@ static bool find_track(int track, int &first_sec, int &last_sec) return false; } -void DCCDManager::playCD(int track, int num_loops, int start_frame, int duration) -{ - int first_sec, last_sec; +void DCCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) { + DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate); + + // If we're playing now, are set to only emulate, return here + if (isPlaying() || onlyEmulate) + return; + + int firstSec, lastSec; #if 1 - if (num_loops) - --num_loops; + if (numLoops) + --numLoops; #endif - if (num_loops>14) num_loops=14; - else if (num_loops<0) num_loops=15; // infinity - if (!find_track(track, first_sec, last_sec)) - return; - if (duration) - last_sec = first_sec + start_frame + duration; - first_sec += start_frame; - play_cdda_sectors(first_sec, last_sec, num_loops); -} -void DCCDManager::stopCD() -{ - stop_cdda(); -} + if (numLoops > 14) + numLoops = 14; + else if (numLoops < 0) + num_loops = 15; // infinity -bool DCCDManager::pollCD() -{ - extern int getCdState(); - return getCdState() == 3; + if (!find_track(track, firstSec, lastSec)) + return; + + if (duration) + lastSec = firstSec + startFrame + duration; + + firstSec += startFrame; + play_cdda_sectors(firstSec, lastSec, numLoops); } -void DCCDManager::updateCD() -{ - // Dummy. The CD drive takes care of itself. +void DCCDManager::stop() { + DefaultAudioCDManager::stop(); + stop_cdda(); } -bool DCCDManager::openCD(int drive) -{ - // Dummy. - return true; +bool DCCDManager::isPlaying() const { + if (DefaultAudioCDManager::isPlaying()) + return true; + + extern int getCdState(); + return getCdState() == 3; } void OSystem_Dreamcast::setWindowCaption(const char *caption) diff --git a/backends/platform/dingux/build.gcw0.sh b/backends/platform/dingux/build.gcw0.sh index eafccb1e51..c1a4fa29c2 100755 --- a/backends/platform/dingux/build.gcw0.sh +++ b/backends/platform/dingux/build.gcw0.sh @@ -3,4 +3,4 @@ export PATH=/opt/gcw0-toolchain/usr/bin:$PATH # Disable high resolution engines since we have 320x240 hardware -./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release --disable-engine=he,mohawk,neverhood,sword1,sword2,sword25,toltecs,wintermute,zvision --disable-mt32emu --disable-hq-scalers && make -j6 gcw-opk && ls -l scummvm.opk +./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release --disable-mt32emu --disable-hq-scalers && make -j6 gcw-opk && ls -l scummvm.opk diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp index c53f57523d..f23192cd9d 100644 --- a/backends/platform/ds/arm9/source/osystem_ds.cpp +++ b/backends/platform/ds/arm9/source/osystem_ds.cpp @@ -715,7 +715,7 @@ void OSystem_DS::deleteMutex(MutexRef mutex) { // and should be replaced by an AudioCDManager subclass, // see backends/audiocd/ and common/system.h -bool OSystem_DS::openCD(int drive) { +bool OSystem_DS::openCD() { return DS::CD::checkCD(); } diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h index f4dbac66f7..9f73e125c2 100644 --- a/backends/platform/ds/arm9/source/osystem_ds.h +++ b/backends/platform/ds/arm9/source/osystem_ds.h @@ -130,7 +130,8 @@ public: // FIXME/TODO: The CD API as follows is *obsolete* // and should be replaced by an AudioCDManager subclass, // see backends/audiocd/ and common/system.h - virtual bool openCD(int drive); + virtual bool openCD(); + virtual void closeCD() {} virtual bool pollCD(); virtual void playCD(int track, int num_loops, int start_frame, int duration); virtual void stopCD(); diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp index 38a2d7441c..7652c0d833 100644 --- a/backends/platform/sdl/macosx/macosx.cpp +++ b/backends/platform/sdl/macosx/macosx.cpp @@ -27,9 +27,10 @@ #ifdef MACOSX -#include "backends/platform/sdl/macosx/macosx.h" +#include "backends/audiocd/macosx/macosx-audiocd.h" #include "backends/mixer/doublebuffersdl/doublebuffersdl-mixer.h" #include "backends/platform/sdl/macosx/appmenu_osx.h" +#include "backends/platform/sdl/macosx/macosx.h" #include "backends/updates/macosx/macosx-updates.h" #include "backends/taskbar/macosx/macosx-taskbar.h" @@ -170,4 +171,8 @@ Common::String OSystem_MacOSX::getSystemLanguage() const { #endif // USE_DETECTLANG } +AudioCDManager *OSystem_MacOSX::createAudioCDManager() { + return createMacOSXAudioCDManager(); +} + #endif diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h index c8b4beaeec..6905284a5f 100644 --- a/backends/platform/sdl/macosx/macosx.h +++ b/backends/platform/sdl/macosx/macosx.h @@ -38,6 +38,11 @@ public: virtual void init(); virtual void initBackend(); virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); + +protected: + // Override createAudioCDManager() to get our Mac-specific + // version. + virtual AudioCDManager *createAudioCDManager(); }; #endif diff --git a/backends/platform/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp index 525c74a91a..e2a642b288 100644 --- a/backends/platform/sdl/posix/posix.cpp +++ b/backends/platform/sdl/posix/posix.cpp @@ -36,6 +36,11 @@ #include "backends/fs/posix/posix-fs.h" #include "backends/taskbar/unity/unity-taskbar.h" +#ifdef USE_LINUXCD +#include "backends/audiocd/linux/linux-audiocd.h" +#endif + +#include <errno.h> #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> @@ -239,4 +244,12 @@ bool OSystem_POSIX::displayLogFile() { } +AudioCDManager *OSystem_POSIX::createAudioCDManager() { +#ifdef USE_LINUXCD + return createLinuxAudioCDManager(); +#else + return OSystem_SDL::createAudioCDManager(); +#endif +} + #endif diff --git a/backends/platform/sdl/posix/posix.h b/backends/platform/sdl/posix/posix.h index f67515ddb3..0514d30191 100644 --- a/backends/platform/sdl/posix/posix.h +++ b/backends/platform/sdl/posix/posix.h @@ -59,6 +59,8 @@ protected: virtual Common::String getDefaultConfigFileName(); virtual Common::WriteStream *createLogFile(); + + virtual AudioCDManager *createAudioCDManager(); }; #endif diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index 219755219f..551605a4b4 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -52,6 +52,21 @@ typedef struct { int FAKE; } FAKE_FILE; #define strncasecmp FAKE_strncasecmp #endif +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_exit) +#undef exit +#define exit FAKE_exit +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_abort) +#undef abort +#define abort FAKE_abort +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_system) +#undef system +#define system FAKE_system +#endif + // HACK: SDL might include windows.h which defines its own ARRAYSIZE. // However, we want to use the version from common/util.h. Thus, we make sure // that we actually have this definition after including the SDL headers. @@ -146,6 +161,21 @@ typedef struct { int FAKE; } FAKE_FILE; #define strncasecmp FORBIDDEN_SYMBOL_REPLACEMENT #endif +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_exit) +#undef exit +#define exit(a) FORBIDDEN_SYMBOL_REPLACEMENT +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_abort) +#undef abort +#define abort() FORBIDDEN_SYMBOL_REPLACEMENT +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_system) +#undef system +#define system(a) FORBIDDEN_SYMBOL_REPLACEMENT +#endif + // SDL 2 has major API changes. We redefine constants which got renamed to // ease the transition. This is sometimes dangerous because the values changed // too! diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index fffb9d56d9..c55753194b 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -245,15 +245,7 @@ void OSystem_SDL::initBackend() { _timerManager = new SdlTimerManager(); #endif - if (_audiocdManager == 0) { - // Audio CD support was removed with SDL 2.0 -#if SDL_VERSION_ATLEAST(2, 0, 0) - _audiocdManager = new DefaultAudioCDManager(); -#else - _audiocdManager = new SdlAudioCDManager(); -#endif - - } + _audiocdManager = createAudioCDManager(); // Setup a custom program icon. _window->setupIcon(); @@ -491,6 +483,15 @@ Common::TimerManager *OSystem_SDL::getTimerManager() { #endif } +AudioCDManager *OSystem_SDL::createAudioCDManager() { + // Audio CD support was removed with SDL 2.0 +#if SDL_VERSION_ATLEAST(2, 0, 0) + return new DefaultAudioCDManager(); +#else + return new SdlAudioCDManager(); +#endif +} + #ifdef USE_OPENGL const OSystem::GraphicsMode *OSystem_SDL::getSupportedGraphicsModes() const { diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 5ee56d0568..c93c8308a7 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -104,6 +104,11 @@ protected: */ virtual void initSDL(); + /** + * Create the audio CD manager + */ + virtual AudioCDManager *createAudioCDManager(); + // Logging virtual Common::WriteStream *createLogFile() { return 0; } Backends::Log::Log *_logger; diff --git a/backends/platform/sdl/win32/win32.cpp b/backends/platform/sdl/win32/win32.cpp index 0f70c00b40..fbab7eb782 100644 --- a/backends/platform/sdl/win32/win32.cpp +++ b/backends/platform/sdl/win32/win32.cpp @@ -35,6 +35,7 @@ #include "common/error.h" #include "common/textconsole.h" +#include "backends/audiocd/win32/win32-audiocd.h" #include "backends/platform/sdl/win32/win32.h" #include "backends/platform/sdl/win32/win32-window.h" #include "backends/saves/windows/windows-saves.h" @@ -318,4 +319,8 @@ void OSystem_Win32::addSysArchivesToSearchSet(Common::SearchSet &s, int priority OSystem_SDL::addSysArchivesToSearchSet(s, priority); } +AudioCDManager *OSystem_Win32::createAudioCDManager() { + return createWin32AudioCDManager(); +} + #endif diff --git a/backends/platform/sdl/win32/win32.h b/backends/platform/sdl/win32/win32.h index 473e78ff0b..ca0843e834 100644 --- a/backends/platform/sdl/win32/win32.h +++ b/backends/platform/sdl/win32/win32.h @@ -49,6 +49,10 @@ protected: virtual Common::String getDefaultConfigFileName(); virtual Common::WriteStream *createLogFile(); + + // Override createAudioCDManager() to get our Mac-specific + // version. + virtual AudioCDManager *createAudioCDManager(); }; #endif diff --git a/backends/platform/tizen/graphics.cpp b/backends/platform/tizen/graphics.cpp index 759c4e519d..61dbfc38fb 100644 --- a/backends/platform/tizen/graphics.cpp +++ b/backends/platform/tizen/graphics.cpp @@ -56,6 +56,7 @@ result TizenGraphicsManager::Construct() { loadEgl(); // Notify the OpenGL code about our context. + setContextType(OpenGL::kContextGLES); // We default to RGB565 and RGBA5551 which is closest to the actual output // mode we setup. @@ -206,3 +207,7 @@ bool TizenGraphicsManager::loadVideoMode(uint requestedWidth, uint requestedHeig void TizenGraphicsManager::refreshScreen() { eglSwapBuffers(_eglDisplay, _eglSurface); } + +void *TizenGraphicsManager::getProcAddress(const char *name) const { + return eglGetProcAddress(name); +} diff --git a/backends/platform/tizen/graphics.h b/backends/platform/tizen/graphics.h index 1522d66bbe..1798b078d8 100644 --- a/backends/platform/tizen/graphics.h +++ b/backends/platform/tizen/graphics.h @@ -63,6 +63,8 @@ protected: void refreshScreen(); + void *getProcAddress(const char *name) const; + const Graphics::Font *getFontOSD(); private: diff --git a/backends/platform/tizen/system.cpp b/backends/platform/tizen/system.cpp index a235456670..1820a28791 100644 --- a/backends/platform/tizen/system.cpp +++ b/backends/platform/tizen/system.cpp @@ -81,36 +81,41 @@ struct TizenSaveFileManager : public DefaultSaveFileManager { }; bool TizenSaveFileManager::removeSavefile(const Common::String &filename) { - Common::String savePathName = getSavePath(); + // Assure the savefile name cache is up-to-date. + assureCached(getSavePath()); + if (getError().getCode() != Common::kNoError) + return false; - checkPath(Common::FSNode(savePathName)); - if (getError().getCode() != Common::kNoError) { + // Obtain node if exists. + SaveFileCache::const_iterator file = _saveFileCache.find(filename); + if (file == _saveFileCache.end()) { return false; - } + } else { + const Common::FSNode fileNode = file->_value; + // Remove from cache, this invalidates the 'file' iterator. + _saveFileCache.erase(file); + file = _saveFileCache.end(); - // recreate FSNode since checkPath may have changed/created the directory - Common::FSNode savePath(savePathName); - Common::FSNode file = savePath.getChild(filename); + String unicodeFileName; + StringUtil::Utf8ToString(fileNode.getPath().c_str(), unicodeFileName); - String unicodeFileName; - StringUtil::Utf8ToString(file.getPath().c_str(), unicodeFileName); + switch (Tizen::Io::File::Remove(unicodeFileName)) { + case E_SUCCESS: + return true; - switch (Tizen::Io::File::Remove(unicodeFileName)) { - case E_SUCCESS: - return true; + case E_ILLEGAL_ACCESS: + setError(Common::kWritePermissionDenied, "Search or write permission denied: " + + file.getName()); + break; - case E_ILLEGAL_ACCESS: - setError(Common::kWritePermissionDenied, "Search or write permission denied: " + - file.getName()); - break; + default: + setError(Common::kPathDoesNotExist, "removeSavefile: '" + file.getName() + + "' does not exist or path is invalid"); + break; + } - default: - setError(Common::kPathDoesNotExist, "removeSavefile: '" + file.getName() + - "' does not exist or path is invalid"); - break; + return false; } - - return false; } // diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index 4f7013724a..daec36ae72 100644 --- a/backends/saves/default/default-saves.cpp +++ b/backends/saves/default/default-saves.cpp @@ -60,22 +60,15 @@ void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) { } Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String &pattern) { - Common::String savePathName = getSavePath(); - checkPath(Common::FSNode(savePathName)); + // Assure the savefile name cache is up-to-date. + assureCached(getSavePath()); if (getError().getCode() != Common::kNoError) return Common::StringArray(); - // recreate FSNode since checkPath may have changed/created the directory - Common::FSNode savePath(savePathName); - - Common::FSDirectory dir(savePath); - Common::ArchiveMemberList savefiles; Common::StringArray results; - Common::String search(pattern); - - if (dir.listMatchingMembers(savefiles, search) > 0) { - for (Common::ArchiveMemberList::const_iterator file = savefiles.begin(); file != savefiles.end(); ++file) { - results.push_back((*file)->getName()); + for (SaveFileCache::const_iterator file = _saveFileCache.begin(), end = _saveFileCache.end(); file != end; ++file) { + if (file->_key.matchString(pattern, true)) { + results.push_back(file->_key); } } @@ -83,68 +76,81 @@ Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String & } Common::InSaveFile *DefaultSaveFileManager::openForLoading(const Common::String &filename) { - // Ensure that the savepath is valid. If not, generate an appropriate error. - Common::String savePathName = getSavePath(); - checkPath(Common::FSNode(savePathName)); + // Assure the savefile name cache is up-to-date. + assureCached(getSavePath()); if (getError().getCode() != Common::kNoError) - return 0; - - // recreate FSNode since checkPath may have changed/created the directory - Common::FSNode savePath(savePathName); + return nullptr; - Common::FSNode file = savePath.getChild(filename); - if (!file.exists()) - return 0; - - // Open the file for reading - Common::SeekableReadStream *sf = file.createReadStream(); - - return Common::wrapCompressedReadStream(sf); + SaveFileCache::const_iterator file = _saveFileCache.find(filename); + if (file == _saveFileCache.end()) { + return nullptr; + } else { + // Open the file for loading. + Common::SeekableReadStream *sf = file->_value.createReadStream(); + return Common::wrapCompressedReadStream(sf); + } } Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String &filename, bool compress) { - // Ensure that the savepath is valid. If not, generate an appropriate error. - Common::String savePathName = getSavePath(); - checkPath(Common::FSNode(savePathName)); + // Assure the savefile name cache is up-to-date. + const Common::String savePathName = getSavePath(); + assureCached(savePathName); if (getError().getCode() != Common::kNoError) - return 0; + return nullptr; - // recreate FSNode since checkPath may have changed/created the directory - Common::FSNode savePath(savePathName); + // Obtain node. + SaveFileCache::const_iterator file = _saveFileCache.find(filename); + Common::FSNode fileNode; - Common::FSNode file = savePath.getChild(filename); + // If the file did not exist before, we add it to the cache. + if (file == _saveFileCache.end()) { + const Common::FSNode savePath(savePathName); + fileNode = savePath.getChild(filename); + } else { + fileNode = file->_value; + } - // Open the file for saving - Common::WriteStream *sf = file.createWriteStream(); + // Open the file for saving. + Common::WriteStream *const sf = fileNode.createWriteStream(); + Common::OutSaveFile *const result = compress ? Common::wrapCompressedWriteStream(sf) : sf; - return compress ? Common::wrapCompressedWriteStream(sf) : sf; + // Add file to cache now that it exists. + _saveFileCache[filename] = Common::FSNode(fileNode.getPath()); + + return result; } bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) { - Common::String savePathName = getSavePath(); - checkPath(Common::FSNode(savePathName)); + // Assure the savefile name cache is up-to-date. + assureCached(getSavePath()); if (getError().getCode() != Common::kNoError) return false; - // recreate FSNode since checkPath may have changed/created the directory - Common::FSNode savePath(savePathName); - - Common::FSNode file = savePath.getChild(filename); - - // FIXME: remove does not exist on all systems. If your port fails to - // compile because of this, please let us know (scummvm-devel or Fingolfin). - // There is a nicely portable workaround, too: Make this method overloadable. - if (remove(file.getPath().c_str()) != 0) { + // Obtain node if exists. + SaveFileCache::const_iterator file = _saveFileCache.find(filename); + if (file == _saveFileCache.end()) { + return false; + } else { + const Common::FSNode fileNode = file->_value; + // Remove from cache, this invalidates the 'file' iterator. + _saveFileCache.erase(file); + file = _saveFileCache.end(); + + // FIXME: remove does not exist on all systems. If your port fails to + // compile because of this, please let us know (scummvm-devel). + // There is a nicely portable workaround, too: Make this method overloadable. + if (remove(fileNode.getPath().c_str()) != 0) { #ifndef _WIN32_WCE - if (errno == EACCES) - setError(Common::kWritePermissionDenied, "Search or write permission denied: "+file.getName()); + if (errno == EACCES) + setError(Common::kWritePermissionDenied, "Search or write permission denied: "+fileNode.getName()); - if (errno == ENOENT) - setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid"); + if (errno == ENOENT) + setError(Common::kPathDoesNotExist, "removeSavefile: '"+fileNode.getName()+"' does not exist or path is invalid"); #endif - return false; - } else { - return true; + return false; + } else { + return true; + } } } @@ -171,4 +177,43 @@ Common::String DefaultSaveFileManager::getSavePath() const { return dir; } +void DefaultSaveFileManager::assureCached(const Common::String &savePathName) { + // Check that path exists and is usable. + checkPath(Common::FSNode(savePathName)); + + if (_cachedDirectory == savePathName) { + return; + } + + _saveFileCache.clear(); + _cachedDirectory.clear(); + + if (getError().getCode() != Common::kNoError) { + warning("DefaultSaveFileManager::assureCached: Can not cache path '%s': '%s'", savePathName.c_str(), getErrorDesc().c_str()); + return; + } + + // FSNode can cache its members, thus create it after checkPath to reflect + // actual file system state. + const Common::FSNode savePath(savePathName); + + Common::FSList children; + if (!savePath.getChildren(children, Common::FSNode::kListFilesOnly)) { + return; + } + + // Build the savefile name cache. + for (Common::FSList::const_iterator file = children.begin(), end = children.end(); file != end; ++file) { + if (_saveFileCache.contains(file->getName())) { + warning("DefaultSaveFileManager::assureCached: Name clash when building cache, ignoring file '%s'", file->getName().c_str()); + } else { + _saveFileCache[file->getName()] = *file; + } + } + + // Only now store that we cached 'savePathName' to indicate we successfully + // cached the directory. + _cachedDirectory = savePathName; +} + #endif // !defined(DISABLE_DEFAULT_SAVEFILEMANAGER) diff --git a/backends/saves/default/default-saves.h b/backends/saves/default/default-saves.h index 81f45f96b8..bf4ca0229d 100644 --- a/backends/saves/default/default-saves.h +++ b/backends/saves/default/default-saves.h @@ -27,6 +27,7 @@ #include "common/savefile.h" #include "common/str.h" #include "common/fs.h" +#include "common/hashmap.h" /** * Provides a default savefile manager implementation for common platforms. @@ -54,6 +55,30 @@ protected: * Sets the internal error and error message accordingly. */ virtual void checkPath(const Common::FSNode &dir); + + /** + * Assure that the given save path is cached. + * + * @param savePathName String representation of save path to cache. + */ + void assureCached(const Common::String &savePathName); + + typedef Common::HashMap<Common::String, Common::FSNode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SaveFileCache; + + /** + * Cache of all the save files in the currently cached directory. + * + * Modify with caution because we only re-cache when the save path changed! + * This needs to be updated inside at least openForSaving and + * removeSavefile. + */ + SaveFileCache _saveFileCache; + +private: + /** + * The currently cached directory. + */ + Common::String _cachedDirectory; }; #endif diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 19702ea36d..a9116bf5f2 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -100,8 +100,9 @@ static const char HELP_STRING[] = " -u, --dump-scripts Enable script dumping if a directory called 'dumps'\n" " exists in the current directory\n" "\n" - " --cdrom=NUM CD drive to play CD audio from (default: 0 = first\n" - " drive)\n" + " --cdrom=DRIVE CD drive to play CD audio from; can either be a\n" + " drive, path, or numeric index (default: 0 = best\n" + " choice drive)\n" " --joystick[=NUM] Enable joystick input (default: 0 = first joystick)\n" " --platform=WORD Specify platform of game (allowed values: 2gs, 3do,\n" " acorn, amiga, atari, c64, fmtowns, nes, mac, pc, pc98,\n" @@ -373,6 +374,12 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha // We defer checking whether this is a valid target to a later point. return s; } else { + // On MacOS X prior to 10.9 the OS is sometimes adding a -psn_X_XXXXXX argument (where X are digits) + // to pass the process serial number. We need to ignore it to avoid an error. +#ifdef MACOSX + if (strncmp(s, "-psn_", 5) == 0) + continue; +#endif bool isLongCmd = (s[0] == '-' && s[1] == '-'); diff --git a/common/savefile.h b/common/savefile.h index b0c4d31f53..9fca07f9d5 100644 --- a/common/savefile.h +++ b/common/savefile.h @@ -56,6 +56,12 @@ typedef WriteStream OutSaveFile; * i.e. typically save states, but also configuration files and similar * things. * + * Savefile names represent SaveFiles. These names are case insensitive, that + * means a name of "Kq1.000" represents the same savefile as "kq1.000". In + * addition, SaveFileManager does not allow for names which contain path + * separators like '/' or '\'. This is because we do not support directories + * in SaveFileManager. + * * While not declared as a singleton, it is effectively used as such, * with OSystem::getSavefileManager returning a pointer to the single * SaveFileManager instances to be used. @@ -115,49 +121,56 @@ public: * exports from the Quest for Glory series. QfG5 is a 3D game and won't be * supported by ScummVM. * - * @param name the name of the savefile - * @param compress toggles whether to compress the resulting save file - * (default) or not. - * @return pointer to an OutSaveFile, or NULL if an error occurred. + * @param name The name of the savefile. + * @param compress Toggles whether to compress the resulting save file + * (default) or not. + * @return Pointer to an OutSaveFile, or NULL if an error occurred. */ virtual OutSaveFile *openForSaving(const String &name, bool compress = true) = 0; /** * Open the file with the specified name in the given directory for loading. - * @param name the name of the savefile - * @return pointer to an InSaveFile, or NULL if an error occurred. + * + * @param name The name of the savefile. + * @return Pointer to an InSaveFile, or NULL if an error occurred. */ virtual InSaveFile *openForLoading(const String &name) = 0; /** * Removes the given savefile from the system. - * @param name the name of the savefile to be removed. + * + * @param name The name of the savefile to be removed. * @return true if no error occurred, false otherwise. */ virtual bool removeSavefile(const String &name) = 0; /** * Renames the given savefile. - * @param oldName Old name. - * @param newName New name. + * + * @param oldName Old name. + * @param newName New name. * @return true if no error occurred. false otherwise. */ virtual bool renameSavefile(const String &oldName, const String &newName); /** * Copy the given savefile. - * @param oldName Old name. - * @param newName New name. + * + * @param oldName Old name. + * @param newName New name. * @return true if no error occurred. false otherwise. */ virtual bool copySavefile(const String &oldName, const String &newName); /** - * Request a list of available savegames with a given DOS-style pattern, - * also known as "glob" in the POSIX world. Refer to the Common::matchString() - * function to learn about the precise pattern format. - * @param pattern Pattern to match. Wildcards like * or ? are available. - * @return list of strings for all present file names. + * List available savegames matching a given pattern. + * + * Our pattern format is based on DOS paterns, also known as "glob" in the + * POSIX world. Please refer to the Common::matchString() function to learn + * about the precise pattern format. + * + * @param pattern Pattern to match. Wildcards like * or ? are available. + * @return List of strings for all present file names. * @see Common::matchString() */ virtual StringArray listSavefiles(const String &pattern) = 0; @@ -133,8 +133,7 @@ _png=auto _theoradec=auto _faad=auto _fluidsynth=auto -_opengl=auto -_opengles=auto +_opengl_mode=auto _readline=auto _freetype2=auto _taskbar=auto @@ -940,6 +939,15 @@ Optional Features: --enable-verbose-build enable regular echoing of commands during build process --disable-bink don't build with Bink video support + --opengl-mode=MODE OpenGL (ES) mode to use for OpenGL output [auto] + available modes: auto for autodetection + none for disabling any OpenGL usage + any for runtime detection + gl for forcing OpenGL + gles for forcing OpenGL ES + gles2 for forcing OpenGL ES 2 + WARNING: only specify this manually if you know what + you are doing! Optional Libraries: --with-alsa-prefix=DIR Prefix where alsa is installed (optional) @@ -964,9 +972,6 @@ Optional Libraries: --with-mpeg2-prefix=DIR Prefix where libmpeg2 is installed (optional) --enable-mpeg2 enable mpeg2 codec for cutscenes [autodetect] - --with-opengl-prefix=DIR Prefix where OpenGL (ES) is installed (optional) - --disable-opengl disable OpenGL (ES) support [autodetect] - --with-jpeg-prefix=DIR Prefix where libjpeg is installed (optional) --disable-jpeg disable JPEG decoder [autodetect] @@ -1071,10 +1076,11 @@ for ac_option in $@; do --disable-updates) _updates=no ;; --enable-libunity) _libunity=yes ;; --disable-libunity) _libunity=no ;; - --enable-opengl) _opengl=yes ;; - --disable-opengl) _opengl=no ;; --enable-bink) _bink=yes ;; --disable-bink) _bink=no ;; + --opengl-mode=*) + _opengl_mode=`echo $ac_option | cut -d '=' -f 2` + ;; --enable-verbose-build) _verbose_build=yes ;; --enable-plugins) _dynamic_modules=yes ;; --default-dynamic) _plugins_default=dynamic ;; @@ -1175,11 +1181,6 @@ for ac_option in $@; do LIBUNITY_CFLAGS="-I$arg/include" LIBUNITY_LIBS="-L$arg/lib" ;; - --with-opengl-prefix=*) - arg=`echo $ac_option | cut -d '=' -f 2` - OPENGL_CFLAGS="-I$arg/include" - OPENGL_LIBS="-L$arg/lib" - ;; --backend=*) _backend=`echo $ac_option | cut -d '=' -f 2` ;; @@ -2659,10 +2660,11 @@ if test -n "$_host"; then # since SDL2 manages dispmanx/GLES2 very well internally. # SDL1 is bit-rotten on this platform. _sdlconfig=sdl2-config - # OpenGL(ES) support is mature enough as to be the best option on + # OpenGL ES support is mature enough as to be the best option on # the Raspberry Pi, so it's enabled by default. - _opengl=yes - _opengles=yes + # The Raspberry Pi always supports OpenGL ES 2.0 contexts, thus we + # take advantage of those. + _opengl_mode=gles2 ;; dreamcast) append_var DEFINES "-DDISABLE_DEFAULT_SAVEFILEMANAGER" @@ -2990,6 +2992,8 @@ if test -n "$_host"; then _mt32emu=no _timidity=no _vkeybd=yes + # Tizen relies on the OpenGL ES output thus we always enable it. + _opengl_mode=gles ;; webos) _backend="webos" @@ -3236,7 +3240,7 @@ esac # # Enable High resolution engines (>320x240) support only for backends which support it # -case $_backend in +case $_host in gcw0) if test "$_highres" = yes ; then _highres=yes @@ -4150,113 +4154,115 @@ echocheck "OpenGL" case $_backend in openpandora) - # Only enable OpenGL ES on the OpanPandora if --enable-opengl is passed in explicitly. - if test "$_opengl" = yes ; then - _opengl=yes - _opengles=yes - OPENGL_LIBS="-lGLES_CM -lEGL -lX11" - OPENGL_CFLAGS="$OPENGL_LIBS" - append_var LIBS "$OPENGL_LIBS" - append_var INCLUDES "$OPENGL_CFLAGS" + # Only enable OpenGL ES on the OpanPandora if --opengl-mode=gles is passed in explicitly. + if test "$_opengl_mode" = "gles" ; then + append_var LIBS "-lGLES_CM -lEGL -lX11" + else + _opengl_mode=none fi ;; esac -if test "$_opengl" = auto ; then - _opengl=no - if test "$_backend" = "sdl" ; then - # Try different header filenames - # 1) GL/gl.h This is usually used on POSIX and Windows systems - # 2) OpenGL/gl.h This is used on Mac OS X - # 3) GLES/gl.h This is used for OpenGL ES 1.x - for i in "GL/gl.h" "OpenGL/gl.h" "GLES/gl.h"; do - # Test the current header for OpenGL - cat > $TMPC << EOF -#include <$i> -#include <stdio.h> -int main(void) { printf("ANTIVIRUS FALSE POSITIVE WORKAROUND"); return GL_VERSION_1_1; } -EOF - cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && break +if test "$_opengl_mode" = auto ; then + case $_backend in + sdl) + case $_sdlversion in + 1.2.*) + # Stock SDL 1.2 only supports OpenGL contexts. + _opengl_mode=gl + ;; - # Test the current header for OpenGL ES - cat > $TMPC << EOF -#include <$i> -int main(void) { return GL_OES_VERSION_1_1; } -EOF - cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && _opengles=yes && break + 2.0.*) + # SDL2 supports both OpenGL + OpenGL ES contexts. + # However, Mac OS X only allows OpenGL context creation at + # this time, thus we limit us to OpenGL on that platform. + case $_host_os in + darwin*) + _opengl_mode=gl + ;; + + *) + _opengl_mode=any + ;; + esac + ;; + esac + ;; - # Test the current header for OpenGL ES on SBCs (Raspberry Pi, Cubieboard, etc) - cat > $TMPC << EOF -#include <$i> -int main(void) { return GL_VERSION_ES_CM_1_1; } -EOF - cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && _opengles=yes && break - done - fi + tizen) + # Tizen always runs in GLES mode + _opengl_mode=gles + ;; + + *) + _opengl_mode=none + ;; + esac fi -if test "$_opengl" = yes ; then - # Our simple test case - cat > $TMPC << EOF -int main(void) { return 0; } -EOF - _opengl=no - # Try different library names - if test "$_opengles" = "yes" ; then - # 1) GLES_CM This is usually used for OpenGL ES 1.1 (Common profile) - # 2) GLESv1_CM This is used by the Windows Mali OpenGL ES 1.1 Emulator - # 3) glesv1 This is used by the Linux Mali OpenGL ES 1.1 Emulator - _opengles=no - for lib in "-lGLES_CM" "-lGLESv1_CM" "-lglesv1"; do - if cc_check_no_clean $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS $lib - then - _opengl=yes - _opengles=yes - OPENGL_LIBS="$OPENGL_LIBS $lib" - break - fi - done - else - # 1) -framework OpenGL This is used on Mac OS X - # 2) GL This is usually used on POSIX systems - # 3) opengl32 This is used on Windows - # - # We try "-framework OpenGL" first here to assure it will always be - # picked up by the configure script on Mac OS X, even when a libGL - # exists. - for lib in "-framework OpenGL" "-lGL" "-lopengl32"; do - if cc_check_no_clean $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS $lib - then - _opengl=yes - OPENGL_LIBS="$OPENGL_LIBS $lib" - break - fi - done - fi - cc_check_clean +_opengl=yes +case $_opengl_mode in + auto) + # This case should never occur but better safe than sorry. + echo "no" + _opengl=no + ;; - if test "$_opengl" = yes ; then - append_var LIBS "$OPENGL_LIBS" - append_var INCLUDES "$OPENGL_CFLAGS" - fi -fi + none) + echo "no" + _opengl=no + ;; -case $_host_os in - tizen) - # components live in non-standard locations so just assume sane SDK - _opengl=yes - _opengles=yes + any) + echo "yes (runtime detection)" + add_line_to_config_h "#undef USE_GLES_MODE" ;; -esac -if test "$_opengles" = "yes" ; then - echo "yes (OpenGL ES)" -else - echo "$_opengl" -fi + gl) + echo "yes (OpenGL)" + add_line_to_config_h "#define USE_GLES_MODE 0" + ;; + + gles) + echo "yes (OpenGL ES)" + add_line_to_config_h "#define USE_GLES_MODE 1" + ;; + + gles2) + echo "yes (OpenGL ES 2)" + add_line_to_config_h "#define USE_GLES_MODE 2" + ;; + + *) + echo "invalid mode specification '$_opengl_mode'. Aborting." + exit 1 + ;; +esac define_in_config_if_yes "$_opengl" "USE_OPENGL" -define_in_config_if_yes "$_opengles" "USE_GLES" + +# +# Check for Linux CD-ROM support +# +case $_host_os in + *linux*) + echocheck "Linux CD-ROM" + linuxcd=no + cat > $TMPC << EOF +#include <linux/cdrom.h> +#include <sys/types.h> +int main(void) { + int x = CDROMREADAUDIO; + dev_t dev; + return major(dev) + x; +} +EOF + cc_check && linuxcd=yes + define_in_config_if_yes "$linuxcd" 'USE_LINUXCD' + echo "$linuxcd" + ;; +esac + # # Check for nasm diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 65b7601a54..0242af1cfb 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -125,7 +125,6 @@ int main(int argc, char *argv[]) { ProjectType projectType = kProjectNone; int msvcVersion = 12; - bool useSDL2 = false; // Parse command line arguments using std::cout; @@ -269,7 +268,7 @@ int main(int argc, char *argv[]) { } else if (!std::strcmp(argv[i], "--tests")) { setup.tests = true; } else if (!std::strcmp(argv[i], "--sdl2")) { - useSDL2 = true; + setup.useSDL2 = true; } else { std::cerr << "ERROR: Unknown parameter \"" << argv[i] << "\"\n"; return -1; @@ -349,11 +348,15 @@ int main(int argc, char *argv[]) { setup.defines.push_back("IPHONE"); } setup.defines.push_back("SDL_BACKEND"); - if (!useSDL2) { - cout << "\nLinking to SDL 1.2\n\n"; + if (!setup.useSDL2) { + cout << "\nBuilding against SDL 1.2\n\n"; setup.libraries.push_back("sdl"); } else { - cout << "\nLinking to SDL 2.0\n\n"; + cout << "\nBuilding against SDL 2.0\n\n"; + // TODO: This also defines USE_SDL2 in the preprocessor, we don't do + // this in our configure/make based build system. Adapt create_project + // to replicate this behavior. + setup.defines.push_back("USE_SDL2"); setup.libraries.push_back("sdl2"); } setup.libraries.push_back("winmm"); @@ -939,7 +942,7 @@ const Feature s_features[] = { { "mad", "USE_MAD", "libmad", true, "libmad (MP3) support" }, { "vorbis", "USE_VORBIS", "libvorbisfile_static libvorbis_static libogg_static", true, "Ogg Vorbis support" }, { "flac", "USE_FLAC", "libFLAC_static win_utf8_io_static", true, "FLAC support" }, - { "png", "USE_PNG", "libpng", true, "libpng support" }, + { "png", "USE_PNG", "libpng16", true, "libpng support" }, { "faad", "USE_FAAD", "libfaad", false, "AAC support" }, { "mpeg2", "USE_MPEG2", "libmpeg2", false, "MPEG-2 support" }, { "theora", "USE_THEORADEC", "libtheora_static", true, "Theora decoding support" }, @@ -954,7 +957,8 @@ const Feature s_features[] = { { "16bit", "USE_RGB_COLOR", "", true, "16bit color support" }, { "mt32emu", "USE_MT32EMU", "", true, "integrated MT-32 emulator" }, { "nasm", "USE_NASM", "", true, "IA-32 assembly support" }, // This feature is special in the regard, that it needs additional handling. - { "opengl", "USE_OPENGL", "opengl32", true, "OpenGL support" }, + { "opengl", "USE_OPENGL", "", true, "OpenGL support" }, + { "opengles", "USE_GLES", "", true, "forced OpenGL ES mode" }, { "taskbar", "USE_TASKBAR", "", true, "Taskbar integration support" }, { "translation", "USE_TRANSLATION", "", true, "Translation support" }, { "vkeybd", "ENABLE_VKEYBD", "", false, "Virtual keyboard support"}, diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h index fb207f3f59..1e417d485b 100644 --- a/devtools/create_project/create_project.h +++ b/devtools/create_project/create_project.h @@ -229,15 +229,17 @@ struct BuildSetup { StringList testDirs; ///< List of all folders containing tests bool devTools; ///< Generate project files for the tools - bool tests; ///< Generate project files for the tests + bool tests; ///< Generate project files for the tests bool runBuildEvents; ///< Run build events as part of the build (generate revision number and copy engine/theme data & needed files to the build folder bool createInstaller; ///< Create NSIS installer after the build + bool useSDL2; ///< Whether to use SDL2 or not. BuildSetup() { devTools = false; tests = false; runBuildEvents = false; createInstaller = false; + useSDL2 = false; } }; diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp index a326bd721a..2c6a89543f 100644 --- a/devtools/create_project/msbuild.cpp +++ b/devtools/create_project/msbuild.cpp @@ -319,12 +319,6 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s for (StringList::const_iterator i = setup.libraries.begin(); i != setup.libraries.end(); ++i) libraries += *i + ".lib;"; - if (_version == 14) { - std::string debug = isRelease ? "" : "d"; - libraries += "libvcruntime" + debug + ".lib;"; - libraries += "libucrt" + debug + ".lib;"; - } - project << "\t\t<Link>\n" "\t\t\t<OutputFile>$(OutDir)" << ((setup.devTools || setup.tests) ? name : setup.projectName) << ".exe</OutputFile>\n" "\t\t\t<AdditionalDependencies>" << libraries << "%(AdditionalDependencies)</AdditionalDependencies>\n" @@ -370,17 +364,17 @@ void MSBuildProvider::outputGlobalPropFile(const BuildSetup &setup, std::ofstrea "<Project DefaultTargets=\"Build\" ToolsVersion=\"" << (_version >= 12 ? _version : 4) << ".0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" "\t<PropertyGroup>\n" "\t\t<_PropertySheetDisplayName>" << setup.projectDescription << "_Global</_PropertySheetDisplayName>\n" - "\t\t<ExecutablePath>$(" << LIBS_DEFINE << ")\\bin;$(ExecutablePath)</ExecutablePath>\n" - "\t\t<LibraryPath>$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << ";$(LibraryPath)</LibraryPath>\n" - "\t\t<IncludePath>$(" << LIBS_DEFINE << ")\\include;$(" << LIBS_DEFINE << ")\\include\\SDL;$(IncludePath)</IncludePath>\n" + "\t\t<ExecutablePath>$(" << LIBS_DEFINE << ")\\bin;$(" << LIBS_DEFINE << ")\\bin\\" << (bits == 32 ? "x86" : "x64") << ";$(ExecutablePath)</ExecutablePath>\n" + "\t\t<LibraryPath>$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << ";$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << "\\$(Configuration);$(LibraryPath)</LibraryPath>\n" + "\t\t<IncludePath>$(" << LIBS_DEFINE << ")\\include;$(" << LIBS_DEFINE << ")\\include\\" << (setup.useSDL2 ? "SDL2" : "SDL") << ";$(IncludePath)</IncludePath>\n" "\t\t<OutDir>$(Configuration)" << bits << "\\</OutDir>\n" - "\t\t<IntDir>$(Configuration)" << bits << "/$(ProjectName)\\</IntDir>\n" + "\t\t<IntDir>$(Configuration)" << bits << "\\$(ProjectName)\\</IntDir>\n" "\t</PropertyGroup>\n" "\t<ItemDefinitionGroup>\n" "\t\t<ClCompile>\n" "\t\t\t<DisableLanguageExtensions>true</DisableLanguageExtensions>\n" "\t\t\t<DisableSpecificWarnings>" << warnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n" - "\t\t\t<AdditionalIncludeDirectories>$(" << LIBS_DEFINE << ")\\include;.;" << prefix << ";" << prefix << "\\engines;" << (setup.tests ? prefix + "\\test\\cxxtest;" : "") << "$(TargetDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n" + "\t\t\t<AdditionalIncludeDirectories>.;" << prefix << ";" << prefix << "\\engines;" << (setup.tests ? prefix + "\\test\\cxxtest;" : "") << "$(TargetDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n" "\t\t\t<PreprocessorDefinitions>" << definesList << "%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" "\t\t\t<ExceptionHandling>" << ((setup.devTools || setup.tests) ? "Sync" : "") << "</ExceptionHandling>\n"; @@ -437,10 +431,14 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t\t<StringPooling>true</StringPooling>\n" "\t\t\t<BufferSecurityCheck>false</BufferSecurityCheck>\n" "\t\t\t<DebugInformationFormat></DebugInformationFormat>\n" - "\t\t\t<RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n" + "\t\t\t<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\n" "\t\t\t<EnablePREfast>" << (configuration == "Analysis" ? "true" : "false") << "</EnablePREfast>\n" "\t\t</ClCompile>\n" + "\t\t<Lib>\n" + "\t\t\t<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>\n" + "\t\t</Lib>\n" "\t\t<Link>\n" + "\t\t\t<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>\n" "\t\t\t<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n" "\t\t\t<SetChecksum>true</SetChecksum>\n"; } else { @@ -448,11 +446,17 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t\t<PreprocessorDefinitions>WIN32;" << (configuration == "LLVM" ? "_CRT_SECURE_NO_WARNINGS;" : "") << "%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" "\t\t\t<MinimalRebuild>true</MinimalRebuild>\n" "\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\n" - "\t\t\t<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n" + "\t\t\t<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\n" "\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\n" - "\t\t\t<TreatWarningAsError>false</TreatWarningAsError>\n" - "\t\t\t<DebugInformationFormat>" << (isWin32 ? "EditAndContinue" : "ProgramDatabase") << "</DebugInformationFormat>\n" // For x64 format Edit and continue is not supported, thus we default to Program Database - "\t\t\t<EnablePREfast>" << (configuration == "Analysis" ? "true" : "false") << "</EnablePREfast>\n"; + "\t\t\t<TreatWarningAsError>false</TreatWarningAsError>\n"; + if (_version >= 14) { + // Since MSVC 2015 Edit and Continue is support for x64 too. + properties << "\t\t\t<DebugInformationFormat>" << "EditAndContinue" << "</DebugInformationFormat>\n"; + } else { + // Older MSVC versions did not support Edit and Continue for x64, thus we do not use it. + properties << "\t\t\t<DebugInformationFormat>" << (isWin32 ? "EditAndContinue" : "ProgramDatabase") << "</DebugInformationFormat>\n"; + } + properties << "\t\t\t<EnablePREfast>" << (configuration == "Analysis" ? "true" : "false") << "</EnablePREfast>\n"; if (configuration == "LLVM") { // FIXME The LLVM cl wrapper does not seem to work properly with the $(TargetDir) path so we hard-code the build folder until the issue is resolved @@ -463,8 +467,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b properties << "\t\t</ClCompile>\n" "\t\t<Link>\n" "\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\n" - "\t\t\t<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>\n" - "\t\t\t<IgnoreSpecificDefaultLibraries>libcmt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n"; + "\t\t\t<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>\n"; } properties << "\t\t</Link>\n" diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp index a43730fbe2..bfe7f522f0 100644 --- a/devtools/create_project/xcode.cpp +++ b/devtools/create_project/xcode.cpp @@ -447,9 +447,6 @@ void XcodeProvider::setupFrameworksBuildPhase(const BuildSetup &setup) { DEF_SYSFRAMEWORK("UIKit"); DEF_SYSTBD("libiconv"); - // Optionals: - DEF_SYSFRAMEWORK("OpenGL"); - // Local libraries DEF_LOCALLIB_STATIC("libFLAC"); DEF_LOCALLIB_STATIC("libmad"); @@ -570,8 +567,6 @@ void XcodeProvider::setupFrameworksBuildPhase(const BuildSetup &setup) { frameworks_osx.push_back("IOKit.framework"); frameworks_osx.push_back("Cocoa.framework"); frameworks_osx.push_back("AudioUnit.framework"); - // Optionals: - frameworks_osx.push_back("OpenGL.framework"); order = 0; for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) { @@ -932,7 +927,11 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) { ADD_SETTING_LIST(scummvmOSX_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvmOSX_defines, kSettingsNoQuote | kSettingsAsList, 5); ADD_SETTING_QUOTE(scummvmOSX_Debug, "GCC_VERSION", ""); ValueList scummvmOSX_HeaderPaths; - scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL"); + if (setup.useSDL2) { + scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL2"); + } else { + scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL"); + } scummvmOSX_HeaderPaths.push_back("/opt/local/include"); scummvmOSX_HeaderPaths.push_back("/opt/local/include/freetype2"); scummvmOSX_HeaderPaths.push_back("include/"); @@ -948,7 +947,6 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) { ADD_SETTING_LIST(scummvmOSX_Debug, "LIBRARY_SEARCH_PATHS", scummvmOSX_LibPaths, kSettingsNoQuote | kSettingsAsList, 5); ADD_SETTING_QUOTE(scummvmOSX_Debug, "OTHER_CFLAGS", ""); ValueList scummvmOSX_LdFlags; - scummvmOSX_LdFlags.push_back("-lSDLmain"); scummvmOSX_LdFlags.push_back("-logg"); scummvmOSX_LdFlags.push_back("-lpng"); scummvmOSX_LdFlags.push_back("-ljpeg"); @@ -958,7 +956,13 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) { scummvmOSX_LdFlags.push_back("-lvorbis"); scummvmOSX_LdFlags.push_back("-lmad"); scummvmOSX_LdFlags.push_back("-lFLAC"); - scummvmOSX_LdFlags.push_back("-lSDL"); + if (setup.useSDL2) { + scummvmOSX_LdFlags.push_back("-lSDL2main"); + scummvmOSX_LdFlags.push_back("-lSDL2"); + } else { + scummvmOSX_LdFlags.push_back("-lSDLmain"); + scummvmOSX_LdFlags.push_back("-lSDL"); + } scummvmOSX_LdFlags.push_back("-lz"); ADD_SETTING_LIST(scummvmOSX_Debug, "OTHER_LDFLAGS", scummvmOSX_LdFlags, kSettingsAsList, 5); ADD_SETTING(scummvmOSX_Debug, "PREBINDING", "NO"); diff --git a/engines/access/access.cpp b/engines/access/access.cpp index bc9bcb4b08..c12761af4a 100644 --- a/engines/access/access.cpp +++ b/engines/access/access.cpp @@ -436,20 +436,9 @@ void AccessEngine::copyBF1BF2() { } void AccessEngine::copyBF2Vid() { - const byte *srcP = (const byte *)_buffer2.getPixels(); - byte *destP = (byte *)_screen->getBasePtr(_screen->_windowXAdd, - _screen->_windowYAdd + _screen->_screenYOff); - - for (int yp = 0; yp < _screen->_vWindowLinesTall; ++yp) { - Common::copy(srcP, srcP + _screen->_vWindowBytesWide, destP); - srcP += _buffer2.pitch; - destP += _screen->pitch; - } - - // Add dirty rect for affected area - Common::Rect r(_screen->_vWindowBytesWide, _screen->_vWindowLinesTall); - r.moveTo(_screen->_windowXAdd, _screen->_windowYAdd + _screen->_screenYOff); - _screen->addDirtyRect(r); + _screen->blitFrom(_buffer2, + Common::Rect(0, 0, _screen->_vWindowBytesWide, _screen->_vWindowLinesTall), + Common::Point(_screen->_windowXAdd, _screen->_windowYAdd)); } void AccessEngine::playVideo(int videoNum, const Common::Point &pt) { diff --git a/engines/access/asurface.cpp b/engines/access/asurface.cpp index f693e6a3a0..2518ff6ad8 100644 --- a/engines/access/asurface.cpp +++ b/engines/access/asurface.cpp @@ -110,7 +110,7 @@ void ImageEntryList::addToList(ImageEntry &ie) { int ASurface::_clipWidth; int ASurface::_clipHeight; -ASurface::ASurface(): Graphics::Surface() { +ASurface::ASurface(): Graphics::ManagedSurface() { _leftSkip = _rightSkip = 0; _topSkip = _bottomSkip = 0; _lastBoundsX = _lastBoundsY = 0; @@ -122,65 +122,14 @@ ASurface::ASurface(): Graphics::Surface() { } ASurface::~ASurface() { - free(); _savedBlock.free(); } -void ASurface::create(uint16 width, uint16 height) { - Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8()); -} - void ASurface::clearBuffer() { byte *pSrc = (byte *)getPixels(); Common::fill(pSrc, pSrc + w * h, 0); } -bool ASurface::clip(Common::Rect &r) { - int skip; - _leftSkip = _rightSkip = 0; - _topSkip = _bottomSkip = 0; - - if (r.left > _clipWidth || r.left < 0) { - if (r.left >= 0) - return true; - - skip = -r.left; - r.setWidth(r.width() - skip); - _leftSkip = skip; - r.moveTo(0, r.top); - } - - int right = r.right - 1; - if (right < 0) - return true; - else if (right > _clipWidth) { - skip = right - _clipWidth; - r.setWidth(r.width() - skip); - _rightSkip = skip; - } - - if (r.top > _clipHeight || r.top < 0) { - if (r.top >= 0) - return true; - - skip = -r.top; - r.setHeight(r.height() - skip); - _topSkip = skip; - r.moveTo(r.left, 0); - } - - int bottom = r.bottom - 1; - if (bottom < 0) - return true; - else if (bottom > _clipHeight) { - skip = bottom - _clipHeight; - _bottomSkip = skip; - r.setHeight(r.height() - skip); - } - - return false; -} - void ASurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Point &pt) { SpriteFrame *frame = sprite->getFrame(frameNum); Common::Rect r(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h); @@ -195,81 +144,7 @@ void ASurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Poi } } -void ASurface::transBlitFrom(ASurface *src, const Common::Point &destPos) { - if (getPixels() == nullptr) - create(w, h); - - for (int yp = 0; yp < src->h; ++yp) { - const byte *srcP = (const byte *)src->getBasePtr(0, yp); - byte *destP = (byte *)getBasePtr(destPos.x, destPos.y + yp); - - for (int xp = 0; xp < this->w; ++xp, ++srcP, ++destP) { - if (*srcP != TRANSPARENCY) - *destP = *srcP; - } - } -} - -void ASurface::transBlitFrom(ASurface *src, const Common::Rect &bounds) { - const int SCALE_LIMIT = 0x100; - int scaleX = SCALE_LIMIT * bounds.width() / src->w; - int scaleY = SCALE_LIMIT * bounds.height() / src->h; - int scaleXCtr = 0, scaleYCtr = 0; - - for (int yCtr = 0, destY = bounds.top; yCtr < src->h; ++yCtr) { - // Handle skipping lines if Y scaling - scaleYCtr += scaleY; - if (scaleYCtr < SCALE_LIMIT) - continue; - scaleYCtr -= SCALE_LIMIT; - - // Handle off-screen lines - if (destY >= this->h) - break; - - if (destY >= 0) { - // Handle drawing the line - const byte *pSrc = (const byte *)src->getBasePtr(0, yCtr); - byte *pDest = (byte *)getBasePtr(bounds.left, destY); - scaleXCtr = 0; - int x = bounds.left; - - for (int xCtr = 0; xCtr < src->w; ++xCtr, ++pSrc) { - // Handle horizontal scaling - scaleXCtr += scaleX; - if (scaleXCtr < SCALE_LIMIT) - continue; - scaleXCtr -= SCALE_LIMIT; - - // Only handle on-screen pixels - if (x >= this->w) - break; - if (x >= 0 && *pSrc != 0) - *pDest = *pSrc; - - ++pDest; - ++x; - } - } - - ++destY; - } -} - -void ASurface::transBlitFrom(ASurface &src) { - blitFrom(src); -} - -void ASurface::blitFrom(const Graphics::Surface &src) { - assert(w >= src.w && h >= src.h); - for (int y = 0; y < src.h; ++y) { - const byte *srcP = (const byte *)src.getBasePtr(0, y); - byte *destP = (byte *)getBasePtr(0, y); - Common::copy(srcP, srcP + src.w, destP); - } -} - -void ASurface::copyBuffer(Graphics::Surface *src) { +void ASurface::copyBuffer(Graphics::ManagedSurface *src) { blitFrom(*src); } @@ -282,14 +157,11 @@ void ASurface::plotB(SpriteFrame *frame, const Common::Point &pt) { } void ASurface::sPlotF(SpriteFrame *frame, const Common::Rect &bounds) { - transBlitFrom(frame, bounds); + transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, false); } void ASurface::sPlotB(SpriteFrame *frame, const Common::Rect &bounds) { - ASurface flippedFrame; - frame->flipHorizontal(flippedFrame); - - transBlitFrom(&flippedFrame, bounds); + transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, true); } void ASurface::copyBlock(ASurface *src, const Common::Rect &bounds) { @@ -324,22 +196,22 @@ void ASurface::restoreBlock() { } void ASurface::drawRect() { - Graphics::Surface::fillRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2), _lColor); + Graphics::ManagedSurface::fillRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2), _lColor); } void ASurface::drawLine(int x1, int y1, int x2, int y2, int col) { - Graphics::Surface::drawLine(x1, y1, x2, y2, col); + Graphics::ManagedSurface::drawLine(x1, y1, x2, y2, col); } void ASurface::drawLine() { - Graphics::Surface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor); + Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor); } void ASurface::drawBox() { - Graphics::Surface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor); - Graphics::Surface::drawLine(_orgX1, _orgY2, _orgX2, _orgY2, _lColor); - Graphics::Surface::drawLine(_orgX2, _orgY1, _orgX2, _orgY1, _lColor); - Graphics::Surface::drawLine(_orgX2, _orgY2, _orgX2, _orgY2, _lColor); + Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor); + Graphics::ManagedSurface::drawLine(_orgX1, _orgY2, _orgX2, _orgY2, _lColor); + Graphics::ManagedSurface::drawLine(_orgX2, _orgY1, _orgX2, _orgY1, _lColor); + Graphics::ManagedSurface::drawLine(_orgX2, _orgY2, _orgX2, _orgY2, _lColor); } void ASurface::flipHorizontal(ASurface &dest) { @@ -373,4 +245,50 @@ void ASurface::moveBufferDown() { Common::copy_backward(p, p + (pitch * (h - TILE_HEIGHT)), p + (pitch * h)); } +bool ASurface::clip(Common::Rect &r) { + int skip; + _leftSkip = _rightSkip = 0; + _topSkip = _bottomSkip = 0; + + if (r.left > _clipWidth || r.left < 0) { + if (r.left >= 0) + return true; + + skip = -r.left; + r.setWidth(r.width() - skip); + _leftSkip = skip; + r.moveTo(0, r.top); + } + + int right = r.right - 1; + if (right < 0) + return true; + else if (right > _clipWidth) { + skip = right - _clipWidth; + r.setWidth(r.width() - skip); + _rightSkip = skip; + } + + if (r.top > _clipHeight || r.top < 0) { + if (r.top >= 0) + return true; + + skip = -r.top; + r.setHeight(r.height() - skip); + _topSkip = skip; + r.moveTo(r.left, 0); + } + + int bottom = r.bottom - 1; + if (bottom < 0) + return true; + else if (bottom > _clipHeight) { + skip = bottom - _clipHeight; + _bottomSkip = skip; + r.setHeight(r.height() - skip); + } + + return false; +} + } // End of namespace Access diff --git a/engines/access/asurface.h b/engines/access/asurface.h index dd05c8067b..ec18ec09c3 100644 --- a/engines/access/asurface.h +++ b/engines/access/asurface.h @@ -27,7 +27,7 @@ #include "common/array.h" #include "common/memstream.h" #include "common/rect.h" -#include "graphics/surface.h" +#include "graphics/managed_surface.h" #include "access/data.h" namespace Access { @@ -35,7 +35,7 @@ namespace Access { class SpriteResource; class SpriteFrame; -class ASurface : public Graphics::Surface { +class ASurface : virtual public Graphics::ManagedSurface { private: Graphics::Surface _savedBlock; @@ -61,14 +61,8 @@ public: virtual ~ASurface(); - void create(uint16 width, uint16 height); - - bool empty() const { return w == 0 || h == 0 || pixels == nullptr; } - void clearBuffer(); - bool clip(Common::Rect &r); - void plotImage(SpriteResource *sprite, int frameNum, const Common::Point &pt); /** @@ -102,18 +96,8 @@ public: virtual void drawLine(); virtual void drawBox(); - - virtual void transBlitFrom(ASurface *src, const Common::Point &destPos); - - virtual void transBlitFrom(ASurface *src, const Common::Rect &bounds); - virtual void transBlitFrom(ASurface &src); - - virtual void blitFrom(const Graphics::Surface &src); - - virtual void copyBuffer(Graphics::Surface *src); - - virtual void addDirtyRect(const Common::Rect &r) {} + virtual void copyBuffer(Graphics::ManagedSurface *src); void copyTo(ASurface *dest); @@ -126,6 +110,8 @@ public: void moveBufferUp(); void moveBufferDown(); + + bool clip(Common::Rect &r); }; class SpriteFrame : public ASurface { diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h index 9556cd9f67..7d9509ca43 100644 --- a/engines/access/detection_tables.h +++ b/engines/access/detection_tables.h @@ -49,7 +49,7 @@ static const AccessGameDescription gameDescriptions[] = { AD_ENTRY1s("c00.ap", "aeb429ff015596144c0df06886c84825", 303753), Common::ES_ESP, Common::kPlatformDOS, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NONE) }, GType_Amazon, diff --git a/engines/access/events.cpp b/engines/access/events.cpp index d62b05c33f..21ff0d0928 100644 --- a/engines/access/events.cpp +++ b/engines/access/events.cpp @@ -115,7 +115,7 @@ void EventsManager::setCursor(CursorType cursorId) { } } -void EventsManager::setCursorData(Graphics::Surface *src, const Common::Rect &r) { +void EventsManager::setCursorData(Graphics::ManagedSurface *src, const Common::Rect &r) { _invCursor.create(r.width(), r.height(), Graphics::PixelFormat::createFormatCLUT8()); _invCursor.copyRectToSurface(*src, 0, 0, r); } @@ -281,8 +281,7 @@ void EventsManager::nextFrame() { // Give time to the debugger _vm->_debugger->onFrame(); - // TODO: Refactor for dirty rects - _vm->_screen->updateScreen(); + _vm->_screen->update(); } void EventsManager::nextTimer() { diff --git a/engines/access/events.h b/engines/access/events.h index b8c5f0ee5e..5acbb71c9d 100644 --- a/engines/access/events.h +++ b/engines/access/events.h @@ -100,7 +100,7 @@ public: /** * Set the image for the inventory cursor */ - void setCursorData(Graphics::Surface *src, const Common::Rect &r); + void setCursorData(Graphics::ManagedSurface *src, const Common::Rect &r); /** * Return the current cursor Id diff --git a/engines/access/files.cpp b/engines/access/files.cpp index b9c0f7080d..48276ee477 100644 --- a/engines/access/files.cpp +++ b/engines/access/files.cpp @@ -130,13 +130,13 @@ void FileManager::openFile(Resource *res, const Common::String &filename) { error("Could not open file - %s", filename.c_str()); } -void FileManager::loadScreen(Graphics::Surface *dest, int fileNum, int subfile) { +void FileManager::loadScreen(Graphics::ManagedSurface *dest, int fileNum, int subfile) { Resource *res = loadFile(fileNum, subfile); handleScreen(dest, res); delete res; } -void FileManager::handleScreen(Graphics::Surface *dest, Resource *res) { +void FileManager::handleScreen(Graphics::ManagedSurface *dest, Resource *res) { _vm->_screen->loadRawPalette(res->_stream); if (_setPaletteFlag) _vm->_screen->setPalette(); @@ -147,20 +147,17 @@ void FileManager::handleScreen(Graphics::Surface *dest, Resource *res) { res->_size -= res->_stream->pos(); handleFile(res); - if (dest != _vm->_screen) - dest->w = _vm->_screen->w; + Graphics::Surface destSurface = dest->getSubArea(Common::Rect(0, 0, + _vm->_screen->w, _vm->_screen->h)); - if (dest->w == dest->pitch) { - res->_stream->read((byte *)dest->getPixels(), dest->w * dest->h); + if (destSurface.w == destSurface.pitch) { + res->_stream->read((byte *)destSurface.getPixels(), destSurface.w * destSurface.h); } else { - for (int y = 0; y < dest->h; ++y) { - byte *pDest = (byte *)dest->getBasePtr(0, y); - res->_stream->read(pDest, dest->w); + for (int y = 0; y < destSurface.h; ++y) { + byte *pDest = (byte *)destSurface.getBasePtr(0, y); + res->_stream->read(pDest, destSurface.w); } } - - if (dest == _vm->_screen) - _vm->_screen->addDirtyRect(Common::Rect(0, 0, dest->w, dest->h)); } void FileManager::loadScreen(int fileNum, int subfile) { diff --git a/engines/access/files.h b/engines/access/files.h index d081934e91..61fccc2431 100644 --- a/engines/access/files.h +++ b/engines/access/files.h @@ -26,7 +26,7 @@ #include "common/scummsys.h" #include "common/array.h" #include "common/file.h" -#include "graphics/surface.h" +#include "graphics/managed_surface.h" #include "access/decompress.h" namespace Access { @@ -81,7 +81,7 @@ private: /** * Handles loading a screen surface and palette with decoded resource */ - void handleScreen(Graphics::Surface *dest, Resource *res); + void handleScreen(Graphics::ManagedSurface *dest, Resource *res); /** * Open up a sub-file container file @@ -133,7 +133,7 @@ public: /** * Load a screen resource onto a designated surface */ - void loadScreen(Graphics::Surface *dest, int fileNum, int subfile); + void loadScreen(Graphics::ManagedSurface *dest, int fileNum, int subfile); }; } // End of namespace Access diff --git a/engines/access/font.cpp b/engines/access/font.cpp index 8af183f193..6ae65e43f0 100644 --- a/engines/access/font.cpp +++ b/engines/access/font.cpp @@ -151,13 +151,12 @@ void Font::drawString(ASurface *s, const Common::String &msg, const Common::Poin int Font::drawChar(ASurface *s, char c, Common::Point &pt) { Graphics::Surface &ch = _chars[c - ' ']; - - s->addDirtyRect(Common::Rect(pt.x, pt.y, pt.x + ch.w, pt.y + ch.h)); + Graphics::Surface dest = s->getSubArea(Common::Rect(pt.x, pt.y, pt.x + ch.w, pt.y + ch.h)); // Loop through the lines of the character for (int y = 0; y < ch.h; ++y) { byte *pSrc = (byte *)ch.getBasePtr(0, y); - byte *pDest = (byte *)s->getBasePtr(pt.x, pt.y + y); + byte *pDest = (byte *)dest.getBasePtr(0, y); // Loop through the horizontal pixels of the line for (int x = 0; x < ch.w; ++x, ++pSrc, ++pDest) { diff --git a/engines/access/screen.cpp b/engines/access/screen.cpp index aa15abd59a..9700640b71 100644 --- a/engines/access/screen.cpp +++ b/engines/access/screen.cpp @@ -69,8 +69,6 @@ void Screen::clearScreen() { clearBuffer(); if (_vesaMode) _vm->_clearSummaryFlag = true; - - addDirtyRect(Common::Rect(0, 0, this->w, this->h)); } void Screen::setDisplayScan() { @@ -89,28 +87,14 @@ void Screen::setPanel(int num) { _msVirtualOffset = _virtualOffsetsTable[num]; } -void Screen::updateScreen() { +void Screen::update() { if (_vm->_startup >= 0) { if (--_vm->_startup == -1) _fadeIn = true; return; } - - // Merge the dirty rects - mergeDirtyRects(); - - // Loop through copying dirty areas to the physical screen - Common::List<Common::Rect>::iterator i; - for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) { - const Common::Rect &r = *i; - const byte *srcP = (const byte *)getBasePtr(r.left, r.top); - g_system->copyRectToScreen(srcP, this->pitch, r.left, r.top, - r.width(), r.height()); - } - - // Signal the physical screen to update - g_system->updateScreen(); - _dirtyRects.clear(); + markAllDirty();//****DEBUG**** + Graphics::Screen::update(); } void Screen::setInitialPalettte() { @@ -153,7 +137,7 @@ void Screen::loadRawPalette(Common::SeekableReadStream *stream) { void Screen::updatePalette() { g_system->getPaletteManager()->setPalette(&_tempPalette[0], 0, PALETTE_COUNT); - updateScreen(); + update(); } void Screen::savePalette() { @@ -293,22 +277,7 @@ void Screen::drawBox() { ASurface::drawBox(); } -void Screen::transBlitFrom(ASurface *src, const Common::Point &destPos) { - addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + src->w, destPos.y + src->h)); - ASurface::transBlitFrom(src, destPos); -} - -void Screen::transBlitFrom(ASurface *src, const Common::Rect &bounds) { - addDirtyRect(bounds); - ASurface::transBlitFrom(src, bounds); -} - -void Screen::blitFrom(const Graphics::Surface &src) { - addDirtyRect(Common::Rect(0, 0, src.w, src.h)); - ASurface::blitFrom(src); -} - -void Screen::copyBuffer(Graphics::Surface *src) { +void Screen::copyBuffer(Graphics::ManagedSurface *src) { addDirtyRect(Common::Rect(0, 0, src->w, src->h)); ASurface::copyBuffer(src); } @@ -349,51 +318,7 @@ void Screen::cyclePaletteBackwards() { } void Screen::flashPalette(int count) { - warning("TODO: Implement flashPalette"); -} - -void Screen::addDirtyRect(const Common::Rect &r) { - _dirtyRects.push_back(r); - assert(r.isValidRect() && r.width() > 0 && r.height() > 0); -} - -void Screen::mergeDirtyRects() { - Common::List<Common::Rect>::iterator rOuter, rInner; - - // Ensure dirty rect list has at least two entries - rOuter = _dirtyRects.begin(); - for (int i = 0; i < 2; ++i, ++rOuter) { - if (rOuter == _dirtyRects.end()) - return; - } - - // Process the dirty rect list to find any rects to merge - for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { - rInner = rOuter; - while (++rInner != _dirtyRects.end()) { - - if ((*rOuter).intersects(*rInner)) { - // these two rectangles overlap or - // are next to each other - merge them - - unionRectangle(*rOuter, *rOuter, *rInner); - - // remove the inner rect from the list - _dirtyRects.erase(rInner); - - // move back to beginning of list - rInner = rOuter; - } - } - } + // No implementation needed in ScummVM } -bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) { - destRect = src1; - destRect.extend(src2); - - return !destRect.isEmpty(); -} - - } // End of namespace Access diff --git a/engines/access/screen.h b/engines/access/screen.h index 6fa0fe3812..a022741f91 100644 --- a/engines/access/screen.h +++ b/engines/access/screen.h @@ -26,15 +26,13 @@ #include "common/scummsys.h" #include "common/rect.h" #include "common/stream.h" +#include "graphics/screen.h" #include "access/asurface.h" namespace Access { class AccessEngine; -#define PALETTE_COUNT 256 -#define PALETTE_SIZE (256 * 3) - struct ScreenSave { int _clipWidth; int _clipHeight; @@ -47,7 +45,7 @@ struct ScreenSave { int _screenYOff; }; -class Screen : public ASurface { +class Screen : public virtual ASurface, public virtual Graphics::Screen { private: AccessEngine *_vm; byte _tempPalette[PALETTE_SIZE]; @@ -66,10 +64,6 @@ private: Common::List<Common::Rect> _dirtyRects; void updatePalette(); - - void mergeDirtyRects(); - - bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2); public: int _vesaMode; int _startColor, _numColors; @@ -87,6 +81,11 @@ public: bool _screenChangeFlag; bool _fadeIn; public: + /** + * Updates the screen + */ + virtual void update(); + virtual void copyBlock(ASurface *src, const Common::Rect &bounds); virtual void restoreBlock(); @@ -95,15 +94,7 @@ public: virtual void drawBox(); - virtual void transBlitFrom(ASurface *src, const Common::Point &destPos); - - virtual void transBlitFrom(ASurface *src, const Common::Rect &bounds); - - virtual void blitFrom(const Graphics::Surface &src); - - virtual void copyBuffer(Graphics::Surface *src); - - virtual void addDirtyRect(const Common::Rect &r); + virtual void copyBuffer(Graphics::ManagedSurface *src); public: Screen(AccessEngine *vm); @@ -114,11 +105,6 @@ public: void setPanel(int num); /** - * Update the underlying screen - */ - void updateScreen(); - - /** * Fade out screen */ void forceFadeOut(); diff --git a/engines/access/video.cpp b/engines/access/video.cpp index 5fc5f6762c..e3ff457c3b 100644 --- a/engines/access/video.cpp +++ b/engines/access/video.cpp @@ -157,7 +157,7 @@ void VideoPlayer::playVideo() { // If the video is playing on the screen surface, add a dirty rect if (_vidSurface == _vm->_screen) - _vm->_screen->addDirtyRect(_videoBounds); + _vm->_screen->markAllDirty(); getFrame(); if (++_videoFrame == _frameCount) { diff --git a/engines/access/video/movie_decoder.cpp b/engines/access/video/movie_decoder.cpp index 05ec25d54c..1406e549ad 100644 --- a/engines/access/video/movie_decoder.cpp +++ b/engines/access/video/movie_decoder.cpp @@ -719,7 +719,7 @@ bool AccessEngine::playMovie(const Common::String &filename, const Common::Point g_system->getPaletteManager()->setPalette(palette, 0, 256); } - _screen->updateScreen(); + _screen->update(); } } diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 0cacce2421..110ba10632 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -885,6 +885,12 @@ void TextMgr::stringEdit(int16 stringMaxLen) { _inputStringRow = _textPos.row; _inputStringColumn = _textPos.column; + if (_inputCursorChar) { + // Cursor character is shown, which means we are one beyond the start of the input + // Adjust the column for predictive input dialog + _inputStringColumn--; + } + // Caller can set the input string _inputStringCursorPos = 0; while (_inputStringCursorPos < inputStringLen) { diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp index 95bcc68234..5240cdd771 100644 --- a/engines/agos/event.cpp +++ b/engines/agos/event.cpp @@ -427,7 +427,7 @@ void AGOSEngine::delay(uint amount) { uint32 cur = start; uint this_delay, vgaPeriod; - _system->getAudioCDManager()->updateCD(); + _system->getAudioCDManager()->update(); _debugger->onFrame(); @@ -538,7 +538,7 @@ void AGOSEngine::delay(uint amount) { if (_leftButton == 1) _leftButtonCount++; - _system->getAudioCDManager()->updateCD(); + _system->getAudioCDManager()->update(); _system->updateScreen(); diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp index d04f1735d6..3a092e652c 100644 --- a/engines/agos/res_snd.cpp +++ b/engines/agos/res_snd.cpp @@ -228,7 +228,7 @@ void AGOSEngine_Simon1::playMusic(uint16 music, uint16 track) { // Support for compressed music from the ScummVM Music Enhancement Project _system->getAudioCDManager()->stop(); - _system->getAudioCDManager()->play(music + 1, -1, 0, 0); + _system->getAudioCDManager()->play(music + 1, -1, 0, 0, true); if (_system->getAudioCDManager()->isPlaying()) return; diff --git a/engines/bbvs/bbvs.cpp b/engines/bbvs/bbvs.cpp index d40d5e482f..6ae663479d 100644 --- a/engines/bbvs/bbvs.cpp +++ b/engines/bbvs/bbvs.cpp @@ -137,6 +137,21 @@ BbvsEngine::~BbvsEngine() { } void BbvsEngine::newGame() { + memset(_easterEggInput, 0, sizeof(_easterEggInput)); + _gameTicks = 0; + _playVideoNumber = 0; + memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus)); + memset(_gameVars, 0, sizeof(_gameVars)); + memset(_sceneVisited, 0, sizeof(_sceneVisited)); + + _mouseX = 160; + _mouseY = 120; + _mouseButtons = 0; + + _currVerbNum = kVerbLook; + _currTalkObjectIndex = -1; + _currSceneNum = 0; + _currInventoryItem = -1; _newSceneNum = 32; } @@ -162,24 +177,10 @@ Common::Error BbvsEngine::run() { _sound = new SoundMan(); allocSnapshot(); - memset(_easterEggInput, 0, sizeof(_easterEggInput)); - _gameTicks = 0; - _playVideoNumber = 0; - _bootSaveSlot = -1; - - memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus)); - memset(_gameVars, 0, sizeof(_gameVars)); - memset(_sceneVisited, 0, sizeof(_sceneVisited)); - - _mouseX = 160; - _mouseY = 120; - _mouseButtons = 0; + newGame(); - _currVerbNum = kVerbLook; - _currInventoryItem = -1; - _currTalkObjectIndex = -1; - _currSceneNum = 0; + _bootSaveSlot = -1; _newSceneNum = 31; if (ConfMan.hasKey("save_slot")) diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index e52fc464d5..19a2d8a82e 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -222,7 +222,7 @@ void manageEvents() { mouseData.left = mouseLeft; mouseData.right = mouseRight; - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); } void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY) { diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index 7cab067371..a8b4c085ff 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -939,6 +939,10 @@ PCSound::PCSound(Audio::Mixer *mixer, CineEngine *vm) } _player = new PCSoundFxPlayer(_soundDriver); + + // Ensure the CD is open + if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD)) + g_system->getAudioCDManager()->open(); } PCSound::~PCSound() { diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index c72d77c281..9ac9031fb7 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -183,9 +183,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam const Common::FSNode gameDataDir(ConfMan.get("path")); SearchMan.addSubDirectoryMatching(gameDataDir, "audio"); - int cd_num = ConfMan.getInt("cdrom"); - if (cd_num >= 0) - _system->getAudioCDManager()->openCD(cd_num); + _system->getAudioCDManager()->open(); _lang = kEnglish; diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp index 148dae76f5..c576b37660 100644 --- a/engines/drascula/sound.cpp +++ b/engines/drascula/sound.cpp @@ -133,7 +133,7 @@ void DrasculaEngine::stopMusic() { } void DrasculaEngine::updateMusic() { - _system->getAudioCDManager()->updateCD(); + _system->getAudioCDManager()->update(); } int DrasculaEngine::musicStatus() { diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 24bdb858d8..d995f26d9f 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -296,9 +296,7 @@ Common::Error GobEngine::run() { if (isCD()) checkCD(); - int cd_num = ConfMan.getInt("cdrom"); - if (cd_num >= 0) - _system->getAudioCDManager()->openCD(cd_num); + _system->getAudioCDManager()->open(); _global->_debugFlag = 1; _video->_doRangeClamp = true; diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp index 2021cef6e8..bbc290eccf 100644 --- a/engines/groovie/groovie.cpp +++ b/engines/groovie/groovie.cpp @@ -257,11 +257,7 @@ Common::Error GroovieEngine::run() { // the same cd if (getPlatform() != Common::kPlatformIOS) { checkCD(); - - // Initialize the CD - int cd_num = ConfMan.getInt("cdrom"); - if (cd_num >= 0) - _system->getAudioCDManager()->openCD(cd_num); + _system->getAudioCDManager()->open(); } while (!shouldQuit()) { diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 65ab4f31ef..646f908b94 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -68,11 +68,14 @@ bool SoundTowns::init() { _player->driver()->intf()->callback(70, 0x33);*/ _player->driver()->setOutputVolume(1, 118, 118); + // Initialize CD for audio + g_system->getAudioCDManager()->open(); + return true; } void SoundTowns::process() { - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); } void SoundTowns::playTrack(uint8 track) { @@ -95,7 +98,7 @@ void SoundTowns::playTrack(uint8 track) { if (_musicEnabled == 2 && trackNum != -1) { _player->driver()->setOutputVolume(1, 118, 118); g_system->getAudioCDManager()->play(trackNum + 1, loop ? -1 : 1, 0, 0); - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); _cdaPlaying = true; } else if (_musicEnabled) { playEuphonyTrack(READ_LE_UINT32(&res()->cdaTable[tTableIndex]), loop); @@ -108,7 +111,7 @@ void SoundTowns::playTrack(uint8 track) { void SoundTowns::haltTrack() { _lastTrack = -1; g_system->getAudioCDManager()->stop(); - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); _cdaPlaying = false; for (int i = 0; i < 6; i++) @@ -407,6 +410,10 @@ bool SoundPC98::init() { _driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26); bool reslt = _driver->init(); updateVolumeSettings(); + + // Initialize CD for audio + g_system->getAudioCDManager()->open(); + return reslt; } @@ -471,7 +478,7 @@ void SoundPC98::playTrack(uint8 track) { void SoundPC98::haltTrack() { _lastTrack = -1; g_system->getAudioCDManager()->stop(); - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); _driver->reset(); } @@ -529,6 +536,10 @@ bool SoundTownsPC98_v2::init() { if (_resInfo[_currentResourceSet]) if (_resInfo[_currentResourceSet]->cdaTableSize) _vm->checkCD(); + + // Initialize CD for audio + bool hasRealCD = g_system->getAudioCDManager()->open(); + // FIXME: While checking for 'track1.XXX(X)' looks like // a good idea, we should definitely not be doing this // here. Basically our filenaming scheme could change @@ -538,7 +549,7 @@ bool SoundTownsPC98_v2::init() { // check if we have access to CD audio. Resource *r = _vm->resource(); if (_musicEnabled && - (r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla") + (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla") || r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla"))) _musicEnabled = 2; else @@ -580,7 +591,7 @@ void SoundTownsPC98_v2::loadSoundFile(Common::String file) { } void SoundTownsPC98_v2::process() { - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); } void SoundTownsPC98_v2::playTrack(uint8 track) { @@ -610,7 +621,7 @@ void SoundTownsPC98_v2::playTrack(uint8 track) { if (_musicEnabled == 2 && trackNum != -1) { g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0); - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); } else if (_musicEnabled) { _driver->cont(); } @@ -621,7 +632,7 @@ void SoundTownsPC98_v2::playTrack(uint8 track) { void SoundTownsPC98_v2::haltTrack() { _lastTrack = -1; g_system->getAudioCDManager()->stop(); - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); _driver->reset(); } diff --git a/engines/made/made.cpp b/engines/made/made.cpp index ab07ef757b..f1539297ee 100644 --- a/engines/made/made.cpp +++ b/engines/made/made.cpp @@ -67,9 +67,7 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng _console = new MadeConsole(this); - int cd_num = ConfMan.getInt("cdrom"); - if (cd_num >= 0) - _system->getAudioCDManager()->openCD(cd_num); + _system->getAudioCDManager()->open(); _pmvPlayer = new PmvPlayer(this, _mixer); _res = new ResourceReader(); @@ -270,7 +268,7 @@ void MadeEngine::handleEvents() { } } - _system->getAudioCDManager()->updateCD(); + _system->getAudioCDManager()->update(); } diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp index 740c19abad..b9731b1d31 100644 --- a/engines/mads/debugger.cpp +++ b/engines/mads/debugger.cpp @@ -158,7 +158,7 @@ bool Debugger::Cmd_ShowCodes(int argc, const char **argv) { Scene &scene = _vm->_game->_scene; // Copy the depth/walk surface to the background and flag for screen refresh - scene._depthSurface.copyTo(&scene._backgroundSurface); + scene._depthSurface.blitFrom(scene._backgroundSurface); scene._spriteSlots.fullRefresh(); // Draw the locations of scene nodes onto the background diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp index d9a1e53964..fa656a90c1 100644 --- a/engines/mads/dialogs.cpp +++ b/engines/mads/dialogs.cpp @@ -54,21 +54,19 @@ Dialog::~Dialog() { void Dialog::save() { _savedSurface = new MSurface(_width, _height); - _vm->_screen.copyTo(_savedSurface, + _savedSurface->blitFrom(*_vm->_screen, Common::Rect(_position.x, _position.y, _position.x + _width, _position.y + _height), Common::Point()); - _vm->_screen.copyRectToScreen(getBounds()); +// _vm->_screen->copyRectToScreen(getBounds()); } void Dialog::restore() { if (_savedSurface) { - _savedSurface->copyTo(&_vm->_screen, _position); + _vm->_screen->blitFrom(*_savedSurface, _position); delete _savedSurface; _savedSurface = nullptr; - _vm->_screen.copyRectToScreen(getBounds()); - Common::copy(&_dialogPalette[0], &_dialogPalette[8 * 3], &_vm->_palette->_mainPalette[248 * 3]); _vm->_palette->setPalette(&_vm->_palette->_mainPalette[248 * 3], 248, 8); @@ -87,16 +85,16 @@ void Dialog::draw() { // Draw the dialog // Fill entire content of dialog Common::Rect bounds = getBounds(); - _vm->_screen.fillRect(bounds, TEXTDIALOG_BACKGROUND); + _vm->_screen->fillRect(bounds, TEXTDIALOG_BACKGROUND); // Draw the outer edge lines - _vm->_screen.hLine(_position.x + 1, _position.y + _height - 2, + _vm->_screen->hLine(_position.x + 1, _position.y + _height - 2, _position.x + _width - 2, TEXTDIALOG_EDGE); - _vm->_screen.hLine(_position.x, _position.y + _height - 1, + _vm->_screen->hLine(_position.x, _position.y + _height - 1, _position.x + _width - 1, TEXTDIALOG_EDGE); - _vm->_screen.vLine(_position.x + _width - 2, _position.y + 2, + _vm->_screen->vLine(_position.x + _width - 2, _position.y + 2, _position.y + _height - 2, TEXTDIALOG_EDGE); - _vm->_screen.vLine(_position.x + _width - 1, _position.y + 1, + _vm->_screen->vLine(_position.x + _width - 1, _position.y + 1, _position.y + _height - 1, TEXTDIALOG_EDGE); // Draw the gravelly dialog content @@ -125,8 +123,9 @@ void Dialog::calculateBounds() { void Dialog::drawContent(const Common::Rect &r, int seed, byte color1, byte color2) { uint16 currSeed = seed ? seed : 0xB78E; + Graphics::Surface dest = _vm->_screen->getSubArea(r); for (int yp = 0; yp < r.height(); ++yp) { - byte *destP = _vm->_screen.getBasePtr(r.left, r.top + yp); + byte *destP = (byte *)dest.getBasePtr(0, yp); for (int xp = 0; xp < r.width(); ++xp) { uint16 seedAdjust = currSeed; @@ -326,7 +325,7 @@ void TextDialog::draw() { for (int lineNum = 0; lineNum <= _numLines; ++lineNum) { if (_lineXp[lineNum] == -1) { // Draw a line across the entire dialog - _vm->_screen.hLine(_position.x + 2, + _vm->_screen->hLine(_position.x + 2, lineYp + (_font->getHeight() + 1) / 2, _position.x + _width - 4, TEXTDIALOG_BLACK); } else { @@ -336,21 +335,19 @@ void TextDialog::draw() { if (_lineXp[lineNum] & 0x40) ++yp; - _font->writeString(&_vm->_screen, _lines[lineNum], + _font->writeString(_vm->_screen, _lines[lineNum], Common::Point(xp, yp), 1); if (_lineXp[lineNum] & 0x80) { // Draw an underline under the text int lineWidth = _font->getWidth(_lines[lineNum], 1); - _vm->_screen.hLine(xp, yp + _font->getHeight(), xp + lineWidth, + _vm->_screen->hLine(xp, yp + _font->getHeight(), xp + lineWidth, TEXTDIALOG_BLACK); } } lineYp += _font->getHeight() + 1; } - - _vm->_screen.copyRectToScreen(getBounds()); } void TextDialog::calculateBounds() { @@ -360,10 +357,10 @@ void TextDialog::calculateBounds() { if (_position.y == -1) _position.y = 100 - (_height / 2); - if ((_position.x + _width) > _vm->_screen.getWidth()) - _position.x = _vm->_screen.getWidth() - (_position.x + _width); - if ((_position.y + _height) > _vm->_screen.getHeight()) - _position.y = _vm->_screen.getHeight() - (_position.y + _height); + if ((_position.x + _width) > _vm->_screen->w) + _position.x = _vm->_screen->w - (_position.x + _width); + if ((_position.y + _height) > _vm->_screen->h) + _position.y = _vm->_screen->h - (_position.y + _height); } void TextDialog::drawWithInput() { @@ -452,7 +449,7 @@ FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) { } FullScreenDialog::~FullScreenDialog() { - _vm->_screen.resetClipBounds(); + _vm->_screen->resetClipBounds(); _vm->_game->_scene.restrictScene(); } @@ -491,16 +488,13 @@ void FullScreenDialog::display() { game._trigger = 0; // Clear the screen and draw the upper and lower horizontal lines - _vm->_screen.empty(); + _vm->_screen->clear(); _vm->_palette->setLowRange(); - _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2); - _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2); - _vm->_screen.resetClipBounds(); - _vm->_screen.copyRectToScreen(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); + _vm->_screen->hLine(0, 20, MADS_SCREEN_WIDTH, 2); + _vm->_screen->hLine(0, 179, MADS_SCREEN_WIDTH, 2); // Restrict the screen to the area between the two lines - _vm->_screen.setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH, - DIALOG_TOP + MADS_SCENE_HEIGHT)); + _vm->_screen->setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH, DIALOG_TOP + MADS_SCENE_HEIGHT)); _vm->_game->_scene.restrictScene(); if (_screenId > 0) diff --git a/engines/mads/dragonsphere/dragonsphere_scenes.cpp b/engines/mads/dragonsphere/dragonsphere_scenes.cpp index 938931e80d..a18d03d143 100644 --- a/engines/mads/dragonsphere/dragonsphere_scenes.cpp +++ b/engines/mads/dragonsphere/dragonsphere_scenes.cpp @@ -218,7 +218,7 @@ void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, int variant) { } void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) { - byte *destP = depthSurface.getData(); + byte *destP = (byte *)depthSurface.getPixels(); byte *walkMap = new byte[stream->size()]; stream->read(walkMap, stream->size()); diff --git a/engines/mads/events.cpp b/engines/mads/events.cpp index 7463704c4b..de83260b0f 100644 --- a/engines/mads/events.cpp +++ b/engines/mads/events.cpp @@ -98,7 +98,7 @@ void EventsManager::changeCursor() { // Check for hotspot indication pixels along the right-hand and bottom // row. Put together, these give the cursor's hotspot x,y int hotspotX = 0, hotspotY = 0; - byte *cursorData = cursor->getData(); + const byte *cursorData = (const byte *)cursor->getPixels(); for (int idx = 0; idx < cursor->w; ++idx) { if (cursorData[(cursor->h - 1) * cursor->w + idx] != transIndex) hotspotX = idx; @@ -110,7 +110,7 @@ void EventsManager::changeCursor() { // Reduce the cursor data to remove the last column from each row, since // the cursor routines don't have a pitch option byte *destCursor = new byte[(cursor->w - 1) * (cursor->h - 1)]; - byte *srcP = cursorData; + const byte *srcP = cursorData; byte *destP = destCursor; for (int idx = 0; idx < (cursor->h - 1); ++idx) { @@ -217,7 +217,7 @@ bool EventsManager::checkForNextFrameCounter() { _vm->_debugger->onFrame(); // Display the frame - _vm->_screen.updateScreen(); + _vm->_screen->update(); // Signal the ScummVM debugger _vm->_debugger->onFrame(); diff --git a/engines/mads/font.cpp b/engines/mads/font.cpp index 3e6d23fe6f..3828c3df8e 100644 --- a/engines/mads/font.cpp +++ b/engines/mads/font.cpp @@ -167,16 +167,13 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common return x; int bottom = y + height - 1; - if (bottom > surface->getHeight() - 1) { - height -= MIN(height, bottom - (surface->getHeight() - 1)); + if (bottom > surface->h - 1) { + height -= MIN(height, bottom - (surface->h - 1)); } if (height <= 0) return x; - byte *destPtr = surface->getBasePtr(x, y); - uint8 *oldDestPtr = destPtr; - int xPos = x; const char *text = msg.c_str(); @@ -185,10 +182,11 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common int charWidth = _charWidths[(byte)theChar]; if (charWidth > 0) { - if (xPos + charWidth > xEnd) return xPos; + Graphics::Surface dest = surface->getSubArea( + Common::Rect(xPos, y, xPos + charWidth, y + height)); uint8 *charData = &_charData[_charOffs[(byte)theChar]]; int bpp = getBpp(charWidth); @@ -196,6 +194,8 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common charData += bpp * skipY; for (int i = 0; i < height; i++) { + byte *destPtr = (byte *)dest.getBasePtr(0, i); + for (int j = 0; j < bpp; j++) { if (*charData & 0xc0) *destPtr = _fontColors[(*charData & 0xc0) >> 6]; @@ -211,22 +211,13 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common destPtr++; charData++; } - - destPtr += surface->getWidth() - bpp * 4; - } - - destPtr = oldDestPtr + charWidth + spaceWidth; - oldDestPtr = destPtr; - } xPos += charWidth + spaceWidth; - } return xPos; - } int Font::getWidth(const Common::String &msg, int spaceWidth) { diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp index 8ebea2a3b2..0a6741ba7a 100644 --- a/engines/mads/game.cpp +++ b/engines/mads/game.cpp @@ -498,7 +498,7 @@ void Game::loadGame(int slotNumber) { _scene._currentSceneId = -2; _sectionNumber = _scene._nextSceneId / 100; _scene._frameStartTime = _vm->_events->getFrameCounter(); - _vm->_screen._shakeCountdown = -1; + _vm->_screen->_shakeCountdown = -1; // Default the selected inventory item to the first one, if the player has any _scene._userInterface._selectedInvIndex = _objects._inventoryList.size() > 0 ? 0 : -1; @@ -600,7 +600,8 @@ void Game::createThumbnail() { uint8 thumbPalette[PALETTE_SIZE]; _vm->_palette->grabPalette(thumbPalette, 0, PALETTE_COUNT); _saveThumb = new Graphics::Surface(); - ::createThumbnail(_saveThumb, _vm->_screen.getData(), MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT, thumbPalette); + ::createThumbnail(_saveThumb, (const byte *)_vm->_screen->getPixels(), + MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT, thumbPalette); } void Game::syncTimers(SyncType slaveType, int slaveId, SyncType masterType, int masterId) { diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp index deccb5ba4f..29bcd10094 100644 --- a/engines/mads/mads.cpp +++ b/engines/mads/mads.cpp @@ -94,7 +94,7 @@ void MADSEngine::initialize() { _palette = new Palette(this); Font::init(this); _font = new Font(); - _screen.init(); + _screen = new Screen(); _sound = new SoundManager(this, _mixer); _audio = new AudioPlayer(_mixer, getGameID()); _game = Game::init(this); @@ -102,7 +102,7 @@ void MADSEngine::initialize() { loadOptions(); - _screen.empty(); + _screen->clear(); } void MADSEngine::loadOptions() { diff --git a/engines/mads/mads.h b/engines/mads/mads.h index eb808de32f..52f71f7c79 100644 --- a/engines/mads/mads.h +++ b/engines/mads/mads.h @@ -100,7 +100,7 @@ public: GameConversations * _gameConv; Palette *_palette; Resources *_resources; - ScreenSurface _screen; + Screen *_screen; SoundManager *_sound; AudioPlayer *_audio; bool _easyMouse; diff --git a/engines/mads/menu_views.cpp b/engines/mads/menu_views.cpp index 10d5a2179a..9050ca6081 100644 --- a/engines/mads/menu_views.cpp +++ b/engines/mads/menu_views.cpp @@ -253,7 +253,7 @@ void TextView::processCommand() { SceneInfo *sceneInfo = SceneInfo::init(_vm); sceneInfo->_width = MADS_SCREEN_WIDTH; sceneInfo->_height = MADS_SCENE_HEIGHT; - _spareScreens[spareIndex].setSize(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT); + _spareScreens[spareIndex].create(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT); sceneInfo->loadMadsV1Background(screenId, "", SCENEFLAG_TRANSLATE, _spareScreens[spareIndex]); @@ -346,9 +346,11 @@ void TextView::doFrame() { // If a screen transition is in progress and it's time for another column, handle it if (_spareScreen) { - byte *srcP = _spareScreen->getBasePtr(_translationX, 0); - byte *bgP = scene._backgroundSurface.getBasePtr(_translationX, 0); - byte *screenP = (byte *)_vm->_screen.getBasePtr(_translationX, 0); + const byte *srcP = (const byte *)_spareScreen->getBasePtr(_translationX, 0); + byte *bgP = (byte *)scene._backgroundSurface.getBasePtr(_translationX, 0); + + Graphics::Surface dest = _vm->_screen->getSubArea(Common::Rect(_translationX, 0, _translationX + 1, 0)); + byte *screenP = (byte *)dest.getBasePtr(0, 0); for (int y = 0; y < MADS_SCENE_HEIGHT; ++y, srcP += MADS_SCREEN_WIDTH, bgP += MADS_SCREEN_WIDTH, screenP += MADS_SCREEN_WIDTH) { @@ -356,10 +358,6 @@ void TextView::doFrame() { *screenP = *srcP; } - // Flag the column of the screen is modified - _vm->_screen.copyRectToScreen(Common::Rect(_translationX, 0, - _translationX + 1, MADS_SCENE_HEIGHT)); - // Keep moving the column to copy to the right if (++_translationX == MADS_SCREEN_WIDTH) { // Surface transition is complete @@ -571,6 +569,7 @@ void AnimationView::doFrame() { void AnimationView::loadNextResource() { Scene &scene = _vm->_game->_scene; Palette &palette = *_vm->_palette; + Screen &screen = *_vm->_screen; ResourceEntry &resEntry = _resources[_resourceIndex]; Common::Array<PaletteCycle> paletteCycles; @@ -587,12 +586,15 @@ void AnimationView::loadNextResource() { // Handle the bars at the top/bottom if (resEntry._showWhiteBars) { // For animations the screen has been clipped to the middle 156 rows. - // So although it's slightly messy, bypass our screen class entirely, - // and draw the horizontal lines directly on the physiacl screen surface - Graphics::Surface *s = g_system->lockScreen(); - s->hLine(0, 20, MADS_SCREEN_WIDTH, 253); - s->hLine(0, 179, MADS_SCREEN_WIDTH, 253); - g_system->unlockScreen(); + // So although it's slightly messy, temporarily reset clip bounds + // so we can redraw the white lines + Common::Rect clipBounds = screen.getClipBounds(); + screen.resetClipBounds(); + + screen.hLine(0, 20, MADS_SCREEN_WIDTH, 253); + screen.hLine(0, 179, MADS_SCREEN_WIDTH, 253); + + screen.setClipBounds(clipBounds); } // Load the new animation diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp index f768624278..40c69c0f08 100644 --- a/engines/mads/msurface.cpp +++ b/engines/mads/msurface.cpp @@ -32,37 +32,6 @@ namespace MADS { MADSEngine *MSurface::_vm = nullptr; -MSurface::MSurface() { - pixels = nullptr; - _freeFlag = false; -} - -MSurface::MSurface(int width, int height) { - pixels = nullptr; - _freeFlag = false; - setSize(width, height); -} - -MSurface::~MSurface() { - if (_freeFlag) - Graphics::Surface::free(); -} - -void MSurface::setSize(int width, int height) { - if (_freeFlag) - Graphics::Surface::free(); - Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - _freeFlag = true; -} - -void MSurface::setPixels(byte *pData, int horizSize, int vertSize) { - _freeFlag = false; - pixels = pData; - w = pitch = horizSize; - h = vertSize; - format.bytesPerPixel = 1; -} - int MSurface::scaleValue(int value, int scale, int err) { int scaled = 0; while (value--) { @@ -76,7 +45,6 @@ int MSurface::scaleValue(int value, int scale, int err) { } void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect) { - enum { kStatusSkip, kStatusScale, @@ -116,8 +84,8 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo return; int heightAmt = scaledHeight; - byte *src = info.sprite->getData(); - byte *dst = getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY); + const byte *src = (const byte *)info.sprite->getPixels(); + byte *dst = (byte *)getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY); int status = kStatusSkip; byte *scaledLineBuf = new byte[scaledWidth]; @@ -138,7 +106,7 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo byte *lineDst = scaledLineBuf; int curErrX = errX; int width = scaledWidth; - byte *tempSrc = src; + const byte *tempSrc = src; int startX = clipX; while (width > 0) { byte pixel = *tempSrc++; @@ -201,63 +169,136 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo } delete[] scaledLineBuf; - } -void MSurface::empty() { - Common::fill(getBasePtr(0, 0), getBasePtr(0, h), 0); +void MSurface::scrollX(int xAmount) { + if (xAmount == 0) + return; + + byte buffer[80]; + int direction = (xAmount > 0) ? -1 : 1; + int xSize = ABS(xAmount); + assert(xSize <= 80); + + byte *srcP = (byte *)getBasePtr(0, 0); + + for (int y = 0; y < this->h; ++y, srcP += pitch) { + if (direction < 0) { + // Copy area to be overwritten + Common::copy(srcP, srcP + xSize, &buffer[0]); + // Shift the remainder of the line over the given area + Common::copy(srcP + xSize, srcP + this->w, srcP); + // Move buffered area to the end of the line + Common::copy(&buffer[0], &buffer[xSize], srcP + this->w - xSize); + } else { + // Copy area to be overwritten + Common::copy_backward(srcP + this->w - xSize, srcP + this->w, &buffer[80]); + // Shift the remainder of the line over the given area + Common::copy_backward(srcP, srcP + this->w - xSize, srcP + this->w); + // Move buffered area to the start of the line + Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize); + } + } + + markAllDirty(); } -void MSurface::copyFrom(MSurface *src, const Common::Rect &srcBounds, - const Common::Point &destPos, int transparentColor) { - // Validation of the rectangle and position - int destX = destPos.x, destY = destPos.y; - if ((destX >= w) || (destY >= h)) +void MSurface::scrollY(int yAmount) { + if (yAmount == 0) return; - Common::Rect copyRect = srcBounds; - if (destX < 0) { - copyRect.left += -destX; - destX = 0; - } else if (destX + copyRect.width() > w) { - copyRect.right -= destX + copyRect.width() - w; - } - if (destY < 0) { - copyRect.top += -destY; - destY = 0; - } else if (destY + copyRect.height() > h) { - copyRect.bottom -= destY + copyRect.height() - h; + int direction = (yAmount > 0) ? 1 : -1; + int ySize = ABS(yAmount); + assert(ySize < (this->h / 2)); + assert(this->w == pitch); + + int blockSize = ySize * this->w; + byte *tempData = new byte[blockSize]; + byte *pixelsP = (byte *)getBasePtr(0, 0); + + if (direction > 0) { + // Buffer the lines to be overwritten + byte *srcP = (byte *)getBasePtr(0, this->h - ySize); + Common::copy(srcP, srcP + (pitch * ySize), tempData); + // Vertically shift all the lines + Common::copy_backward(pixelsP, pixelsP + (pitch * (this->h - ySize)), + pixelsP + (pitch * this->h)); + // Transfer the buffered lines top the top of the screen + Common::copy(tempData, tempData + blockSize, pixelsP); + } else { + // Buffer the lines to be overwritten + Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData); + // Vertically shift all the lines + Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * this->h), pixelsP); + // Transfer the buffered lines to the bottom of the screen + Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (this->h - ySize))); } - if (!copyRect.isValidRect()) - return; + markAllDirty(); + delete[] tempData; +} - // Copy the specified area +void MSurface::translate(Common::Array<RGB6> &palette) { + for (int y = 0; y < this->h; ++y) { + byte *pDest = (byte *)getBasePtr(0, y); + + for (int x = 0; x < this->w; ++x, ++pDest) { + if (*pDest < 255) // scene 752 has some palette indices of 255 + *pDest = palette[*pDest]._palIndex; + } + } - byte *data = src->getData(); - byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left); - byte *destPtr = (byte *)pixels + (destY * getWidth()) + destX; + markAllDirty(); +} - for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { - if (transparentColor == -1) { - // No transparency, so copy line over - Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr); - } else { - // Copy each byte one at a time checking for the transparency color - for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) - if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr]; +void MSurface::translate(byte map[PALETTE_COUNT]) { + for (int y = 0; y < this->h; ++y) { + byte *pDest = (byte *)getBasePtr(0, y); + + for (int x = 0; x < this->w; ++x, ++pDest) { + *pDest = map[*pDest]; } + } + + markAllDirty(); +} + +MSurface *MSurface::flipHorizontal() const { + MSurface *dest = new MSurface(this->w, this->h); + + for (int y = 0; y < this->h; ++y) { + const byte *srcP = getBasePtr(this->w - 1, y); + byte *destP = dest->getBasePtr(0, y); - srcPtr += src->getWidth(); - destPtr += getWidth(); + for (int x = 0; x < this->w; ++x) + *destP++ = *srcP--; + } + + return dest; +} + +void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, + const Common::Point &destPos, const Common::Rect &srcRect) { + // Loop through the lines + for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) { + const byte *srcP = (const byte *)srcSurface.getBasePtr(srcRect.left, srcRect.top + yCtr); + byte *destP = (byte *)getBasePtr(destPos.x, destPos.y + yCtr); + + // Copy the line over + for (int xCtr = 0; xCtr < srcRect.width(); ++xCtr, ++srcP, ++destP) { + *destP = paletteMap[*srcP]; + } } + + addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + srcRect.width(), + destPos.y + srcRect.height())); } -void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, +void MSurface::copyFrom(MSurface &src, const Common::Point &destPos, int depth, DepthSurface *depthSurface, int scale, bool flipped, int transparentColor) { int destX = destPos.x, destY = destPos.y; - int frameWidth = src->getWidth(); - int frameHeight = src->getHeight(); + int frameWidth = src.w; + int frameHeight = src.h; int direction = flipped ? -1 : 1; int highestDim = MAX(frameWidth, frameHeight); @@ -271,7 +312,8 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, distCtr += scale; if (distCtr < 100) { lineDist[distIndex] = false; - } else { + } + else { lineDist[distIndex] = true; distCtr -= 100; @@ -290,18 +332,20 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, // Special case for quicker drawing of non-scaled images if (scale == 100 || scale == -1) { // Copy the specified area - Common::Rect copyRect(0, 0, src->getWidth(), src->getHeight()); + Common::Rect copyRect(0, 0, src.w, src.h); if (destX < 0) { copyRect.left += -destX; destX = 0; - } else if (destX + copyRect.width() > w) { + } + else if (destX + copyRect.width() > w) { copyRect.right -= destX + copyRect.width() - w; } if (destY < 0) { copyRect.top += -destY; destY = 0; - } else if (destY + copyRect.height() > h) { + } + else if (destY + copyRect.height() > h) { copyRect.bottom -= destY + copyRect.height() - h; } @@ -311,9 +355,9 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, if (flipped) copyRect.moveTo(0, copyRect.top); - byte *data = src->getData(); - byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left); - byte *destPtr = (byte *)pixels + (destY * pitch) + destX; + byte *data = src.getPixels(); + byte *srcPtr = data + (src.w * copyRect.top + copyRect.left); + byte *destPtr = (byte *)getPixels() + (destY * pitch) + destX; if (flipped) srcPtr += copyRect.width() - 1; @@ -329,18 +373,18 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, destPtr[xCtr] = *srcP; } - srcPtr += src->getWidth(); - destPtr += getWidth(); + srcPtr += src.w; + destPtr += this->w; } return; } // Start of draw logic for scaled sprites - const byte *srcPixelsP = src->getData(); + const byte *srcPixelsP = src.getPixels(); - int destRight = this->getWidth() - 1; - int destBottom = this->getHeight() - 1; + int destRight = this->w - 1; + int destBottom = this->h - 1; // Check x bounding area int spriteLeft = 0; @@ -387,7 +431,7 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, spriteLeft = spriteLeft * direction; // Loop through the lines of the sprite - for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src->pitch) { + for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src.pitch) { if (!lineDist[yp]) // Not a display line, so skip it continue; @@ -411,8 +455,8 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, continue; // Get depth of current output pixel in depth surface - Common::Point pt((destP - (byte *)this->pixels) % this->pitch, - (destP - (byte *)this->pixels) / this->pitch); + Common::Point pt((destP - (byte *)getPixels()) % this->pitch, + (destP - (byte *)getPixels()) / this->pitch); int pixelDepth = (depthSurface == nullptr) ? 15 : depthSurface->getDepth(pt); if ((*srcP != transparentColor) && (depth <= pixelDepth)) @@ -424,119 +468,8 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth, // Move to the next destination line destPixelsP += this->pitch; } -} - -void MSurface::scrollX(int xAmount) { - if (xAmount == 0) - return; - - byte buffer[80]; - int direction = (xAmount > 0) ? -1 : 1; - int xSize = ABS(xAmount); - assert(xSize <= 80); - - byte *srcP = getBasePtr(0, 0); - - for (int y = 0; y < this->h; ++y, srcP += pitch) { - if (direction < 0) { - // Copy area to be overwritten - Common::copy(srcP, srcP + xSize, &buffer[0]); - // Shift the remainder of the line over the given area - Common::copy(srcP + xSize, srcP + this->w, srcP); - // Move buffered area to the end of the line - Common::copy(&buffer[0], &buffer[xSize], srcP + this->w - xSize); - } else { - // Copy area to be overwritten - Common::copy_backward(srcP + this->w - xSize, srcP + this->w, &buffer[80]); - // Shift the remainder of the line over the given area - Common::copy_backward(srcP, srcP + this->w - xSize, srcP + this->w); - // Move buffered area to the start of the line - Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize); - } - } -} - -void MSurface::scrollY(int yAmount) { - if (yAmount == 0) - return; - - int direction = (yAmount > 0) ? 1 : -1; - int ySize = ABS(yAmount); - assert(ySize < (this->h / 2)); - assert(this->w == pitch); - - int blockSize = ySize * this->w; - byte *tempData = new byte[blockSize]; - byte *pixelsP = getBasePtr(0, 0); - - if (direction > 0) { - // Buffer the lines to be overwritten - byte *srcP = (byte *)getBasePtr(0, this->h - ySize); - Common::copy(srcP, srcP + (pitch * ySize), tempData); - // Vertically shift all the lines - Common::copy_backward(pixelsP, pixelsP + (pitch * (this->h - ySize)), - pixelsP + (pitch * this->h)); - // Transfer the buffered lines top the top of the screen - Common::copy(tempData, tempData + blockSize, pixelsP); - } else { - // Buffer the lines to be overwritten - Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData); - // Vertically shift all the lines - Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * this->h), pixelsP); - // Transfer the buffered lines to the bottom of the screen - Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (this->h - ySize))); - } - - delete[] tempData; -} - -void MSurface::translate(Common::Array<RGB6> &palette) { - for (int y = 0; y < this->h; ++y) { - byte *pDest = getBasePtr(0, y); - - for (int x = 0; x < this->w; ++x, ++pDest) { - if (*pDest < 255) // scene 752 has some palette indices of 255 - *pDest = palette[*pDest]._palIndex; - } - } -} - -void MSurface::translate(byte map[PALETTE_COUNT]) { - for (int y = 0; y < this->h; ++y) { - byte *pDest = getBasePtr(0, y); - - for (int x = 0; x < this->w; ++x, ++pDest) { - *pDest = map[*pDest]; - } - } -} - -MSurface *MSurface::flipHorizontal() const { - MSurface *dest = new MSurface(this->w, this->h); - - for (int y = 0; y < this->h; ++y) { - const byte *srcP = getBasePtr(this->w - 1, y); - byte *destP = dest->getBasePtr(0, y); - - for (int x = 0; x < this->w; ++x) - *destP++ = *srcP--; - } - - return dest; -} - -void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, - const Common::Point &destPos, const Common::Rect &srcRect) { - // Loop through the lines - for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) { - const byte *srcP = srcSurface.getBasePtr(srcRect.left, srcRect.top + yCtr); - byte *destP = getBasePtr(destPos.x, destPos.y + yCtr); - // Copy the line over - for (int xCtr = 0; xCtr < srcRect.width(); ++xCtr, ++srcP, ++destP) { - *destP = paletteMap[*srcP]; - } - } + addDirtyRect(Common::Rect(destX, destY, destX + frameWidth, destY + frameHeight)); } /*------------------------------------------------------------------------*/ @@ -544,26 +477,26 @@ void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, int DepthSurface::getDepth(const Common::Point &pt) { if (_depthStyle == 2) { int bits = (3 - (pt.x % 4)) * 2; - byte v = *getBasePtr(pt.x >> 2, pt.y); + byte v = *(const byte *)getBasePtr(pt.x >> 2, pt.y); return v >> bits; } else { if (pt.x < 0 || pt.y < 0 || pt.x >= this->w || pt.y >= this->h) return 0; - return *getBasePtr(pt.x, pt.y) & 0xF; + return *(const byte *)getBasePtr(pt.x, pt.y) & 0xF; } } int DepthSurface::getDepthHighBit(const Common::Point &pt) { if (_depthStyle == 2) { int bits = (3 - (pt.x % 4)) * 2; - byte v = *getBasePtr(pt.x >> 2, pt.y); + byte v = *(const byte *)getBasePtr(pt.x >> 2, pt.y); return (v >> bits) & 2; } else { if (pt.x < 0 || pt.y < 0 || pt.x >= this->w || pt.y >= this->h) return 0; - return *getBasePtr(pt.x, pt.y) & 0x80; + return *(const byte *)getBasePtr(pt.x, pt.y) & 0x80; } } diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h index 8930737b0a..e92770900d 100644 --- a/engines/mads/msurface.h +++ b/engines/mads/msurface.h @@ -25,7 +25,7 @@ #include "common/scummsys.h" #include "common/rect.h" -#include "graphics/surface.h" +#include "graphics/managed_surface.h" #include "mads/palette.h" namespace MADS { @@ -50,10 +50,14 @@ struct SpriteInfo { /* * MADS graphics surface */ -class MSurface : public Graphics::Surface { +class MSurface : virtual public Graphics::ManagedSurface { +private: + /** + * Helper method for calculating new dimensions when scaling a sprite + */ + int scaleValue(int value, int scale, int err); protected: static MADSEngine *_vm; - bool _freeFlag; public: /** * Sets the engine reference used all surfaces @@ -61,11 +65,6 @@ public: static void setVm(MADSEngine *vm) { _vm = vm; } /** - * Helper method for calculating new dimensions when scaling a sprite - */ - static int scaleValue(int value, int scale, int err); - - /** * Base method for descendents to load their contents */ virtual void load(const Common::String &resName) {} @@ -73,126 +72,50 @@ public: /** * Basic constructor */ - MSurface(); + MSurface() : Graphics::ManagedSurface() {} /** * Constructor for a surface with fixed dimensions */ - MSurface(int width, int height); + MSurface(int width, int height) : Graphics::ManagedSurface(width, height) {} /** * Destructor */ - virtual ~MSurface(); - - /** - * Reinitializes a surface to have a given set of dimensions - */ - void setSize(int width, int height); - - /** - * Sets the pixels the surface is associated with - * @remarks The surface will not free the data block - */ - void setPixels(byte *pData, int horizSize, int vertSize); - - /** - * Draws an arbitrary line on the screen using a specified color - * @param startPos Starting position - * @param endPos Ending position - * @param color Color to use - */ - void line(const Common::Point &startPos, const Common::Point &endPos, byte color); - - /** - * Draws a sprite - * @param pt Position to draw sprite at - * @param info General sprite details - * @param clipRect Clipping rectangle to constrain sprite drawing within - */ - void drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect); - - /** - * Returns the width of the surface - */ - int getWidth() const { return w; } - - /** - * Returns the height of the surface - */ - int getHeight() const { return h; } + virtual ~MSurface() {} /** - * Returns the size of the surface as a Rect + * Return a rect containing the bounds of the surface */ - Common::Rect getBounds() const { - return Common::Rect(0, 0, w, h); - } + Common::Rect getBounds() { return Common::Rect(0, 0, this->w, this->h); } /** - * Returns a pointer to the surface data + * Return the pixels for the surface */ - byte *getData() { return (byte *)Graphics::Surface::getPixels(); } + inline byte *getPixels() { return (byte *)Graphics::ManagedSurface::getPixels(); } /** - * Returns a pointer to a given position within the surface + * Return the pixels for the surface */ - byte *getBasePtr(int x, int y) { return (byte *)Graphics::Surface::getBasePtr(x, y); } + inline const void *getPixels() const { return (const byte *)Graphics::ManagedSurface::getPixels(); } /** - * Returns a pointer to a given position within the surface - */ - const byte *getBasePtr(int x, int y) const { return (const byte *)Graphics::Surface::getBasePtr(x, y); } - - /** - * Clears the surface - */ - void empty(); - - /** - * Copys a sub-section of another surface into the current one. - * @param src Source surface - * @param srcBounds Area of source surface to copy - * @param destPos Destination position to draw in current surface - * @param transparentColor Transparency palette index - */ - void copyFrom(MSurface *src, const Common::Rect &srcBounds, const Common::Point &destPos, - int transparentColor = -1); - - /** - * Copys a sub-section of another surface into the current one. - * @param src Source surface - * @param destPos Destination position to draw in current surface - * @param depth Depth of sprite - * @param depthSurface Depth surface to use with sprite depth - * @param scale Scale for image - * @param flipped Flag for whether image is to be flipped - * @param transparentColor Transparency palette index - */ - void copyFrom(MSurface *src, const Common::Point &destPos, int depth, DepthSurface *depthSurface, - int scale, bool flipped, int transparentColor = -1); - - /** - * Copies the surface to a given destination surface + * Return a pointer to a given position on the surface */ - void copyTo(MSurface *dest, int transparentColor = -1) { - dest->copyFrom(this, Common::Rect(w, h), Common::Point(), transparentColor); - } + byte *getBasePtr(int x, int y) { return (byte *)Graphics::ManagedSurface::getBasePtr(x, y); } /** - * Copies the surface to a given destination surface + * Return a pointer to a given position on the surface */ - void copyTo(MSurface *dest, const Common::Point &pt, int transparentColor = -1) { - dest->copyFrom(this, Common::Rect(w, h), pt, transparentColor); - } + inline const byte *getBasePtr(int x, int y) const { return (const byte *)Graphics::ManagedSurface::getBasePtr(x, y); } /** - * Copies the surface to a given destination surface + * Draws a sprite + * @param pt Position to draw sprite at + * @param info General sprite details + * @param clipRect Clipping rectangle to constrain sprite drawing within */ - void copyTo(MSurface *dest, const Common::Rect &srcBounds, const Common::Point &destPos, - int transparentColor = -1) { - dest->copyFrom(this, srcBounds, destPos, transparentColor); - } + void drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect); /** * Scroll the screen horizontally by a given amount @@ -227,6 +150,19 @@ public: */ void copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, const Common::Point &destPos, const Common::Rect &srcRect); + + /** + * Copys a sub-section of another surface into the current one. + * @param src Source surface + * @param destPos Destination position to draw in current surface + * @param depth Depth of sprite + * @param depthSurface Depth surface to use with sprite depth + * @param scale Scale for image + * @param flipped Flag for whether image is to be flipped + * @param transparentColor Transparency palette index + */ + void copyFrom(MSurface &src, const Common::Point &destPos, int depth, DepthSurface *depthSurface, + int scale, bool flipped, int transparentColor = -1); }; class DepthSurface : public MSurface { @@ -239,7 +175,7 @@ public: /** * Constructor */ - DepthSurface() : _depthStyle(0) {} + DepthSurface() : MSurface(), _depthStyle(0) {} /** * Returns the depth at a given position diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp index 58e60fe323..2af80f517e 100644 --- a/engines/mads/nebular/dialogs_nebular.cpp +++ b/engines/mads/nebular/dialogs_nebular.cpp @@ -438,11 +438,10 @@ void CopyProtectionDialog::show() { Common::KeyState curKey; const Common::Rect inputArea(110, 165, 210, 175); MSurface *origInput = new MSurface(inputArea.width(), inputArea.height()); - _vm->_screen.frameRect(inputArea, TEXTDIALOG_BLACK); - _vm->_screen.copyTo(origInput, inputArea, Common::Point(0, 0)); - _font->setColors(TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE); - _vm->_screen.copyRectToScreen(inputArea); - _vm->_screen.updateScreen(); + _vm->_screen->frameRect(inputArea, TEXTDIALOG_BLACK); + origInput->blitFrom(*_vm->_screen, inputArea, Common::Point(0, 0)); + _font->setColors(TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE); + _vm->_screen->update(); bool firstTime = true; @@ -470,11 +469,10 @@ void CopyProtectionDialog::show() { _textInput = _hogEntry._word[0]; } - _vm->_screen.copyFrom(origInput, Common::Rect(0, 0, inputArea.width(), inputArea.height()), Common::Point(inputArea.left, inputArea.top)); - _font->writeString(&_vm->_screen, _textInput, + _vm->_screen->blitFrom(*origInput, Common::Point(inputArea.left, inputArea.top)); + _font->writeString(_vm->_screen, _textInput, Common::Point(inputArea.left + 2, inputArea.top + 1), 1); - _vm->_screen.copyRectToScreen(inputArea); - _vm->_screen.updateScreen(); + _vm->_screen->update(); } origInput->free(); @@ -537,7 +535,7 @@ void PictureDialog::save() { // Save the entire screen _savedSurface = new MSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); - _vm->_screen.copyTo(_savedSurface); + _savedSurface->blitFrom(*_vm->_screen); // Save palette information Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE], &_palette[0]); @@ -568,7 +566,7 @@ void PictureDialog::save() { // Remap the greyed out screen to use the small greyscale range // at the top end of the palette - _vm->_screen.translate(map); + _vm->_screen->translate(map); // Load the inventory picture Common::String setName = Common::String::format("*OB%.3d.SS", _objectId); @@ -578,13 +576,12 @@ void PictureDialog::save() { // Get the inventory frame, and adjust the dialog position to allow for it MSprite *frame = asset->getFrame(0); _position.y = frame->h + 12; - if ((_position.y + _height) > _vm->_screen.getHeight()) - _position.y -= (_position.y + _height) - _vm->_screen.getHeight(); + if ((_position.y + _height) > _vm->_screen->h) + _position.y -= (_position.y + _height) - _vm->_screen->h; // Draw the inventory picture - frame->copyTo(&_vm->_screen, Common::Point(160 - frame->w / 2, 6), + _vm->_screen->transBlitFrom(*frame, Common::Point(160 - frame->w / 2, 6), frame->getTransparencyIndex()); - _vm->_screen.copyRectToScreen(_vm->_screen.getBounds()); // Adjust the dialog colors to use TEXTDIALOG_CONTENT1 -= 10; @@ -598,13 +595,11 @@ void PictureDialog::save() { void PictureDialog::restore() { if (_savedSurface) { - _savedSurface->copyTo(&_vm->_screen); + _vm->_screen->blitFrom(*_savedSurface); _savedSurface->free(); delete _savedSurface; _savedSurface = nullptr; - _vm->_screen.copyRectToScreen(_vm->_screen.getBounds()); - // Restore palette information Palette &palette = *_vm->_palette; Common::copy(&_palette[0], &_palette[PALETTE_SIZE], &palette._mainPalette[0]); @@ -691,7 +686,6 @@ void GameDialog::display() { } GameDialog::~GameDialog() { - _vm->_screen.resetClipBounds(); _vm->_game->_scene._currentSceneId = RETURNING_FROM_DIALOG; } diff --git a/engines/mads/nebular/menu_nebular.cpp b/engines/mads/nebular/menu_nebular.cpp index 0520294b29..cd81efe0f0 100644 --- a/engines/mads/nebular/menu_nebular.cpp +++ b/engines/mads/nebular/menu_nebular.cpp @@ -384,8 +384,8 @@ void AdvertView::show() { // Load the advert background onto the screen SceneInfo *sceneInfo = SceneInfo::init(_vm); sceneInfo->load(screenId, 0, Common::String(), 0, _vm->_game->_scene._depthSurface, - _vm->_screen); - _vm->_screen.copyRectToScreen(_vm->_screen.getBounds()); + *_vm->_screen); + _vm->_screen->markAllDirty(); _vm->_palette->setFullPalette(_vm->_palette->_mainPalette); delete sceneInfo; diff --git a/engines/mads/nebular/nebular_scenes.cpp b/engines/mads/nebular/nebular_scenes.cpp index da419a70a2..40228b4b7d 100644 --- a/engines/mads/nebular/nebular_scenes.cpp +++ b/engines/mads/nebular/nebular_scenes.cpp @@ -323,8 +323,8 @@ void SceneInfoNebular::loadCodes(MSurface &depthSurface, int variant) { } void SceneInfoNebular::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) { - byte *destP = depthSurface.getData(); - byte *endP = depthSurface.getBasePtr(0, depthSurface.h); + byte *destP = (byte *)depthSurface.getPixels(); + byte *endP = (byte *)depthSurface.getBasePtr(0, depthSurface.h); byte runLength = stream->readByte(); while (destP < endP && runLength > 0) { diff --git a/engines/mads/nebular/nebular_scenes3.cpp b/engines/mads/nebular/nebular_scenes3.cpp index 0fb13a706c..7323ee893d 100644 --- a/engines/mads/nebular/nebular_scenes3.cpp +++ b/engines/mads/nebular/nebular_scenes3.cpp @@ -2818,7 +2818,7 @@ void Scene318::step() { if (_internCounter >= 3600) { _vm->_sound->command(59); - _vm->_screen._shakeCountdown = 20; + _vm->_screen->_shakeCountdown = 20; _internWalkingFl = true; } } @@ -3288,22 +3288,22 @@ void Scene319::step() { if (_animMode == 2) { if (_animFrame == 13) - _vm->_screen._shakeCountdown = 40; + _vm->_screen->_shakeCountdown = 40; if (_animFrame == 16) - _vm->_screen._shakeCountdown = 1; + _vm->_screen->_shakeCountdown = 1; } if (_animMode == 3) { if (_animFrame == 11) - _vm->_screen._shakeCountdown = 60; + _vm->_screen->_shakeCountdown = 60; if (_animFrame == 18) - _vm->_screen._shakeCountdown = 1; + _vm->_screen->_shakeCountdown = 1; } if ((_animMode == 4) && (_animFrame == 16)) - _vm->_screen._shakeCountdown = 80; + _vm->_screen->_shakeCountdown = 80; if ((nextFrame >= 0) && (nextFrame != _scene->_animation[0]->getCurrentFrame())) { _scene->_animation[0]->setCurrentFrame(nextFrame); @@ -3326,7 +3326,7 @@ void Scene319::step() { _animFrame = _scene->_animation[0]->getCurrentFrame(); _slacheTalkingFl = true; - _vm->_screen._shakeCountdown = 1; + _vm->_screen->_shakeCountdown = 1; for (int i = 0; i <= 1; i++) { int oldIdx = _globals._sequenceIndexes[i]; @@ -3350,7 +3350,7 @@ void Scene319::step() { _vm->_palette->setColorValues(0, 0, 0); _vm->_palette->fadeOut(_vm->_palette->_mainPalette, nullptr, 18, 228, 248, 0, 1, 16); - _vm->_screen._shakeCountdown = 1; + _vm->_screen->_shakeCountdown = 1; _scene->_reloadSceneFlag = true; break; @@ -3731,7 +3731,7 @@ void Scene320::step() { case 417: case 457: - _vm->_screen._shakeCountdown = 40; + _vm->_screen->_shakeCountdown = 40; _vm->_sound->command(59); break; diff --git a/engines/mads/phantom/phantom_scenes.cpp b/engines/mads/phantom/phantom_scenes.cpp index f7a7153fbe..7ef627ceeb 100644 --- a/engines/mads/phantom/phantom_scenes.cpp +++ b/engines/mads/phantom/phantom_scenes.cpp @@ -191,7 +191,7 @@ void SceneInfoPhantom::loadCodes(MSurface &depthSurface, int variant) { } void SceneInfoPhantom::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) { - byte *destP = depthSurface.getData(); + byte *destP = (byte *)depthSurface.getPixels(); byte *walkMap = new byte[stream->size()]; stream->read(walkMap, stream->size()); diff --git a/engines/mads/rails.cpp b/engines/mads/rails.cpp index ee0ca98cd3..46d9e0ebd3 100644 --- a/engines/mads/rails.cpp +++ b/engines/mads/rails.cpp @@ -149,7 +149,7 @@ int Rails::scanPath(const Common::Point &srcPos, const Common::Point &destPos) { ++xDiff; ++yDiff; - const byte *srcP = _depthSurface->getBasePtr(srcPos.x, srcPos.y); + const byte *srcP = (const byte *)_depthSurface->getBasePtr(srcPos.x, srcPos.y); int index = xAmount; // Outer loop diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp index 83ab1151a9..66f56f9407 100644 --- a/engines/mads/scene.cpp +++ b/engines/mads/scene.cpp @@ -89,8 +89,7 @@ Scene::~Scene() { } void Scene::restrictScene() { - _sceneSurface.init(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH, - _vm->_screen.getPixels(), Graphics::PixelFormat::createFormatCLUT8()); + _sceneSurface.create(*_vm->_screen, Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT)); } void Scene::clearVocab() { @@ -517,7 +516,7 @@ void Scene::drawElements(ScreenTransition transitionType, bool surfaceFlag) { if (_posAdjust != Common::Point(0, 0)) warning("Adjust used %d %d", _posAdjust.x, _posAdjust.y); // Copy background for the dirty areas to the screen - _dirtyAreas.copy(&_backgroundSurface, &_vm->_screen, _posAdjust); + _dirtyAreas.copy(&_backgroundSurface, _vm->_screen, _posAdjust); // Handle dirty areas for foreground objects _spriteSlots.setDirtyAreas(); @@ -528,11 +527,11 @@ void Scene::drawElements(ScreenTransition transitionType, bool surfaceFlag) { _spriteSlots.drawSprites(&_sceneSurface); // Draw text elements onto the view - _textDisplay.draw(&_vm->_screen); + _textDisplay.draw(_vm->_screen); if (transitionType) { // Fading in the screen - _vm->_screen.transition(transitionType, surfaceFlag); + _vm->_screen->transition(transitionType, surfaceFlag); _vm->_sound->startQueuedCommands(); } else { // Copy dirty areas to the screen diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp index 7b0e64c1fe..5323178ec7 100644 --- a/engines/mads/scene_data.cpp +++ b/engines/mads/scene_data.cpp @@ -242,13 +242,13 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, int height = _height; if (!bgSurface.getPixels() || (bgSurface.w != width) || (bgSurface.h != height)) { - bgSurface.setSize(width, height); + bgSurface.create(width, height); } if (_depthStyle == 2) width >>= 2; if (!depthSurface.getPixels()) { - depthSurface.setSize(width, height); + depthSurface.create(width, height); } loadCodes(depthSurface, variant); @@ -288,7 +288,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, assert(asset && _depthStyle != 2); MSprite *spr = asset->getFrame(si._frameNumber); - bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface, + bgSurface.copyFrom(*spr, si._position, si._depth, &depthSurface, si._scale, false, spr->getTransparencyIndex()); } @@ -455,7 +455,7 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName, newHeight = tileCount * tileHeight; if (bgSurface.w != newWidth || bgSurface.h != newHeight) - bgSurface.setSize(newWidth, newHeight); + bgSurface.create(newWidth, newHeight); // -------------------------------------------------------------------------------- @@ -477,7 +477,7 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName, //debugCN(kDebugGraphics, "Tile: %i, compressed size: %i\n", i, compressedTileDataSize); - newTile->empty(); + newTile->clear(); byte *compressedTileData = new byte[compressedTileDataSize]; @@ -503,7 +503,8 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName, TileSetIterator tile = tileSet.begin(); for (int i = 0; i < tileIndex; i++) ++tile; - ((*tile).get())->copyTo(&bgSurface, Common::Point(x * tileWidth, y * tileHeight)); + + bgSurface.blitFrom(*(*tile).get(), Common::Point(x * tileWidth, y * tileHeight)); ((*tile).get())->free(); } } diff --git a/engines/mads/screen.cpp b/engines/mads/screen.cpp index 90fbbe7e2a..05f9de61e2 100644 --- a/engines/mads/screen.cpp +++ b/engines/mads/screen.cpp @@ -69,7 +69,6 @@ void DirtyArea::setArea(int width, int height, int maxWidth, int maxHeight) { _active = true; } - void DirtyArea::setSpriteSlot(const SpriteSlot *spriteSlot) { int width, height; Scene &scene = _vm->_game->_scene; @@ -215,12 +214,13 @@ void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common: Common::Point destPos(srcBounds.left, srcBounds.top); if ((*this)[i]._active && bounds.isValidRect()) { - srcSurface->copyTo(destSurface, bounds, destPos); + destSurface->blitFrom(*srcSurface, bounds, destPos); } } } void DirtyAreas::copyToScreen() { +/* for (uint i = 0; i < size(); ++i) { const Common::Rect &bounds = (*this)[i]._bounds; @@ -229,9 +229,10 @@ void DirtyAreas::copyToScreen() { continue; if ((*this)[i]._active && (*this)[i]._bounds.isValidRect()) { - _vm->_screen.copyRectToScreen(bounds); + _vm->_screen->copyRectToScreen(bounds); } } + */ } void DirtyAreas::reset() { @@ -554,38 +555,17 @@ void ScreenObjects::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -ScreenSurface::ScreenSurface() { +Screen::Screen(): Graphics::Screen(), MSurface() { + // Create the screen surface separately on another surface, since the screen + // surface will be subject to change as the clipping area is altered + _rawSurface.create(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); + resetClipBounds(); + _shakeCountdown = -1; _random = 0x4D2; - _surfacePixels = nullptr; -} - -void ScreenSurface::init() { - // Set the size for the screen - setSize(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); - - // Store a copy of the raw pixels pointer for the screen, since the surface - // itself may be later changed to only a subset of the screen - _surfacePixels = (byte *)getPixels(); - _freeFlag = false; -} - -ScreenSurface::~ScreenSurface() { - ::free(_surfacePixels); } -void ScreenSurface::copyRectToScreen(const Common::Rect &bounds) { - const byte *buf = getBasePtr(bounds.left, bounds.top); - - Common::Rect destBounds = bounds; - destBounds.translate(_clipBounds.left, _clipBounds.top); - - if (bounds.width() != 0 && bounds.height() != 0) - g_system->copyRectToScreen(buf, this->pitch, destBounds.left, destBounds.top, - destBounds.width(), destBounds.height()); -} - -void ScreenSurface::updateScreen() { +void Screen::update() { if (_shakeCountdown >= 0) { _random = _random * 5 + 1; int offset = (_random >> 8) & 3; @@ -596,27 +576,42 @@ void ScreenSurface::updateScreen() { // offset width shown at the very right. The offset changes to give // an effect of shaking the screen offset *= 4; - const byte *buf = getBasePtr(offset, 0); - g_system->copyRectToScreen(buf, this->pitch, 0, 0, - this->pitch - offset, this->h); + const byte *buf = (const byte *)getBasePtr(offset, 0); + g_system->copyRectToScreen(buf, this->pitch, 0, 0, this->pitch - offset, this->h); if (offset > 0) - g_system->copyRectToScreen(this->pixels, this->pitch, + g_system->copyRectToScreen(getPixels(), this->pitch, this->pitch - offset, 0, offset, this->h); + return; } - g_system->updateScreen(); + // Reset any clip bounds if active whilst the screen is updated + Common::Rect clipBounds = getClipBounds(); + resetClipBounds(); + + // Update the screen + Graphics::Screen::update(); + + // Revert back to whatever clipping is active + setClipBounds(clipBounds); } -void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag) { +void Screen::transition(ScreenTransition transitionType, bool surfaceFlag) { Palette &pal = *_vm->_palette; Scene &scene = _vm->_game->_scene; byte palData[PALETTE_SIZE]; + // The original loads the new scene to the screen surface for some of the + // transition types like fade out/in, so we need to clear the dirty rects so + // it doesn't prematurely get blitted to the physical screen before fade out + Common::Rect clipBounds = getClipBounds(); + clearDirtyRects(); + switch (transitionType) { case kTransitionFadeIn: - case kTransitionFadeOutIn: + case kTransitionFadeOutIn: { Common::fill(&pal._colorValues[0], &pal._colorValues[3], 0); Common::fill(&pal._colorFlags[0], &pal._colorFlags[3], false); + resetClipBounds(); if (transitionType == kTransitionFadeOutIn) { // Fade out @@ -628,9 +623,11 @@ void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag Common::fill(&palData[0], &palData[PALETTE_SIZE], 0); pal.setFullPalette(palData); - copyRectToScreen(getBounds()); + markAllDirty(); + update(); pal.fadeIn(palData, pal._mainPalette, 0, 256, 0, 1, 1, 16); break; + } case kTransitionBoxInBottomLeft: case kTransitionBoxInBottomRight: @@ -666,19 +663,13 @@ void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag // Quick transitions break; } -} -void ScreenSurface::setClipBounds(const Common::Rect &r) { - _clipBounds = r; - setPixels(_surfacePixels + pitch * r.top + r.left, r.width(), r.height()); - this->pitch = MADS_SCREEN_WIDTH; + // Reset clipping + markAllDirty(); + setClipBounds(clipBounds); } -void ScreenSurface::resetClipBounds() { - setClipBounds(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); -} - -void ScreenSurface::panTransition(MSurface &newScreen, byte *palData, int entrySide, +void Screen::panTransition(MSurface &newScreen, byte *palData, int entrySide, const Common::Point &srcPos, const Common::Point &destPos, ThroughBlack throughBlack, bool setPalette, int numTicks) { EventsManager &events = *_vm->_events; @@ -735,8 +726,6 @@ void ScreenSurface::panTransition(MSurface &newScreen, byte *palData, int entryS srcPos.x + xAt + 1, srcPos.y + size.y)); } - copyRectToScreen(Common::Rect(xAt, destPos.y, xAt + 1, destPos.y + size.y)); - // Slight delay events.pollEvents(); g_system->delayMillis(1); @@ -747,16 +736,18 @@ void ScreenSurface::panTransition(MSurface &newScreen, byte *palData, int entryS } if (throughBlack == THROUGH_BLACK2) { + /* Common::Rect r(srcPos.x, srcPos.y, srcPos.x + size.x, srcPos.y + size.y); copyRectToSurface(newScreen, destPos.x, destPos.y, r); copyRectToScreen(r); + */ } } /** * Translates the current screen from the old palette to the new palette */ -void ScreenSurface::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteMap) { +void Screen::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteMap) { Palette &palette = *_vm->_palette; byte oldPalette[PALETTE_SIZE]; byte oldMap[PALETTE_COUNT]; @@ -775,7 +766,7 @@ void ScreenSurface::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteM destP += 2 * RGB_SIZE; } - Common::Rect oldClip = _clipBounds; + Common::Rect oldClip = getClipBounds(); resetClipBounds(); copyRectTranslate(*this, oldMap, Common::Point(0, 0), @@ -790,7 +781,7 @@ void ScreenSurface::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteM * Palettes consist of 128 RGB entries for the foreground and background * respectively, with the two interleaved together. So the start */ -void ScreenSurface::swapPalette(const byte *palData, byte swapTable[PALETTE_COUNT], +void Screen::swapPalette(const byte *palData, byte swapTable[PALETTE_COUNT], bool foreground) { int start = foreground ? 1 : 0; const byte *dynamicList = &palData[start * RGB_SIZE]; @@ -815,5 +806,12 @@ void ScreenSurface::swapPalette(const byte *palData, byte swapTable[PALETTE_COUN } } +void Screen::setClipBounds(const Common::Rect &r) { + create(_rawSurface, r); +} + +void Screen::resetClipBounds() { + setClipBounds(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT)); +} } // End of namespace MADS diff --git a/engines/mads/screen.h b/engines/mads/screen.h index d910e88633..626080580e 100644 --- a/engines/mads/screen.h +++ b/engines/mads/screen.h @@ -25,6 +25,7 @@ #include "common/scummsys.h" #include "common/array.h" +#include "graphics/screen.h" #include "mads/msurface.h" #include "mads/action.h" @@ -207,11 +208,10 @@ public: void synchronize(Common::Serializer &s); }; -class ScreenSurface : public MSurface { +class Screen : virtual public Graphics::Screen, virtual public MSurface { private: uint16 _random; - byte *_surfacePixels; - Common::Rect _clipBounds; + MSurface _rawSurface; void panTransition(MSurface &newScreen, byte *palData, int entrySide, const Common::Point &srcPos, const Common::Point &destPos, @@ -226,36 +226,40 @@ public: /** * Constructor */ - ScreenSurface(); + Screen(); /** * Destructor */ - ~ScreenSurface(); + virtual ~Screen() {} /** - * Initialize the surface + * Updates the physical screen with contents of the internal surface */ - void init(); + virtual void update(); /** - * Copys an area of the screen surface to the ScmmVM physical screen buffer - * @param bounds Area of screen surface to copy + * Transition to a new screen with a given effect */ - void copyRectToScreen(const Common::Rect &bounds); + void transition(ScreenTransition transitionType, bool surfaceFlag); /** - * Updates the screen with the contents of the surface + * Set the screen drawing area to a sub-section of the real screen */ - void updateScreen(); - - void transition(ScreenTransition transitionType, bool surfaceFlag); - void setClipBounds(const Common::Rect &r); + /** + * Reset back to drawing on the entirety of the screen + */ void resetClipBounds(); - const Common::Rect &getClipBounds() { return _clipBounds; } + /** + * Return the current drawing/clip area + */ + const Common::Rect getClipBounds() const { + const Common::Point pt = getOffsetFromOwner(); + return Common::Rect(pt.x, pt.y, pt.x + this->w, pt.y + this->h); + } }; } // End of namespace MADS diff --git a/engines/mads/sequence.cpp b/engines/mads/sequence.cpp index 50b37de7ea..2afe089d4a 100644 --- a/engines/mads/sequence.cpp +++ b/engines/mads/sequence.cpp @@ -237,8 +237,8 @@ bool SequenceList::loadSprites(int seqIndex) { if ((seqEntry._flags != 0) || (seqEntry._dynamicHotspotIndex >= 0)) { SpriteAsset &spriteSet = *scene._sprites[seqEntry._spritesIndex]; MSprite *frame = spriteSet.getFrame(seqEntry._frameIndex - 1); - int width = frame->getWidth() * seqEntry._scale / 200; - int height = frame->getHeight() * seqEntry._scale / 100; + int width = frame->w * seqEntry._scale / 200; + int height = frame->h * seqEntry._scale / 100; Common::Point pt = spriteSlot._position; // Handle sprite movement, if present diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp index 0a1c0b710d..fc8ddf22d2 100644 --- a/engines/mads/sprites.cpp +++ b/engines/mads/sprites.cpp @@ -59,10 +59,10 @@ MSprite::MSprite() : MSurface() { } MSprite::MSprite(Common::SeekableReadStream *source, const Common::Array<RGB6> &palette, - const Common::Rect &bounds) - : MSurface(bounds.width(), bounds.height()), - _offset(Common::Point(bounds.left, bounds.top)), _transparencyIndex(TRANSPARENT_COLOR_INDEX) { + const Common::Rect &bounds): MSurface(), _transparencyIndex(TRANSPARENT_COLOR_INDEX), + _offset(Common::Point(bounds.left, bounds.top)) { // Load the sprite data + create(bounds.width(), bounds.height()); loadSprite(source, palette); } @@ -74,8 +74,8 @@ void MSprite::loadSprite(Common::SeekableReadStream *source, byte *outp, *lineStart; bool newLine = false; - outp = getData(); - lineStart = getData(); + outp = getPixels(); + lineStart = getPixels(); int spriteSize = this->w * this->h; byte transIndex = getTransparencyIndex(); Common::fill(outp, outp + spriteSize, transIndex); @@ -84,7 +84,7 @@ void MSprite::loadSprite(Common::SeekableReadStream *source, byte cmd1, cmd2, count, pixel; if (newLine) { - outp = lineStart + getWidth(); + outp = lineStart + this->w; lineStart = outp; newLine = false; } @@ -126,7 +126,7 @@ void MSprite::loadSprite(Common::SeekableReadStream *source, // Do a final iteration over the sprite to convert it's pixels to // the final positions in the main palette spriteSize = this->w * this->h; - for (outp = getData(); spriteSize > 0; --spriteSize, ++outp) { + for (outp = getPixels(); spriteSize > 0; --spriteSize, ++outp) { if (*outp != transIndex) *outp = palette[*outp]._palIndex; } @@ -257,12 +257,12 @@ void SpriteSlots::drawBackground() { } if (spriteSlot._depth <= 1) { - frame->copyTo(&scene._backgroundSurface, pt, frame->getTransparencyIndex()); + scene._backgroundSurface.transBlitFrom(*frame, pt, frame->getTransparencyIndex()); } else if (scene._depthStyle == 0) { - scene._backgroundSurface.copyFrom(frame, pt, spriteSlot._depth, &scene._depthSurface, + scene._backgroundSurface.copyFrom(*frame, pt, spriteSlot._depth, &scene._depthSurface, -1, false, frame->getTransparencyIndex()); } else { - frame->copyTo(&scene._backgroundSurface, pt, frame->getTransparencyIndex()); + scene._backgroundSurface.transBlitFrom(*frame, pt, frame->getTransparencyIndex()); } } } @@ -319,7 +319,7 @@ void SpriteSlots::drawSprites(MSurface *s) { if ((slot._scale < 100) && (slot._scale != -1)) { // Scaled drawing - s->copyFrom(sprite, slot._position, slot._depth, &scene._depthSurface, + s->copyFrom(*sprite, slot._position, slot._depth, &scene._depthSurface, slot._scale, flipped, sprite->getTransparencyIndex()); } else { int xp, yp; @@ -334,7 +334,7 @@ void SpriteSlots::drawSprites(MSurface *s) { if (slot._depth > 1) { // Draw the frame with depth processing - s->copyFrom(sprite, Common::Point(xp, yp), slot._depth, &scene._depthSurface, + s->copyFrom(*sprite, Common::Point(xp, yp), slot._depth, &scene._depthSurface, -1, flipped, sprite->getTransparencyIndex()); } else { MSurface *spr = sprite; @@ -344,7 +344,7 @@ void SpriteSlots::drawSprites(MSurface *s) { } // No depth, so simply draw the image - spr->copyTo(s, Common::Point(xp, yp), sprite->getTransparencyIndex()); + s->transBlitFrom(*spr, Common::Point(xp, yp), sprite->getTransparencyIndex()); // Free sprite if it was a flipped one if (flipped) { diff --git a/engines/mads/user_interface.cpp b/engines/mads/user_interface.cpp index e4b09ff54c..8f7cb0a24b 100644 --- a/engines/mads/user_interface.cpp +++ b/engines/mads/user_interface.cpp @@ -112,7 +112,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) { Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); } else { // Copy area - userInterface._surface.copyTo(&userInterface, dirtyArea._bounds, + userInterface.blitFrom(userInterface._surface, dirtyArea._bounds, Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); } } @@ -155,7 +155,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) { if (slot._segmentId == IMG_SPINNING_OBJECT) { MSprite *sprite = asset->getFrame(frameNumber - 1); - sprite->copyTo(&userInterface, slot._position, + userInterface.transBlitFrom(*sprite, slot._position, sprite->getTransparencyIndex()); } else { MSprite *sprite = asset->getFrame(frameNumber - 1); @@ -185,7 +185,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) { // Flag area of screen as needing update Common::Rect r = dirtyArea._bounds; r.translate(0, scene._interfaceY); - _vm->_screen.copyRectToScreen(r); + //_vm->_screen->copyRectToScreen(r); } } } @@ -339,10 +339,10 @@ UserInterface::UserInterface(MADSEngine *vm) : _vm(vm), _dirtyAreas(vm), Common::fill(&_categoryIndexes[0], &_categoryIndexes[7], 0); // Map the user interface to the bottom of the game's screen surface - byte *pData = _vm->_screen.getBasePtr(0, MADS_SCENE_HEIGHT); - setPixels(pData, MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT); + create(*_vm->_screen, Common::Rect(0, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH, + MADS_SCREEN_HEIGHT)); - _surface.setSize(MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT); + _surface.create(MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT); } void UserInterface::load(const Common::String &resName) { @@ -367,7 +367,7 @@ void UserInterface::load(const Common::String &resName) { // Read in the surface data Common::SeekableReadStream *pixelsStream = madsPack.getItemStream(1); - pixelsStream->read(_surface.getData(), MADS_SCREEN_WIDTH * MADS_INTERFACE_HEIGHT); + pixelsStream->read(_surface.getPixels(), MADS_SCREEN_WIDTH * MADS_INTERFACE_HEIGHT); delete pixelsStream; } @@ -390,7 +390,7 @@ void UserInterface::setup(InputMode inputMode) { resName += ".INT"; load(resName); - _surface.copyTo(this); + blitFrom(_surface); } _vm->_game->_screenObjects._inputMode = inputMode; @@ -455,9 +455,9 @@ void UserInterface::mergeFrom(MSurface *src, const Common::Rect &srcBounds, // Copy the specified area - byte *data = src->getData(); - byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left); - byte *destPtr = (byte *)this->pixels + (destY * getWidth()) + destX; + byte *data = src->getPixels(); + byte *srcPtr = data + (src->w * copyRect.top + copyRect.left); + byte *destPtr = (byte *)getPixels() + (destY * this->w) + destX; for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { // Process each line of the area @@ -468,8 +468,8 @@ void UserInterface::mergeFrom(MSurface *src, const Common::Rect &srcBounds, destPtr[xCtr] = srcPtr[xCtr]; } - srcPtr += src->getWidth(); - destPtr += getWidth(); + srcPtr += src->w; + destPtr += this->w; } } @@ -593,7 +593,7 @@ void UserInterface::scrollbarChanged() { _uiSlots.add(r); _uiSlots.draw(false, false); drawScroller(); - updateRect(r); +// updateRect(r); } void UserInterface::writeVocab(ScrCategory category, int id) { @@ -1012,7 +1012,7 @@ void UserInterface::selectObject(int invIndex) { _uiSlots.add(bounds); _uiSlots.draw(false, false); drawItemVocabList(); - updateRect(bounds); + //updateRect(bounds); } } @@ -1036,7 +1036,7 @@ void UserInterface::updateSelection(ScrCategory category, int newIndex, int *idx _uiSlots.add(bounds); _uiSlots.draw(false, false); drawInventoryList(); - updateRect(bounds); + //updateRect(bounds); _inventoryChanged = false; if (invList.size() < 2) { @@ -1052,25 +1052,19 @@ void UserInterface::updateSelection(ScrCategory category, int newIndex, int *idx if (oldIndex >= 0) { writeVocab(category, oldIndex); - if (getBounds(category, oldIndex, bounds)) - updateRect(bounds); +/* if (getBounds(category, oldIndex, bounds)) + updateRect(bounds); */ } if (newIndex >= 0) { writeVocab(category, newIndex); - if (getBounds(category, newIndex, bounds)) - updateRect(bounds); +/* if (getBounds(category, newIndex, bounds)) + updateRect(bounds); */ } } } -void UserInterface::updateRect(const Common::Rect &bounds) { - Common::Rect r = bounds; - r.translate(0, MADS_SCENE_HEIGHT); - _vm->_screen.copyRectToScreen(r); -} - void UserInterface::scrollerChanged() { warning("TODO: scrollerChanged"); } diff --git a/engines/mads/user_interface.h b/engines/mads/user_interface.h index 60cc1f736d..9232dc1bb1 100644 --- a/engines/mads/user_interface.h +++ b/engines/mads/user_interface.h @@ -190,8 +190,6 @@ private: * Draw a UI textual element */ void writeVocab(ScrCategory category, int id); - - void updateRect(const Common::Rect &bounds); public: MSurface _surface; UISlots _uiSlots; diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index cb42ac0aaa..ad940aaf8b 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -1862,8 +1862,10 @@ void Interface::drawStatusBar() { int stringWidth; int color; // The default colors in the Spanish version of IHNM are shifted by one - // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)" - int offset = (_vm->getLanguage() == Common::ES_ESP) ? 1 : 0; + // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This + // also applies to the German and French versions (bug #7064 - "IHNM: + // text mistake in german version"). + int offset = (_vm->getLanguage() == Common::ES_ESP || _vm->getLanguage() == Common::DE_DEU || _vm->getLanguage() == Common::FR_FRA) ? 1 : 0; // Disable the status text in IHNM when the chapter is 8 if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 8) diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 532b59d3c7..77a21e7f93 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -578,9 +578,11 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) { } #ifdef ENABLE_IHNM } else if (getGameId() == GID_IHNM) { - // The default colors in the Spanish version of IHNM are shifted by one - // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)" - int offset = (getLanguage() == Common::ES_ESP) ? 1 : 0; + // The default colors in the Spanish, version of IHNM are shifted by one + // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This + // also applies to the German and French versions (bug #7064 - "IHNM: + // text mistake in german version"). + int offset = (getLanguage() == Common::ES_ESP || getLanguage() == Common::DE_DEU || getLanguage() == Common::FR_FRA) ? 1 : 0; switch (knownColor) { case(kKnownColorTransparent): diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index d84500cc60..76c819961e 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -1651,14 +1651,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { #ifdef ENABLE_SCI32 - // King's Quest 7 - English Windows (from abevi) - // VERSION 1.65c - {"kq7", "", { - {"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195}, - {"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925}, - AD_LISTEND}, - Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 7 - English Windows (from the King's Quest Collection) // Executable scanning reports "2.100.002", VERSION file reports "1.4" {"kq7", "", { @@ -1667,6 +1659,33 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 7 - English Windows-interpreter-only (supplied by m_kiewitz) + // SCI interpreter version 2.100.002, VERSION file reports "1.51" + {"kq7", "", { + {"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697}, + {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576}, + {"resource.aud", 0, "c2a988a16053eb98c7b73a75139902a0", 217716879}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // King's Quest 7 - German Windows-interpreter-only (supplied by markcoolio in bug report #2727402) + // SCI interpreter version 2.100.002, VERSION file reports "1.51" + // same as English 1.51, only resource.aud/resource.sfx are different + {"kq7", "", { + {"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697}, + {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576}, + {"resource.aud", 0, "3f17bcaf8a9ff6a6c2d4de1a2078fdcc", 258119621}, + AD_LISTEND}, + Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // King's Quest 7 - English Windows (from abevi) + // VERSION 1.65c + {"kq7", "", { + {"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195}, + {"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // King's Quest 7 - English DOS (from FRG) // SCI interpreter version 2.100.002, VERSION file reports "2.00b" {"kq7", "", { @@ -1683,14 +1702,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 7 - German Windows (supplied by markcoolio in bug report #2727402) - // SCI interpreter version 2.100.002 - {"kq7", "", { - {"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697}, - {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576}, - AD_LISTEND}, - Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // King's Quest 7 - Spanish DOS (from jvprat) // Executable scanning reports "2.100.002", VERSION file reports "2.00" {"kq7", "", { @@ -2635,6 +2646,28 @@ static const struct ADGameDescription SciGameDescriptions[] = { Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 + // Phantasmagoria - English DOS/Windows (from csnover) + // Windows executable scanning reports "2.100.002" - "Aug 06 1995" + // DOS executable scanning reports "2.100.002" - "May 24 1995" + // VERSION file reports "1.000.000" + {"phantasmagoria", "", { + {"resmap.001", 0, "43c395f312a190e67b90b2c1e93a79e2", 11518}, + {"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 65844612}, + {"resmap.002", 0, "94f142cfe8ec4107b6a42876cb603ed0", 12058}, + {"ressci.002", 0, "3aae6559aa1df273bc542d5ac6330d75", 71588691}, + {"resmap.003", 0, "39e9abd4501b5b6168dd07379c0be753", 12334}, + {"ressci.003", 0, "3aae6559aa1df273bc542d5ac6330d75", 73651084}, + {"resmap.004", 0, "434f9704658229fef322c863d2422a9a", 12556}, + {"ressci.004", 0, "3aae6559aa1df273bc542d5ac6330d75", 75811935}, + {"resmap.005", 0, "3ff9b4f7301800825c0ed008e091205e", 12604}, + {"ressci.005", 0, "3aae6559aa1df273bc542d5ac6330d75", 78814934}, + {"resmap.006", 0, "27ad413313e2a3ec3c53250e7ff5b2d1", 12532}, + {"ressci.006", 0, "3aae6559aa1df273bc542d5ac6330d75", 77901360}, + {"resmap.007", 0, "aa8175cfc93242af6f5e65bdceaafc0d", 7972}, + //{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Phantasmagoria - English DOS (from jvprat) // Executable scanning reports "2.100.002", VERSION file reports "1.100.000UK" {"phantasmagoria", "", { @@ -2733,7 +2766,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI3_GAMES - // Phantasmagoria 2 - English Windows (from jvprat) + // Some versions of Phantasmagoria 2 were heavily censored. + // Censored versions (data files are currently unknown to us): UK, Australia, first English release in Germany + + // Phantasmagoria 2 - English Windows (from jvprat) - US release + // Note: Fully uncensored + // // Executable scanning reports "3.000.000", VERSION file reports "001.0.06" {"phantasmagoria2", "", { {"resmap.001", 0, "0a961e135f4f7effb195158325856633", 1108}, @@ -2749,11 +2787,23 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Phantasmagoria 2 - German DOS/Windows + // Phantasmagoria 2 - English DOS (GOG version) (supplied by littleboy in patch #1360) + // Note: Fully uncensored, basically the US release, but ressci.* merged into ressci.000 + // + // Executable scanning reports "3.000.000" - "Dec 07 1996 09:29:03" + // VERSION file reports "001.0.06" + {"phantasmagoria2", "", { + {"ressci.000", 0, "c54f26d9f43f908151263254b6d97053", 108134481}, + {"resmap.000", 0, "de154a223a9ef4ea7358b76adc38ef5b", 2956}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // Phantasmagoria 2 - German DOS/Windows (supplied by AReim1982) + // Note: Fully uncensored, but one scene is missing probably because of a mastering error (Curtis + Therese meeting near water cooler) + // // Windows executable scanning reports "unknown" - "Dec 07 1996 15:42:02" // DOS executable scanning reports "unknown" - "Dec 07 1996 08:35:12" // VERSION file reports "000.1.0vu" (HEX: 30 30 30 2E 31 00 2E 30 76 FA 0D 0A) - // Supplied by AReim1982 {"phantasmagoria2", "", { {"resmap.001", 0, "d62f48ff8bddb39503b97e33439482c9", 1114}, {"ressci.001", 0, "4ebc2b8455c74ad205ae592eec27313a", 24590716}, @@ -2767,16 +2817,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"ressci.005", 0, "e94005890d22dd3b7f605a2a7c025803", 68232146}, AD_LISTEND}, Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - - // Phantasmagoria 2 - English DOS (GOG version) - ressci.* merged in ressci.000 - // Executable scanning reports "3.000.000" - "Dec 07 1996 09:29:03" - // VERSION file reports "001.0.06" - // Supplied by littleboy in patch #3112884 - {"phantasmagoria2", "", { - {"ressci.000", 0, "c54f26d9f43f908151263254b6d97053", 108134481}, - {"resmap.000", 0, "de154a223a9ef4ea7358b76adc38ef5b", 2956}, - AD_LISTEND}, - Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #endif // ENABLE_SCI3_GAMES #endif // ENABLE_SCI32 diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index f844d96c35..62566a74b2 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -412,7 +412,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv); reg_t kTextColors(EngineState *s, int argc, reg_t *argv); reg_t kTextFonts(EngineState *s, int argc, reg_t *argv); reg_t kShow(EngineState *s, int argc, reg_t *argv); -reg_t kRemapColors(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv); reg_t kDummy(EngineState *s, int argc, reg_t *argv); reg_t kEmpty(EngineState *s, int argc, reg_t *argv); reg_t kStub(EngineState *s, int argc, reg_t *argv); @@ -452,8 +452,14 @@ reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv); reg_t kMulDiv(EngineState *s, int argc, reg_t *argv); -reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv); -reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv); + +reg_t kRemapColors(EngineState *s, int argc, reg_t *argv); +reg_t kRemapOff(EngineState *s, int argc, reg_t *argv); +reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv); +reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv); +reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv); +reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv); +reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv); reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); @@ -480,10 +486,13 @@ reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv); reg_t kAddPlane(EngineState *s, int argc, reg_t *argv); reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv); reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv); +reg_t kMovePlaneItems(EngineState *s, int argc, reg_t *argv); reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv); reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv); reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv); reg_t kFrameOut(EngineState *s, int argc, reg_t *argv); +reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv); +reg_t kCelWide32(EngineState *s, int argc, reg_t *argv); reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv); // kOnMe for SCI2, kIsOnMe for SCI2.1 reg_t kInPolygon(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 6258197206..3463d05e77 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -352,6 +352,17 @@ static const SciKernelMapSubEntry kList_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kRemapColors_subops[] = { + { SIG_SCI32, 0, MAP_CALL(RemapOff), "(i)", NULL }, + { SIG_SCI32, 1, MAP_CALL(RemapByRange), "iiii(i)", NULL }, + { SIG_SCI32, 2, MAP_CALL(RemapByPercent), "ii(i)", NULL }, + { SIG_SCI32, 3, MAP_CALL(RemapToGray), "ii(i)", NULL }, + { SIG_SCI32, 4, MAP_CALL(RemapToPercentGray), "iii(i)", NULL }, + { SIG_SCI32, 5, MAP_CALL(RemapSetNoMatchRange), "ii", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kString_subops[] = { { SIG_SCI32, 0, MAP_CALL(StringNew), "i(i)", NULL }, { SIG_SCI32, 1, MAP_CALL(StringSize), "[or]", NULL }, @@ -446,12 +457,16 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL }, { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, + { MAP_CALL(CantBeHere), SIG_SCI16, SIGFOR_ALL, "o(l)", NULL, NULL }, #ifdef ENABLE_SCI32 - { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL }, + { MAP_CALL(CantBeHere), SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL }, +#endif + { MAP_CALL(CelHigh), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelHigh_workarounds }, + { MAP_CALL(CelWide), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelWide_workarounds }, +#ifdef ENABLE_SCI32 + { "CelHigh", kCelHigh32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL }, + { "CelWide", kCelWide32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL }, #endif - { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, - { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds }, - { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds }, { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL }, { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL }, { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL }, @@ -550,9 +565,9 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, kReadNumber_workarounds }, - { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, + { "RemapColors", kRemapColors16, SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, #ifdef ENABLE_SCI32 - { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(RemapColors), SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", kRemapColors_subops, NULL }, #endif { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, @@ -568,7 +583,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL }, { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL }, { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, - { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL }, + { MAP_CALL(SetNowSeen), SIG_SCI16, SIGFOR_ALL, "o(i)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { MAP_CALL(SetNowSeen), SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL }, +#endif { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds }, { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL }, @@ -693,7 +711,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_DUMMY(MarkMemory), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(GetHighItemPri), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_DUMMY(InvertRect), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL }, { MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(TextWidth), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "ri", NULL, NULL }, { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL }, @@ -712,8 +730,8 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(GetConfig), SIG_EVERYWHERE, "ro", NULL, NULL }, { MAP_CALL(GetSierraProfileInt), SIG_EVERYWHERE, "rri", NULL, NULL }, - { MAP_CALL(CelInfo), SIG_EVERYWHERE, "iiiiii", NULL, NULL }, - { MAP_CALL(SetLanguage), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(CelInfo), SIG_SINCE_SCI21MID, SIGFOR_ALL, "iiiiii", NULL, NULL }, + { MAP_CALL(SetLanguage), SIG_SINCE_SCI21MID, SIGFOR_ALL, "r", NULL, NULL }, { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "i(.*)", kScrollWindow_subops, NULL }, { MAP_CALL(SetFontRes), SIG_SCI21EARLY, SIGFOR_ALL, "ii", NULL, NULL }, { MAP_CALL(Font), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kFont_subops, NULL }, @@ -769,9 +787,9 @@ static SciKernelMapEntry s_kernelMap[] = { // <lskovlun> The idea, if I understand correctly, is that the engine generates events // of a special HotRect type continuously when the mouse is on that rectangle - // MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons - // SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end - // (inclusive) are set to 0 + // Used by SQ6 to scroll through the inventory via the up/down buttons + { MAP_CALL(MovePlaneItems), SIG_SINCE_SCI21, SIGFOR_ALL, "oii(i)", NULL, NULL }, + { MAP_CALL(SetPalStyleRange), SIG_EVERYWHERE, "ii", NULL, NULL }, { MAP_CALL(MorphOn), SIG_EVERYWHERE, "", NULL, NULL }, diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index 254342111b..534d9ce713 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -83,11 +83,12 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { } // For a real event we use its associated mouse position - mousePos = curEvent.mousePos; #ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1_EARLY) - g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); + if (getSciVersion() >= SCI_VERSION_2) + mousePos = curEvent.mousePosSci; + else #endif + mousePos = curEvent.mousePos; // Limit the mouse cursor position, if necessary g_sci->_gfxCursor->refreshPosition(); diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 93d0d91b65..73236b98ed 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -441,8 +441,15 @@ reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) { reg_t curObject = argv[0]; reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; - reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); - return canBeHere; +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + return g_sci->_gfxCompare->kernelCantBeHere32(curObject, listReference); + } else { +#endif + return g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); +#ifdef ENABLE_SCI32 + } +#endif } reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) { @@ -572,9 +579,17 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { } reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxCompare->kernelSetNowSeen(argv[0]); - - return s->r_acc; +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]); + return NULL_REG; + } else { +#endif + g_sci->_gfxCompare->kernelSetNowSeen(argv[0]); + return s->r_acc; +#ifdef ENABLE_SCI32 + } +#endif } reg_t kPalette(EngineState *s, int argc, reg_t *argv) { @@ -1243,7 +1258,7 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) { } // Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4 -reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { +reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv) { uint16 operation = argv[0].toUint16(); switch (operation) { diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 9c5d214488..7850a10006 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -45,6 +45,7 @@ #include "sci/graphics/paint16.h" #include "sci/graphics/picture.h" #include "sci/graphics/ports.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/view.h" @@ -63,59 +64,59 @@ namespace Sci { extern void showScummVMDialog(const Common::String &message); reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) { - // Returns 0 if the screen width or height is less than 640 or 400, - // respectively. - if (g_system->getWidth() < 640 || g_system->getHeight() < 400) + const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer(); + if (buffer.screenWidth < 640 || buffer.screenHeight < 400) return make_reg(0, 0); return make_reg(0, 1); } -// SCI32 variant, can't work like sci16 variants -reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) { - // TODO -// reg_t curObject = argv[0]; -// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; - - return NULL_REG; -} - reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kAddScreenItem %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kAddScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]); - return NULL_REG; + return s->r_acc; } reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) { - debugC(7, kDebugLevelGraphics, "kUpdateScreenItem %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(7, kDebugLevelGraphics, "kUpdateScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]); - return NULL_REG; + return s->r_acc; } reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kDeleteScreenItem %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kDeleteScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]); - return NULL_REG; + return s->r_acc; } reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kAddPlane %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kAddPlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelAddPlane(argv[0]); return s->r_acc; } reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) { - debugC(7, kDebugLevelGraphics, "kUpdatePlane %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(7, kDebugLevelGraphics, "kUpdatePlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]); return s->r_acc; } reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kDeletePlane %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kDeletePlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelDeletePlane(argv[0]); return s->r_acc; } +reg_t kMovePlaneItems(EngineState *s, int argc, reg_t *argv) { + const reg_t plane = argv[0]; + const int16 deltaX = argv[1].toSint16(); + const int16 deltaY = argv[2].toSint16(); + const bool scrollPics = argc > 3 ? argv[3].toUint16() : false; + + g_sci->_gfxFrameout->kernelMovePlaneItems(plane, deltaX, deltaY, scrollPics); + return s->r_acc; +} + reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) { reg_t planeObj = argv[0]; GuiResourceId pictureId = argv[1].toUint16(); @@ -124,7 +125,7 @@ reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) { bool mirrorX = argc > 4 ? argv[4].toSint16() : false; g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, x, y, mirrorX); - return NULL_REG; + return s->r_acc; } reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) { @@ -136,12 +137,12 @@ reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxFrameout->kernelFrameOut(showBits); s->speedThrottler(16); s->_throttleTrigger = true; - return NULL_REG; + return s->r_acc; } reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxFrameout->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16()); - return NULL_REG; + return s->r_acc; } reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) { @@ -150,7 +151,6 @@ reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) { return make_reg(0, objRect1.intersects(objRect2)); } -// Tests if the coordinate is on the passed object reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) { int16 x = argv[0].toSint16(); int16 y = argv[1].toSint16(); @@ -228,7 +228,7 @@ reg_t kTextSize32(EngineState *s, int argc, reg_t *argv) { rect[1] = make_reg(0, textRect.top); rect[2] = make_reg(0, textRect.right - 1); rect[3] = make_reg(0, textRect.bottom - 1); - return NULL_REG; + return s->r_acc; } reg_t kTextWidth(EngineState *s, int argc, reg_t *argv) { @@ -307,31 +307,50 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { // on the KernelMgr g_sci->_gfxFrameout->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); - return NULL_REG; + return s->r_acc; +} + +reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv) { + GuiResourceId resourceId = argv[0].toUint16(); + int16 loopNo = argv[1].toSint16(); + int16 celNo = argv[2].toSint16(); + CelObjView celObj(resourceId, loopNo, celNo); + return make_reg(0, mulru(celObj._height, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight, celObj._scaledHeight))); +} + +reg_t kCelWide32(EngineState *s, int argc, reg_t *argv) { + GuiResourceId resourceId = argv[0].toUint16(); + int16 loopNo = argv[1].toSint16(); + int16 celNo = argv[2].toSint16(); + CelObjView celObj(resourceId, loopNo, celNo); + return make_reg(0, mulru(celObj._width, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth, celObj._scaledWidth))); } reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) { // Used by Shivers 1, room 23601 to determine what blocks on the red door puzzle board // are occupied by pieces already - switch (argv[0].toUint16()) { // subops 0 - 4 - // 0 - return the view - // 1 - return the loop - // 2, 3 - nop - case 4: { - GuiResourceId viewId = argv[1].toSint16(); - int16 loopNo = argv[2].toSint16(); - int16 celNo = argv[3].toSint16(); - int16 x = argv[4].toUint16(); - int16 y = argv[5].toUint16(); - byte color = g_sci->_gfxCache->kernelViewGetColorAtCoordinate(viewId, loopNo, celNo, x, y); - return make_reg(0, color); - } - default: { - kStub(s, argc, argv); - return s->r_acc; - } + CelObjView view(argv[1].toUint16(), argv[2].toSint16(), argv[3].toSint16()); + + int16 result = 0; + + switch (argv[0].toUint16()) { + case 0: + result = view._displace.x; + break; + case 1: + result = view._displace.y; + break; + case 2: + case 3: + // null operation + break; + case 4: + result = view.readPixel(argv[4].toSint16(), argv[5].toSint16(), view._mirrorX); + break; } + + return make_reg(0, result); } reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { @@ -488,13 +507,13 @@ reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv) { // which case we could just get the font directly ourselves. g_sci->_gfxText32->setFont(argv[0].toUint16()); g_sci->_gfxText32->_scaledHeight = (g_sci->_gfxText32->_font->getHeight() * g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight + g_sci->_gfxText32->_scaledHeight - 1) / g_sci->_gfxText32->_scaledHeight; - return NULL_REG; + return make_reg(0, g_sci->_gfxText32->_scaledHeight); } reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxText32->_scaledWidth = argv[0].toUint16(); g_sci->_gfxText32->_scaledHeight = argv[1].toUint16(); - return NULL_REG; + return s->r_acc; } reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { @@ -519,7 +538,7 @@ reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) { reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv) { s->_segMan->freeHunkEntry(argv[0]); - return NULL_REG; + return s->r_acc; } reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv) { @@ -573,7 +592,6 @@ reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) { reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { // called e.g. from TextButton::createBitmap() in Torin's Passage, script 64894 - // bitmap, text, textLeft, textTop, textRight, textBottom, foreColor, backColor, skipColor, fontNo, alignment, borderColor, dimmed BitmapResource bitmap(argv[0]); Common::String text = s->_segMan->getString(argv[1]); Common::Rect textRect( @@ -604,36 +622,23 @@ reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { textCel.draw(bitmapBuffer, textRect, Common::Point(textRect.left, textRect.top), false); s->_segMan->freeHunkEntry(textBitmapObject); - return NULL_REG; + return s->r_acc; } reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) { - // bitmap, left, top, right, bottom, color - // called e.g. from TextView::init() and TextView::draw() in Torin's Passage, script 64890 - return kStubNull(s, argc + 1, argv - 1); -#if 0 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - uint16 x = argv[2].toUint16(); - uint16 y = argv[3].toUint16(); - uint16 fillWidth = argv[4].toUint16(); // width - 1 - uint16 fillHeight = argv[5].toUint16(); // height - 1 - uint16 back = argv[6].toUint16(); - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - uint16 width = MIN<uint16>(totalWidth - x, fillWidth); - uint16 height = MIN<uint16>(totalHeight - y, fillHeight); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; + BitmapResource bitmap(argv[0]); + Common::Rect fillRect( + argv[1].toSint16(), + argv[2].toSint16(), + argv[3].toSint16() + 1, + argv[4].toSint16() + 1 + ); - for (uint16 curY = 0; curY < height; curY++) { - for (uint16 curX = 0; curX < width; curX++) { - bitmap[(curY + y) * totalWidth + (curX + x)] = back; - } - } -#endif + Buffer buffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels()); + buffer.fillRect(fillRect, argv[5].toSint16()); + return s->r_acc; } reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv) { @@ -649,9 +654,9 @@ reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv) { } reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv) { - // bitmap, x, y - - return kStubNull(s, argc + 1, argv - 1); + BitmapResource bitmap(argv[0]); + bitmap.setDisplace(Common::Point(argv[1].toSint16(), argv[2].toSint16())); + return s->r_acc; } reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv) { @@ -691,11 +696,6 @@ reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv) { return kStub(s, argc + 1, argv - 1); } -// Used for edit boxes in save/load dialogs. It's a rewritten version of kEditControl, -// but it handles events on its own, using an internal loop, instead of using SCI -// scripts for event management like kEditControl does. Called by script 64914, -// DEdit::hilite(). - reg_t kEditText(EngineState *s, int argc, reg_t *argv) { return g_sci->_gfxControls32->kernelEditText(argv[0]); } @@ -706,12 +706,12 @@ reg_t kAddLine(EngineState *s, int argc, reg_t *argv) { reg_t plane = argv[0]; Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16()); Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16()); - // argv[5] is unknown (a number, usually 200) + byte priority = (byte)argv[5].toUint16(); byte color = (byte)argv[6].toUint16(); - byte priority = (byte)argv[7].toUint16(); - byte control = (byte)argv[8].toUint16(); - // argv[9] is unknown (usually a small number, 1 or 2). Thickness, perhaps? -// return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, control); + byte style = (byte)argv[7].toUint16(); // 0: solid, 1: dashed, 2: pattern + byte pattern = (byte)argv[8].toUint16(); + byte thickness = (byte)argv[9].toUint16(); +// return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, 0); return s->r_acc; #endif } @@ -770,7 +770,7 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { // Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270) reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxFrameout->_palMorphIsOn = true; - return NULL_REG; + return s->r_acc; } reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv) { @@ -778,7 +778,7 @@ reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv) { uint16 toColor = argv[1].toUint16(); uint16 percent = argv[2].toUint16(); g_sci->_gfxPalette32->setFade(percent, fromColor, toColor); - return NULL_REG; + return s->r_acc; } reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) { @@ -796,18 +796,14 @@ reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) { } g_sci->_gfxPalette32->kernelPalVarySet(paletteId, percent, time, fromColor, toColor); - return NULL_REG; + return s->r_acc; } reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv) { int time = argc > 0 ? argv[0].toSint16() * 60 : 0; int16 percent = argc > 1 ? argv[1].toSint16() : 0; - - // TODO: GK1 adds a third optional parameter here, at the end of chapter 1 - // (during the sunset/sunrise sequence, the parameter is 1) - g_sci->_gfxPalette32->setVaryPercent(percent, time, -1, -1); - return NULL_REG; + return s->r_acc; } reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv) { @@ -816,7 +812,7 @@ reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv) { reg_t kPalVaryOff(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxPalette32->varyOff(); - return NULL_REG; + return s->r_acc; } reg_t kPalVaryMergeTarget(EngineState *s, int argc, reg_t *argv) { @@ -828,7 +824,7 @@ reg_t kPalVaryMergeTarget(EngineState *s, int argc, reg_t *argv) { reg_t kPalVarySetTime(EngineState *s, int argc, reg_t *argv) { int time = argv[0].toSint16() * 60; g_sci->_gfxPalette32->setVaryTime(time); - return NULL_REG; + return s->r_acc; } reg_t kPalVarySetTarget(EngineState *s, int argc, reg_t *argv) { @@ -913,79 +909,57 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { - // TODO -#if 0 - uint16 operation = argv[0].toUint16(); - - switch (operation) { - case 0: { // turn remapping off - // WORKAROUND: Game scripts in QFG4 erroneously turn remapping off in room - // 140 (the character point allocation screen) and never turn it back on, - // even if it's clearly used in that screen. - if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140) - return s->r_acc; - - int16 color = (argc >= 2) ? argv[1].toSint16() : 0; - if (color > 0) - warning("kRemapColors(0) called with base %d", color); - //g_sci->_gfxPalette32->resetRemapping(); - } - break; - case 1: { // remap by range - uint16 color = argv[1].toUint16(); - uint16 from = argv[2].toUint16(); - uint16 to = argv[3].toUint16(); - uint16 delta = argv[4].toUint16(); - uint16 depth = (argc >= 6) ? argv[5].toUint16() : 0; - if (depth > 0) - warning("kRemapColors(1) called with 6 parameters, depth is %d", depth); - //g_sci->_gfxPalette32->setRemappingRange(color, from, to, delta); - } - break; - case 2: { // remap by percent - uint16 color = argv[1].toUint16(); - uint16 percent = argv[2].toUint16(); // 0 - 100 - uint16 depth = (argc >= 4) ? argv[3].toUint16() : 0; - if (depth >= 0) - warning("RemapByPercent called with 4 parameters, depth is %d", depth); - //g_sci->_gfxPalette32->setRemappingPercent(color, percent); - } - break; - case 3: { // remap to gray - // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0. - // In this room, it's used for the cloud before Baba Yaga appears. - uint16 color = argv[1].toUint16(); - uint16 percent = argv[2].toUint16(); // 0 - 100 - uint16 depth = (argc >= 4) ? argv[3].toUint16() : 0; - if (depth >= 0) - warning("RemapToGray called with 4 parameters, depth is %d", depth); - //g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); - } - break; - case 4: { // remap to percent gray - // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200 - uint16 color = argv[1].toUint16(); - uint16 percent = argv[2].toUint16(); // 0 - 100 - uint16 grayPercent = argv[3].toUint16(); - uint16 depth = (argc >= 5) ? argv[4].toUint16() : 0; - if (argc >= 5) - warning("RemapToGrayPercent called with 5 parameters, depth is %d", depth); - //g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); - } - break; - case 5: { // don't map to range - //uint16 start = argv[1].toSint16(); - //uint16 count = argv[2].toUint16(); +reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} - kStub(s, argc, argv); - } - break; - default: - break; - } -#endif +reg_t kRemapOff(EngineState *s, int argc, reg_t *argv) { + byte color = (argc >= 1) ? argv[0].toUint16() : 0; + g_sci->_gfxRemap32->remapOff(color); + return s->r_acc; +} + +reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte from = argv[1].toUint16(); + byte to = argv[2].toUint16(); + byte base = argv[3].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingRange(color, from, to, base); + return s->r_acc; +} + +reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte percent = argv[1].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingPercent(color, percent); + return s->r_acc; +} + +reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte gray = argv[1].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingToGray(color, gray); + return s->r_acc; +} + +reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte gray = argv[1].toUint16(); + byte percent = argv[2].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingToPercentGray(color, gray, percent); + return s->r_acc; +} +reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv) { + byte from = argv[0].toUint16(); + byte count = argv[1].toUint16(); + g_sci->_gfxRemap32->setNoMatchRange(from, count); return s->r_acc; } diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index f598cf7457..1c08bf597c 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -765,11 +765,14 @@ reg_t kStringCopy(EngineState *s, int argc, reg_t *argv) { } // The original engine ignores bad copies too - if (index2 > string2Size) + if (index2 >= string2Size) return NULL_REG; // A count of -1 means fill the rest of the array - uint32 count = argv[4].toSint16() == -1 ? string2Size - index2 + 1 : argv[4].toUint16(); + uint32 count = string2Size - index2; + if (argv[4].toSint16() != -1) { + count = MIN(count, (uint32)argv[4].toUint16()); + } // reg_t strAddress = argv[0]; SciString *string1 = s->_segMan->lookupString(argv[0]); diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index ae7ab431f8..fcb65157d8 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -1012,6 +1012,26 @@ void gamestate_afterRestoreFixUp(EngineState *s, int savegameId) { g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals break; + case GID_KQ6: + if (g_sci->isCD()) { + // WORKAROUND: + // For the CD version of King's Quest 6, set global depending on current hires/lowres state + // The game sets a global at the start depending on it and some things check that global + // instead of checking platform like for example the game action menu. + // This never happened in the original interpreter, because the original DOS interpreter + // was only capable of lowres graphics and the original Windows 3.11 interpreter was only capable + // of hires graphics. Saved games were not compatible between those two. + // Which means saving during lowres mode, then going into hires mode and restoring that saved game, + // will result in some graphics being incorrect (lowres). + // That's why we are setting the global after restoring a saved game depending on hires/lowres state. + // The CD demo of KQ6 does the same and uses the exact same global. + if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) { + s->variables[VAR_GLOBAL][0xA9].setOffset(1); + } else { + s->variables[VAR_GLOBAL][0xA9].setOffset(0); + } + } + break; case GID_PQ2: // HACK: Same as above - enable the save game menu option when loading in PQ2 (bug #6875). // It gets disabled in the game's death screen. diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index a4e19dc8b9..3832f4cf04 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -378,6 +378,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places { GID_SQ6, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game { GID_TORIN, -1, 64017, 0, "oFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version + { GID_TORIN, 10000, 64029, 0, "oMessager", "nextMsg", NULL, 3, { WORKAROUND_FAKE, 0 } }, // start of chapter one SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp index 59af8334eb..fb1f557ad6 100644 --- a/engines/sci/graphics/cache.cpp +++ b/engines/sci/graphics/cache.cpp @@ -102,8 +102,4 @@ int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) { return getView(viewId)->getCelCount(loopNo); } -byte GfxCache::kernelViewGetColorAtCoordinate(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y) { - return getView(viewId)->getColorAtCoordinate(loopNo, celNo, x, y); -} - } // End of namespace Sci diff --git a/engines/sci/graphics/cache.h b/engines/sci/graphics/cache.h index 33fa4fe399..61952718a9 100644 --- a/engines/sci/graphics/cache.h +++ b/engines/sci/graphics/cache.h @@ -49,8 +49,6 @@ public: int16 kernelViewGetLoopCount(GuiResourceId viewId); int16 kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo); - byte kernelViewGetColorAtCoordinate(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y); - private: void purgeFontCache(); void purgeViewCache(); diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index 389270ec42..693bc5f196 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -27,6 +27,7 @@ #include "sci/graphics/frameout.h" #include "sci/graphics/palette32.h" #include "sci/graphics/picture.h" +#include "sci/graphics/remap.h" #include "sci/graphics/text32.h" #include "sci/graphics/view.h" @@ -109,25 +110,42 @@ void CelObj::deinit() { template<bool FLIP, typename READER> struct SCALER_NoScale { +#ifndef NDEBUG + const byte *_rowEdge; +#endif const byte *_row; READER _reader; const int16 _lastIndex; + const int16 _sourceX; + const int16 _sourceY; - SCALER_NoScale(const CelObj &celObj, const int16 maxWidth) : + SCALER_NoScale(const CelObj &celObj, const int16 maxWidth, const Common::Point &scaledPosition) : _reader(celObj, FLIP ? celObj._width : maxWidth), - _lastIndex(celObj._width - 1) {} + _lastIndex(celObj._width - 1), + _sourceX(scaledPosition.x), + _sourceY(scaledPosition.y) {} - inline void setSource(const int16 x, const int16 y) { - _row = _reader.getRow(y); + inline void setTarget(const int16 x, const int16 y) { + _row = _reader.getRow(y - _sourceY); if (FLIP) { - _row += _lastIndex - x; +#ifndef NDEBUG + _rowEdge = _row - 1; +#endif + _row += _lastIndex - (x - _sourceX); + assert(_row > _rowEdge); } else { - _row += x; +#ifndef NDEBUG + _rowEdge = _row + _lastIndex + 1; +#endif + _row += x - _sourceX; + assert(_row < _rowEdge); } } inline byte read() { + assert(_row != _rowEdge); + if (FLIP) { return *_row--; } else { @@ -138,55 +156,96 @@ struct SCALER_NoScale { template<bool FLIP, typename READER> struct SCALER_Scale { +#ifndef NDEBUG + int16 _maxX; +#endif const byte *_row; READER _reader; - const CelScalerTable *_table; int16 _x; - const uint16 _lastIndex; + static int16 _valuesX[1024]; + static int16 _valuesY[1024]; - SCALER_Scale(const CelObj &celObj, const int16 maxWidth, const Ratio scaleX, const Ratio scaleY) : + SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) : +#ifndef NDEBUG + _maxX(targetRect.right - 1), +#endif // The maximum width of the scaled object may not be as // wide as the source data it requires if downscaling, // so just always make the reader decompress an entire // line of source data when scaling - _reader(celObj, celObj._width), - _table(CelObj::_scaler->getScalerTable(scaleX, scaleY)), - _lastIndex(maxWidth - 1) {} - - inline void setSource(const int16 x, const int16 y) { - _row = _reader.getRow(_table->valuesY[y]); + _reader(celObj, celObj._width) { + // In order for scaling ratios to apply equally across objects that + // start at different positions on the screen, the pixels that are + // read from the source bitmap must all use the same pattern of + // division. In other words, cels must follow the same scaling pattern + // as if they were drawn starting at an even multiple of the scaling + // ratio, even if they were not. + // + // To get the correct source pixel when reading out through the scaler, + // the engine creates a lookup table for each axis that translates + // directly from target positions to the indexes of source pixels using + // the global cadence for the given scaling ratio. + + const CelScalerTable *table = CelObj::_scaler->getScalerTable(scaleX, scaleY); + + const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); if (FLIP) { - _x = _lastIndex - x; + int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); + } } else { - _x = x; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x] - unscaledX; + } + } + + const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y] - unscaledY; } } + inline void setTarget(const int16 x, const int16 y) { + _row = _reader.getRow(_valuesY[y]); + _x = x; + assert(_x >= 0 && _x <= _maxX); + } + inline byte read() { - if (FLIP) { - return _row[_table->valuesX[_x--]]; - } else { - return _row[_table->valuesX[_x++]]; - } + assert(_x >= 0 && _x <= _maxX); + return _row[_valuesX[_x++]]; } }; +template<bool FLIP, typename READER> +int16 SCALER_Scale<FLIP, READER>::_valuesX[1024]; +template<bool FLIP, typename READER> +int16 SCALER_Scale<FLIP, READER>::_valuesY[1024]; + #pragma mark - #pragma mark CelObj - Resource readers struct READER_Uncompressed { private: +#ifndef NDEBUG + const int16 _sourceHeight; +#endif byte *_pixels; const int16 _sourceWidth; public: READER_Uncompressed(const CelObj &celObj, const int16) : +#ifndef NDEBUG + _sourceHeight(celObj._height), +#endif _sourceWidth(celObj._width) { byte *resource = celObj.getResPointer(); _pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24); } inline const byte *getRow(const int16 y) const { + assert(y >= 0 && y < _sourceHeight); return _pixels + y * _sourceWidth; } }; @@ -210,7 +269,7 @@ public: _sourceHeight(celObj._height), _transparentColor(celObj._transparentColor), _maxWidth(maxWidth) { - assert(_maxWidth <= celObj._width); + assert(maxWidth <= celObj._width); byte *celHeader = _resource + celObj._celHeaderOffset; _dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24); @@ -219,6 +278,7 @@ public: } inline const byte *getRow(const int16 y) { + assert(y >= 0 && y < _sourceHeight); if (y != _y) { // compressed data segment for row byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4); @@ -227,7 +287,7 @@ public: byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4); uint8 length; - for (int i = 0; i < _maxWidth; i += length) { + for (int16 i = 0; i < _maxWidth; i += length) { byte controlByte = *row++; length = controlByte; @@ -274,153 +334,101 @@ struct MAPPER_NoMDNoSkip { } }; +struct MAPPER_Map { + inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { + if (pixel != skipColor) { + if (pixel < g_sci->_gfxRemap32->getStartColor()) { + *target = pixel; + } else { + if (g_sci->_gfxRemap32->remapEnabled(pixel)) + *target = g_sci->_gfxRemap32->remapColor(pixel, *target); + } + } + } +}; + void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const { - const Buffer &priorityMap = g_sci->_gfxFrameout->getPriorityMap(); const Common::Point &scaledPosition = screenItem._scaledPosition; const Ratio &scaleX = screenItem._ratioX; const Ratio &scaleY = screenItem._ratioY; if (_remap) { - if (g_sci->_gfxFrameout->_hasRemappedScreenItem) { - const uint8 priority = MAX((int16)0, MIN((int16)255, screenItem._priority)); - - // NOTE: In the original engine code, there was a second branch for - // _remap here that would then call the following functions if _remap was false: - // - // drawHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawUncompHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawUncompNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // scaleDraw(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8) - // scaleDrawUncomp(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8) - // - // However, obviously, _remap cannot be false here. This dead code branch existed in - // at least SCI2/GK1 and SCI2.1/SQ6. - + // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, + // but since we are already in a `_remap` branch, there is no reason to check it + // again + if (g_sci->_gfxRemap32->getRemapCount()) { if (scaleX.isOne() && scaleY.isOne()) { if (_compressionType == kCelCompressionNone) { if (_drawMirrored) { - drawUncompHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipMap(target, targetRect, scaledPosition); } else { - drawUncompNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipMap(target, targetRect, scaledPosition); } } else { if (_drawMirrored) { - drawHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawHzFlipMap(target, targetRect, scaledPosition); } else { - drawNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawNoFlipMap(target, targetRect, scaledPosition); } } } else { if (_compressionType == kCelCompressionNone) { - scaleDrawUncompMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition); } else { - scaleDrawMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition); } } } else { - // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, - // but since we are already in a `_remap` branch, there is no reason to check it - // again - if (/* TODO: g_Remap_numActiveRemaps */ false) { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_drawMirrored) { - drawUncompHzFlipMap(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipMap(target, targetRect, scaledPosition); - } + if (scaleX.isOne() && scaleY.isOne()) { + if (_compressionType == kCelCompressionNone) { + if (_drawMirrored) { + drawUncompHzFlip(target, targetRect, scaledPosition); } else { - if (_drawMirrored) { - drawHzFlipMap(target, targetRect, scaledPosition); - } else { - drawNoFlipMap(target, targetRect, scaledPosition); - } + drawUncompNoFlip(target, targetRect, scaledPosition); } } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition); + if (_drawMirrored) { + drawHzFlip(target, targetRect, scaledPosition); } else { - scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition); + drawNoFlip(target, targetRect, scaledPosition); } } } else { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_drawMirrored) { - drawUncompHzFlip(target, targetRect, scaledPosition); - } else { - drawUncompNoFlip(target, targetRect, scaledPosition); - } - } else { - if (_drawMirrored) { - drawHzFlip(target, targetRect, scaledPosition); - } else { - drawNoFlip(target, targetRect, scaledPosition); - } - } + if (_compressionType == kCelCompressionNone) { + scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition); } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition); - } else { - scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition); - } + scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition); } } } } else { - if (g_sci->_gfxFrameout->_hasRemappedScreenItem) { - const uint8 priority = MAX((int16)0, MIN((int16)255, screenItem._priority)); - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { + if (scaleX.isOne() && scaleY.isOne()) { + if (_compressionType == kCelCompressionNone) { + if (_transparent) { if (_drawMirrored) { - drawUncompHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipNoMD(target, targetRect, scaledPosition); } else { - drawUncompNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipNoMD(target, targetRect, scaledPosition); } } else { if (_drawMirrored) { - drawHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition); } else { - drawNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition); } } } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + if (_drawMirrored) { + drawHzFlipNoMD(target, targetRect, scaledPosition); } else { - scaleDrawNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + drawNoFlipNoMD(target, targetRect, scaledPosition); } } } else { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_transparent) { - if (_drawMirrored) { - drawUncompHzFlipNoMD(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipNoMD(target, targetRect, scaledPosition); - } - } else { - if (_drawMirrored) { - drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition); - } - } - } else { - if (_drawMirrored) { - drawHzFlipNoMD(target, targetRect, scaledPosition); - } else { - drawNoFlipNoMD(target, targetRect, scaledPosition); - } - } + if (_compressionType == kCelCompressionNone) { + scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition); } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition); - } else { - scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition); - } + scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition); } } } @@ -574,18 +582,15 @@ struct RENDERER { _skipColor(skipColor) {} inline void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - const int16 sourceX = targetRect.left - scaledPosition.x; - const int16 sourceY = targetRect.top - scaledPosition.y; - byte *targetPixel = (byte *)target.getPixels() + target.screenWidth * targetRect.top + targetRect.left; const int16 skipStride = target.screenWidth - targetRect.width(); const int16 targetWidth = targetRect.width(); const int16 targetHeight = targetRect.height(); - for (int y = 0; y < targetHeight; ++y) { - _scaler.setSource(sourceX, sourceY + y); + for (int16 y = 0; y < targetHeight; ++y) { + _scaler.setTarget(targetRect.left, targetRect.top + y); - for (int x = 0; x < targetWidth; ++x) { + for (int16 x = 0; x < targetWidth; ++x) { _mapper.draw(targetPixel++, _scaler.read(), _skipColor); } @@ -598,7 +603,7 @@ template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { MAPPER mapper; - SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width()); + SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition); RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor); renderer.draw(target, targetRect, scaledPosition); } @@ -607,7 +612,7 @@ template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const { MAPPER mapper; - SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaleX, scaleY); + SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY); RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor); renderer.draw(target, targetRect, scaledPosition); } @@ -620,49 +625,60 @@ void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Co debug("drawHzFlip"); dummyFill(target, targetRect); } + void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("drawNoFlip"); dummyFill(target, targetRect); } + void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("drawUncompNoFlip"); dummyFill(target, targetRect); } + void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("drawUncompHzFlip"); dummyFill(target, targetRect); } + void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("scaleDraw"); dummyFill(target, targetRect); } + void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("scaleDrawUncomp"); dummyFill(target, targetRect); } + void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawHzFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawNoFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompNoFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompHzFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawMap"); - dummyFill(target, targetRect); + if (_drawMirrored) + render<MAPPER_Map, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + else + render<MAPPER_Map, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); } + void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawUncompMap"); - dummyFill(target, targetRect); + if (_drawMirrored) + render<MAPPER_Map, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + else + render<MAPPER_Map, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); } void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { @@ -680,43 +696,29 @@ void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect void CelObj::drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMDNoSkip, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMD, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMDNoSkip, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - if (_drawMirrored) { + if (_drawMirrored) render<MAPPER_NoMD, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } else { + else render<MAPPER_NoMD, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } } void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - if (_drawMirrored) { + if (_drawMirrored) render<MAPPER_NoMD, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } else { + else render<MAPPER_NoMD, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } } -// TODO: These functions may all be vestigial. -void CelObj::drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} - #pragma mark - #pragma mark CelObjView CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) { @@ -832,8 +834,8 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int bool CelObjView::analyzeUncompressedForRemap() const { byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { - uint8 pixel = pixels[i]; - if (/* TODO: pixel >= Remap::minRemapColor && pixel <= Remap::maxRemapColor */ false && pixel != _transparentColor) { + byte pixel = pixels[i]; + if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) { return true; } } @@ -841,7 +843,16 @@ bool CelObjView::analyzeUncompressedForRemap() const { } bool CelObjView::analyzeForRemap() const { - // TODO: Implement decompression and analysis + READER_Compressed reader(*this, _width); + for (int y = 0; y < _height; y++) { + const byte *curRow = reader.getRow(y); + for (int x = 0; x < _width; x++) { + byte pixel = curRow[x]; + if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) { + return true; + } + } + } return false; } diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 8d030cfd4a..0bb4b03ae2 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -104,6 +104,10 @@ struct CelInfo32 { bitmap == other.bitmap ); } + + inline bool operator!=(const CelInfo32 &other) { + return !(*this == other); + } }; class CelObj; @@ -180,13 +184,16 @@ public: CelScaler() : _scaleTables(), _activeIndex(0) { - CelScalerTable &table = _scaleTables[_activeIndex]; + CelScalerTable &table = _scaleTables[0]; table.scaleX = Ratio(); table.scaleY = Ratio(); for (int i = 0; i < ARRAYSIZE(table.valuesX); ++i) { table.valuesX[i] = i; table.valuesY[i] = i; } + for (int i = 1; i < ARRAYSIZE(_scaleTables); ++i) { + _scaleTables[i] = _scaleTables[0]; + } } /** @@ -391,18 +398,15 @@ private: void drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; + void drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - void drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; + // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32 + void drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; @@ -411,12 +415,7 @@ private: void drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - void drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; + // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32 #pragma mark - #pragma mark CelObj - Caching diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 8333459b64..729eeeaf81 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -67,7 +67,7 @@ uint16 GfxCompare::isOnControl(uint16 screenMask, const Common::Rect &rect) { return result; } -reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) { +reg_t GfxCompare::canBeHereCheckRectList(const reg_t checkObject, const Common::Rect &checkRect, const List *list, const uint16 signalFlags) const { reg_t curAddress = list->first; Node *curNode = _segMan->lookupNode(curAddress); reg_t curObject; @@ -78,7 +78,7 @@ reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect & curObject = curNode->value; if (curObject != checkObject) { signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); - if (!(signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate))) { + if (!(signal & signalFlags)) { curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); @@ -111,9 +111,6 @@ uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) { void GfxCompare::kernelSetNowSeen(reg_t objectReference) { GfxView *view = NULL; Common::Rect celRect(0, 0); - // TODO/FIXME: Torin's menu code tries to draw special views with an ID of 0xFFFF, which - // are not currently handled properly and cause a crash. These might be text views that - // are not properly implemented. GuiResourceId viewId = (GuiResourceId)readSelectorValue(_segMan, objectReference, SELECTOR(view)); int16 loopNo = readSelectorValue(_segMan, objectReference, SELECTOR(loop)); int16 celNo = readSelectorValue(_segMan, objectReference, SELECTOR(cel)); @@ -124,26 +121,8 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z)); view = _cache->getView(viewId); - -#ifdef ENABLE_SCI32 - if (view->isSci2Hires()) - view->adjustToUpscaledCoordinates(y, x); - else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) - _coordAdjuster->fromScriptToDisplay(y, x); -#endif - view->getCelRect(loopNo, celNo, x, y, z, celRect); -#ifdef ENABLE_SCI32 - if (view->isSci2Hires()) { - view->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - view->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); - } else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) { - _coordAdjuster->fromDisplayToScript(celRect.top, celRect.left); - _coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right); - } -#endif - if (lookupSelector(_segMan, objectReference, SELECTOR(nsTop), NULL, NULL) == kSelectorVariable) { setNSRect(objectReference, celRect); } @@ -151,32 +130,57 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { Common::Rect checkRect; - Common::Rect adjustedRect; - uint16 signal, controlMask; uint16 result; checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); + uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); if (!checkRect.isValidRect()) { // can occur in Iceman and Mother Goose - HACK? TODO: is this really occuring in sierra sci? check this warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom); return NULL_REG; // this means "can be here" } - adjustedRect = _coordAdjuster->onControl(checkRect); - - signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); - controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); + Common::Rect adjustedRect = _coordAdjuster->onControl(checkRect); + uint16 controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); result = isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask; if ((!result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) { List *list = _segMan->lookupList(listReference); if (!list) error("kCanBeHere called with non-list as parameter"); - return canBeHereCheckRectList(curObject, checkRect, list); + return canBeHereCheckRectList(curObject, checkRect, list, kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate); + } + + return make_reg(0, result); +} + +reg_t GfxCompare::kernelCantBeHere32(const reg_t curObject, const reg_t listReference) const { + // Most of SCI32 graphics code converts rects from the VM to exclusive + // rects before operating on them, but this call leverages SCI16 engine + // code that operates on inclusive rects, so the rect's bottom-right + // point is not modified like in other SCI32 kernel calls + Common::Rect checkRect( + readSelectorValue(_segMan, curObject, SELECTOR(brLeft)), + readSelectorValue(_segMan, curObject, SELECTOR(brTop)), + readSelectorValue(_segMan, curObject, SELECTOR(brRight)), + readSelectorValue(_segMan, curObject, SELECTOR(brBottom)) + ); + + uint16 result = 0; + uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); + const uint16 signalFlags = kSignalIgnoreActor | kSignalHidden; + + if ((signal & signalFlags) == 0) { + List *list = _segMan->lookupList(listReference); + if (!list) { + error("kCantBeHere called with non-list as parameter"); + } + result = !canBeHereCheckRectList(curObject, checkRect, list, signalFlags).isNull(); } + return make_reg(0, result); } diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h index 88b44aeeb1..c7005980d0 100644 --- a/engines/sci/graphics/compare.h +++ b/engines/sci/graphics/compare.h @@ -40,6 +40,7 @@ public: uint16 kernelOnControl(byte screenMask, const Common::Rect &rect); void kernelSetNowSeen(reg_t objectReference); reg_t kernelCanBeHere(reg_t curObject, reg_t listReference); + reg_t kernelCantBeHere32(const reg_t curObject, const reg_t listReference) const; bool kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position); void kernelBaseSetter(reg_t object); Common::Rect getNSRect(reg_t object); @@ -58,7 +59,7 @@ private: * *different* from checkObject, has a brRect which is contained inside * checkRect. */ - reg_t canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list); + reg_t canBeHereCheckRectList(const reg_t checkObject, const Common::Rect &checkRect, const List *list, const uint16 signalFlags) const; }; } // End of namespace Sci diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 655e59de00..6454a1eb32 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -47,6 +47,7 @@ #include "sci/graphics/paint32.h" #include "sci/graphics/palette32.h" #include "sci/graphics/picture.h" +#include "sci/graphics/remap.h" #include "sci/graphics/text32.h" #include "sci/graphics/plane32.h" #include "sci/graphics/screen_item32.h" @@ -55,8 +56,6 @@ namespace Sci { -// TODO/FIXME: This is partially guesswork - static int dissolveSequences[2][20] = { /* SCI2.1early- */ { 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080 }, /* SCI2.1mid+ */ { 0, 0, 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080, 73728, 132096, 466944 } @@ -81,7 +80,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd _showStyles(nullptr), // TODO: Stop using _gfxScreen _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), - _priorityMap(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), _remapOccurred(false), _frameNowVisible(false), _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()), @@ -117,6 +115,22 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd _defaultUnknownC = unknownCDefaults[1]; } + switch (g_sci->getGameId()) { + case GID_GK2: + case GID_LIGHTHOUSE: + case GID_LSL7: + case GID_PHANTASMAGORIA2: + case GID_PQSWAT: + case GID_TORIN: + case GID_RAMA: + _currentBuffer.scriptWidth = 640; + _currentBuffer.scriptHeight = 480; + break; + default: + // default script width for other games is 320x200 + break; + } + // TODO: Nothing in the renderer really uses this. Currently, // the cursor renderer does, and kLocalToGlobal/kGlobalToLocal // do, but in the real engine (1) the cursor is handled in @@ -246,16 +260,11 @@ void GfxFrameout::syncWithScripts(bool addElements) { void GfxFrameout::kernelAddScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); -// TODO: Remove -// debug("Adding screen item %04x:%04x to plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); - _segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewInserted); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kAddScreenItem"); + error("kAddScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object)); } ScreenItem *screenItem = plane->_screenItemList.findByObject(object); @@ -273,16 +282,12 @@ void GfxFrameout::kernelUpdateScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kUpdateScreenItem"); + error("kUpdateScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object)); } ScreenItem *screenItem = plane->_screenItemList.findByObject(object); if (screenItem == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid screen item passed to kUpdateScreenItem"); + error("kUpdateScreenItem: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); } screenItem->update(object); @@ -297,18 +302,11 @@ void GfxFrameout::kernelDeleteScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - // TODO: Remove -// warning("Invalid plane selector %04x:%04x passed to kDeleteScreenItem (real engine ignores this)", PRINT_REG(object)); return; } -// TODO: Remove -// debug("Deleting screen item %04x:%04x from plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); - ScreenItem *screenItem = plane->_screenItemList.findByObject(object); if (screenItem == nullptr) { -// TODO: Remove -// warning("Invalid screen item %04x:%04x passed to kDeleteScreenItem (real engine ignores this)", PRINT_REG(object)); return; } @@ -339,8 +337,7 @@ void GfxFrameout::kernelAddPlane(const reg_t object) { void GfxFrameout::kernelUpdatePlane(const reg_t object) { Plane *plane = _planes.findByObject(object); if (plane == nullptr) { - warning("plane %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - error("Invalid plane selector passed to kUpdatePlane"); + error("kUpdatePlane: Plane %04x:%04x not found", PRINT_REG(object)); } plane->update(object); @@ -350,8 +347,7 @@ void GfxFrameout::kernelUpdatePlane(const reg_t object) { void GfxFrameout::kernelDeletePlane(const reg_t object) { Plane *plane = _planes.findByObject(object); if (plane == nullptr) { - warning("plane %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - error("Invalid plane selector passed to kDeletePlane"); + error("kDeletePlane: Plane %04x:%04x not found", PRINT_REG(object)); } if (plane->_created) { @@ -359,8 +355,6 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) { // just ends up doing this anyway so we skip the extra indirection _planes.erase(plane); } else { - // TODO: Remove -// debug("Deleting plane %04x:%04x", PRINT_REG(object)); plane->_created = 0; plane->_deleted = g_sci->_gfxFrameout->getScreenCount(); } @@ -369,7 +363,7 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) { void GfxFrameout::deletePlane(Plane &planeToFind) { Plane *plane = _planes.findByObject(planeToFind._object); if (plane == nullptr) { - error("Invalid plane passed to deletePlane"); + error("deletePlane: Plane %04x:%04x not found", PRINT_REG(planeToFind._object)); } if (plane->_created) { @@ -381,6 +375,33 @@ void GfxFrameout::deletePlane(Plane &planeToFind) { } } +void GfxFrameout::kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics) { + Plane *plane = _planes.findByObject(object); + if (plane == nullptr) { + error("kMovePlaneItems: Plane %04x:%04x not found", PRINT_REG(object)); + } + + plane->scrollScreenItems(deltaX, deltaY, scrollPics); + + for (ScreenItemList::iterator it = plane->_screenItemList.begin(); it != plane->_screenItemList.end(); ++it) { + ScreenItem &screenItem = **it; + + // If object is a number, the screen item from the + // engine, not a script, and should be ignored + if (screenItem._object.isNumber()) { + continue; + } + + if (deltaX != 0) { + writeSelectorValue(_segMan, screenItem._object, SELECTOR(x), readSelectorValue(_segMan, screenItem._object, SELECTOR(x)) + deltaX); + } + + if (deltaY != 0) { + writeSelectorValue(_segMan, screenItem._object, SELECTOR(y), readSelectorValue(_segMan, screenItem._object, SELECTOR(y)) + deltaY); + } + } +} + int16 GfxFrameout::kernelGetHighPlanePri() { return _planes.getTopSciPlanePriority(); } @@ -417,8 +438,7 @@ void GfxFrameout::updatePlane(Plane &plane) { void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 x, const int16 y, const bool mirrorX) { Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kAddPicAt"); + error("kAddPicAt: Plane %04x:%04x not found", PRINT_REG(planeObject)); } plane->addPic(pictureId, Common::Point(x, y), mirrorX); } @@ -440,9 +460,8 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) screenItemLists.resize(_planes.size()); eraseLists.resize(_planes.size()); - // _numActiveRemaps was a global in SCI engine - if (/* TODO Remap::_numActiveRemaps > 0 */ false && _remapOccurred) { - // remapMarkRedraw(); + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); } calcLists(screenItemLists, eraseLists, rect); @@ -766,16 +785,6 @@ void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) { } void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { - _hasRemappedScreenItem = false; - if (/* TODO: g_Remap_UnknownCounter2 */ false && !_priorityMap.isNull()) { - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { - if ((*it)->screenItem->getCelObj()._remap) { - _hasRemappedScreenItem = true; - break; - } - } - } - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { DrawItem &drawItem = **it; mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); @@ -820,9 +829,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry Palette sourcePalette(*_palette->getNextPalette()); alterVmap(sourcePalette, sourcePalette, -1, styleRanges); - // TODO: unsure if this is what this variable actually - // represents, but it is the correct variable number - int16 lastRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); + int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight()); _showList.add(rect); @@ -838,11 +845,9 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry screenItemLists.resize(_planes.size()); eraseLists.resize(_planes.size()); - // TODO: Remap - // _numActiveRemaps was a global in SCI engine - // if (Remap::_numActiveRemaps > 0 && _remapOccurred) { - // _screen->remapMarkRedraw(); - // } + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } calcLists(screenItemLists, eraseLists, calcRect); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { @@ -865,7 +870,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry Palette nextPalette(*_palette->getNextPalette()); - if (lastRoom < 1000) { + if (prevRoom < 1000) { for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { if (styleRanges[i] == -1 || styleRanges[i] == 0) { sourcePalette.colors[i] = nextPalette.colors[i]; @@ -889,7 +894,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry if (showStyle && showStyle->type != kShowStyleUnknown) { // TODO: SCI2.1mid transition effects // processEffects(); - warning("Transition not implemented!"); + warning("Transition %d not implemented!", showStyle->type); } else { showBits(); } @@ -897,15 +902,12 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry _frameNowVisible = true; for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) { -// TODO: -// plane->updateRedrawAllCount(); + (*plane)->_redrawAllCount = getScreenCount(); } - // TODO: Remap - // _numActiveRemaps was a global in SCI engine - // if (Remap::_numActiveRemaps > 0 && _remapOccurred) { - // _screen->remapMarkRedraw(); - // } + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } calcLists(screenItemLists, eraseLists, calcRect); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { @@ -1031,7 +1033,10 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co int8 styleRangeValue = styleRanges[currentValue]; if (styleRangeValue == -1 && styleRangeValue == style) { currentValue = pixels[pixelIndex] = clut[currentValue]; - styleRangeValue = styleRanges[clut[currentValue]]; + // NOTE: In original engine this assignment happens outside of the + // condition, but if the branch is not followed the value is just + // going to be the same as it was before + styleRangeValue = styleRanges[currentValue]; } if ( @@ -1082,44 +1087,7 @@ inline ShowStyleEntry *GfxFrameout::deleteShowStyleInternal(ShowStyleEntry *cons lastEntry->next = showStyle->next; } - // NOTE: Differences from SCI2/2.1early engine: - // 1. Memory of ShowStyle-owned objects was freed before ShowStyle was - // removed from the linked list, but since this operation is position - // independent, it has been moved after removal from the list for - // consistency with SCI2.1mid+ - // 2. In SCI2, `screenItems` was a pointer to an array of pointers, so - // extra deletes were performed here; we use an owned container object - // instead, which is automatically freed when ShowStyle is freed -#if 0 - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - uint8 type = showStyle->type; - - if (type >= 1 && type <= 10) { - ScreenItemList &styleItems = showStyle->screenItems; - for (ScreenItemList::iterator it = styleItems.begin(); it != styleItems.end(); ++it) { - if (active) { - // TODO: _screen->deleteScreenItem(showStyle->plane, *it->id); - _screenItems.remove(*it); - } - delete *it; - } - } else if (type == 11 || type == 12) { - if (!showStyle->bitmapMemId.isNull()) { - _segMan->freeHunkEntry(showStyle->bitmapMemId); - } - if (showStyle->bitmapScreenItem != nullptr) { - // TODO: _screen->deleteScreenItem(showStyle->plane, showStyle->bitmapScreenItem->id); - _screenItems.remove(showStyle->bitmapScreenItem); - delete showStyle->bitmapScreenItem; - } - } - } else { -#endif - delete[] showStyle->fadeColorRanges; -#if 0 - } -#endif - + delete[] showStyle->fadeColorRanges; delete showStyle; // TODO: Verify that this is the correct entry to return @@ -1131,11 +1099,20 @@ inline ShowStyleEntry *GfxFrameout::deleteShowStyleInternal(ShowStyleEntry *cons // and need to be fixed in future // TODO: SQ6 does not use 'priority' (exists since SCI2) or 'blackScreen' (exists since SCI3); // check to see if other versions use or if they are just always ignored -void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, const ShowStyleType type, const int16 seconds, const int16 back, const int16 priority, const int16 animate, const int16 frameOutNow, const reg_t &pFadeArray, const int16 divisions, const int16 blackScreen) { +void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 back, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen) { bool hasDivisions = false; bool hasFadeArray = false; - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + + // KQ7 2.0b uses a mismatched version of the Styler script (SCI2.1early script + // for SCI2.1mid engine), so the calls it makes to kSetShowStyle are wrong and + // put `divisions` where `pFadeArray` is supposed to be + if (getSciVersion() == SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() == GID_KQ7) { + hasDivisions = argc > 7; + hasFadeArray = false; + divisions = argc > 7 ? pFadeArray.toSint16() : -1; + pFadeArray = NULL_REG; + } else if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { hasDivisions = argc > 7; hasFadeArray = false; } else if (getSciVersion() < SCI_VERSION_3) { @@ -1165,20 +1142,12 @@ void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, c error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj)); } - // TODO: This is Plane.gameRect in SCI engine, not planeRect. Engine uses - // Plane::ConvGameRectToPlaneRect to convert from gameRect to planeRect. - // Also this never gets used by SQ6 so it is not clear what it does yet - // Common::Rect gameRect = plane.planeRect; - bool createNewEntry = true; ShowStyleEntry *entry = findShowStyleForPlane(planeObj); if (entry != nullptr) { + // TODO: SCI2.1early has different criteria for show style reuse bool useExisting = true; - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - useExisting = plane->_planeRect.width() == entry->width && plane->_planeRect.height() == entry->height; - } - if (useExisting) { useExisting = entry->divisions == (hasDivisions ? divisions : _defaultDivisions[type]) && entry->unknownC == _defaultUnknownC[type]; } @@ -1207,39 +1176,28 @@ void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, c entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; entry->plane = planeObj; -#if 0 - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - entry->bitmapMemId = NULL_REG; - entry->screenItems.empty(); - entry->width = plane->_planeRect.width(); - entry->height = plane->_planeRect.height(); - } else { -#endif - entry->fadeColorRanges = nullptr; - if (hasFadeArray) { - // NOTE: SCI2.1mid engine does no check to verify that an array is - // successfully retrieved, and SegMan will cause a fatal error - // if we try to use a memory segment that is not an array - SciArray<reg_t> *table = _segMan->lookupArray(pFadeArray); - - uint32 rangeCount = table->getSize(); - entry->fadeColorRangesCount = rangeCount; - - // NOTE: SCI engine code always allocates memory even if the range - // table has no entries, but this does not really make sense, so - // we avoid the allocation call in this case - if (rangeCount > 0) { - entry->fadeColorRanges = new uint16[rangeCount]; - for (size_t i = 0; i < rangeCount; ++i) { - entry->fadeColorRanges[i] = table->getValue(i).toUint16(); - } + entry->fadeColorRanges = nullptr; + if (hasFadeArray) { + // NOTE: SCI2.1mid engine does no check to verify that an array is + // successfully retrieved, and SegMan will cause a fatal error + // if we try to use a memory segment that is not an array + SciArray<reg_t> *table = _segMan->lookupArray(pFadeArray); + + uint32 rangeCount = table->getSize(); + entry->fadeColorRangesCount = rangeCount; + + // NOTE: SCI engine code always allocates memory even if the range + // table has no entries, but this does not really make sense, so + // we avoid the allocation call in this case + if (rangeCount > 0) { + entry->fadeColorRanges = new uint16[rangeCount]; + for (size_t i = 0; i < rangeCount; ++i) { + entry->fadeColorRanges[i] = table->getValue(i).toUint16(); } - } else { - entry->fadeColorRangesCount = 0; } -#if 0 + } else { + entry->fadeColorRangesCount = 0; } -#endif } // NOTE: The original engine had no nullptr check and would just crash @@ -1256,9 +1214,6 @@ void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, c entry->delay = (seconds * 60 + entry->divisions - 1) / entry->divisions; if (entry->delay == 0) { -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE && entry->fadeColorRanges != nullptr) { -#endif if (entry->fadeColorRanges != nullptr) { delete[] entry->fadeColorRanges; } @@ -1272,235 +1227,13 @@ void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, c } if (createNewEntry) { - // TODO: Implement SCI3, which may or may not actually have - // the same transitions as SCI2/SCI2.1early, but implemented - // differently -#if 0 - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - switch (entry->type) { - case kShowStyleHShutterIn: - case kShowStyleHShutterOut: - prepareShowStyleWipe(entry, priority, 2, true); - break; - - case kShowStyleVShutterIn: - case kShowStyleVShutterOut: - prepareShowStyleWipe(entry, priority, 2, false); - break; - - case kShowStyleHWipe1: - case kShowStyleHWipe2: - prepareShowStyleWipe(entry, priority, 1, true); - break; - - case kShowStyleVWipe1: - case kShowStyleVWipe2: - prepareShowStyleWipe(entry, priority, 1, false); - break; - - case kShowStyleIrisIn: - case kShowStyleIrisOut: - prepareShowStyleIris(entry, priority); - break; - - case kShowStyle11: - case kShowStyle12: - prepareShowStylePixels(entry, priority, plane->planeRect); - break; - - default: - break; - } - } -#endif - + // TODO: Implement SCI2.1early and SCI3 entry->next = _showStyles; _showStyles = entry; } } } -#if 0 -void addFrameoutEntryInternal(ShowStyleEntry *const showStyle, const int16 priority, const CelInfo32 &celInfo, const Common::Rect &rect) { - ScreenItem *screenItem = new ScreenItem; - screenItem->plane = showStyle->plane; - screenItem->celInfo = celInfo; - screenItem->celRect = rect; - screenItem->isInList = true; - screenItem->priority = priority; - screenItem->visible = true; - showStyle->screenItems.push_back(screenItem); -} - -void GfxFrameout::prepareShowStyleWipe(ShowStyleEntry *const showStyle, const int16 priority, const int16 edgeCount, const bool horizontal) { - assert(edgeCount == 1 || edgeCount == 2); - - const int numScreenItems = showStyle->divisions * edgeCount; - const int extra = edgeCount > 1 ? 1 : 0; - - showStyle->edgeCount = edgeCount; - showStyle->screenItems.reserve(numScreenItems); - - CelInfo32 celInfo; - celInfo.bitmap = NULL_REG; - celInfo.type = kCelObjTypeView; - celInfo.color = showStyle->color; - - for (int i = 0; i < numScreenItems; ++i) { - Common::Rect rect; - - if (horizontal) { - rect.top = 0; - rect.bottom = showStyle->height - 1; - rect.left = (showStyle->width * i) / (showStyle->divisions * edgeCount); - rect.right = ((i + 1) * (showStyle->width + extra)) / (showStyle->divisions * edgeCount) - 1; - } else { - rect.left = 0; - rect.right = showStyle->width - 1; - rect.top = (showStyle->height * i) / (showStyle->divisions * edgeCount); - rect.bottom = ((i + 1) * (showStyle->height + extra)) / (showStyle->divisions * edgeCount) - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - if (edgeCount == 2) { - if (horizontal) { - int temp = rect.left; - rect.left = showStyle->width - rect.right - 1; - rect.right = showStyle->width - temp - 1; - } else { - int temp = rect.top; - rect.top = showStyle->height - rect.bottom - 1; - rect.bottom = showStyle->height - temp - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - } - } -} - -void GfxFrameout::prepareShowStyleIris(ShowStyleEntry *const showStyle, const int16 priority) { - const int edgeCount = 4; - const int numScreenItems = showStyle->divisions * edgeCount; - - showStyle->edgeCount = edgeCount; - showStyle->screenItems.reserve(numScreenItems); - - CelInfo32 celInfo; - celInfo.bitmap = NULL_REG; - celInfo.type = kCelObjTypeView; - celInfo.color = showStyle->color; - - for (int i = 0; i < numScreenItems; ++i) { - Common::Rect rect; - - rect.right = showStyle->width - ((showStyle->width * i) / (showStyle->divisions * 2)) - 1; - rect.left = (showStyle->width * i) / (showStyle->divisions * 2); - rect.top = (showStyle->height * i) / (showStyle->divisions * 2); - rect.bottom = ((i + 1) * (showStyle->height + 1)) / (showStyle->divisions * 2) - 1; - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - { - int temp = rect.top; - rect.top = showStyle->height - rect.bottom - 1; - rect.bottom = showStyle->height - temp - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - rect.top = ((i + 1) * (showStyle->height + 1)) / (showStyle->divisions * 2); - rect.right = ((i + 1) * (showStyle->width + 1)) / (showStyle->divisions * 2) - 1; - rect.bottom = ((i + 1) * (showStyle->height + 1)) / (showStyle->divisions * 2) - 1; - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - { - int temp = rect.left; - rect.left = showStyle->width - rect.right - 1; - rect.right = showStyle->width - temp - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - } -} - -void GfxFrameout::prepareShowStylePixels(ShowStyleEntry *const showStyle, const int16 priority, const Common::Rect planeGameRect) { - const int bitmapSize = showStyle->width * showStyle->height; - - // TODO: Verify that memory type 0x200 (what GK1 engine uses) - // is Hunk type - reg_t bitmapMemId = _segMan->allocateHunkEntry("ShowStylePixels()", bitmapSize + sizeof(GfxBitmapHeader)); - showStyle->bitmapMemId = bitmapMemId; - - // TODO: SCI2 GK1 uses a Bitmap constructor function to - // do this work - byte *bitmap = _segMan->getHunkPointer(bitmapMemId); - GfxBitmapHeader *header = (GfxBitmapHeader *)bitmap; - byte *bitmapData = bitmap + sizeof(GfxBitmapHeader); - - // TODO: These are defaults from the Bitmap constructor in - // GK1, not specific values set by this function. - // TODO: This probably should not even be using a struct at - // all since this information is machine endian dependent - // and will be reversed for Mac versions or when running - // ScummVM on big-endian systems. GK1 used packed structs - // everywhere so this probably worked better there too. - header->field_18 = 36; - header->field_1c = 36; - memset(header, 0, sizeof(GfxBitmapHeader)); - - header->width = showStyle->width; - header->height = showStyle->height; - header->field_8 = 250; - header->size = bitmapSize; - - // TODO: Scaled dimensions in bitmap headers was not added - // until SCI2.1mid. It is not clear what the right thing to - // do here is. - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { - header->scaledWidth = _currentBuffer.scriptWidth; - header->scaledHeight = _currentBuffer.scriptHeight; - } - - Common::Rect copyRect; - // TODO: planeGameRect is supposedly in script coordinates, - // which are usually 320x200. If bitsSaveDisplayScreen is - // in native resolution then seemingly this function will - // not work properly since we will be not copy enough bits, - // or from the correct location. - copyRect.left = planeGameRect.left; - copyRect.top = planeGameRect.top; - copyRect.right = planeGameRect.left + showStyle->width; - copyRect.bottom = planeGameRect.top + showStyle->height; - _screen->bitsSaveDisplayScreen(copyRect, bitmapData); - - CelInfo32 celInfo; - celInfo.bitmap = bitmapMemId; - celInfo.type = kCelObjTypeMem; - - ScreenItem *screenItem = new ScreenItem; - - screenItem->position.x = 0; - screenItem->position.y = 0; - - showStyle->bitmapScreenItem = screenItem; - screenItem->priority = priority; - - // TODO: Have not seen/identified this particular flag yet in - // SCI2.1mid (SQ6) engine; maybe (1) a duplicate of `created`, - // or (2) does not exist any more, or (3) one of the other - // still-unidentified fields. Probably need to look at the - // GK1 source for its use in drawing algorithms to determine - // if/how this correlates to ScreenItem members in the - // SCI2.1mid engine. -// screenItem->isInList = true; - - Plane *plane = _planes.findByObject(showStyle.plane); - plane->_screenItemList.add(screenItem); -} -#endif - // NOTE: Different version of SCI engine support different show styles // SCI2 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12, 13, 14 // SCI2.1 implements 0, 1/2/3/4/5/6/7/8/9/10/11/12/15, 13, 14 @@ -1542,53 +1275,15 @@ void GfxFrameout::processShowStyles() { case kShowStyleWipeLeft: case kShowStyleWipeUp: case kShowStyleIrisOut: -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyleWipe(-1, showStyle); - } -#endif - break; case kShowStyleHShutterIn: case kShowStyleVShutterIn: case kShowStyleWipeRight: case kShowStyleWipeDown: case kShowStyleIrisIn: -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyleWipe(1, showStyle); - } -#endif - break; case kShowStyle11: -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyle11(showStyle); - } -#endif - break; case kShowStyle12: case kShowStyleUnknown: { -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyle12(showStyle); - } -#endif + retval = processShowStyleMorph(showStyle); break; } case kShowStyleFadeOut: { @@ -1682,285 +1377,6 @@ bool GfxFrameout::processShowStyleFade(const int direction, ShowStyleEntry *cons return false; } -// TODO: Rect sizes are wrong, rects in SCI are inclusive of bottom/right but -// in ScummVM are exclusive so extra ±1 operations here are wrong -#if 0 -bool GfxFrameout::processShowStyleWipe(const ShowStyleEntry *const style) { - const int16 divisions = style->divisions; - Common::Rect rect(divisions, divisions); - - const Plane *const plane = _visibleScreen->planeList->findByObject(style->plane); - - const int16 planeLeft = plane->field_4C.left; - const int16 planeTop = plane->field_4C.top; - const int16 planeRight = plane->field_4C.right; - const int16 planeBottom = plane->field_4C.bottom; - const int16 planeWidth = planeRight - planeLeft + 1; - const int16 planeHeight = planeBottom - planeTop + 1; - - const int16 divisionWidth = planeWidth / divisions - 1; - int16 shutterDivisionWidth = planeWidth / (2 * divisions); - if (shutterDivisionWidth >= 0) { - const int16 heightPerDivision = planeHeight / divisions; - int16 shutterMiddleX = divisions * shutterDivisionWidth; - for (int16 x = divisionWidth - shutterDivisionWidth; x <= divisionWidth; ++x) { - int16 divisionTop = 0; - for (int16 y = 0; y < heightPerDivision; ++y, divisionTop += divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + divisions - 1; - rect.left = planeLeft + shutterMiddleX; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - // number of divisions does not divide evenly into plane height, - // draw the remainder - if (planeHeight % divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + planeHeight % divisions - 1; - rect.left = planeLeft + shutterMiddleX; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - - divisionTop = 0; - for (int16 y = 0; y < heightPerDivision; ++y, divisionTop += divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + divisions - 1; - rect.left = planeLeft + divisions * x; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - if (planeHeight % divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + planeHeight % divisions - 1; - rect.left = planeLeft + divisions * x; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - - shutterMiddleX -= divisions; - --shutterDivisionWidth; - } - } - - if (planeWidth % divisions) { - const int16 roundedPlaneWidth = divisions * divisionWidth; - int16 divisionTop = 0; - for (int16 y = 0; y < planeHeight / divisions; ++y, divisionTop += divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + divisions - 1; - rect.left = planeLeft + roundedPlaneWidth; - rect.right = rect.left + planeWidth % divisions + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - if (planeHeight % divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + planeHeight % divisions - 1; - rect.left = planeLeft + roundedPlaneWidth; - rect.right = rect.left + planeWidth % divisions + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - } - - rect.right = planeRight; - rect.left = planeLeft; - rect.top = planeTop; - rect.bottom = planeBottom; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); -} -#endif -#if 0 -bool GfxFrameout::processShowStyleWipe(const int direction, ShowStyleEntry *const showStyle) { - if (showStyle->currentStep < showStyle->divisions) { - int index; - if (direction <= 0) { - index = showStyle->divisions - showStyle->currentStep - 1; - } else { - index = showStyle->currentStep; - } - - index *= showStyle->edgeCount; - - if (showStyle->edgeCount > 0) { - for (int i = 0; i < showStyle->edgeCount; ++i) { - if (showStyle->fadeUp) { - ScreenItem *screenItem = showStyle->screenItems[index + i]; - if (screenItem != nullptr) { - // TODO: _screen->deleteScreenItem(screenItem); - _screenItems.remove(screenItem); - - delete screenItem; - showStyle->screenItems[index + i] = nullptr; - } - } else { - ScreenItem *screenItem = showStyle->screenItems[index + i]; - // TODO: _screen->addScreenItem(screenItem); - _screenItems.push_back(screenItem); - } - } - - ++showStyle->currentStep; - showStyle->nextTick += showStyle->delay; - } - } - - if (showStyle->currentStep >= showStyle->divisions) { - if (showStyle->fadeUp) { - showStyle->processed = true; - } - - return true; - } - - return false; -} - -void fillRect(byte *data, const Common::Rect &rect, const int16 color, const int16 stride) { - -} - -bool GfxFrameout::processShowStyle11(ShowStyleEntry *const showStyle) { - int divisions = showStyle->divisions * showStyle->divisions; - - byte *bitmapData = _segMan->getHunkPointer(showStyle->bitmapMemId) + sizeof(GfxBitmapHeader); - - int ebx; - - if (showStyle->currentStep == 0) { - int ctr = 0; - int bloot = divisions; - do { - bloot >>= 1; - ++ctr; - } while (bloot != 1); - - showStyle->dissolveSeed = _dissolveSequenceSeeds[ctr]; - ebx = 800; - showStyle->unknown3A = 800; - showStyle->dissolveInitial = 800; - } else { - int ebx = showStyle->unknown3A; - do { - int eax = ebx >> 1; - if (ebx & 1) { - ebx = showStyle->dissolveSeed ^ eax; - } else { - ebx = eax; - } - } while (ebx >= divisions); - - if (ebx == showStyle->dissolveInitial) { - ebx = 0; - } - } - - Common::Rect rect; - - rect.left = (showStyle->width + showStyle->divisions - 1) / showStyle->divisions; - rect.top = (showStyle->height + showStyle->divisions - 1) / showStyle->divisions; - - if (showStyle->currentStep <= showStyle->divisions) { - int ebp = 0; - do { - int ecx = ebx % showStyle->divisions; - - Common::Rect drawRect; - drawRect.left = rect.left * ecx; - drawRect.right = drawRect.left + rect.left - 1; - drawRect.top = rect.top * ebx; - drawRect.bottom = rect.top * ebx + rect.top - 1; - - bool doit; - if (drawRect.right >= 0 && drawRect.bottom >= 0 && drawRect.left <= rect.right && drawRect.top <= rect.bottom) { - doit = true; - } else { - doit = false; - } - - if (doit) { - if (drawRect.left < 0) { - drawRect.left = 0; - } - - if (drawRect.top < 0) { - drawRect.top = 0; - } - - if (drawRect.right > rect.right) { - drawRect.right = rect.right; - } - - if (drawRect.bottom > rect.bottom) { - drawRect.bottom = rect.bottom; - } - } else { - drawRect.right = 0; - drawRect.bottom = 0; - drawRect.left = 0; - drawRect.top = 0; - } - - fillRect(bitmapData, drawRect, showStyle->color, showStyle->width); - - int eax = ebx; - do { - eax >>= 1; - if (ebx & 1) { - ebx = showStyle->dissolveSeed; - ebx ^= eax; - } else { - ebx = eax; - } - } while (ebx >= divisions); - - if (showStyle->currentStep != showStyle->divisions) { - ebp++; - } else { - drawRect.left = 0; - drawRect.top = 0; - drawRect.right = showStyle->width - 1; - drawRect.bottom = showStyle->height - 1; - fillRect(bitmapData, drawRect, showStyle->color, showStyle->width); - } - - } while (ebp <= showStyle->divisions); - - showStyle->unknown3A = ebx; - ++showStyle->currentStep; - showStyle->nextTick += showStyle->delay; - // _screen->updateScreenItem(showStyle->bitmapScreenItem); - } - - if (showStyle->currentStep >= showStyle->divisions) { - if (showStyle->fadeUp) { - showStyle->processed = true; - } - - return true; - } - - return false; -} - -bool GfxFrameout::processShowStyle12(ShowStyleEntry *const showStyle) { - return true; -} -#endif - void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { if (_showStyles != nullptr) { processShowStyles(); @@ -1969,8 +1385,8 @@ void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { _palMorphIsOn = false; } else { // TODO: Window scroll -// if (g_ScrollWindow) { -// doScroll(); +// if (g_PlaneScroll) { +// processScrolls(); // } frameOut(shouldShowBits); @@ -1981,7 +1397,7 @@ void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { #pragma mark Mouse cursor reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &position, bool checkPixel) const { - reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); + const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _visiblePlanes.findByObject(planeObject); if (plane == nullptr) { return make_reg(0, 0); @@ -1992,6 +1408,13 @@ reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &positio return make_reg(0, 0); } + // NOTE: The original engine passed a copy of the ScreenItem into isOnMe + // as a hack around the fact that the screen items in `_visiblePlanes` + // did not have their `_celObj` pointers cleared when their CelInfo was + // updated by `Plane::decrementScreenItemArrayCounts`. We handle this + // this more intelligently by clearing `_celObj` in the copy assignment + // operator, which is only ever called by `decrementScreenItemArrayCounts` + // anyway. return make_reg(0, isOnMe(*screenItem, *plane, position, checkPixel)); } @@ -2028,6 +1451,33 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const return true; } +void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { + const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane)); + + Plane *plane = _planes.findByObject(planeObject); + if (plane == nullptr) { + error("kSetNowSeen: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(screenItemObject)); + } + + ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject); + if (screenItem == nullptr) { + error("kSetNowSeen: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItemObject), PRINT_REG(planeObject)); + } + + Common::Rect result = screenItem->getNowSeenRect(*plane); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsLeft), result.left); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1); +} + +void GfxFrameout::remapMarkRedraw() { + for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) { + Plane *p = *it; + p->remapMarkRedraw(); + } +} + #pragma mark - #pragma mark Debugging diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index f864abc5bc..8ed95a00de 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -130,64 +130,6 @@ struct ShowStyleEntry { bool processed; // - // Engine-specific properties for SCI2 through 2.1early - // - - // TODO: Could union this stuff to save literally - // several bytes of memory. - - /** - * The width of the plane. Used to determine the correct - * size of screen items for wipes. - */ - int width; - - /** - * The height of the plane. Used to determine the correct - * size of screen items for wipes. - */ - int height; - - /** - * The number of edges that a transition operates on. - * Slide wipe: 1 edge - * Reveal wipe: 2 edges - * Iris wipe: 4 edges - */ - // TODO: I have no idea why SCI engine stores this instead - // of a screenItems count - int edgeCount; - - /** - * Used by transition types 1 through 10. - * One screen item per division per edge. - */ - ScreenItemList screenItems; - - /** - * Used by transition types 11 and 12. A copy of the - * visible frame buffer. - */ - // TODO: This is a reg_t in SCI engine; not sure if - // we can avoid allocation through SegMan or not. - reg_t bitmapMemId; - - /** - * Used by transition types 11 and 12. A screen item - * used to display the associated bitmap data. - */ - ScreenItem *bitmapScreenItem; - - /** - * A number used to pick pixels to dissolve by types - * 11 and 12. - */ - int dissolveSeed; - int unknown3A; - // max? - int dissolveInitial; - - // // Engine specific properties for SCI2.1mid through SCI3 // @@ -240,11 +182,13 @@ public: #pragma mark Screen items private: void deleteScreenItem(ScreenItem *screenItem, const reg_t plane); + void remapMarkRedraw(); public: void kernelAddScreenItem(const reg_t object); void kernelUpdateScreenItem(const reg_t object); void kernelDeleteScreenItem(const reg_t object); + void kernelSetNowSeen(const reg_t screenItemObject) const; #pragma mark - #pragma mark Planes @@ -293,6 +237,7 @@ public: void kernelAddPlane(const reg_t object); void kernelUpdatePlane(const reg_t object); void kernelDeletePlane(const reg_t object); + void kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics); int16 kernelGetHighPlanePri(); #pragma mark - @@ -323,14 +268,11 @@ private: bool processShowStyleNone(ShowStyleEntry *showStyle); bool processShowStyleMorph(ShowStyleEntry *showStyle); bool processShowStyleFade(const int direction, ShowStyleEntry *showStyle); -#if 0 - bool processShowStyleWipe(const int direction, ShowStyleEntry *const showStyle); -#endif public: // NOTE: This signature is taken from SCI3 Phantasmagoria 2 // and is valid for all implementations of SCI32 - void kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, const reg_t &pFadeArray, const int16 divisions, const int16 blackScreen); + void kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen); #pragma mark - #pragma mark Rendering @@ -349,13 +291,6 @@ private: */ Buffer _currentBuffer; - // TODO: In SCI2.1/SQ6, priority map pixels are not allocated - // by default. In SCI2/GK1, pixels are allocated, but not used - // anywhere except within CelObj::Draw in seemingly the same - // way they are used in SCI2.1/SQ6: that is, never read, only - // written. - Buffer _priorityMap; - /** * TODO: Documentation */ @@ -454,12 +389,6 @@ private: public: /** - * TODO: Document - * This is used by CelObj::Draw. - */ - bool _hasRemappedScreenItem; - - /** * Whether palMorphFrameOut should be used instead of * frameOut for rendering. Used by kMorphOn to * explicitly enable palMorphFrameOut for one frame. @@ -485,11 +414,6 @@ public: */ void alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges); - // TODO: SCI2 engine never uses priority map? - inline Buffer &getPriorityMap() { - return _priorityMap; - } - // NOTE: This function is used within ScreenItem subsystem and assigned // to various booleanish fields that seem to represent the state of the // screen item (created, updated, deleted). In GK1/DOS, Phant1/m68k, diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index 82e8d779f1..6844011675 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -28,6 +28,7 @@ #include "sci/event.h" #include "sci/resource.h" #include "sci/graphics/palette32.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" namespace Sci { @@ -223,9 +224,7 @@ int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const i bool GfxPalette32::updateForFrame() { applyAll(); _versionUpdated = false; - // TODO: Implement remapping - // return g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); - return false; + return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette); } void GfxPalette32::updateFFrame() { @@ -233,8 +232,7 @@ void GfxPalette32::updateFFrame() { _nextPalette.colors[i] = _sourcePalette.colors[i]; } _versionUpdated = false; - // TODO: Implement remapping - // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); + g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette); } void GfxPalette32::updateHardware() { diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index b8388d11ea..a5450776dc 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -257,6 +257,7 @@ public: void cycleAllOff(); void applyAllCycles(); void applyCycles(); + const bool *getCyclemap() { return _cycleMap; } #pragma mark - #pragma mark Fading diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 4487070948..d05e4f79e1 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -27,6 +27,7 @@ #include "sci/graphics/frameout.h" #include "sci/graphics/lists32.h" #include "sci/graphics/plane32.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/screen_item32.h" @@ -317,7 +318,7 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList vitem != nullptr && !vitem->_screenRect.isEmpty() ) { - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps? + if (g_sci->_gfxRemap32->getRemapCount()) { mergeToRectList(vitem->_screenRect, eraseList); } else { eraseList.add(vitem->_screenRect); @@ -325,11 +326,10 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList } } else if (item->_created) { // add item to draw list - item->getCelObj(); item->calcRects(*this); if(!item->_screenRect.isEmpty()) { - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps? + if (g_sci->_gfxRemap32->getRemapCount()) { drawList.add(item, item->_screenRect); mergeToRectList(item->_screenRect, eraseList); } else { @@ -338,9 +338,8 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList } } else if (item->_updated) { // add old rect to erase list, new item to draw list - item->getCelObj(); item->calcRects(*this); - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps + if (g_sci->_gfxRemap32->getRemapCount()) { // if item and vitem don't overlap, ... if (item->_screenRect.isEmpty() || i >= visiblePlaneItemCount || @@ -397,7 +396,6 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // over the currently inserted entries later. DrawList::size_type drawListSizePrimary = drawList.size(); - // NOTE: Setting this to true fixes the menu bars in GK1 if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"???? _screenItemList.sort(); bool encounteredPic = false; @@ -455,7 +453,7 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList } } - if (/* TODO: g_Remap_numActiveRemaps == 0 */ true) { // no remaps active? + if (g_sci->_gfxRemap32->getRemapCount() == 0) { // no remaps active? // Add all items that overlap with items in the drawlist and have higher // priority. @@ -691,7 +689,6 @@ void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList if (*screenItemPtr != nullptr) { ScreenItem &screenItem = **screenItemPtr; if (!screenItem._deleted) { - screenItem.getCelObj(); screenItem.calcRects(*this); if (!screenItem._screenRect.isEmpty()) { drawList.add(&screenItem, screenItem._screenRect); @@ -801,6 +798,31 @@ void Plane::update(const reg_t object) { _back = readSelectorValue(segMan, object, SELECTOR(back)); } +void Plane::scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics) { + _redrawAllCount = g_sci->_gfxFrameout->getScreenCount(); + + for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) { + if (*it != nullptr) { + ScreenItem &screenItem = **it; + if (!screenItem._deleted && (screenItem._celInfo.type != kCelTypePic || scrollPics)) { + screenItem._position.x += deltaX; + screenItem._position.y += deltaY; + } + } + } +} + +void Plane::remapMarkRedraw() { + for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) { + if (*screenItemPtr != nullptr) { + ScreenItem &screenItem = **screenItemPtr; + if (screenItem.getCelObj()._remap && !screenItem._deleted && !screenItem._created) { + screenItem._updated = g_sci->_gfxFrameout->getScreenCount(); + } + } + } +} + #pragma mark - #pragma mark PlaneList void PlaneList::add(Plane *plane) { diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index d844919c9e..770a6fa445 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -166,7 +166,7 @@ public: * another plane and cleared when draw list calculation * occurs. */ - int _priorityChanged; // ? + int _priorityChanged; /** * A handle to the VM object corresponding to this @@ -182,7 +182,12 @@ public: int16 _priority; /** - * TODO: Document + * Whether or not all screen items in this plane should + * be redrawn on the next frameout, instead of just + * the screen items marked as updated. This is set when + * visual changes to the plane itself are made that + * affect the rendering of the entire plane, and cleared + * once those changes are rendered by `redrawAll`. */ int _redrawAllCount; @@ -303,6 +308,13 @@ public: */ void update(const reg_t object); + /** + * Modifies the position of all non-pic screen items + * by the given delta. If `scrollPics` is true, pic + * items are also repositioned. + */ + void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics); + #pragma mark - #pragma mark Plane - Pic private: @@ -423,6 +435,8 @@ public: * and adds them to the given draw and erase lists. */ void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); + + void remapMarkRedraw(); }; #pragma mark - diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp index 1f1ae4da0e..e331eaf971 100644 --- a/engines/sci/graphics/remap.cpp +++ b/engines/sci/graphics/remap.cpp @@ -23,6 +23,7 @@ #include "sci/sci.h" #include "sci/resource.h" #include "sci/graphics/palette.h" +#include "sci/graphics/palette32.h" #include "sci/graphics/remap.h" #include "sci/graphics/screen.h" @@ -31,8 +32,8 @@ namespace Sci { #pragma mark - #pragma mark SCI16 remapping (QFG4 demo) -GfxRemap::GfxRemap(GfxScreen *screen, GfxPalette *palette) - : _screen(screen), _palette(palette) { +GfxRemap::GfxRemap(GfxPalette *palette) + : _palette(palette) { _remapOn = false; resetRemapping(); } @@ -107,28 +108,279 @@ void GfxRemap::updateRemapping() { #pragma mark - #pragma mark SCI32 remapping -#if 0 -// TODO -void GfxRemap32::setRemappingPercentGray(byte color, byte percent) { - _remapOn = true; +#ifdef ENABLE_SCI32 - // We need to defer the setup of the remapping table every time the screen - // palette is changed, so that kernelFindColor() can find the correct - // colors. Set it once here, in case the palette stays the same and update - // it on each palette change by copySysPaletteToScreen(). - _remappingPercentToSet = percent; +GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) { + for (int i = 0; i < REMAP_COLOR_COUNT; i++) + _remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone); + _noMapStart = _noMapCount = 0; + _update = false; + _remapCount = 0; - // Note: This is not what the original does, but the results are the same visually - for (int i = 0; i < 256; i++) { - byte rComponent = (byte)(_sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100); - byte gComponent = (byte)(_sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100); - byte bComponent = (byte)(_sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100); - byte luminosity = rComponent + gComponent + bComponent; - _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); + // The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle + _remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) ? 244 : 254; +} + +void GfxRemap32::remapOff(byte color) { + if (!color) { + for (int i = 0; i < REMAP_COLOR_COUNT; i++) + _remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone); + + _remapCount = 0; + } else { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + const byte index = _remapEndColor - color; + _remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone); + _remapCount--; } - _remappingType[color] = kRemappingByPercent; + _update = true; +} + +void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setRemappingPercent(byte color, byte percent) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setRemappingToGray(byte color, byte gray) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setNoMatchRange(byte from, byte count) { + _noMapStart = from; + _noMapCount = count; +} + +bool GfxRemap32::remapEnabled(byte color) const { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + const byte index = _remapEndColor - color; + return (_remaps[index].type != kRemappingNone); +} + +byte GfxRemap32::remapColor(byte color, byte target) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + const byte index = _remapEndColor - color; + if (_remaps[index].type != kRemappingNone) + return _remaps[index].remap[target]; + else + return target; +} + +void GfxRemap32::initColorArrays(byte index) { + Palette *curPalette = &_palette->_sysPalette; + RemapParams *curRemap = &_remaps[index]; + + memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color)); + memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color)); +} + +bool GfxRemap32::updateRemap(byte index, bool palChanged) { + int result; + RemapParams *curRemap = &_remaps[index]; + const Palette *curPalette = &_palette->_sysPalette; + const Palette *nextPalette = _palette->getNextPalette(); + bool changed = false; + + if (!_update && !palChanged) + return false; + + Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false); + + switch (curRemap->type) { + case kRemappingNone: + return false; + case kRemappingByRange: + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) { + if (curRemap->from <= i && i <= curRemap->to) + result = i + curRemap->base; + else + result = i; + + if (curRemap->remap[i] != result) { + changed = true; + curRemap->remap[i] = result; + } + + curRemap->colorChanged[i] = true; + } + return changed; + case kRemappingByPercent: + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + // NOTE: This method uses nextPalette instead of curPalette + Color color = nextPalette->colors[i]; + + if (curRemap->curColor[i] != color) { + curRemap->colorChanged[i] = true; + curRemap->curColor[i] = color; + } + + if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i]) { + byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255); + byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255); + byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255); + byte used = curRemap->targetColor[i].used; + + Color newColor = { used, red, green, blue }; + if (curRemap->targetColor[i] != newColor) { + _targetChanged[i] = true; + curRemap->targetColor[i] = newColor; + } + } + } + + changed = applyRemap(index); + Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false); + curRemap->oldPercent = curRemap->percent; + return changed; + case kRemappingToGray: + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + Color color = curPalette->colors[i]; + + if (curRemap->curColor[i] != color) { + curRemap->colorChanged[i] = true; + curRemap->curColor[i] = color; + } + + if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i]) { + byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8; + byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255); + byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255); + byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255); + byte used = curRemap->targetColor[i].used; + + Color newColor = { used, red, green, blue }; + if (curRemap->targetColor[i] != newColor) { + _targetChanged[i] = true; + curRemap->targetColor[i] = newColor; + } + } + } + + changed = applyRemap(index); + Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false); + curRemap->oldGray = curRemap->gray; + return changed; + case kRemappingToPercentGray: + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + Color color = curPalette->colors[i]; + + if (curRemap->curColor[i] != color) { + curRemap->colorChanged[i] = true; + curRemap->curColor[i] = color; + } + + if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i]) { + byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8; + lumosity = lumosity * curRemap->percent / 100; + byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255); + byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255); + byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255); + byte used = curRemap->targetColor[i].used; + + Color newColor = { used, red, green, blue }; + if (curRemap->targetColor[i] != newColor) { + _targetChanged[i] = true; + curRemap->targetColor[i] = newColor; + } + } + } + + changed = applyRemap(index); + Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false); + curRemap->oldPercent = curRemap->percent; + curRemap->oldGray = curRemap->gray; + return changed; + default: + return false; + } } + +static int colorDistance(Color a, Color b) { + int rDiff = (a.r - b.r) * (a.r - b.r); + int gDiff = (a.g - b.g) * (a.g - b.g); + int bDiff = (a.b - b.b) * (a.b - b.b); + return rDiff + gDiff + bDiff; +} + +bool GfxRemap32::applyRemap(byte index) { + RemapParams *curRemap = &_remaps[index]; + const bool *cycleMap = _palette->getCyclemap(); + bool unmappedColors[NON_REMAPPED_COLOR_COUNT]; + Color newColors[NON_REMAPPED_COLOR_COUNT]; + bool changed = false; + + Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false); + if (_noMapCount) + Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true); + + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) { + if (cycleMap[i]) + unmappedColors[i] = true; + } + + int curColor = 0; + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + if (curRemap->colorChanged[i] && !unmappedColors[i]) + newColors[curColor++] = curRemap->curColor[i]; + } + + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + Color targetColor = curRemap->targetColor[i]; + bool colorChanged = curRemap->colorChanged[curRemap->remap[i]]; + + if (!_targetChanged[i] && !colorChanged) + continue; + + if (_targetChanged[i] && colorChanged) + if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i]) + continue; + + int diff = 0; + int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors); + if (result != -1 && curRemap->remap[i] != result) { + changed = true; + curRemap->remap[i] = result; + curRemap->distance[i] = diff; + } + } + + return changed; +} + +bool GfxRemap32::remapAllTables(bool palChanged) { + bool changed = false; + + for (int i = 0; i < REMAP_COLOR_COUNT; i++) { + changed |= updateRemap(i, palChanged); + } + + _update = false; + return changed; +} + #endif } // End of namespace Sci diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h index e92eaffad2..d012568f7f 100644 --- a/engines/sci/graphics/remap.h +++ b/engines/sci/graphics/remap.h @@ -33,16 +33,22 @@ class GfxScreen; enum ColorRemappingType { kRemappingNone = 0, kRemappingByRange = 1, - kRemappingByPercent = 2 + kRemappingByPercent = 2, + kRemappingToGray = 3, + kRemappingToPercentGray = 4 }; +#define REMAP_COLOR_COUNT 9 +#define NON_REMAPPED_COLOR_COUNT 236 + /** * Remap class, handles color remapping */ class GfxRemap { public: - GfxRemap(GfxScreen *screen, GfxPalette *_palette); + GfxRemap(GfxPalette *_palette); ~GfxRemap(); + void resetRemapping(); void setRemappingPercent(byte color, byte percent); void setRemappingRange(byte color, byte from, byte to, byte base); @@ -64,11 +70,82 @@ private: }; #ifdef ENABLE_SCI32 + +struct RemapParams { + byte from; + byte to; + byte base; + byte gray; + byte oldGray; + byte percent; + byte oldPercent; + ColorRemappingType type; + Color curColor[256]; + Color targetColor[256]; + byte distance[256]; + byte remap[256]; + bool colorChanged[256]; + + RemapParams() { + from = to = base = gray = oldGray = percent = oldPercent = 0; + type = kRemappingNone; + + // curColor and targetColor are initialized in GfxRemap32::initColorArrays + memset(curColor, 0, 256 * sizeof(Color)); + memset(targetColor, 0, 256 * sizeof(Color)); + memset(distance, 0, 256); + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) + remap[i] = i; + Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true); + } + + RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) { + from = from_; + to = to_; + base = base_; + gray = oldGray = gray_; + percent = oldPercent = percent_; + type = type_; + + // curColor and targetColor are initialized in GfxRemap32::initColorArrays + memset(curColor, 0, 256 * sizeof(Color)); + memset(targetColor, 0, 256 * sizeof(Color)); + memset(distance, 0, 256); + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) + remap[i] = i; + Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true); + } +}; + class GfxRemap32 { public: - GfxRemap32(GfxScreen *screen, GfxPalette *_palette) {} + GfxRemap32(GfxPalette32 *palette); ~GfxRemap32() {} - //void setRemappingPercentGray(byte color, byte percent); + + void remapOff(byte color); + void setRemappingRange(byte color, byte from, byte to, byte base); + void setRemappingPercent(byte color, byte percent); + void setRemappingToGray(byte color, byte gray); + void setRemappingToPercentGray(byte color, byte gray, byte percent); + void setNoMatchRange(byte from, byte count); + bool remapEnabled(byte color) const; + byte remapColor(byte color, byte target); + bool remapAllTables(bool palChanged); + int getRemapCount() const { return _remapCount; } + int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; } + int getEndColor() const { return _remapEndColor; } +private: + GfxPalette32 *_palette; + RemapParams _remaps[REMAP_COLOR_COUNT]; + bool _update; + byte _noMapStart, _noMapCount; + bool _targetChanged[NON_REMAPPED_COLOR_COUNT]; + byte _remapEndColor; + int _remapCount; + + void initColorArrays(byte index); + bool applyRemap(byte index); + bool updateRemap(byte index, bool palChanged); }; #endif diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index 4a42221875..c3fdbb6845 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -115,7 +115,17 @@ _screenRect(other._screenRect) { } void ScreenItem::operator=(const ScreenItem &other) { - _celInfo = other._celInfo; + // NOTE: The original engine did not check for differences in `_celInfo` + // to clear `_celObj` here; instead, it unconditionally set `_celInfo`, + // didn't clear `_celObj`, and did hacky stuff in `kIsOnMe` to avoid + // testing a mismatched `_celObj`. See `GfxFrameout::kernelIsOnMe` for + // more detail. + if (_celInfo != other._celInfo) { + _celInfo = other._celInfo; + delete _celObj; + _celObj = nullptr; + } + _screenRect = other._screenRect; _mirrorX = other._mirrorX; _useInsetRect = other._useInsetRect; @@ -228,7 +238,9 @@ void ScreenItem::calcRects(const Plane &plane) { const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - Common::Rect celRect(_celObj->_width, _celObj->_height); + const CelObj &celObj = getCelObj(); + + Common::Rect celRect(celObj._width, celObj._height); if (_useInsetRect) { if (_insetRect.intersects(celRect)) { _insetRect.clip(celRect); @@ -239,28 +251,33 @@ void ScreenItem::calcRects(const Plane &plane) { _insetRect = celRect; } - Ratio newRatioX; - Ratio newRatioY; + Ratio scaleX, scaleY; if (_scale.signal & kScaleSignalDoScaling32) { if (_scale.signal & kScaleSignalUseVanishingPoint) { int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); - newRatioX = Ratio(num, 128); - newRatioY = Ratio(num, 128); + scaleX = Ratio(num, 128); + scaleY = Ratio(num, 128); } else { - newRatioX = Ratio(_scale.x, 128); - newRatioY = Ratio(_scale.y, 128); + scaleX = Ratio(_scale.x, 128); + scaleY = Ratio(_scale.y, 128); } } - if (newRatioX.getNumerator() && newRatioY.getNumerator()) { + if (scaleX.getNumerator() && scaleY.getNumerator()) { _screenItemRect = _insetRect; - if (_celObj->_scaledWidth != scriptWidth || _celObj->_scaledHeight != scriptHeight) { + const Ratio celToScreenX(screenWidth, celObj._scaledWidth); + const Ratio celToScreenY(screenHeight, celObj._scaledHeight); + + // Cel may use a coordinate system that is not the same size as the + // script coordinate system (usually this means high-resolution + // pictures with low-resolution scripts) + if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) { if (_useInsetRect) { - Ratio celScriptXRatio(_celObj->_scaledWidth, scriptWidth); - Ratio celScriptYRatio(_celObj->_scaledHeight, scriptHeight); - mulru(_screenItemRect, celScriptXRatio, celScriptYRatio, 0); + const Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); + const Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); + mulru(_screenItemRect, scriptToCelX, scriptToCelY, 0); if (_screenItemRect.intersects(celRect)) { _screenItemRect.clip(celRect); @@ -269,48 +286,47 @@ void ScreenItem::calcRects(const Plane &plane) { } } - int displaceX = _celObj->_displace.x; - int displaceY = _celObj->_displace.y; + int displaceX = celObj._displace.x; + int displaceY = celObj._displace.y; - if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) { - displaceX = _celObj->_width - _celObj->_displace.x - 1; + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + displaceX = celObj._width - celObj._displace.x - 1; } - if (!newRatioX.isOne() || !newRatioY.isOne()) { - mulinc(_screenItemRect, newRatioX, newRatioY); - displaceX = (displaceX * newRatioX).toInt(); - displaceY = (displaceY * newRatioY).toInt(); + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(_screenItemRect, scaleX, scaleY); + displaceX = (displaceX * scaleX).toInt(); + displaceY = (displaceY * scaleY).toInt(); } - Ratio celXRatio(screenWidth, _celObj->_scaledWidth); - Ratio celYRatio(screenHeight, _celObj->_scaledHeight); + mulinc(_screenItemRect, celToScreenX, celToScreenY); + displaceX = (displaceX * celToScreenX).toInt(); + displaceY = (displaceY * celToScreenY).toInt(); - displaceX = (displaceX * celXRatio).toInt(); - displaceY = (displaceY * celYRatio).toInt(); - - mulinc(_screenItemRect, celXRatio, celYRatio); + const Ratio scriptToScreenX = Ratio(screenWidth, scriptWidth); + const Ratio scriptToScreenY = Ratio(screenHeight, scriptHeight); if (/* TODO: dword_C6288 */ false && _celInfo.type == kCelTypePic) { _scaledPosition.x = _position.x; _scaledPosition.y = _position.y; } else { - _scaledPosition.x = (_position.x * screenWidth / scriptWidth) - displaceX; - _scaledPosition.y = (_position.y * screenHeight / scriptHeight) - displaceY; + _scaledPosition.x = (_position.x * scriptToScreenX).toInt() - displaceX; + _scaledPosition.y = (_position.y * scriptToScreenY).toInt() - displaceY; } _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y); - if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) { + if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) { Common::Rect temp(_insetRect); - if (!newRatioX.isOne()) { - mulinc(temp, newRatioX, Ratio()); + if (!scaleX.isOne()) { + mulinc(temp, scaleX, Ratio()); } - mulinc(temp, celXRatio, Ratio()); + mulinc(temp, celToScreenX, Ratio()); CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); - temp.translate(celObjPic->_relativePosition.x * screenWidth / scriptWidth - displaceX, 0); + temp.translate((celObjPic->_relativePosition.x * scriptToScreenX).toInt() - displaceX, 0); // TODO: This is weird. int deltaX = plane._planeRect.width() - temp.right - 1 - temp.left; @@ -323,16 +339,16 @@ void ScreenItem::calcRects(const Plane &plane) { _scaledPosition.y += plane._planeRect.top; _screenItemRect.translate(plane._planeRect.left, plane._planeRect.top); - _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth); - _ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight); + _ratioX = scaleX * celToScreenX; + _ratioY = scaleY * celToScreenY; } else { - int displaceX = _celObj->_displace.x; - if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) { - displaceX = _celObj->_width - _celObj->_displace.x - 1; + int displaceX = celObj._displace.x; + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + displaceX = celObj._width - celObj._displace.x - 1; } - if (!newRatioX.isOne() || !newRatioY.isOne()) { - mulinc(_screenItemRect, newRatioX, newRatioY); + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(_screenItemRect, scaleX, scaleY); // TODO: This was in the original code, baked into the // multiplication though it is not immediately clear // why this is the only one that reduces the BR corner @@ -340,20 +356,20 @@ void ScreenItem::calcRects(const Plane &plane) { _screenItemRect.bottom -= 1; } - _scaledPosition.x = _position.x - (displaceX * newRatioX).toInt(); - _scaledPosition.y = _position.y - (_celObj->_displace.y * newRatioY).toInt(); + _scaledPosition.x = _position.x - (displaceX * scaleX).toInt(); + _scaledPosition.y = _position.y - (celObj._displace.y * scaleY).toInt(); _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y); - if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) { + if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) { Common::Rect temp(_insetRect); - if (!newRatioX.isOne()) { - mulinc(temp, newRatioX, Ratio()); + if (!scaleX.isOne()) { + mulinc(temp, scaleX, Ratio()); temp.right -= 1; } CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); - temp.translate(celObjPic->_relativePosition.x - (displaceX * newRatioX).toInt(), celObjPic->_relativePosition.y - (_celObj->_displace.y * newRatioY).toInt()); + temp.translate(celObjPic->_relativePosition.x - (displaceX * scaleX).toInt(), celObjPic->_relativePosition.y - (celObj._displace.y * scaleY).toInt()); // TODO: This is weird. int deltaX = plane._gameRect.width() - temp.right - 1 - temp.left; @@ -366,15 +382,13 @@ void ScreenItem::calcRects(const Plane &plane) { _scaledPosition.y += plane._gameRect.top; _screenItemRect.translate(plane._gameRect.left, plane._gameRect.top); - if (screenWidth != _celObj->_scaledWidth || _celObj->_scaledHeight != screenHeight) { - Ratio celXRatio(screenWidth, _celObj->_scaledWidth); - Ratio celYRatio(screenHeight, _celObj->_scaledHeight); - mulru(_scaledPosition, celXRatio, celYRatio); - mulru(_screenItemRect, celXRatio, celYRatio, 1); + if (celObj._scaledWidth != screenWidth || celObj._scaledHeight != screenHeight) { + mulru(_scaledPosition, celToScreenX, celToScreenY); + mulru(_screenItemRect, celToScreenX, celToScreenY, 1); } - _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth); - _ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight); + _ratioX = scaleX * celToScreenX; + _ratioY = scaleY * celToScreenY; } _screenRect = _screenItemRect; @@ -495,6 +509,109 @@ void ScreenItem::update(const reg_t object) { _deleted = 0; } +// TODO: This code is quite similar to calcRects, so try to deduplicate +// if possible +Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { + CelObj &celObj = getCelObj(); + + Common::Rect celObjRect(celObj._width, celObj._height); + Common::Rect nsRect; + + if (_useInsetRect) { + // TODO: This is weird. Checking to see if the inset rect is + // fully inside the bounds of the celObjRect, and then + // clipping to the celObjRect, is pretty useless. + if (_insetRect.right > 0 && _insetRect.bottom > 0 && _insetRect.left < celObj._width && _insetRect.top < celObj._height) { + nsRect = _insetRect; + nsRect.clip(celObjRect); + } else { + nsRect = Common::Rect(); + } + } else { + nsRect = celObjRect; + } + + const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + Ratio scaleX, scaleY; + if (_scale.signal & kScaleSignalDoScaling32) { + if (_scale.signal & kScaleSignalUseVanishingPoint) { + int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); + scaleX = Ratio(num, 128); + scaleY = Ratio(num, 128); + } else { + scaleX = Ratio(_scale.x, 128); + scaleY = Ratio(_scale.y, 128); + } + } + + if (scaleX.getNumerator() == 0 || scaleY.getNumerator() == 0) { + return Common::Rect(); + } + + int16 displaceX = celObj._displace.x; + int16 displaceY = celObj._displace.y; + + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + displaceX = celObj._width - displaceX - 1; + } + + if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) { + if (_useInsetRect) { + Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); + Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); + mulru(nsRect, scriptToCelX, scriptToCelY, 0); + + // TODO: This is weird. Checking to see if the inset rect is + // fully inside the bounds of the celObjRect, and then + // clipping to the celObjRect, is pretty useless. + if (nsRect.right > 0 && nsRect.bottom > 0 && nsRect.left < celObj._width && nsRect.top < celObj._height) { + nsRect.clip(celObjRect); + } else { + nsRect = Common::Rect(); + } + } + + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(nsRect, scaleX, scaleY); + // TODO: This was in the original code, baked into the + // multiplication though it is not immediately clear + // why this is the only one that reduces the BR corner + nsRect.right -= 1; + nsRect.bottom -= 1; + } + + Ratio celToScriptX(scriptWidth, celObj._scaledWidth); + Ratio celToScriptY(scriptHeight, celObj._scaledHeight); + + displaceX = (displaceX * scaleX * celToScriptX).toInt(); + displaceY = (displaceY * scaleY * celToScriptY).toInt(); + + mulinc(nsRect, celToScriptX, celToScriptY); + nsRect.translate(_position.x - displaceX, _position.y - displaceY); + } else { + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(nsRect, scaleX, scaleY); + // TODO: This was in the original code, baked into the + // multiplication though it is not immediately clear + // why this is the only one that reduces the BR corner + nsRect.right -= 1; + nsRect.bottom -= 1; + } + + displaceX = (displaceX * scaleX).toInt(); + displaceY = (displaceY * scaleY).toInt(); + nsRect.translate(_position.x - displaceX, _position.y - displaceY); + + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + nsRect.translate(plane._gameRect.width() - nsRect.width(), 0); + } + } + + return nsRect; +} + #pragma mark - #pragma mark ScreenItemList ScreenItem *ScreenItemList::findByObject(const reg_t object) const { diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index 52782acc56..977d80ebad 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -259,6 +259,13 @@ public: * VM object. */ void update(const reg_t object); + + /** + * Gets the "now seen" rect for the screen item, which + * represents the current size and position of the + * screen item on the screen in script coordinates. + */ + Common::Rect getNowSeenRect(const Plane &plane) const; }; #pragma mark - diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index fa19047a4c..99ffc6e328 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -625,4 +625,33 @@ int16 GfxText32::getStringWidth(const Common::String &text) { return getTextWidth(text, 0, 10000); } +int16 GfxText32::getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling) { + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + Common::Rect scaledRect(textRect); + if (doScaling) { + mulinc(scaledRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight)); + } + + Common::String oldText = _text; + _text = text; + + uint charIndex = index; + int16 maxWidth = scaledRect.width(); + int16 lineCount = (scaledRect.height() - 2) / _font->getHeight(); + while (lineCount--) { + getLongest(&charIndex, maxWidth); + } + + _text = oldText; + return charIndex - index; +} + +int16 GfxText32::getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling) { + setFont(fontId); + return getTextCount(text, index, textRect, doScaling); +} + + } // End of namespace Sci diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 472d5e0956..5768ea0c59 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -458,6 +458,20 @@ public: * Retrieves the width of a line of text. */ int16 getStringWidth(const Common::String &text); + + /** + * Gets the number of characters of `text`, starting + * from `index`, that can be safely rendered into + * `textRect`. + */ + int16 getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling); + + /** + * Gets the number of characters of `text`, starting + * from `index`, that can be safely rendered into + * `textRect` using the given font. + */ + int16 getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling); }; } // End of namespace Sci diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index ff3fc70619..1939e66179 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -975,13 +975,4 @@ void GfxView::adjustBackUpscaledCoordinates(int16 &y, int16 &x) { _screen->adjustBackUpscaledCoordinates(y, x, _sci2ScaleRes); } -byte GfxView::getColorAtCoordinate(int16 loopNo, int16 celNo, int16 x, int16 y) { - const CelInfo *celInfo = getCelInfo(loopNo, celNo); - const byte *bitmap = getBitmap(loopNo, celNo); - const int16 celWidth = celInfo->width; - - bitmap += (celWidth * y); - return bitmap[x]; -} - } // End of namespace Sci diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h index d8803db208..91590208c1 100644 --- a/engines/sci/graphics/view.h +++ b/engines/sci/graphics/view.h @@ -85,8 +85,6 @@ public: void adjustToUpscaledCoordinates(int16 &y, int16 &x); void adjustBackUpscaledCoordinates(int16 &y, int16 &x); - byte getColorAtCoordinate(int16 loopNo, int16 celNo, int16 x, int16 y); - private: void initData(GuiResourceId resourceId); void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount); diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 6bc6650245..e14d12b918 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -684,12 +684,12 @@ void SciEngine::initGraphics() { if (getSciVersion() >= SCI_VERSION_2) { _gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen); _gfxPalette16 = _gfxPalette32; - _gfxRemap32 = new GfxRemap32(_gfxScreen, _gfxPalette32); + _gfxRemap32 = new GfxRemap32(_gfxPalette32); } else { #endif _gfxPalette16 = new GfxPalette(_resMan, _gfxScreen); if (getGameId() == GID_QFG4DEMO) - _gfxRemap16 = new GfxRemap(_gfxScreen, _gfxPalette16); + _gfxRemap16 = new GfxRemap(_gfxPalette16); #ifdef ENABLE_SCI32 } #endif diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 2474db81d9..7df3d38163 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -206,8 +206,8 @@ enum SciVersion { SCI_VERSION_1_LATE, // Dr. Brain 1, EcoQuest 1, Longbow, PQ3, SQ1, LSL5, KQ5 CD SCI_VERSION_1_1, // Dr. Brain 2, EcoQuest 1 CD, EcoQuest 2, KQ6, QFG3, SQ4CD, XMAS 1992 and many more SCI_VERSION_2, // GK1, PQ4 floppy, QFG4 floppy - SCI_VERSION_2_1_EARLY, // GK2 demo, KQ7, LSL6 hires, PQ4, QFG4 floppy - SCI_VERSION_2_1_MIDDLE, // GK2, KQ7, MUMG Deluxe, Phantasmagoria 1, PQ4CD, PQ:SWAT, QFG4CD, Shivers 1, SQ6, Torin + SCI_VERSION_2_1_EARLY, // GK2 demo, KQ7 1.4/1.51, LSL6 hires, PQ4CD, QFG4 floppy + SCI_VERSION_2_1_MIDDLE, // GK2, KQ7 2.00b, MUMG Deluxe, Phantasmagoria 1, PQ:SWAT, QFG4CD, Shivers 1, SQ6, Torin SCI_VERSION_2_1_LATE, // demos of LSL7, Lighthouse, RAMA SCI_VERSION_3 // LSL7, Lighthouse, RAMA, Phantasmagoria 2 }; diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 57f0415285..a74bfa245f 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -28,6 +28,7 @@ #include "backends/audiocd/audiocd.h" +#include "common/config-manager.h" #include "common/file.h" #include "common/memstream.h" #include "common/system.h" @@ -44,7 +45,7 @@ namespace Sci { AudioPlayer::AudioPlayer(ResourceManager *resMan) : _resMan(resMan), _audioRate(11025), - _syncResource(NULL), _syncOffset(0), _audioCdStart(0) { + _syncResource(NULL), _syncOffset(0), _audioCdStart(0), _initCD(false) { _mixer = g_system->getMixer(); _wPlayFlag = false; @@ -511,6 +512,12 @@ void AudioPlayer::stopSoundSync() { } int AudioPlayer::audioCdPlay(int track, int start, int duration) { + if (!_initCD) { + // Initialize CD mode if we haven't already + g_system->getAudioCDManager()->open(); + _initCD = true; + } + if (getSciVersion() == SCI_VERSION_1_1) { // King's Quest VI CD Audio format _audioCdStart = g_system->getMillis(); diff --git a/engines/sci/sound/audio.h b/engines/sci/sound/audio.h index 9e65d6e0c8..4a8b26567d 100644 --- a/engines/sci/sound/audio.h +++ b/engines/sci/sound/audio.h @@ -97,6 +97,7 @@ private: uint _syncOffset; uint32 _audioCdStart; bool _wPlayFlag; + bool _initCD; }; } // End of namespace Sci diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp index 4d4be2c3c2..55bbeeef98 100644 --- a/engines/scumm/players/player_ad.cpp +++ b/engines/scumm/players/player_ad.cpp @@ -50,7 +50,7 @@ Player_AD::Player_AD(ScummEngine *scumm) writeReg(0x01, 0x20); _engineMusicTimer = 0; - _soundPlaying = -1; + _musicResource = -1; _curOffset = 0; @@ -104,8 +104,8 @@ void Player_AD::startSound(int sound) { stopMusic(); // Lock the new music resource - _soundPlaying = sound; - _vm->_res->lock(rtSound, _soundPlaying); + _musicResource = sound; + _vm->_res->lock(rtSound, _musicResource); // Start the new music resource _musicData = res; @@ -150,7 +150,7 @@ void Player_AD::startSound(int sound) { void Player_AD::stopSound(int sound) { Common::StackLock lock(_mutex); - if (sound == _soundPlaying) { + if (sound == _musicResource) { stopMusic(); } else { for (int i = 0; i < ARRAYSIZE(_sfx); ++i) { @@ -178,7 +178,17 @@ int Player_AD::getMusicTimer() { } int Player_AD::getSoundStatus(int sound) const { - return (sound == _soundPlaying); + if (sound == _musicResource) { + return true; + } + + for (int i = 0; i < ARRAYSIZE(_sfx); ++i) { + if (_sfx[i].resource == sound) { + return true; + } + } + + return false; } void Player_AD::saveLoadWithSerializer(Serializer *ser) { @@ -193,7 +203,7 @@ void Player_AD::saveLoadWithSerializer(Serializer *ser) { if (ser->getVersion() >= VER(96)) { int32 res[4] = { - _soundPlaying, _sfx[0].resource, _sfx[1].resource, _sfx[2].resource + _musicResource, _sfx[0].resource, _sfx[1].resource, _sfx[2].resource }; // The first thing we save is a list of sound resources being played @@ -461,13 +471,13 @@ void Player_AD::startMusic() { } void Player_AD::stopMusic() { - if (_soundPlaying == -1) { + if (_musicResource == -1) { return; } // Unlock the music resource if present - _vm->_res->unlock(rtSound, _soundPlaying); - _soundPlaying = -1; + _vm->_res->unlock(rtSound, _musicResource); + _musicResource = -1; // Stop the music playback _curOffset = 0; @@ -510,7 +520,7 @@ void Player_AD::updateMusic() { // important to note that we need to parse a command directly // at the new position, i.e. there is no time value we need to // parse. - if (_soundPlaying == -1) { + if (_musicResource == -1) { return; } else { continue; diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h index 63fda3cc7c..9cd1a06261 100644 --- a/engines/scumm/players/player_ad.h +++ b/engines/scumm/players/player_ad.h @@ -68,7 +68,7 @@ private: OPL::OPL *_opl2; - int _soundPlaying; + int _musicResource; int32 _engineMusicTimer; struct SfxSlot; diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 24d676a1ff..d9148ed300 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1275,10 +1275,7 @@ void ScummEngine::setupScumm() { // On some systems it's not safe to run CD audio games from the CD. if (_game.features & GF_AUDIOTRACKS && !Common::File::exists("CDDA.SOU")) { checkCD(); - - int cd_num = ConfMan.getInt("cdrom"); - if (cd_num >= 0) - _system->getAudioCDManager()->openCD(cd_num); + _system->getAudioCDManager()->open(); } // Create the sound manager @@ -2611,8 +2608,12 @@ bool ScummEngine::startManiac() { Common::String path = dom.getVal("path"); if (path.hasPrefix(currentPath)) { - path.erase(0, currentPath.size() + 1); - if (path.equalsIgnoreCase("maniac")) { + path.erase(0, currentPath.size()); + // Do a case-insensitive non-path-mode match of the remainder. + // While strictly speaking it's too broad, this matchString + // ignores the presence or absence of trailing path separators + // in either currentPath or path. + if (path.matchString("*maniac*", true, false)) { maniacTarget = iter->_key; break; } diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 4d70ee8482..404bdd022c 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -1093,7 +1093,7 @@ int Sound::pollCD() const { void Sound::updateCD() { if (!_isLoomSteam) - g_system->getAudioCDManager()->updateCD(); + g_system->getAudioCDManager()->update(); } AudioCDManager::Status Sound::getCDStatus() { diff --git a/engines/sherlock/animation.cpp b/engines/sherlock/animation.cpp index 681e71d0f6..4442c1da85 100644 --- a/engines/sherlock/animation.cpp +++ b/engines/sherlock/animation.cpp @@ -23,6 +23,8 @@ #include "sherlock/animation.h" #include "sherlock/sherlock.h" #include "sherlock/scalpel/scalpel_screen.h" +#include "sherlock/scalpel/3do/scalpel_3do_screen.h" + #include "common/algorithm.h" namespace Sherlock { @@ -89,7 +91,7 @@ bool Animation::play(const Common::String &filename, bool intro, int minDelay, i // Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame, // since we don't want the offsets in the image file to be used, just the explicit position we specify - screen.transBlitFrom(images[imageFrame]._frame, pt); + screen.SHtransBlitFrom(images[imageFrame]._frame, pt); } else { // At this point, either the sprites for the frame has been complete, or there weren't any sprites // at all to draw for the frame @@ -201,7 +203,7 @@ bool Animation::play3DO(const Common::String &filename, bool intro, int minDelay // Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame, // since we don't want the offsets in the image file to be used, just the explicit position we specify - screen._backBuffer1.transBlitFrom(images[imageFrame]._frame, pt); + screen._backBuffer1.SHtransBlitFrom(images[imageFrame]._frame, pt); if (!fadeActive) screen.slamArea(pt.x, pt.y, images[imageFrame]._frame.w, images[imageFrame]._frame.h); } else { diff --git a/engines/sherlock/events.cpp b/engines/sherlock/events.cpp index 4b0b7dfb3f..6cfee5d822 100644 --- a/engines/sherlock/events.cpp +++ b/engines/sherlock/events.cpp @@ -143,7 +143,7 @@ void Events::setCursor(CursorId cursorId, const Common::Point &cursorPos, const // Form a single surface containing both frames Surface s(r.width(), r.height()); - s.fill(TRANSPARENCY); + s.clear(TRANSPARENCY); // Draw the passed image Common::Point drawPos; @@ -151,11 +151,11 @@ void Events::setCursor(CursorId cursorId, const Common::Point &cursorPos, const drawPos.x = -cursorPt.x; if (cursorPt.y < 0) drawPos.y = -cursorPt.y; - s.blitFrom(surface, Common::Point(drawPos.x, drawPos.y)); + s.SHblitFrom(surface, Common::Point(drawPos.x, drawPos.y)); // Draw the cursor image drawPos = Common::Point(MAX(cursorPt.x, (int16)0), MAX(cursorPt.y, (int16)0)); - s.transBlitFrom(cursorImg, Common::Point(drawPos.x, drawPos.y)); + s.SHtransBlitFrom(cursorImg, Common::Point(drawPos.x, drawPos.y)); // Set up hotspot position for cursor, adjusting for cursor image's position within the surface Common::Point hotspot; @@ -163,7 +163,7 @@ void Events::setCursor(CursorId cursorId, const Common::Point &cursorPos, const hotspot = Common::Point(8, 8); hotspot += drawPos; // Set the cursor - setCursor(s.getRawSurface(), hotspot.x, hotspot.y); + setCursor(s, hotspot.x, hotspot.y); } void Events::animateCursorIfNeeded() { diff --git a/engines/sherlock/fonts.cpp b/engines/sherlock/fonts.cpp index 8e36c3908a..5a14881f1c 100644 --- a/engines/sherlock/fonts.cpp +++ b/engines/sherlock/fonts.cpp @@ -43,7 +43,7 @@ void Fonts::setVm(SherlockEngine *vm) { _charCount = 0; } -void Fonts::free() { +void Fonts::freeFont() { delete _font; } @@ -213,7 +213,7 @@ void Fonts::writeString(Surface *surface, const Common::String &str, if (curChar < _charCount) { ImageFrame &frame = (*_font)[curChar]; - surface->transBlitFrom(frame, Common::Point(charPos.x, charPos.y + _yOffsets[curChar]), false, overrideColor); + surface->SHtransBlitFrom(frame, Common::Point(charPos.x, charPos.y + _yOffsets[curChar]), false, overrideColor); charPos.x += frame._frame.w + 1; } else { warning("Invalid character encountered - %d", (int)curChar); diff --git a/engines/sherlock/fonts.h b/engines/sherlock/fonts.h index a527cc73c0..3594d466c2 100644 --- a/engines/sherlock/fonts.h +++ b/engines/sherlock/fonts.h @@ -57,7 +57,7 @@ public: /** * Frees the font manager */ - static void free(); + static void freeFont(); /** * Set the font to use for writing text on the screen diff --git a/engines/sherlock/image_file.h b/engines/sherlock/image_file.h index da260ab30b..778332b726 100644 --- a/engines/sherlock/image_file.h +++ b/engines/sherlock/image_file.h @@ -46,6 +46,11 @@ struct ImageFrame { Graphics::Surface _frame; /** + * Converts an ImageFrame record to a surface for convenience in passing to drawing methods + */ + operator const Graphics::Surface &() { return _frame; } + + /** * Decompress a single frame for the sprite */ void decompressFrame(const byte *src, bool isRoseTattoo); diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk index 7fa7896691..0d17d0b249 100644 --- a/engines/sherlock/module.mk +++ b/engines/sherlock/module.mk @@ -3,6 +3,7 @@ MODULE := engines/sherlock MODULE_OBJS = \ scalpel/scalpel.o \ scalpel/3do/movie_decoder.o \ + scalpel/3do/scalpel_3do_screen.o \ scalpel/drivers/adlib.o \ scalpel/drivers/mt32.o \ scalpel/tsage/logo.o \ @@ -30,6 +31,7 @@ MODULE_OBJS = \ tattoo/tattoo_people.o \ tattoo/tattoo_resources.o \ tattoo/tattoo_scene.o \ + tattoo/tattoo_screen.o \ tattoo/tattoo_talk.o \ tattoo/tattoo_user_interface.o \ tattoo/widget_base.o \ diff --git a/engines/sherlock/objects.cpp b/engines/sherlock/objects.cpp index a89b8dce86..644c0c74c9 100644 --- a/engines/sherlock/objects.cpp +++ b/engines/sherlock/objects.cpp @@ -365,8 +365,8 @@ bool BaseObject::checkEndOfSequence() { if (seq == 99) { --_frameNumber; - screen._backBuffer1.transBlitFrom(*_imageFrame, _position); - screen._backBuffer2.transBlitFrom(*_imageFrame, _position); + screen._backBuffer1.SHtransBlitFrom(*_imageFrame, _position); + screen._backBuffer2.SHtransBlitFrom(*_imageFrame, _position); _type = INVALID; } else if (IS_ROSE_TATTOO && _talkSeq && seq == 0) { setObjTalkSequence(_talkSeq); diff --git a/engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp b/engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp new file mode 100644 index 0000000000..f8112d8add --- /dev/null +++ b/engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp @@ -0,0 +1,286 @@ +/* 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 "sherlock/scalpel/scalpel_screen.h" +#include "sherlock/scalpel/scalpel.h" +#include "sherlock/scalpel/3do/scalpel_3do_screen.h" + +namespace Sherlock { + +namespace Scalpel { + +Scalpel3DOScreen::Scalpel3DOScreen(SherlockEngine *vm): ScalpelScreen(vm) { +} + +void Scalpel3DOScreen::SHblitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) { + if (!_vm->_isScreenDoubled) { + ScalpelScreen::SHblitFrom(src, pt, srcBounds); + return; + } + + Common::Rect srcRect = srcBounds; + Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height()); + + if (!srcRect.isValidRect() || !clip(srcRect, destRect)) + return; + + // Add dirty area remapped to the 640x200 surface + addDirtyRect(Common::Rect(destRect.left * 2, destRect.top * 2, destRect.right * 2, destRect.bottom * 2)); + + // Transfer the area, doubling each pixel + for (int yp = 0; yp < srcRect.height(); ++yp) { + const uint16 *srcP = (const uint16 *)src.getBasePtr(srcRect.left, srcRect.top + yp); + uint16 *destP = (uint16 *)getBasePtr(destRect.left * 2, (destRect.top + yp) * 2); + + for (int xp = srcRect.left; xp < srcRect.right; ++xp, ++srcP, destP += 2) { + *destP = *srcP; + *(destP + 1) = *srcP; + *(destP + 640) = *srcP; + *(destP + 640 + 1) = *srcP; + } + } +} + +void Scalpel3DOScreen::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, + bool flipped, int overrideColor) { + error("TODO: Refactor"); +#if 0 + if (!_vm->_isScreenDoubled) { + ScalpelScreen::transBlitFromUnscaled(src, pt, flipped, overrideColor); + return; + } + + Common::Rect drawRect(0, 0, src.w, src.h); + Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h); + + // Clip the display area to on-screen + if (!clip(drawRect, destRect)) + // It's completely off-screen + return; + + if (flipped) + drawRect = Common::Rect(src.w - drawRect.right, src.h - drawRect.bottom, + src.w - drawRect.left, src.h - drawRect.top); + + Common::Point destPt(destRect.left, destRect.top); + addDirtyRect(Common::Rect(destPt.x * 2, destPt.y * 2, (destPt.x + drawRect.width()) * 2, + (destPt.y + drawRect.height()) * 2)); + + assert(src.format.bytesPerPixel == 2 && _surface.format.bytesPerPixel == 2); + + for (int yp = 0; yp < drawRect.height(); ++yp) { + const uint16 *srcP = (const uint16 *)src.getBasePtr( + flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp); + uint16 *destP = (uint16 *)getBasePtr(destPt.x * 2, (destPt.y + yp) * 2); + + for (int xp = 0; xp < drawRect.width(); ++xp, destP += 2) { + // RGB 0, 0, 0 -> transparent on 3DO + if (*srcP) { + *destP = *srcP; + *(destP + 1) = *srcP; + *(destP + 640) = *srcP; + *(destP + 640 + 1) = *srcP; + } + + srcP = flipped ? srcP - 1 : srcP + 1; + } + } +#endif +} + +void Scalpel3DOScreen::fillRect(const Common::Rect &r, uint color) { + if (_vm->_isScreenDoubled) + ScalpelScreen::fillRect(Common::Rect(r.left * 2, r.top * 2, r.right * 2, r.bottom * 2), color); + else + ScalpelScreen::fillRect(r, color); +} + +void Scalpel3DOScreen::fadeIntoScreen3DO(int speed) { + Events &events = *_vm->_events; + uint16 *currentScreenBasePtr = (uint16 *)getPixels(); + uint16 *targetScreenBasePtr = (uint16 *)_backBuffer.getPixels(); + uint16 currentScreenPixel = 0; + uint16 targetScreenPixel = 0; + + uint16 currentScreenPixelRed = 0; + uint16 currentScreenPixelGreen = 0; + uint16 currentScreenPixelBlue = 0; + + uint16 targetScreenPixelRed = 0; + uint16 targetScreenPixelGreen = 0; + uint16 targetScreenPixelBlue = 0; + + uint16 screenWidth = SHERLOCK_SCREEN_WIDTH; + uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT; + uint16 screenX = 0; + uint16 screenY = 0; + uint16 pixelsChanged = 0; + + clearDirtyRects(); + + do { + pixelsChanged = 0; + uint16 *currentScreenPtr = currentScreenBasePtr; + uint16 *targetScreenPtr = targetScreenBasePtr; + + for (screenY = 0; screenY < screenHeight; screenY++) { + for (screenX = 0; screenX < screenWidth; screenX++) { + currentScreenPixel = *currentScreenPtr; + targetScreenPixel = *targetScreenPtr; + + if (currentScreenPixel != targetScreenPixel) { + // pixel doesn't match, adjust accordingly + currentScreenPixelRed = currentScreenPixel & 0xF800; + currentScreenPixelGreen = currentScreenPixel & 0x07E0; + currentScreenPixelBlue = currentScreenPixel & 0x001F; + targetScreenPixelRed = targetScreenPixel & 0xF800; + targetScreenPixelGreen = targetScreenPixel & 0x07E0; + targetScreenPixelBlue = targetScreenPixel & 0x001F; + + if (currentScreenPixelRed != targetScreenPixelRed) { + if (currentScreenPixelRed < targetScreenPixelRed) { + currentScreenPixelRed += 0x0800; + } else { + currentScreenPixelRed -= 0x0800; + } + } + if (currentScreenPixelGreen != targetScreenPixelGreen) { + // Adjust +2/-2 because we are running RGB555 at RGB565 + if (currentScreenPixelGreen < targetScreenPixelGreen) { + currentScreenPixelGreen += 0x0040; + } else { + currentScreenPixelGreen -= 0x0040; + } + } + if (currentScreenPixelBlue != targetScreenPixelBlue) { + if (currentScreenPixelBlue < targetScreenPixelBlue) { + currentScreenPixelBlue += 0x0001; + } else { + currentScreenPixelBlue -= 0x0001; + } + } + + uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue; + *currentScreenPtr = v; + if (_vm->_isScreenDoubled) { + *(currentScreenPtr + 1) = v; + *(currentScreenPtr + 640) = v; + *(currentScreenPtr + 640 + 1) = v; + } + + pixelsChanged++; + } + + currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1; + targetScreenPtr++; + } + + if (_vm->_isScreenDoubled) + currentScreenPtr += 640; + } + + // Too much considered dirty at the moment + if (_vm->_isScreenDoubled) + addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2)); + else + addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight)); + + events.pollEvents(); + events.delay(10 * speed); + } while ((pixelsChanged) && (!_vm->shouldQuit())); +} + +void Scalpel3DOScreen::blitFrom3DOcolorLimit(uint16 limitColor) { + uint16 *currentScreenPtr = (uint16 *)getPixels(); + uint16 *targetScreenPtr = (uint16 *)_backBuffer.getPixels(); + uint16 currentScreenPixel = 0; + + uint16 screenWidth = SHERLOCK_SCREEN_WIDTH; + uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT; + uint16 screenX = 0; + uint16 screenY = 0; + + uint16 currentScreenPixelRed = 0; + uint16 currentScreenPixelGreen = 0; + uint16 currentScreenPixelBlue = 0; + + uint16 limitPixelRed = limitColor & 0xF800; + uint16 limitPixelGreen = limitColor & 0x07E0; + uint16 limitPixelBlue = limitColor & 0x001F; + + for (screenY = 0; screenY < screenHeight; screenY++) { + for (screenX = 0; screenX < screenWidth; screenX++) { + currentScreenPixel = *targetScreenPtr; + + currentScreenPixelRed = currentScreenPixel & 0xF800; + currentScreenPixelGreen = currentScreenPixel & 0x07E0; + currentScreenPixelBlue = currentScreenPixel & 0x001F; + + if (currentScreenPixelRed < limitPixelRed) + currentScreenPixelRed = limitPixelRed; + if (currentScreenPixelGreen < limitPixelGreen) + currentScreenPixelGreen = limitPixelGreen; + if (currentScreenPixelBlue < limitPixelBlue) + currentScreenPixelBlue = limitPixelBlue; + + uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue; + *currentScreenPtr = v; + if (_vm->_isScreenDoubled) { + *(currentScreenPtr + 1) = v; + *(currentScreenPtr + 640) = v; + *(currentScreenPtr + 640 + 1) = v; + } + + currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1; + targetScreenPtr++; + } + + if (_vm->_isScreenDoubled) + currentScreenPtr += 640; + } + + // Too much considered dirty at the moment + if (_vm->_isScreenDoubled) + addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2)); + else + addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight)); +} + +uint16 Scalpel3DOScreen::width() const { + return _vm->_isScreenDoubled ? this->w / 2 : this->w; +} + +uint16 Scalpel3DOScreen::height() const { + return _vm->_isScreenDoubled ? this->h / 2 : this->h; +} + +void Scalpel3DOScreen::rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt) { + Common::Rect srcRect(0, 0, src.w, src.h); + Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h); + + addDirtyRect(destRect); + copyRectToSurface(src, destRect.left, destRect.top, srcRect); +} + +} // End of namespace Scalpel + +} // End of namespace Sherlock diff --git a/engines/sherlock/scalpel/3do/scalpel_3do_screen.h b/engines/sherlock/scalpel/3do/scalpel_3do_screen.h new file mode 100644 index 0000000000..422f588b17 --- /dev/null +++ b/engines/sherlock/scalpel/3do/scalpel_3do_screen.h @@ -0,0 +1,76 @@ +/* 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 SHERLOCK_SCALPEL_3DO_SCREEN_H +#define SHERLOCK_SCALPEL_3DO_SCREEN_H + +#include "sherlock/scalpel/scalpel_screen.h" + +namespace Sherlock { + +class SherlockEngine; + +namespace Scalpel { + +class Scalpel3DOScreen : public ScalpelScreen { +protected: + /** + * Draws a sub-section of a surface at a given position within this surface + * Overriden for the 3DO to automatically double the size of everything to the underlying 640x400 surface + */ + virtual void SHblitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds); + + /** + * Draws a surface at a given position within this surface with transparency + */ + virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped, + int overrideColor); +public: + Scalpel3DOScreen(SherlockEngine *vm); + virtual ~Scalpel3DOScreen() {} + + /** + * Draws a sub-section of a surface at a given position within this surface + */ + void rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt); + + /** + * Fade backbuffer 1 into screen (3DO RGB!) + */ + void fadeIntoScreen3DO(int speed); + + void blitFrom3DOcolorLimit(uint16 color); + + /** + * Fill a given area of the surface with a given color + */ + virtual void fillRect(const Common::Rect &r, uint color); + + virtual uint16 width() const; + virtual uint16 height() const; +}; + +} // End of namespace Scalpel + +} // End of namespace Sherlock + +#endif diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp index b17f38b218..cbb202095f 100644 --- a/engines/sherlock/scalpel/scalpel.cpp +++ b/engines/sherlock/scalpel/scalpel.cpp @@ -29,6 +29,7 @@ #include "sherlock/scalpel/scalpel_people.h" #include "sherlock/scalpel/scalpel_scene.h" #include "sherlock/scalpel/scalpel_screen.h" +#include "sherlock/scalpel/3do/scalpel_3do_screen.h" #include "sherlock/scalpel/tsage/logo.h" #include "sherlock/sherlock.h" #include "sherlock/music.h" @@ -371,8 +372,8 @@ bool ScalpelEngine::showCityCutscene() { if (finished) { ImageFile titleImages_LondonNovember("title2.vgs", true); - _screen->_backBuffer1.blitFrom(*_screen); - _screen->_backBuffer2.blitFrom(*_screen); + _screen->_backBuffer1.SHblitFrom(*_screen); + _screen->_backBuffer2.SHblitFrom(*_screen); Common::Point londonPosition; @@ -386,19 +387,19 @@ bool ScalpelEngine::showCityCutscene() { } // London, England - _screen->_backBuffer1.transBlitFrom(titleImages_LondonNovember[0], londonPosition); + _screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[0], londonPosition); _screen->randomTransition(); finished = _events->delay(1000, true); // November, 1888 if (finished) { - _screen->_backBuffer1.transBlitFrom(titleImages_LondonNovember[1], Common::Point(100, 100)); + _screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[1], Common::Point(100, 100)); _screen->randomTransition(); finished = _events->delay(5000, true); } // Transition out the title - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2); + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2); _screen->randomTransition(); } @@ -407,8 +408,8 @@ bool ScalpelEngine::showCityCutscene() { if (finished) { ImageFile titleImages_SherlockHolmesTitle("title.vgs", true); - _screen->_backBuffer1.blitFrom(*_screen); - _screen->_backBuffer2.blitFrom(*_screen); + _screen->_backBuffer1.SHblitFrom(*_screen); + _screen->_backBuffer2.SHblitFrom(*_screen); Common::Point lostFilesPosition; Common::Point sherlockHolmesPosition; @@ -427,17 +428,17 @@ bool ScalpelEngine::showCityCutscene() { } // The Lost Files of - _screen->_backBuffer1.transBlitFrom(titleImages_SherlockHolmesTitle[0], lostFilesPosition); + _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[0], lostFilesPosition); // Sherlock Holmes - _screen->_backBuffer1.transBlitFrom(titleImages_SherlockHolmesTitle[1], sherlockHolmesPosition); + _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[1], sherlockHolmesPosition); // copyright - _screen->_backBuffer1.transBlitFrom(titleImages_SherlockHolmesTitle[2], copyrightPosition); + _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[2], copyrightPosition); _screen->verticalTransition(); finished = _events->delay(4000, true); if (finished) { - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2); + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2); _screen->randomTransition(); finished = _events->delay(2000); } @@ -461,7 +462,7 @@ bool ScalpelEngine::showCityCutscene() { // English, width 175, height 38 alleyPosition = Common::Point(72, 51); } - _screen->transBlitFrom(titleImages_SherlockHolmesTitle[3], alleyPosition); + _screen->SHtransBlitFrom(titleImages_SherlockHolmesTitle[3], alleyPosition); _screen->fadeIn(palette, 3); // Wait until the track got looped and the first few notes were played @@ -537,7 +538,7 @@ bool ScalpelEngine::showAlleyCutscene() { earlyTheFollowingMorningPosition = Common::Point(35, 52); } - _screen->transBlitFrom(titleImages_EarlyTheFollowingMorning[0], earlyTheFollowingMorningPosition); + _screen->SHtransBlitFrom(titleImages_EarlyTheFollowingMorning[0], earlyTheFollowingMorningPosition); // fast fade-in _screen->fadeIn(palette, 1); @@ -641,23 +642,23 @@ bool ScalpelEngine::scrollCredits() { delete stream; // Save a copy of the screen background for use in drawing each credit frame - _screen->_backBuffer1.blitFrom(*_screen); + _screen->_backBuffer1.SHblitFrom(*_screen); // Loop for showing the credits for(int idx = 0; idx < 600 && !_events->kbHit() && !shouldQuit(); ++idx) { // Copy the entire screen background before writing text - _screen->blitFrom(_screen->_backBuffer1); + _screen->SHblitFrom(_screen->_backBuffer1); // Write the text appropriate for the next frame if (idx < 400) - _screen->transBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0); + _screen->SHtransBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0); if (idx > 200) - _screen->transBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0); + _screen->SHtransBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0); // Don't show credit text on the top and bottom ten rows of the screen - _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->w(), 10)); - _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, _screen->h() - 10), - Common::Rect(0, _screen->h() - 10, _screen->w(), _screen->h())); + _screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->width(), 10)); + _screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, _screen->height() - 10), + Common::Rect(0, _screen->height() - 10, _screen->width(), _screen->height())); _events->delay(100); } @@ -670,7 +671,7 @@ bool ScalpelEngine::show3DOSplash() { // 3DO EA Splash screen ImageFile3DO titleImage_3DOSplash("3DOSplash.cel", kImageFile3DOType_Cel); - _screen->transBlitFrom(titleImage_3DOSplash[0]._frame, Common::Point(0, -20)); + _screen->SHtransBlitFrom(titleImage_3DOSplash[0]._frame, Common::Point(0, -20)); bool finished = _events->delay(3000, true); if (finished) { @@ -706,7 +707,7 @@ bool ScalpelEngine::showCityCutscene3DO() { _sound->playAiff("prologue/sounds/rain.aiff", 15, true); // Fade screen to grey - screen._backBuffer1.fill(0xCE59); // RGB565: 25, 50, 25 (grey) + screen._backBuffer1.clear(0xCE59); // RGB565: 25, 50, 25 (grey) screen.fadeIntoScreen3DO(2); } @@ -715,16 +716,16 @@ bool ScalpelEngine::showCityCutscene3DO() { } if (finished) { - screen._backBuffer1.fill(0); // fill backbuffer with black to avoid issues during fade from white + screen._backBuffer1.clear(0); // fill backbuffer with black to avoid issues during fade from white finished = _animation->play3DO("26open1", true, 1, true, 2); } if (finished) { - screen._backBuffer2.blitFrom(screen._backBuffer1); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); // "London, England" ImageFile3DO titleImage_London("title2a.cel", kImageFile3DOType_Cel); - screen._backBuffer1.transBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50)); + screen._backBuffer1.SHtransBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50)); screen.fadeIntoScreen3DO(1); finished = _events->delay(1500, true); @@ -732,7 +733,7 @@ bool ScalpelEngine::showCityCutscene3DO() { if (finished) { // "November, 1888" ImageFile3DO titleImage_November("title2b.cel", kImageFile3DOType_Cel); - screen._backBuffer1.transBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100)); + screen._backBuffer1.SHtransBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100)); screen.fadeIntoScreen3DO(1); finished = _music->waitUntilMSec(14700, 0, 0, 5000); @@ -740,8 +741,8 @@ bool ScalpelEngine::showCityCutscene3DO() { if (finished) { // Restore screen - _screen->_backBuffer1.blitFrom(screen._backBuffer2); - _screen->blitFrom(screen._backBuffer1); + _screen->_backBuffer1.SHblitFrom(screen._backBuffer2); + _screen->SHblitFrom(screen._backBuffer1); } } @@ -751,7 +752,7 @@ bool ScalpelEngine::showCityCutscene3DO() { if (finished) { // "Sherlock Holmes" (title) ImageFile3DO titleImage_SherlockHolmesTitle("title1ab.cel", kImageFile3DOType_Cel); - screen._backBuffer1.transBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5)); + screen._backBuffer1.SHtransBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5)); // Blend in screen.fadeIntoScreen3DO(2); @@ -761,7 +762,7 @@ bool ScalpelEngine::showCityCutscene3DO() { if (finished) { ImageFile3DO titleImage_Copyright("title1c.cel", kImageFile3DOType_Cel); - screen.transBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190)); + screen.SHtransBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190)); finished = _events->delay(3500, true); } } @@ -780,7 +781,7 @@ bool ScalpelEngine::showCityCutscene3DO() { if (finished) { // "In the alley behind the Regency Theatre..." ImageFile3DO titleImage_InTheAlley("title1d.cel", kImageFile3DOType_Cel); - screen._backBuffer1.transBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51)); + screen._backBuffer1.SHtransBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51)); // Fade in screen.fadeIntoScreen3DO(4); @@ -819,7 +820,7 @@ bool ScalpelEngine::showAlleyCutscene3DO() { ImageFile3DO titleImage_ScreamingVictim("scream.cel", kImageFile3DOType_Cel); screen.clear(); - screen.transBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0)); + screen.SHtransBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0)); // Play "scream.aiff" if (_sound->_voices) @@ -848,7 +849,7 @@ bool ScalpelEngine::showAlleyCutscene3DO() { if (finished) { // "Early the following morning on Baker Street..." ImageFile3DO titleImage_EarlyTheFollowingMorning("title3.cel", kImageFile3DOType_Cel); - screen._backBuffer1.transBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51)); + screen._backBuffer1.SHtransBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51)); // Fade in screen.fadeIntoScreen3DO(4); @@ -908,7 +909,7 @@ bool ScalpelEngine::showOfficeCutscene3DO() { ImageFile3DO titleImage_CoffeeNote("note.cel", kImageFile3DOType_Cel); _screen->clear(); - _screen->transBlitFrom(titleImage_CoffeeNote[0]._frame, Common::Point(0, 0)); + _screen->SHtransBlitFrom(titleImage_CoffeeNote[0]._frame, Common::Point(0, 0)); if (_sound->_voices) { finished = _sound->playSound("prologue/sounds/note.aiff", WAIT_KBD_OR_FINISH); @@ -937,7 +938,7 @@ bool ScalpelEngine::showOfficeCutscene3DO() { // TODO: Brighten the image, possibly by doing a partial fade // to white. - _screen->_backBuffer2.blitFrom(_screen->_backBuffer1); + _screen->_backBuffer2.SHblitFrom(_screen->_backBuffer1); for (int nr = 1; finished && nr <= 4; nr++) { char filename[15]; @@ -945,8 +946,8 @@ bool ScalpelEngine::showOfficeCutscene3DO() { ImageFile3DO *creditsImage = new ImageFile3DO(filename, kImageFile3DOType_Cel); ImageFrame *creditsFrame = &(*creditsImage)[0]; for (int i = 0; finished && i < 200 + creditsFrame->_height; i++) { - _screen->blitFrom(_screen->_backBuffer2); - _screen->transBlitFrom(creditsFrame->_frame, Common::Point((320 - creditsFrame->_width) / 2, 200 - i)); + _screen->SHblitFrom(_screen->_backBuffer2); + _screen->SHtransBlitFrom(creditsFrame->_frame, Common::Point((320 - creditsFrame->_width) / 2, 200 - i)); if (!_events->delay(70, true)) finished = false; } @@ -998,7 +999,7 @@ void ScalpelEngine::showLBV(const Common::String &filename) { delete stream; _screen->setPalette(images._palette); - _screen->_backBuffer1.blitFrom(images[0]); + _screen->_backBuffer1.SHblitFrom(images[0]); _screen->verticalTransition(); } @@ -1156,7 +1157,7 @@ void ScalpelEngine::eraseBrumwellMirror() { // If player is in range of the mirror, then restore background from the secondary back buffer if (Common::Rect(70, 100, 200, 200).contains(pt)) { - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(137, 18), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 18), Common::Rect(137, 18, 184, 74)); } } @@ -1218,20 +1219,20 @@ void ScalpelEngine::doBrumwellMirror() { bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT || people[HOLMES]._sequenceNumber == WALK_UPRIGHT || people[HOLMES]._sequenceNumber == STOP_UPRIGHT || people[HOLMES]._sequenceNumber == WALK_DOWNLEFT || people[HOLMES]._sequenceNumber == STOP_DOWNLEFT; - _screen->_backBuffer1.transBlitFrom(imageFrame, pt + Common::Point(38, -imageFrame._frame.h - 25), flipped); + _screen->_backBuffer1.SHtransBlitFrom(imageFrame, pt + Common::Point(38, -imageFrame._frame.h - 25), flipped); // Redraw the mirror borders to prevent the drawn image of Holmes from appearing outside of the mirror - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(114, 18), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(114, 18), Common::Rect(114, 18, 137, 114)); - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(137, 70), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 70), Common::Rect(137, 70, 142, 114)); - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(142, 71), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(142, 71), Common::Rect(142, 71, 159, 114)); - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(159, 72), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(159, 72), Common::Rect(159, 72, 170, 116)); - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(170, 73), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(170, 73), Common::Rect(170, 73, 184, 114)); - _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(184, 18), + _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(184, 18), Common::Rect(184, 18, 212, 114)); } } @@ -1272,7 +1273,7 @@ void ScalpelEngine::showScummVMRestoreDialog() { bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::Point &pos, bool isPortrait) { Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen; Scalpel3DOMovieDecoder *videoDecoder = new Scalpel3DOMovieDecoder(); - Graphics::Surface tempSurface; + Graphics::ManagedSurface tempSurface; Common::Point framePos(pos.x, pos.y); ImageFile3DO *frameImageFile = nullptr; @@ -1307,7 +1308,7 @@ bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::P // If we're to show the movie at half-size, we'll need a temporary intermediate surface if (halfSize) - tempSurface.create(width / 2, height / 2, _screen->getPixelFormat()); + tempSurface.create(width / 2, height / 2); while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { @@ -1371,19 +1372,19 @@ bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::P } // Point the drawing frame to the temporary surface - frame = &tempSurface; + frame = &tempSurface.rawSurface(); } if (isPortrait && !frameShown) { // Draw the frame (not the frame of the video, but a frame around the video) itself - _screen->transBlitFrom(frameImage->_frame, framePos); + _screen->SHtransBlitFrom(frameImage->_frame, framePos); frameShown = true; } if (isPortrait && !halfSize) { screen.rawBlitFrom(*frame, Common::Point(pos.x * 2, pos.y * 2)); } else { - _screen->blitFrom(*frame, pos); + _screen->SHblitFrom(*frame, pos); } _screen->update(); @@ -1413,9 +1414,9 @@ bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::P } // Restore scene - screen._backBuffer1.blitFrom(screen._backBuffer2); + screen._backBuffer1.SHblitFrom(screen._backBuffer2); _scene->updateBackground(); - screen.slamArea(0, 0, screen.w(), CONTROLS_Y); + screen.slamArea(0, 0, screen.width(), CONTROLS_Y); return !skipVideo; } diff --git a/engines/sherlock/scalpel/scalpel_darts.cpp b/engines/sherlock/scalpel/scalpel_darts.cpp index 87f4566837..c5ba8032f3 100644 --- a/engines/sherlock/scalpel/scalpel_darts.cpp +++ b/engines/sherlock/scalpel/scalpel_darts.cpp @@ -102,7 +102,7 @@ void Darts::playDarts() { score -= lastDart; _roundScore += lastDart; - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); screen.print(Common::Point(DART_INFO_X, DART_INFO_Y), DART_COL_FORE, "Dart # %d", idx + 1); screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 10), DART_COL_FORE, "Scored %d points", lastDart); @@ -154,7 +154,7 @@ void Darts::playDarts() { events.wait(20); } - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); } @@ -166,8 +166,8 @@ void Darts::playDarts() { done |= _vm->shouldQuit(); if (!done) { - screen._backBuffer2.blitFrom((*_dartImages)[0], Common::Point(0, 0)); - screen._backBuffer1.blitFrom(screen._backBuffer2); + screen._backBuffer2.SHblitFrom((*_dartImages)[0], Common::Point(0, 0)); + screen._backBuffer1.SHblitFrom(screen._backBuffer2); screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); } } while (!done); @@ -185,7 +185,7 @@ void Darts::loadDarts() { _dartImages = new ImageFile("darts.vgs"); screen.setPalette(_dartImages->_palette); - screen._backBuffer1.blitFrom((*_dartImages)[0], Common::Point(0, 0)); + screen._backBuffer1.SHblitFrom((*_dartImages)[0], Common::Point(0, 0)); screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); } @@ -245,7 +245,7 @@ void Darts::showNames(int playerNum) { screen.slamArea(STATUS_INFO_X + 50, STATUS_INFO_Y + 10, 81, 12); // Make a copy of the back buffer to the secondary one - screen._backBuffer2.blitFrom(screen._backBuffer1); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); } void Darts::showStatus(int playerNum) { @@ -253,7 +253,7 @@ void Darts::showStatus(int playerNum) { byte color; // Copy scoring screen from secondary back buffer. This will erase any previously displayed status/score info - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, SHERLOCK_SCREEN_WIDTH, STATUS_INFO_Y + 48)); color = (playerNum == 0) ? PLAYER_COLOR : DART_COL_FORE; @@ -292,7 +292,7 @@ int Darts::throwDart(int dartNum, int computer) { if (_vm->shouldQuit()) return 0; - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1), Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); screen.slamRect(Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); @@ -309,9 +309,9 @@ int Darts::throwDart(int dartNum, int computer) { // Copy the bars to the secondary back buffer so that they remain fixed at their selected values // whilst the dart is being animated at being thrown at the board - screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DARTBARHX - 1, DARTHORIZY - 1), + screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DARTBARHX - 1, DARTHORIZY - 1), Common::Rect(DARTBARHX - 1, DARTHORIZY - 1, DARTBARHX + DARTBARSIZE + 3, DARTHORIZY + 10)); - screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1), + screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1), Common::Rect(DARTBARVX - 1, DARTHEIGHTY - 1, DARTBARVX + 11, DARTHEIGHTY + DARTBARSIZE + 3)); // Convert height and width to relative range of -50 to 50, where 0,0 is the exact centre of the board @@ -344,7 +344,7 @@ void Darts::drawDartThrow(const Common::Point &pt) { // Draw the dart Common::Point drawPos(pos.x - frame._width / 2, pos.y - frame._height); - screen._backBuffer1.transBlitFrom(frame, drawPos); + screen._backBuffer1.SHtransBlitFrom(frame, drawPos); screen.slamArea(drawPos.x, drawPos.y, frame._width, frame._height); // Handle erasing old dart frame area @@ -352,14 +352,14 @@ void Darts::drawDartThrow(const Common::Point &pt) { screen.slamRect(oldDrawBounds); oldDrawBounds = Common::Rect(drawPos.x, drawPos.y, drawPos.x + frame._width, drawPos.y + frame._height); - screen._backBuffer1.blitFrom(screen._backBuffer2, drawPos, oldDrawBounds); + screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos, oldDrawBounds); events.wait(2); } // Draw dart in final "stuck to board" form - screen._backBuffer1.transBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top)); - screen._backBuffer2.transBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top)); + screen._backBuffer1.SHtransBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top)); + screen._backBuffer2.SHtransBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top)); screen.slamRect(oldDrawBounds); } @@ -368,8 +368,8 @@ void Darts::erasePowerBars() { screen._backBuffer1.fillRect(Common::Rect(DARTBARHX, DARTHORIZY, DARTBARHX + DARTBARSIZE, DARTHORIZY + 10), BLACK); screen._backBuffer1.fillRect(Common::Rect(DARTBARVX, DARTHEIGHTY, DARTBARVX + 10, DARTHEIGHTY + DARTBARSIZE), BLACK); - screen._backBuffer1.transBlitFrom((*_dartImages)[2], Common::Point(DARTBARHX - 1, DARTHORIZY - 1)); - screen._backBuffer1.transBlitFrom((*_dartImages)[3], Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1)); + screen._backBuffer1.SHtransBlitFrom((*_dartImages)[2], Common::Point(DARTBARHX - 1, DARTHORIZY - 1)); + screen._backBuffer1.SHtransBlitFrom((*_dartImages)[3], Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1)); screen.slamArea(DARTBARHX - 1, DARTHORIZY - 1, DARTBARSIZE + 3, 11); screen.slamArea(DARTBARVX - 1, DARTHEIGHTY - 1, 11, DARTBARSIZE + 3); } @@ -398,11 +398,11 @@ int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, bool i if (isVertical) { screen._backBuffer1.hLine(pt.x, pt.y + DARTBARSIZE - 1 - idx, pt.x + 8, color); - screen._backBuffer1.transBlitFrom((*_dartImages)[3], Common::Point(pt.x - 1, pt.y - 1)); + screen._backBuffer1.SHtransBlitFrom((*_dartImages)[3], Common::Point(pt.x - 1, pt.y - 1)); screen.slamArea(pt.x, pt.y + DARTBARSIZE - 1 - idx, 8, 2); } else { screen._backBuffer1.vLine(pt.x + idx, pt.y, pt.y + 8, color); - screen._backBuffer1.transBlitFrom((*_dartImages)[2], Common::Point(pt.x - 1, pt.y - 1)); + screen._backBuffer1.SHtransBlitFrom((*_dartImages)[2], Common::Point(pt.x - 1, pt.y - 1)); screen.slamArea(pt.x + idx, pt.y, 1, 8); } diff --git a/engines/sherlock/scalpel/scalpel_inventory.cpp b/engines/sherlock/scalpel/scalpel_inventory.cpp index c3e20295fd..6eb8c2c05c 100644 --- a/engines/sherlock/scalpel/scalpel_inventory.cpp +++ b/engines/sherlock/scalpel/scalpel_inventory.cpp @@ -72,11 +72,11 @@ void ScalpelInventory::drawInventory(InvNewMode mode) { loadInv(); if (mode == INVENTORY_DONT_DISPLAY) { - screen._backBuffer = &screen._backBuffer2; + screen.activateBackBuffer2(); } // Draw the window background - Surface &bb = *screen._backBuffer; + Surface &bb = *screen.getBackBuffer(); bb.fillRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y1 + 10), BORDER_COLOR); bb.fillRect(Common::Rect(0, CONTROLS_Y1 + 10, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR); bb.fillRect(Common::Rect(SHERLOCK_SCREEN_WIDTH - 2, CONTROLS_Y1 + 10, @@ -128,7 +128,7 @@ void ScalpelInventory::drawInventory(InvNewMode mode) { ui._windowOpen = true; } else { // Reset the screen back buffer to the first buffer now that drawing is done - screen._backBuffer = &screen._backBuffer1; + screen.activateBackBuffer1(); } assert(IS_SERRATED_SCALPEL); @@ -196,12 +196,12 @@ void ScalpelInventory::invCommands(bool slamIt) { void ScalpelInventory::highlight(int index, byte color) { Screen &screen = *_vm->_screen; - Surface &bb = *screen._backBuffer; + Surface &bb = *screen.getBackBuffer(); int slot = index - _invIndex; ImageFrame &frame = (*_invShapes[slot])[0]; bb.fillRect(Common::Rect(8 + slot * 52, 165, (slot + 1) * 52, 194), color); - bb.transBlitFrom(frame, Common::Point(6 + slot * 52 + ((47 - frame._width) / 2), + bb.SHtransBlitFrom(frame, Common::Point(6 + slot * 52 + ((47 - frame._width) / 2), 163 + ((33 - frame._height) / 2))); screen.slamArea(8 + slot * 52, 165, 44, 30); } @@ -217,12 +217,12 @@ void ScalpelInventory::refreshInv() { ui._infoFlag = true; ui.clearInfo(); - screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(0, CONTROLS_Y), + screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(0, CONTROLS_Y), Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); ui.examine(); if (!talk._talkToAbort) { - screen._backBuffer2.blitFrom((*ui._controlPanel)[0], Common::Point(0, CONTROLS_Y)); + screen._backBuffer2.SHblitFrom((*ui._controlPanel)[0], Common::Point(0, CONTROLS_Y)); loadInv(); } } @@ -264,7 +264,7 @@ void ScalpelInventory::putInv(InvSlamMode slamIt) { // Draw the item image ImageFrame &frame = (*_invShapes[itemNum])[0]; - bb.transBlitFrom(frame, Common::Point(6 + itemNum * 52 + ((47 - frame._width) / 2), + bb.SHtransBlitFrom(frame, Common::Point(6 + itemNum * 52 + ((47 - frame._width) / 2), 163 + ((33 - frame._height) / 2))); } @@ -278,9 +278,9 @@ void ScalpelInventory::putInv(InvSlamMode slamIt) { invCommands(0); } else if (slamIt == SLAM_SECONDARY_BUFFER) { - screen._backBuffer = &screen._backBuffer2; + screen.activateBackBuffer2(); invCommands(0); - screen._backBuffer = &screen._backBuffer1; + screen.activateBackBuffer1(); } } diff --git a/engines/sherlock/scalpel/scalpel_map.cpp b/engines/sherlock/scalpel/scalpel_map.cpp index 0924581e38..ba14b5b300 100644 --- a/engines/sherlock/scalpel/scalpel_map.cpp +++ b/engines/sherlock/scalpel/scalpel_map.cpp @@ -167,13 +167,13 @@ int ScalpelMap::show() { setupSprites(); if (!IS_3DO) { - screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); - screen._backBuffer1.blitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); - screen._backBuffer1.blitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y)); - screen._backBuffer1.blitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); } else { - screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); - screen.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); + screen.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); } _drawMap = true; @@ -238,12 +238,12 @@ int ScalpelMap::show() { changed = false; if (!IS_3DO) { - screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); - screen._backBuffer1.blitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); - screen._backBuffer1.blitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y)); - screen._backBuffer1.blitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y)); } else { - screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); + screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y)); } showPlaces(); @@ -359,7 +359,6 @@ void ScalpelMap::freeSprites() { delete _mapCursors; delete _shapes; delete _iconShapes; - _iconSave.free(); } void ScalpelMap::showPlaces() { @@ -376,7 +375,7 @@ void ScalpelMap::showPlaces() { if (pt.x >= _bigPos.x && (pt.x - _bigPos.x) < SHERLOCK_SCREEN_WIDTH && pt.y >= _bigPos.y && (pt.y - _bigPos.y) < SHERLOCK_SCREEN_HEIGHT) { if (_vm->readFlags(idx)) { - screen._backBuffer1.transBlitFrom((*_iconShapes)[pt._translate], + screen._backBuffer1.SHtransBlitFrom((*_iconShapes)[pt._translate], Common::Point(pt.x - _bigPos.x - 6, pt.y - _bigPos.y - 12)); } } @@ -388,13 +387,13 @@ void ScalpelMap::showPlaces() { } void ScalpelMap::saveTopLine() { - _topLine.blitFrom(_vm->_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 12)); + _topLine.SHblitFrom(_vm->_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 12)); } void ScalpelMap::eraseTopLine() { Screen &screen = *_vm->_screen; - screen._backBuffer1.blitFrom(_topLine, Common::Point(0, 0)); - screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, _topLine.h()); + screen._backBuffer1.SHblitFrom(_topLine, Common::Point(0, 0)); + screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, _topLine.height()); } void ScalpelMap::showPlaceName(int idx, bool highlighted) { @@ -409,7 +408,7 @@ void ScalpelMap::showPlaceName(int idx, bool highlighted) { bool flipped = people[HOLMES]._sequenceNumber == MAP_DOWNLEFT || people[HOLMES]._sequenceNumber == MAP_LEFT || people[HOLMES]._sequenceNumber == MAP_UPLEFT; - screen._backBuffer1.transBlitFrom(*people[HOLMES]._imageFrame, _lDrawnPos, flipped); + screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, _lDrawnPos, flipped); } if (highlighted) { @@ -451,9 +450,9 @@ void ScalpelMap::updateMap(bool flushScreen) { saveIcon(people[HOLMES]._imageFrame, hPos); if (people[HOLMES]._sequenceNumber == MAP_DOWNLEFT || people[HOLMES]._sequenceNumber == MAP_LEFT || people[HOLMES]._sequenceNumber == MAP_UPLEFT) - screen._backBuffer1.transBlitFrom(*people[HOLMES]._imageFrame, hPos, true); + screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, hPos, true); else - screen._backBuffer1.transBlitFrom(*people[HOLMES]._imageFrame, hPos, false); + screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, hPos, false); if (flushScreen) { screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); @@ -553,8 +552,8 @@ void ScalpelMap::saveIcon(ImageFrame *src, const Common::Point &pt) { return; } - assert(size.x <= _iconSave.w() && size.y <= _iconSave.h()); - _iconSave.blitFrom(screen._backBuffer1, Common::Point(0, 0), + assert(size.x <= _iconSave.width() && size.y <= _iconSave.height()); + _iconSave.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y)); _savedPos = pos; _savedSize = size; @@ -565,7 +564,7 @@ void ScalpelMap::restoreIcon() { if (_savedPos.x >= 0 && _savedPos.y >= 0 && _savedPos.x <= SHERLOCK_SCREEN_WIDTH && _savedPos.y < SHERLOCK_SCREEN_HEIGHT) - screen._backBuffer1.blitFrom(_iconSave, _savedPos, Common::Rect(0, 0, _savedSize.x, _savedSize.y)); + screen._backBuffer1.SHblitFrom(_iconSave, _savedPos, Common::Rect(0, 0, _savedSize.x, _savedSize.y)); } void ScalpelMap::highlightIcon(const Common::Point &pt) { diff --git a/engines/sherlock/scalpel/scalpel_scene.cpp b/engines/sherlock/scalpel/scalpel_scene.cpp index b62703e0fb..11fb807c3b 100644 --- a/engines/sherlock/scalpel/scalpel_scene.cpp +++ b/engines/sherlock/scalpel/scalpel_scene.cpp @@ -71,26 +71,26 @@ void ScalpelScene::drawAllShapes() { // Draw all active shapes which are behind the person for (uint idx = 0; idx < _bgShapes.size(); ++idx) { if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE && _bgShapes[idx]._misc == BEHIND) - screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED); } // Draw all canimations which are behind the person for (uint idx = 0; idx < _canimShapes.size(); ++idx) { if (_canimShapes[idx]->_type == ACTIVE_BG_SHAPE && _canimShapes[idx]->_misc == BEHIND) - screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, + screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, _canimShapes[idx]->_flags & OBJ_FLIPPED); } // Draw all active shapes which are normal and behind the person for (uint idx = 0; idx < _bgShapes.size(); ++idx) { if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE && _bgShapes[idx]._misc == NORMAL_BEHIND) - screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED); } // Draw all canimations which are normal and behind the person for (uint idx = 0; idx < _canimShapes.size(); ++idx) { if (_canimShapes[idx]->_type == ACTIVE_BG_SHAPE && _canimShapes[idx]->_misc == NORMAL_BEHIND) - screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, + screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, _canimShapes[idx]->_flags & OBJ_FLIPPED); } @@ -103,7 +103,7 @@ void ScalpelScene::drawAllShapes() { p._sequenceNumber == WALK_UPLEFT || p._sequenceNumber == STOP_UPLEFT || p._sequenceNumber == WALK_DOWNRIGHT || p._sequenceNumber == STOP_DOWNRIGHT); - screen._backBuffer->transBlitFrom(*p._imageFrame, Common::Point(p._position.x / FIXED_INT_MULTIPLIER, + screen.getBackBuffer()->SHtransBlitFrom(*p._imageFrame, Common::Point(p._position.x / FIXED_INT_MULTIPLIER, p._position.y / FIXED_INT_MULTIPLIER - p.frameHeight()), flipped); } } @@ -112,7 +112,7 @@ void ScalpelScene::drawAllShapes() { for (uint idx = 0; idx < _bgShapes.size(); ++idx) { if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) && _bgShapes[idx]._misc == NORMAL_FORWARD) - screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, + screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED); } @@ -120,7 +120,7 @@ void ScalpelScene::drawAllShapes() { for (uint idx = 0; idx < _canimShapes.size(); ++idx) { if ((_canimShapes[idx]->_type == ACTIVE_BG_SHAPE || _canimShapes[idx]->_type == STATIC_BG_SHAPE) && _canimShapes[idx]->_misc == NORMAL_FORWARD) - screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, + screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, _canimShapes[idx]->_flags & OBJ_FLIPPED); } @@ -132,7 +132,7 @@ void ScalpelScene::drawAllShapes() { if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) && _bgShapes[idx]._misc == FORWARD) - screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, + screen.getBackBuffer()->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED); } @@ -140,7 +140,7 @@ void ScalpelScene::drawAllShapes() { for (uint idx = 0; idx < _canimShapes.size(); ++idx) { if ((_canimShapes[idx]->_type == ACTIVE_BG_SHAPE || _canimShapes[idx]->_type == STATIC_BG_SHAPE) && _canimShapes[idx]->_misc == FORWARD) - screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, + screen.getBackBuffer()->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position, _canimShapes[idx]->_flags & OBJ_FLIPPED); } @@ -242,7 +242,7 @@ void ScalpelScene::doBgAnim() { if (people[HOLMES]._type == CHARACTER) screen.restoreBackground(bounds); else if (people[HOLMES]._type == REMOVE) - screen._backBuffer->blitFrom(screen._backBuffer2, pt, bounds); + screen.getBackBuffer()->SHblitFrom(screen._backBuffer2, pt, bounds); for (uint idx = 0; idx < _bgShapes.size(); ++idx) { Object &o = _bgShapes[idx]; @@ -261,7 +261,7 @@ void ScalpelScene::doBgAnim() { Object &o = _bgShapes[idx]; if (o._type == NO_SHAPE && ((o._flags & OBJ_BEHIND) == 0)) { // Restore screen area - screen._backBuffer->blitFrom(screen._backBuffer2, o._position, + screen.getBackBuffer()->SHblitFrom(screen._backBuffer2, o._position, Common::Rect(o._position.x, o._position.y, o._position.x + o._noShapeSize.x, o._position.y + o._noShapeSize.y)); @@ -309,14 +309,14 @@ void ScalpelScene::doBgAnim() { for (uint idx = 0; idx < _bgShapes.size(); ++idx) { Object &o = _bgShapes[idx]; if (o._type == ACTIVE_BG_SHAPE && o._misc == BEHIND) - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } // Draw all canimations which are behind the person for (uint idx = 0; idx < _canimShapes.size(); ++idx) { Object &o = *_canimShapes[idx]; if (o._type == ACTIVE_BG_SHAPE && o._misc == BEHIND) { - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } } @@ -324,14 +324,14 @@ void ScalpelScene::doBgAnim() { for (uint idx = 0; idx < _bgShapes.size(); ++idx) { Object &o = _bgShapes[idx]; if (o._type == ACTIVE_BG_SHAPE && o._misc == NORMAL_BEHIND) - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } // Draw all canimations which are NORMAL and behind the person for (uint idx = 0; idx < _canimShapes.size(); ++idx) { Object &o = *_canimShapes[idx]; if (o._type == ACTIVE_BG_SHAPE && o._misc == NORMAL_BEHIND) { - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } } @@ -344,7 +344,7 @@ void ScalpelScene::doBgAnim() { bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT || people[HOLMES]._sequenceNumber == WALK_UPLEFT || people[HOLMES]._sequenceNumber == STOP_UPLEFT || people[HOLMES]._sequenceNumber == WALK_DOWNRIGHT || people[HOLMES]._sequenceNumber == STOP_DOWNRIGHT; - screen._backBuffer->transBlitFrom(*people[HOLMES]._imageFrame, + screen.getBackBuffer()->SHtransBlitFrom(*people[HOLMES]._imageFrame, Common::Point(tempX, people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES]._imageFrame->_frame.h), flipped); } @@ -352,14 +352,14 @@ void ScalpelScene::doBgAnim() { for (uint idx = 0; idx < _bgShapes.size(); ++idx) { Object &o = _bgShapes[idx]; if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == NORMAL_FORWARD) - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } // Draw all static and active canimations that are NORMAL and are in front of the person for (uint idx = 0; idx < _canimShapes.size(); ++idx) { Object &o = *_canimShapes[idx]; if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == NORMAL_FORWARD) { - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } } @@ -367,19 +367,19 @@ void ScalpelScene::doBgAnim() { for (uint idx = 0; idx < _bgShapes.size(); ++idx) { Object &o = _bgShapes[idx]; if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == FORWARD) - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } // Draw any active portrait if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE) - screen._backBuffer->transBlitFrom(*people._portrait._imageFrame, + screen.getBackBuffer()->SHtransBlitFrom(*people._portrait._imageFrame, people._portrait._position, people._portrait._flags & OBJ_FLIPPED); // Draw all static and active canimations that are in front of the person for (uint idx = 0; idx < _canimShapes.size(); ++idx) { Object &o = *_canimShapes[idx]; if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == FORWARD) { - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } } @@ -387,7 +387,7 @@ void ScalpelScene::doBgAnim() { for (uint idx = 0; idx < _bgShapes.size(); ++idx) { Object &o = _bgShapes[idx]; if (o._type == NO_SHAPE && (o._flags & OBJ_BEHIND) == 0) - screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); + screen.getBackBuffer()->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED); } // Bring the newly built picture to the screen diff --git a/engines/sherlock/scalpel/scalpel_screen.cpp b/engines/sherlock/scalpel/scalpel_screen.cpp index 197a2a2634..15e8436be6 100644 --- a/engines/sherlock/scalpel/scalpel_screen.cpp +++ b/engines/sherlock/scalpel/scalpel_screen.cpp @@ -28,12 +28,15 @@ namespace Sherlock { namespace Scalpel { ScalpelScreen::ScalpelScreen(SherlockEngine *vm) : Screen(vm) { + _backBuffer1.create(320, 200); + _backBuffer2.create(320, 200); + activateBackBuffer1(); } void ScalpelScreen::makeButton(const Common::Rect &bounds, int textX, const Common::String &buttonText, bool textContainsHotkey) { - Surface &bb = *_backBuffer; + Surface &bb = _backBuffer; bb.fillRect(Common::Rect(bounds.left, bounds.top, bounds.right, bounds.top + 1), BUTTON_TOP); bb.fillRect(Common::Rect(bounds.left, bounds.top, bounds.left + 1, bounds.bottom), BUTTON_TOP); bb.fillRect(Common::Rect(bounds.right - 1, bounds.top, bounds.right, bounds.bottom), BUTTON_BOTTOM); @@ -103,273 +106,24 @@ void ScalpelScreen::buttonPrint(const Common::Point &pt, uint color, bool slamIt } void ScalpelScreen::makePanel(const Common::Rect &r) { - _backBuffer->fillRect(r, BUTTON_MIDDLE); - _backBuffer->hLine(r.left, r.top, r.right - 2, BUTTON_TOP); - _backBuffer->hLine(r.left + 1, r.top + 1, r.right - 3, BUTTON_TOP); - _backBuffer->vLine(r.left, r.top, r.bottom - 1, BUTTON_TOP); - _backBuffer->vLine(r.left + 1, r.top + 1, r.bottom - 2, BUTTON_TOP); - - _backBuffer->vLine(r.right - 1, r.top, r.bottom - 1, BUTTON_BOTTOM); - _backBuffer->vLine(r.right - 2, r.top + 1, r.bottom - 2, BUTTON_BOTTOM); - _backBuffer->hLine(r.left, r.bottom - 1, r.right - 1, BUTTON_BOTTOM); - _backBuffer->hLine(r.left + 1, r.bottom - 2, r.right - 1, BUTTON_BOTTOM); + _backBuffer.fillRect(r, BUTTON_MIDDLE); + _backBuffer.hLine(r.left, r.top, r.right - 2, BUTTON_TOP); + _backBuffer.hLine(r.left + 1, r.top + 1, r.right - 3, BUTTON_TOP); + _backBuffer.vLine(r.left, r.top, r.bottom - 1, BUTTON_TOP); + _backBuffer.vLine(r.left + 1, r.top + 1, r.bottom - 2, BUTTON_TOP); + + _backBuffer.vLine(r.right - 1, r.top, r.bottom - 1, BUTTON_BOTTOM); + _backBuffer.vLine(r.right - 2, r.top + 1, r.bottom - 2, BUTTON_BOTTOM); + _backBuffer.hLine(r.left, r.bottom - 1, r.right - 1, BUTTON_BOTTOM); + _backBuffer.hLine(r.left + 1, r.bottom - 2, r.right - 1, BUTTON_BOTTOM); } void ScalpelScreen::makeField(const Common::Rect &r) { - _backBuffer->fillRect(r, BUTTON_MIDDLE); - _backBuffer->hLine(r.left, r.top, r.right - 1, BUTTON_BOTTOM); - _backBuffer->hLine(r.left + 1, r.bottom - 1, r.right - 1, BUTTON_TOP); - _backBuffer->vLine(r.left, r.top + 1, r.bottom - 1, BUTTON_BOTTOM); - _backBuffer->vLine(r.right - 1, r.top + 1, r.bottom - 2, BUTTON_TOP); -} - -/*----------------------------------------------------------------*/ - -void Scalpel3DOScreen::blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) { - if (!_vm->_isScreenDoubled) { - ScalpelScreen::blitFrom(src, pt, srcBounds); - return; - } - - Common::Rect srcRect = srcBounds; - Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height()); - - if (!srcRect.isValidRect() || !clip(srcRect, destRect)) - return; - - // Add dirty area remapped to the 640x200 surface - addDirtyRect(Common::Rect(destRect.left * 2, destRect.top * 2, destRect.right * 2, destRect.bottom * 2)); - - // Transfer the area, doubling each pixel - for (int yp = 0; yp < srcRect.height(); ++yp) { - const uint16 *srcP = (const uint16 *)src.getBasePtr(srcRect.left, srcRect.top + yp); - uint16 *destP = (uint16 *)getBasePtr(destRect.left * 2, (destRect.top + yp) * 2); - - for (int xp = srcRect.left; xp < srcRect.right; ++xp, ++srcP, destP += 2) { - *destP = *srcP; - *(destP + 1) = *srcP; - *(destP + 640) = *srcP; - *(destP + 640 + 1) = *srcP; - } - } -} - -void Scalpel3DOScreen::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, - bool flipped, int overrideColor) { - if (!_vm->_isScreenDoubled) { - ScalpelScreen::transBlitFromUnscaled(src, pt, flipped, overrideColor); - return; - } - - Common::Rect drawRect(0, 0, src.w, src.h); - Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h); - - // Clip the display area to on-screen - if (!clip(drawRect, destRect)) - // It's completely off-screen - return; - - if (flipped) - drawRect = Common::Rect(src.w - drawRect.right, src.h - drawRect.bottom, - src.w - drawRect.left, src.h - drawRect.top); - - Common::Point destPt(destRect.left, destRect.top); - addDirtyRect(Common::Rect(destPt.x * 2, destPt.y * 2, (destPt.x + drawRect.width()) * 2, - (destPt.y + drawRect.height()) * 2)); - - assert(src.format.bytesPerPixel == 2 && _surface.format.bytesPerPixel == 2); - - for (int yp = 0; yp < drawRect.height(); ++yp) { - const uint16 *srcP = (const uint16 *)src.getBasePtr( - flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp); - uint16 *destP = (uint16 *)getBasePtr(destPt.x * 2, (destPt.y + yp) * 2); - - for (int xp = 0; xp < drawRect.width(); ++xp, destP += 2) { - // RGB 0, 0, 0 -> transparent on 3DO - if (*srcP) { - *destP = *srcP; - *(destP + 1) = *srcP; - *(destP + 640) = *srcP; - *(destP + 640 + 1) = *srcP; - } - - srcP = flipped ? srcP - 1 : srcP + 1; - } - } -} - -void Scalpel3DOScreen::fillRect(const Common::Rect &r, uint color) { - if (_vm->_isScreenDoubled) - ScalpelScreen::fillRect(Common::Rect(r.left * 2, r.top * 2, r.right * 2, r.bottom * 2), color); - else - ScalpelScreen::fillRect(r, color); -} - -void Scalpel3DOScreen::fadeIntoScreen3DO(int speed) { - Events &events = *_vm->_events; - uint16 *currentScreenBasePtr = (uint16 *)getPixels(); - uint16 *targetScreenBasePtr = (uint16 *)_backBuffer->getPixels(); - uint16 currentScreenPixel = 0; - uint16 targetScreenPixel = 0; - - uint16 currentScreenPixelRed = 0; - uint16 currentScreenPixelGreen = 0; - uint16 currentScreenPixelBlue = 0; - - uint16 targetScreenPixelRed = 0; - uint16 targetScreenPixelGreen = 0; - uint16 targetScreenPixelBlue = 0; - - uint16 screenWidth = SHERLOCK_SCREEN_WIDTH; - uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT; - uint16 screenX = 0; - uint16 screenY = 0; - uint16 pixelsChanged = 0; - - clearDirtyRects(); - - do { - pixelsChanged = 0; - uint16 *currentScreenPtr = currentScreenBasePtr; - uint16 *targetScreenPtr = targetScreenBasePtr; - - for (screenY = 0; screenY < screenHeight; screenY++) { - for (screenX = 0; screenX < screenWidth; screenX++) { - currentScreenPixel = *currentScreenPtr; - targetScreenPixel = *targetScreenPtr; - - if (currentScreenPixel != targetScreenPixel) { - // pixel doesn't match, adjust accordingly - currentScreenPixelRed = currentScreenPixel & 0xF800; - currentScreenPixelGreen = currentScreenPixel & 0x07E0; - currentScreenPixelBlue = currentScreenPixel & 0x001F; - targetScreenPixelRed = targetScreenPixel & 0xF800; - targetScreenPixelGreen = targetScreenPixel & 0x07E0; - targetScreenPixelBlue = targetScreenPixel & 0x001F; - - if (currentScreenPixelRed != targetScreenPixelRed) { - if (currentScreenPixelRed < targetScreenPixelRed) { - currentScreenPixelRed += 0x0800; - } else { - currentScreenPixelRed -= 0x0800; - } - } - if (currentScreenPixelGreen != targetScreenPixelGreen) { - // Adjust +2/-2 because we are running RGB555 at RGB565 - if (currentScreenPixelGreen < targetScreenPixelGreen) { - currentScreenPixelGreen += 0x0040; - } else { - currentScreenPixelGreen -= 0x0040; - } - } - if (currentScreenPixelBlue != targetScreenPixelBlue) { - if (currentScreenPixelBlue < targetScreenPixelBlue) { - currentScreenPixelBlue += 0x0001; - } else { - currentScreenPixelBlue -= 0x0001; - } - } - - uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue; - *currentScreenPtr = v; - if (_vm->_isScreenDoubled) { - *(currentScreenPtr + 1) = v; - *(currentScreenPtr + 640) = v; - *(currentScreenPtr + 640 + 1) = v; - } - - pixelsChanged++; - } - - currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1; - targetScreenPtr++; - } - - if (_vm->_isScreenDoubled) - currentScreenPtr += 640; - } - - // Too much considered dirty at the moment - if (_vm->_isScreenDoubled) - addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2)); - else - addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight)); - - events.pollEvents(); - events.delay(10 * speed); - } while ((pixelsChanged) && (!_vm->shouldQuit())); -} - -void Scalpel3DOScreen::blitFrom3DOcolorLimit(uint16 limitColor) { - uint16 *currentScreenPtr = (uint16 *)getPixels(); - uint16 *targetScreenPtr = (uint16 *)_backBuffer->getPixels(); - uint16 currentScreenPixel = 0; - - uint16 screenWidth = SHERLOCK_SCREEN_WIDTH; - uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT; - uint16 screenX = 0; - uint16 screenY = 0; - - uint16 currentScreenPixelRed = 0; - uint16 currentScreenPixelGreen = 0; - uint16 currentScreenPixelBlue = 0; - - uint16 limitPixelRed = limitColor & 0xF800; - uint16 limitPixelGreen = limitColor & 0x07E0; - uint16 limitPixelBlue = limitColor & 0x001F; - - for (screenY = 0; screenY < screenHeight; screenY++) { - for (screenX = 0; screenX < screenWidth; screenX++) { - currentScreenPixel = *targetScreenPtr; - - currentScreenPixelRed = currentScreenPixel & 0xF800; - currentScreenPixelGreen = currentScreenPixel & 0x07E0; - currentScreenPixelBlue = currentScreenPixel & 0x001F; - - if (currentScreenPixelRed < limitPixelRed) - currentScreenPixelRed = limitPixelRed; - if (currentScreenPixelGreen < limitPixelGreen) - currentScreenPixelGreen = limitPixelGreen; - if (currentScreenPixelBlue < limitPixelBlue) - currentScreenPixelBlue = limitPixelBlue; - - uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue; - *currentScreenPtr = v; - if (_vm->_isScreenDoubled) { - *(currentScreenPtr + 1) = v; - *(currentScreenPtr + 640) = v; - *(currentScreenPtr + 640 + 1) = v; - } - - currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1; - targetScreenPtr++; - } - - if (_vm->_isScreenDoubled) - currentScreenPtr += 640; - } - - // Too much considered dirty at the moment - if (_vm->_isScreenDoubled) - addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2)); - else - addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight)); -} - -uint16 Scalpel3DOScreen::w() const { - return _vm->_isScreenDoubled ? _surface.w / 2 : _surface.w; -} - -uint16 Scalpel3DOScreen::h() const { - return _vm->_isScreenDoubled ? _surface.h / 2 : _surface.h; -} - -void Scalpel3DOScreen::rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt) { - Common::Rect srcRect(0, 0, src.w, src.h); - Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h); - - addDirtyRect(destRect); - _surface.copyRectToSurface(src, destRect.left, destRect.top, srcRect); + _backBuffer.fillRect(r, BUTTON_MIDDLE); + _backBuffer.hLine(r.left, r.top, r.right - 1, BUTTON_BOTTOM); + _backBuffer.hLine(r.left + 1, r.bottom - 1, r.right - 1, BUTTON_TOP); + _backBuffer.vLine(r.left, r.top + 1, r.bottom - 1, BUTTON_BOTTOM); + _backBuffer.vLine(r.right - 1, r.top + 1, r.bottom - 2, BUTTON_TOP); } } // End of namespace Scalpel diff --git a/engines/sherlock/scalpel/scalpel_screen.h b/engines/sherlock/scalpel/scalpel_screen.h index cee33b8c6c..d9be29c8b2 100644 --- a/engines/sherlock/scalpel/scalpel_screen.h +++ b/engines/sherlock/scalpel/scalpel_screen.h @@ -61,44 +61,6 @@ public: void makeField(const Common::Rect &r); }; -class Scalpel3DOScreen : public ScalpelScreen { -protected: - /** - * Draws a sub-section of a surface at a given position within this surface - * Overriden for the 3DO to automatically double the size of everything to the underlying 640x400 surface - */ - virtual void blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds); - - /** - * Draws a surface at a given position within this surface with transparency - */ - virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped, - int overrideColor); -public: - Scalpel3DOScreen(SherlockEngine *vm) : ScalpelScreen(vm) {} - virtual ~Scalpel3DOScreen() {} - - /** - * Draws a sub-section of a surface at a given position within this surface - */ - void rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt); - - /** - * Fade backbuffer 1 into screen (3DO RGB!) - */ - void fadeIntoScreen3DO(int speed); - - void blitFrom3DOcolorLimit(uint16 color); - - /** - * Fill a given area of the surface with a given color - */ - virtual void fillRect(const Common::Rect &r, uint color); - - virtual uint16 w() const; - virtual uint16 h() const; -}; - } // End of namespace Scalpel } // End of namespace Sherlock diff --git a/engines/sherlock/scalpel/scalpel_talk.cpp b/engines/sherlock/scalpel/scalpel_talk.cpp index b6e9482e3c..ff38c07537 100644 --- a/engines/sherlock/scalpel/scalpel_talk.cpp +++ b/engines/sherlock/scalpel/scalpel_talk.cpp @@ -684,7 +684,7 @@ Common::Point ScalpelTalk::get3doPortraitPosition() const { void ScalpelTalk::drawInterface() { ScalpelScreen &screen = *(ScalpelScreen *)_vm->_screen; - Surface &bb = *screen._backBuffer; + Surface &bb = *screen.getBackBuffer(); bb.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y1 + 10), BORDER_COLOR); bb.fillRect(Common::Rect(0, CONTROLS_Y + 10, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR); diff --git a/engines/sherlock/scalpel/scalpel_user_interface.cpp b/engines/sherlock/scalpel/scalpel_user_interface.cpp index 7ac8d0d5cf..8dae24ecd4 100644 --- a/engines/sherlock/scalpel/scalpel_user_interface.cpp +++ b/engines/sherlock/scalpel/scalpel_user_interface.cpp @@ -148,23 +148,24 @@ void ScalpelUserInterface::reset() { void ScalpelUserInterface::drawInterface(int bufferNum) { Screen &screen = *_vm->_screen; - const ImageFrame &src = (*_controlPanel)[0]; + const Graphics::Surface &src = (*_controlPanel)[0]._frame; int16 x = (!IS_3DO) ? 0 : UI_OFFSET_3DO; if (bufferNum & 1) { if (IS_3DO) screen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BLACK); - screen._backBuffer1.transBlitFrom(src, Common::Point(x, CONTROLS_Y)); + screen._backBuffer1.SHtransBlitFrom(src, Common::Point(x, CONTROLS_Y)); } if (bufferNum & 2) { if (IS_3DO) screen._backBuffer2.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BLACK); - screen._backBuffer2.transBlitFrom(src, Common::Point(x, CONTROLS_Y)); + screen._backBuffer2.SHtransBlitFrom(src, Common::Point(x, CONTROLS_Y)); } if (bufferNum == 3) - screen._backBuffer2.fillRect(0, INFO_LINE, SHERLOCK_SCREEN_WIDTH, INFO_LINE + 10, INFO_BLACK); + screen._backBuffer2.SHfillRect(Common::Rect(0, INFO_LINE, + SHERLOCK_SCREEN_WIDTH, INFO_LINE + 10), INFO_BLACK); } void ScalpelUserInterface::handleInput() { @@ -426,7 +427,7 @@ void ScalpelUserInterface::depressButton(int num) { offsetButton3DO(pt, num); ImageFrame &frame = (*_controls)[num]; - screen._backBuffer1.transBlitFrom(frame, pt); + screen._backBuffer1.SHtransBlitFrom(frame, pt); screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height); } @@ -442,7 +443,7 @@ void ScalpelUserInterface::restoreButton(int num) { events.setCursor(ARROW); // Restore the UI on the back buffer - screen._backBuffer1.blitFrom(screen._backBuffer2, pt, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, pt, Common::Rect(pt.x, pt.y, pt.x + 90, pt.y + 19)); screen.slamArea(pt.x, pt.y, pt.x + frame.w, pt.y + frame.h); @@ -489,7 +490,7 @@ void ScalpelUserInterface::toggleButton(uint16 num) { ImageFrame &frame = (*_controls)[num]; Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]); offsetButton3DO(pt, num); - screen._backBuffer1.transBlitFrom(frame, pt); + screen._backBuffer1.SHtransBlitFrom(frame, pt); screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height); } } else { @@ -1272,7 +1273,7 @@ void ScalpelUserInterface::doLookControl() { // Need to close the window and depress the Look button Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]); offsetButton3DO(pt, 0); - screen._backBuffer2.blitFrom((*_controls)[0], pt); + screen._backBuffer2.SHblitFrom((*_controls)[0], pt); banishWindow(true); _windowBounds.top = CONTROLS_Y1; @@ -1296,14 +1297,14 @@ void ScalpelUserInterface::doLookControl() { // Looking at an inventory object // Backup the user interface Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1); - tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0), + tempSurface.SHblitFrom(screen._backBuffer2, Common::Point(0, 0), Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); inv.drawInventory(INVENTORY_DONT_DISPLAY); banishWindow(true); // Restore the ui - screen._backBuffer2.blitFrom(tempSurface, Common::Point(0, CONTROLS_Y1)); + screen._backBuffer2.SHblitFrom(tempSurface, Common::Point(0, CONTROLS_Y1)); _windowBounds.top = CONTROLS_Y1; _key = _oldKey = _hotkeyLook; @@ -1887,7 +1888,7 @@ void ScalpelUserInterface::journalControl() { // Reset the palette screen.setPalette(screen._cMap); - screen._backBuffer1.blitFrom(screen._backBuffer2); + screen._backBuffer1.SHblitFrom(screen._backBuffer2); scene.updateBackground(); screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); } @@ -1921,9 +1922,9 @@ void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool first Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]); offsetButton3DO(pt, 0); - tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0), - Common::Rect(pt.x, pt.y, pt.x + tempSurface.w(), pt.y + tempSurface.h())); - screen._backBuffer2.transBlitFrom((*_controls)[0], pt); + tempSurface.SHblitFrom(screen._backBuffer2, Common::Point(0, 0), + Common::Rect(pt.x, pt.y, pt.x + tempSurface.width(), pt.y + tempSurface.height())); + screen._backBuffer2.SHtransBlitFrom((*_controls)[0], pt); banishWindow(1); events.setCursor(MAGNIFY); @@ -1933,7 +1934,7 @@ void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool first _menuMode = LOOK_MODE; events.clearEvents(); - screen._backBuffer2.blitFrom(tempSurface, pt); + screen._backBuffer2.SHblitFrom(tempSurface, pt); } else { events.setCursor(ARROW); banishWindow(true); @@ -1967,7 +1968,7 @@ void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool first return; } - Surface &bb = *screen._backBuffer; + Surface &bb = *screen.getBackBuffer(); if (firstTime) { // Only draw the border on the first call _infoFlag = true; @@ -2071,9 +2072,9 @@ void ScalpelUserInterface::summonWindow(const Surface &bgSurface, bool slideUp) if (slideUp) { // Gradually slide up the display of the window - for (int idx = 1; idx <= bgSurface.h(); idx += 2) { - screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - idx), - Common::Rect(0, 0, bgSurface.w(), idx)); + for (int idx = 1; idx <= bgSurface.height(); idx += 2) { + screen.getBackBuffer()->SHblitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - idx), + Common::Rect(0, 0, bgSurface.width(), idx)); screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - idx, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); @@ -2081,21 +2082,21 @@ void ScalpelUserInterface::summonWindow(const Surface &bgSurface, bool slideUp) } } else { // Gradually slide down the display of the window - for (int idx = 1; idx <= bgSurface.h(); idx += 2) { - screen._backBuffer->blitFrom(bgSurface, - Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()), - Common::Rect(0, bgSurface.h() - idx, bgSurface.w(), bgSurface.h())); - screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(), - SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - bgSurface.h() + idx)); + for (int idx = 1; idx <= bgSurface.height(); idx += 2) { + screen.getBackBuffer()->SHblitFrom(bgSurface, + Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height()), + Common::Rect(0, bgSurface.height() - idx, bgSurface.width(), bgSurface.height())); + screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height(), + SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - bgSurface.height() + idx)); events.delay(10); } } // Final display of the entire window - screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()), - Common::Rect(0, 0, bgSurface.w(), bgSurface.h())); - screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(), bgSurface.w(), bgSurface.h()); + screen.getBackBuffer()->SHblitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height()), + Common::Rect(0, 0, bgSurface.width(), bgSurface.height())); + screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height(), bgSurface.width(), bgSurface.height()); _windowOpen = true; } @@ -2106,10 +2107,10 @@ void ScalpelUserInterface::summonWindow(bool slideUp, int height) { // Extract the window that's been drawn on the back buffer Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - height); Common::Rect r(0, height, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); - tempSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), r); + tempSurface.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), r); // Remove drawn window with original user interface - screen._backBuffer1.blitFrom(screen._backBuffer2, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(0, height), r); // Display the window gradually on-screen @@ -2133,7 +2134,7 @@ void ScalpelUserInterface::banishWindow(bool slideUp) { Common::copy_backward(pSrc, pSrcEnd, pDest); // Restore lines from the ui in the secondary back buffer - screen._backBuffer1.blitFrom(screen._backBuffer2, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y), Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + idx)); @@ -2143,14 +2144,14 @@ void ScalpelUserInterface::banishWindow(bool slideUp) { } // Restore final two old lines - screen._backBuffer1.blitFrom(screen._backBuffer2, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - 2), Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 2, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - 2, SHERLOCK_SCREEN_WIDTH, 2); } else { // Restore old area to completely erase window - screen._backBuffer1.blitFrom(screen._backBuffer2, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y), Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, @@ -2170,7 +2171,7 @@ void ScalpelUserInterface::banishWindow(bool slideUp) { } // Show entire final area - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y1), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y1), Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); } diff --git a/engines/sherlock/scalpel/tsage/logo.cpp b/engines/sherlock/scalpel/tsage/logo.cpp index 273d26df74..a885057f35 100644 --- a/engines/sherlock/scalpel/tsage/logo.cpp +++ b/engines/sherlock/scalpel/tsage/logo.cpp @@ -217,7 +217,7 @@ void Object::erase() { Screen &screen = *_vm->_screen; if (_visage.isLoaded() && !_oldBounds.isEmpty()) - screen.blitFrom(screen._backBuffer1, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds); + screen.SHblitFrom(screen._backBuffer1, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds); } void Object::update() { @@ -246,9 +246,9 @@ void Object::update() { _visage.getFrame(s, _frame); // Display the frame - _oldBounds = Common::Rect(_position.x, _position.y, _position.x + s.w(), _position.y + s.h()); + _oldBounds = Common::Rect(_position.x, _position.y, _position.x + s.width(), _position.y + s.height()); _oldBounds.translate(-s._centroid.x, -s._centroid.y); - screen.transBlitFrom(s, Common::Point(_oldBounds.left, _oldBounds.top)); + screen.SHtransBlitFrom(s, Common::Point(_oldBounds.left, _oldBounds.top)); } } @@ -652,7 +652,7 @@ void Logo::loadBackground() { screen.setPalette(palette); // Copy the surface to the screen - screen.blitFrom(screen._backBuffer1); + screen.SHblitFrom(screen._backBuffer1); } void Logo::fade(const byte palette[PALETTE_SIZE], int step) { diff --git a/engines/sherlock/scene.cpp b/engines/sherlock/scene.cpp index 6f9ef179a3..78d0cd862c 100644 --- a/engines/sherlock/scene.cpp +++ b/engines/sherlock/scene.cpp @@ -27,6 +27,7 @@ #include "sherlock/scalpel/scalpel_people.h" #include "sherlock/scalpel/scalpel_scene.h" #include "sherlock/scalpel/scalpel_screen.h" +#include "sherlock/scalpel/3do/scalpel_3do_screen.h" #include "sherlock/tattoo/tattoo.h" #include "sherlock/tattoo/tattoo_scene.h" #include "sherlock/tattoo/tattoo_user_interface.h" @@ -356,7 +357,7 @@ bool Scene::loadScene(const Common::String &filename) { if (IS_ROSE_TATTOO) { // Resize the screen if necessary int fullWidth = SHERLOCK_SCREEN_WIDTH + bgHeader._scrollSize; - if (screen._backBuffer1.w() != fullWidth) { + if (screen._backBuffer1.width() != fullWidth) { screen._backBuffer1.create(fullWidth, SHERLOCK_SCREEN_HEIGHT); screen._backBuffer2.create(fullWidth, SHERLOCK_SCREEN_HEIGHT); } @@ -649,7 +650,7 @@ bool Scene::loadScene(const Common::String &filename) { } // Backup the image and set the palette - screen._backBuffer2.blitFrom(screen._backBuffer1); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); screen.setPalette(screen._cMap); delete rrmStream; @@ -996,12 +997,12 @@ bool Scene::loadScene(const Common::String &filename) { #if 0 // code to show the background - screen.blitFrom(screen._backBuffer1); + screen.SHblitFrom(screen._backBuffer1); _vm->_events->wait(10000); #endif // Backup the image - screen._backBuffer2.blitFrom(screen._backBuffer1); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); } // Handle drawing any on-screen interface @@ -1236,7 +1237,7 @@ void Scene::transitionToScene() { // If the scene is capable of scrolling, set the current scroll so that whoever has control // of the scroll code is in the middle of the screen - if (screen._backBuffer1.w() > SHERLOCK_SCREEN_WIDTH) + if (screen._backBuffer1.width() > SHERLOCK_SCREEN_WIDTH) people[people._walkControl].centerScreenOnPerson(); for (uint objIdx = 0; objIdx < _bgShapes.size(); ++objIdx) { diff --git a/engines/sherlock/screen.cpp b/engines/sherlock/screen.cpp index 74da2a80ea..d96310abb3 100644 --- a/engines/sherlock/screen.cpp +++ b/engines/sherlock/screen.cpp @@ -23,6 +23,8 @@ #include "sherlock/screen.h" #include "sherlock/sherlock.h" #include "sherlock/scalpel/scalpel_screen.h" +#include "sherlock/scalpel/3do/scalpel_3do_screen.h" +#include "sherlock/tattoo/tattoo_screen.h" #include "common/system.h" #include "common/util.h" #include "graphics/palette.h" @@ -31,17 +33,14 @@ namespace Sherlock { Screen *Screen::init(SherlockEngine *vm) { if (vm->getGameID() == GType_RoseTattoo) - return new Screen(vm); + return new Tattoo::TattooScreen(vm); else if (vm->getPlatform() == Common::kPlatform3DO) return new Scalpel::Scalpel3DOScreen(vm); else return new Scalpel::ScalpelScreen(vm); } -Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->getHeight()), _vm(vm), - _backBuffer1(vm->getGameID() == GType_RoseTattoo ? 640 : 320, vm->getGameID() == GType_RoseTattoo ? 480 : 200), - _backBuffer2(vm->getGameID() == GType_RoseTattoo ? 640 : 320, vm->getGameID() == GType_RoseTattoo ? 480 : 200), - _backBuffer(&_backBuffer1) { +Screen::Screen(SherlockEngine *vm) : Graphics::Screen(), _vm(vm) { _transitionSeed = 1; _fadeStyle = false; Common::fill(&_cMap[0], &_cMap[PALETTE_SIZE], 0); @@ -55,40 +54,20 @@ Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->get _fadeBytesRead = _fadeBytesToRead = 0; _oldFadePercent = 0; _flushScreen = false; -} - -Screen::~Screen() { - Fonts::free(); -} - -void Screen::update() { - // Merge the dirty rects - mergeDirtyRects(); - - // Loop through copying dirty areas to the physical screen - Common::List<Common::Rect>::iterator i; - for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) { - const Common::Rect &r = *i; - const byte *srcP = (const byte *)getBasePtr(r.left, r.top); - g_system->copyRectToScreen(srcP, _surface.pitch, r.left, r.top, - r.width(), r.height()); - } - // Signal the physical screen to update - g_system->updateScreen(); - _dirtyRects.clear(); + _backBuffer.create(_backBuffer1, _backBuffer1.getBounds()); } -void Screen::makeAllDirty() { - addDirtyRect(Common::Rect(0, 0, this->w(), this->h())); +Screen::~Screen() { + Fonts::freeFont(); } -void Screen::getPalette(byte palette[PALETTE_SIZE]) { - g_system->getPaletteManager()->grabPalette(palette, 0, PALETTE_COUNT); +void Screen::activateBackBuffer1() { + _backBuffer.create(_backBuffer1, _backBuffer1.getBounds()); } -void Screen::setPalette(const byte palette[PALETTE_SIZE]) { - g_system->getPaletteManager()->setPalette(palette, 0, PALETTE_COUNT); +void Screen::activateBackBuffer2() { + _backBuffer.create(_backBuffer2, _backBuffer2.getBounds()); } int Screen::equalizePalette(const byte palette[PALETTE_SIZE]) { @@ -124,7 +103,7 @@ void Screen::fadeToBlack(int speed) { } setPalette(tempPalette); - fillRect(Common::Rect(0, 0, _surface.w, _surface.h), 0); + fillRect(Common::Rect(0, 0, this->w, this->h), 0); } void Screen::fadeIn(const byte palette[PALETTE_SIZE], int speed) { @@ -136,59 +115,23 @@ void Screen::fadeIn(const byte palette[PALETTE_SIZE], int speed) { setPalette(palette); } -void Screen::addDirtyRect(const Common::Rect &r) { - _dirtyRects.push_back(r); - assert(r.width() > 0 && r.height() > 0); -} - -void Screen::mergeDirtyRects() { - Common::List<Common::Rect>::iterator rOuter, rInner; - - // Process the dirty rect list to find any rects to merge - for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { - rInner = rOuter; - while (++rInner != _dirtyRects.end()) { - - if ((*rOuter).intersects(*rInner)) { - // these two rectangles overlap or - // are next to each other - merge them - - unionRectangle(*rOuter, *rOuter, *rInner); - - // remove the inner rect from the list - _dirtyRects.erase(rInner); - - // move back to beginning of list - rInner = rOuter; - } - } - } -} - -bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) { - destRect = src1; - destRect.extend(src2); - - return !destRect.isEmpty(); -} - void Screen::randomTransition() { Events &events = *_vm->_events; const int TRANSITION_MULTIPLIER = 0x15a4e35; - _dirtyRects.clear(); + clearDirtyRects(); assert(IS_SERRATED_SCALPEL); for (int idx = 0; idx <= 65535 && !_vm->shouldQuit(); ++idx) { _transitionSeed = _transitionSeed * TRANSITION_MULTIPLIER + 1; int offset = _transitionSeed & 0xFFFF; - if (offset < (this->w() * this->h())) - *((byte *)getPixels() + offset) = *((const byte *)_backBuffer->getPixels() + offset); + if (offset < (this->width() * this->height())) + *((byte *)getPixels() + offset) = *((const byte *)_backBuffer.getPixels() + offset); if (idx != 0 && (idx % 300) == 0) { // Ensure there's a full screen dirty rect for the next frame update - if (_dirtyRects.empty()) - addDirtyRect(Common::Rect(0, 0, _surface.w, _surface.h)); + if (!isDirty()) + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); events.pollEvents(); events.delay(1); @@ -196,7 +139,7 @@ void Screen::randomTransition() { } // Make sure everything has been transferred - blitFrom(*_backBuffer); + SHblitFrom(_backBuffer); } void Screen::verticalTransition() { @@ -205,13 +148,13 @@ void Screen::verticalTransition() { byte table[640]; Common::fill(&table[0], &table[640], 0); - for (int yp = 0; yp < this->h(); ++yp) { - for (int xp = 0; xp < this->w(); ++xp) { - int temp = (table[xp] >= (this->h() - 3)) ? this->h() - table[xp] : + for (int yp = 0; yp < this->height(); ++yp) { + for (int xp = 0; xp < this->width(); ++xp) { + int temp = (table[xp] >= (this->height() - 3)) ? this->height() - table[xp] : _vm->getRandomNumber(3) + 1; if (temp) { - blitFrom(_backBuffer1, Common::Point(xp, table[xp]), + SHblitFrom(_backBuffer1, Common::Point(xp, table[xp]), Common::Rect(xp, table[xp], xp + 1, table[xp] + temp)); table[xp] += temp; } @@ -223,7 +166,7 @@ void Screen::verticalTransition() { void Screen::restoreBackground(const Common::Rect &r) { if (r.width() > 0 && r.height() > 0) - _backBuffer->blitFrom(_backBuffer2, Common::Point(r.left, r.top), r); + _backBuffer.SHblitFrom(_backBuffer2, Common::Point(r.left, r.top), r); } void Screen::slamArea(int16 xp, int16 yp, int16 width, int16 height) { @@ -254,11 +197,10 @@ void Screen::slamRect(const Common::Rect &r) { } if (srcRect.isValidRect()) - blitFrom(*_backBuffer, Common::Point(destRect.left, destRect.top), srcRect); + SHblitFrom(_backBuffer, Common::Point(destRect.left, destRect.top), srcRect); } } - void Screen::flushImage(ImageFrame *frame, const Common::Point &pt, int16 *xp, int16 *yp, int16 *width, int16 *height) { Common::Point imgPos = pt + frame->_offset; @@ -335,7 +277,7 @@ void Screen::blockMove(const Common::Rect &r) { } void Screen::blockMove() { - blockMove(Common::Rect(0, 0, w(), h())); + blockMove(Common::Rect(0, 0, width(), height())); } void Screen::print(const Common::Point &pt, uint color, const char *formatStr, ...) { @@ -351,13 +293,13 @@ void Screen::print(const Common::Point &pt, uint color, const char *formatStr, . pos.y--; // Font is always drawing one line higher if (!pos.x) // Center text horizontally - pos.x = (this->w() - width) / 2; + pos.x = (this->width() - width) / 2; Common::Rect textBounds(pos.x, pos.y, pos.x + width, pos.y + _fontHeight); - if (textBounds.right > this->w()) - textBounds.moveTo(this->w() - width, textBounds.top); - if (textBounds.bottom > this->h()) - textBounds.moveTo(textBounds.left, this->h() - _fontHeight); + if (textBounds.right > this->width()) + textBounds.moveTo(this->width() - width, textBounds.top); + if (textBounds.bottom > this->height()) + textBounds.moveTo(textBounds.left, this->height() - _fontHeight); // Write out the string at the given position writeString(str, Common::Point(textBounds.left, textBounds.top), color); @@ -378,27 +320,26 @@ void Screen::gPrint(const Common::Point &pt, uint color, const char *formatStr, } void Screen::writeString(const Common::String &str, const Common::Point &pt, uint overrideColor) { - Fonts::writeString(_backBuffer, str, pt, overrideColor); + Fonts::writeString(&_backBuffer, str, pt, overrideColor); } void Screen::vgaBar(const Common::Rect &r, int color) { - _backBuffer->fillRect(r, color); + _backBuffer.fillRect(r, color); slamRect(r); } void Screen::setDisplayBounds(const Common::Rect &r) { - _sceneSurface.setPixels(_backBuffer1.getBasePtr(r.left, r.top), r.width(), r.height(), _backBuffer1.getPixelFormat()); - - _backBuffer = &_sceneSurface; + _backBuffer.create(_backBuffer1, r); + assert(_backBuffer.width() == r.width()); + assert(_backBuffer.height() == r.height()); } void Screen::resetDisplayBounds() { - _backBuffer = &_backBuffer1; + _backBuffer.create(_backBuffer1, _backBuffer1.getBounds()); } Common::Rect Screen::getDisplayBounds() { - return (_backBuffer == &_sceneSurface) ? Common::Rect(0, 0, _sceneSurface.w(), _sceneSurface.h()) : - Common::Rect(0, 0, this->w(), this->h()); + return _backBuffer.getBounds(); } void Screen::synchronize(Serializer &s) { diff --git a/engines/sherlock/screen.h b/engines/sherlock/screen.h index 04a0c1e505..f05a4f0a90 100644 --- a/engines/sherlock/screen.h +++ b/engines/sherlock/screen.h @@ -25,55 +25,33 @@ #include "common/list.h" #include "common/rect.h" +#include "graphics/screen.h" +#include "sherlock/image_file.h" #include "sherlock/surface.h" #include "sherlock/resources.h" #include "sherlock/saveload.h" namespace Sherlock { -#define PALETTE_SIZE 768 -#define PALETTE_COUNT 256 #define VGA_COLOR_TRANS(x) ((x) * 255 / 63) #define BG_GREYSCALE_RANGE_END 229 #define BLACK 0 class SherlockEngine; -class Screen : public Surface { +class Screen : virtual public Graphics::Screen, virtual public Surface { private: - Common::List<Common::Rect> _dirtyRects; uint32 _transitionSeed; - Surface _sceneSurface; // Rose Tattoo fields int _fadeBytesRead, _fadeBytesToRead; int _oldFadePercent; -private: - /** - * Merges together overlapping dirty areas of the screen - */ - void mergeDirtyRects(); - - /** - * Returns the union of two dirty area rectangles - */ - bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2); protected: SherlockEngine *_vm; + Surface _backBuffer; - /** - * Clear the current dirty rects list - */ - void clearDirtyRects() { _dirtyRects.clear(); } - - /** - * Adds a rectangle to the list of modified areas of the screen during the - * current frame - */ - virtual void addDirtyRect(const Common::Rect &r); public: Surface _backBuffer1, _backBuffer2; - Surface *_backBuffer; bool _fadeStyle; byte _cMap[PALETTE_SIZE]; byte _sMap[PALETTE_SIZE]; @@ -86,24 +64,19 @@ public: virtual ~Screen(); /** - * Handles updating any dirty areas of the screen Surface object to the physical screen - */ - void update(); - - /** - * Makes the whole screen dirty + * Obtain the currently active back buffer. */ - void makeAllDirty(); + Surface *getBackBuffer() { return &_backBuffer; } /** - * Return the currently active palette + * Makes first back buffer active. */ - void getPalette(byte palette[PALETTE_SIZE]); + void activateBackBuffer1(); /** - * Set the palette + * Makes second back buffer active. */ - void setPalette(const byte palette[PALETTE_SIZE]); + void activateBackBuffer2(); /** * Fades from the currently active palette to the passed palette diff --git a/engines/sherlock/sherlock.h b/engines/sherlock/sherlock.h index b85321c385..d3b2d0cac8 100644 --- a/engines/sherlock/sherlock.h +++ b/engines/sherlock/sherlock.h @@ -63,9 +63,9 @@ enum GameType { GType_RoseTattoo = 1 }; -#define SHERLOCK_SCREEN_WIDTH _vm->_screen->w() -#define SHERLOCK_SCREEN_HEIGHT _vm->_screen->h() -#define SHERLOCK_SCENE_WIDTH _vm->_screen->_backBuffer1.w() +#define SHERLOCK_SCREEN_WIDTH _vm->_screen->width() +#define SHERLOCK_SCREEN_HEIGHT _vm->_screen->height() +#define SHERLOCK_SCENE_WIDTH _vm->_screen->_backBuffer1.width() #define SHERLOCK_SCENE_HEIGHT (IS_SERRATED_SCALPEL ? 138 : 480) #define SCENES_COUNT (IS_SERRATED_SCALPEL ? 63 : 101) #define MAX_BGSHAPES (IS_SERRATED_SCALPEL ? 64 : 150) diff --git a/engines/sherlock/surface.cpp b/engines/sherlock/surface.cpp index b7fc76325c..47b7d4a780 100644 --- a/engines/sherlock/surface.cpp +++ b/engines/sherlock/surface.cpp @@ -21,245 +21,16 @@ */ #include "sherlock/surface.h" -#include "sherlock/sherlock.h" -#include "sherlock/resources.h" -#include "common/system.h" -#include "graphics/palette.h" +#include "sherlock/fonts.h" namespace Sherlock { -Surface::Surface(uint16 width, uint16 height) : Fonts(), _freePixels(true) { - create(width, height); -} - -Surface::Surface() : Fonts(), _freePixels(false) { -} - -Surface::~Surface() { - if (_freePixels) - _surface.free(); -} - -void Surface::create(uint16 width, uint16 height) { - if (_freePixels) - _surface.free(); - - if (_vm->getPlatform() == Common::kPlatform3DO) { - _surface.create(width, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); - } else { - _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - } - _freePixels = true; -} - -Graphics::PixelFormat Surface::getPixelFormat() { - return _surface.format; -} - -void Surface::blitFrom(const Surface &src) { - blitFrom(src, Common::Point(0, 0)); -} - -void Surface::blitFrom(const ImageFrame &src) { - blitFrom(src._frame, Common::Point(0, 0)); -} - -void Surface::blitFrom(const Graphics::Surface &src) { - blitFrom(src, Common::Point(0, 0)); -} - -void Surface::blitFrom(const Surface &src, const Common::Point &pt) { - blitFrom(src, pt, Common::Rect(0, 0, src._surface.w, src._surface.h)); -} - -void Surface::blitFrom(const ImageFrame &src, const Common::Point &pt) { - blitFrom(src._frame, pt, Common::Rect(0, 0, src._frame.w, src._frame.h)); -} - -void Surface::blitFrom(const Graphics::Surface &src, const Common::Point &pt) { - blitFrom(src, pt, Common::Rect(0, 0, src.w, src.h)); -} - -void Surface::blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) { - Common::Rect srcRect = srcBounds; - Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height()); - - if (srcRect.isValidRect() && clip(srcRect, destRect)) { - // Surface is at least partially or completely on-screen - addDirtyRect(destRect); - _surface.copyRectToSurface(src, destRect.left, destRect.top, srcRect); - } -} - -void Surface::blitFrom(const ImageFrame &src, const Common::Point &pt, const Common::Rect &srcBounds) { - blitFrom(src._frame, pt, srcBounds); -} - -void Surface::blitFrom(const Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) { - blitFrom(src._surface, pt, srcBounds); -} - -void Surface::transBlitFrom(const ImageFrame &src, const Common::Point &pt, - bool flipped, int overrideColor, int scaleVal) { - Common::Point drawPt(pt.x + src.sDrawXOffset(scaleVal), pt.y + src.sDrawYOffset(scaleVal)); - transBlitFrom(src._frame, drawPt, flipped, overrideColor, scaleVal); -} - -void Surface::transBlitFrom(const Surface &src, const Common::Point &pt, - bool flipped, int overrideColor, int scaleVal) { - const Graphics::Surface &s = src._surface; - transBlitFrom(s, pt, flipped, overrideColor, scaleVal); -} - -void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &pt, - bool flipped, int overrideColor, int scaleVal) { - if (scaleVal == SCALE_THRESHOLD) { - transBlitFromUnscaled(src, pt, flipped, overrideColor); - return; - } - - int destWidth = src.w * SCALE_THRESHOLD / scaleVal; - int destHeight = src.h * SCALE_THRESHOLD / scaleVal; - - // Loop through drawing output lines - for (int destY = pt.y, scaleYCtr = 0; destY < (pt.y + destHeight); ++destY, scaleYCtr += scaleVal) { - if (destY < 0 || destY >= this->h()) - continue; - const byte *srcLine = (const byte *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD); - byte *destLine = (byte *)getBasePtr(pt.x, destY); - - // Loop through drawing individual rows - for (int xCtr = 0, scaleXCtr = 0; xCtr < destWidth; ++xCtr, scaleXCtr += scaleVal) { - int destX = pt.x + xCtr; - if (destX < 0 || destX >= this->w()) - continue; - - byte srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD]; - if (srcVal != TRANSPARENCY) - destLine[xCtr] = srcVal; - } - } - - // Mark the affected area - addDirtyRect(Common::Rect(pt.x, pt.y, pt.x + destWidth, pt.y + destHeight)); -} - -void Surface::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, - bool flipped, int overrideColor) { - Common::Rect drawRect(0, 0, src.w, src.h); - Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h); - - // Clip the display area to on-screen - if (!clip(drawRect, destRect)) - // It's completely off-screen - return; - - if (flipped) - drawRect = Common::Rect(src.w - drawRect.right, drawRect.top, - src.w - drawRect.left, drawRect.bottom); - - Common::Point destPt(destRect.left, destRect.top); - addDirtyRect(Common::Rect(destPt.x, destPt.y, destPt.x + drawRect.width(), - destPt.y + drawRect.height())); - - switch (src.format.bytesPerPixel) { - case 1: - // 8-bit palettized: Draw loop - assert(_surface.format.bytesPerPixel == 1); // Security check - for (int yp = 0; yp < drawRect.height(); ++yp) { - const byte *srcP = (const byte *)src.getBasePtr( - flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp); - byte *destP = (byte *)getBasePtr(destPt.x, destPt.y + yp); - - for (int xp = 0; xp < drawRect.width(); ++xp, ++destP) { - if (*srcP != TRANSPARENCY) - *destP = overrideColor ? overrideColor : *srcP; - - srcP = flipped ? srcP - 1 : srcP + 1; - } - } - break; - case 2: - // 3DO 15-bit RGB565: Draw loop - assert(_surface.format.bytesPerPixel == 2); // Security check - for (int yp = 0; yp < drawRect.height(); ++yp) { - const uint16 *srcP = (const uint16 *)src.getBasePtr( - flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp); - uint16 *destP = (uint16 *)getBasePtr(destPt.x, destPt.y + yp); - - for (int xp = 0; xp < drawRect.width(); ++xp, ++destP) { - if (*srcP) // RGB 0, 0, 0 -> transparent on 3DO - *destP = *srcP; // overrideColor ? overrideColor : *srcP; - - srcP = flipped ? srcP - 1 : srcP + 1; - } - } - break; - default: - error("Surface: unsupported bytesperpixel"); - break; - } -} - -void Surface::fillRect(int x1, int y1, int x2, int y2, uint color) { - fillRect(Common::Rect(x1, y1, x2, y2), color); -} - -void Surface::fillRect(const Common::Rect &r, uint color) { - _surface.fillRect(r, color); - addDirtyRect(r); -} - -void Surface::fill(uint color) { - fillRect(Common::Rect(_surface.w, _surface.h), color); +Surface::Surface() : Graphics::ManagedSurface(), Fonts() { } -bool Surface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) { - if (destBounds.left >= w() || destBounds.top >= h() || - destBounds.right <= 0 || destBounds.bottom <= 0) - return false; - - // Clip the bounds if necessary to fit on-screen - if (destBounds.right > w()) { - srcBounds.right -= destBounds.right - w(); - destBounds.right = w(); - } - - if (destBounds.bottom > h()) { - srcBounds.bottom -= destBounds.bottom - h(); - destBounds.bottom = h(); - } - - if (destBounds.top < 0) { - srcBounds.top += -destBounds.top; - destBounds.top = 0; - } - - if (destBounds.left < 0) { - srcBounds.left += -destBounds.left; - destBounds.left = 0; - } - - return true; -} - -void Surface::clear() { - fillRect(Common::Rect(0, 0, w(), h()), 0); -} - -void Surface::free() { - if (_freePixels) { - _surface.free(); - _freePixels = false; - } -} - -void Surface::setPixels(byte *pixels, int width, int height, Graphics::PixelFormat pixelFormat) { - _surface.format = pixelFormat; - _surface.w = width; - _surface.h = height; - _surface.pitch = width * pixelFormat.bytesPerPixel; - _surface.setPixels(pixels); +Surface::Surface(int width, int height) : Graphics::ManagedSurface(width, height), + Fonts() { + create(width, height); } void Surface::writeString(const Common::String &str, const Common::Point &pt, uint overrideColor) { @@ -278,4 +49,21 @@ void Surface::writeFancyString(const Common::String &str, const Common::Point &p writeString(str, Common::Point(pt.x + 1, pt.y + 1), overrideColor2); } +void Surface::SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt, + bool flipped, int overrideColor, int scaleVal) { + Common::Point drawPt(pt.x + src.sDrawXOffset(scaleVal), pt.y + src.sDrawYOffset(scaleVal)); + SHtransBlitFrom(src._frame, drawPt, flipped, overrideColor, scaleVal); +} + +void Surface::SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt, + bool flipped, int overrideColor, int scaleVal) { + Common::Rect srcRect(0, 0, src.w, src.h); + Common::Rect destRect(pt.x, pt.y, pt.x + src.w * SCALE_THRESHOLD / scaleVal, + pt.y + src.h * SCALE_THRESHOLD / scaleVal); + + Graphics::ManagedSurface::transBlitFrom(src, srcRect, destRect, TRANSPARENCY, + flipped, overrideColor); +} + + } // End of namespace Sherlock diff --git a/engines/sherlock/surface.h b/engines/sherlock/surface.h index 378c9be9cd..7f946b467f 100644 --- a/engines/sherlock/surface.h +++ b/engines/sherlock/surface.h @@ -20,165 +20,96 @@ * */ -#ifndef SHERLOCK_GRAPHICS_H -#define SHERLOCK_GRAPHICS_H +#ifndef SHERLOCK_SURFACE_H +#define SHERLOCK_SURFACE_H #include "common/rect.h" #include "common/platform.h" -#include "graphics/surface.h" +#include "graphics/managed_surface.h" #include "sherlock/fonts.h" +#include "sherlock/image_file.h" namespace Sherlock { #define SCALE_THRESHOLD 0x100 #define TRANSPARENCY 255 -struct ImageFrame; - -class Surface: public Fonts { -private: - bool _freePixels; - - /** - * Copy a surface into this one - */ - void blitFrom(const Graphics::Surface &src); -protected: - Graphics::Surface _surface; - - /** - * Clips the given source bounds so the passed destBounds will be entirely on-screen - */ - bool clip(Common::Rect &srcBounds, Common::Rect &destBounds); - - /** - * Base method stub for signalling dirty rect areas - */ - virtual void addDirtyRect(const Common::Rect &r) {} - - /** - * Draws a sub-section of a surface at a given position within this surface - */ - virtual void blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds); - - /** - * Draws a surface at a given position within this surface with transparency - */ - virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped, - int overrideColor); +/** + * Implements a descendent surface that combines both a managed surface and the font + * drawing code. It also introduces a series of drawing method stubs that the 3DO + * Serrated Scalpel screen overrides to implement sprite doubling + */ +class Surface: virtual public Graphics::ManagedSurface, public Fonts { public: - Surface(uint16 width, uint16 height); - Surface(); - virtual ~Surface(); - /** - * Sets up an internal surface with the specified dimensions that will be automatically freed - * when the surface object is destroyed + * Constructor */ - void create(uint16 width, uint16 height); - - Graphics::PixelFormat getPixelFormat(); - + Surface(); + /** - * Copy a surface into this one + * Constructor */ - void blitFrom(const Surface &src); + Surface(int width, int height); /** - * Copy an image frame into this surface + * Draws a surface on this surface */ - void blitFrom(const ImageFrame &src); + virtual void SHblitFrom(const Graphics::Surface &src) { + Graphics::ManagedSurface::blitFrom(src); + } /** * Draws a surface at a given position within this surface */ - void blitFrom(const Surface &src, const Common::Point &pt); - - /** - * Copy an image frame onto this surface at a given position - */ - void blitFrom(const ImageFrame &src, const Common::Point &pt); + virtual void SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos) { + Graphics::ManagedSurface::blitFrom(src, destPos); + } /** * Draws a sub-section of a surface at a given position within this surface */ - void blitFrom(const Surface &src, const Common::Point &pt, const Common::Rect &srcBounds); - - /** - * Copy a sub-area of a source image frame into this surface at a given position - */ - void blitFrom(const ImageFrame &src, const Common::Point &pt, const Common::Rect &srcBounds); - - /** - * Draws a surface at a given position within this surface - */ - void blitFrom(const Graphics::Surface &src, const Common::Point &pt); + virtual void SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos, const Common::Rect &srcBounds) { + Graphics::ManagedSurface::blitFrom(src, srcBounds, destPos); + } /** * Draws an image frame at a given position within this surface with transparency */ - void transBlitFrom(const ImageFrame &src, const Common::Point &pt, - bool flipped = false, int overrideColor = 0, int scaleVal = 256); - - /** - * Draws a surface at a given position within this surface with transparency - */ - void transBlitFrom(const Surface &src, const Common::Point &pt, - bool flipped = false, int overrideColor = 0, int scaleVal = 256); + void SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt, + bool flipped = false, int overrideColor = 0, int scaleVal = SCALE_THRESHOLD); /** - * Draws a surface at a given position within this surface with transparency + * Draws an image frame at a given position within this surface with transparency */ - void transBlitFrom(const Graphics::Surface &src, const Common::Point &pt, - bool flipped = false, int overrideColor = 0, int scaleVal = 256); + void SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt, + bool flipped = false, int overrideColor = 0, int scaleVal = SCALE_THRESHOLD); /** * Fill a given area of the surface with a given color */ - void fillRect(int x1, int y1, int x2, int y2, uint color); - - /** - * Fill a given area of the surface with a given color - */ - virtual void fillRect(const Common::Rect &r, uint color); - - void fill(uint color); + virtual void SHfillRect(const Common::Rect &r, uint color) { + Graphics::ManagedSurface::fillRect(r, color); + } /** - * Clear the surface + * Return the width of the surface */ - void clear(); - - /** - * Free the underlying surface - */ - void free(); - + virtual uint16 width() const { return this->w; } + /** - * Returns true if the surface is empty + * Return the height of the surface */ - bool empty() const { return _surface.getPixels() == nullptr; } + virtual uint16 height() const { return this->h; } /** - * Set the pixels for the surface to an existing data block + * Draws the given string into the back buffer using the images stored in _font */ - void setPixels(byte *pixels, int width, int height, Graphics::PixelFormat format); - + void writeString(const Common::String &str, const Common::Point &pt, uint overrideColor); + /** - * Draws the given string into the back buffer using the images stored in _font + * Draws a fancy version of the given string at the given position */ - virtual void writeString(const Common::String &str, const Common::Point &pt, uint overrideColor); void writeFancyString(const Common::String &str, const Common::Point &pt, uint overrideColor1, uint overrideColor2); - - inline virtual uint16 w() const { return _surface.w; } - inline virtual uint16 h() const { return _surface.h; } - inline const byte *getPixels() const { return (const byte *)_surface.getPixels(); } - inline byte *getPixels() { return (byte *)_surface.getPixels(); } - inline byte *getBasePtr(int x, int y) { return (byte *)_surface.getBasePtr(x, y); } - inline const byte *getBasePtr(int x, int y) const { return (const byte *)_surface.getBasePtr(x, y); } - inline Graphics::Surface &getRawSurface() { return _surface; } - inline void hLine(int x, int y, int x2, uint color) { _surface.hLine(x, y, x2, color); } - inline void vLine(int x, int y, int y2, uint color) { _surface.vLine(x, y, y2, color); } }; } // End of namespace Sherlock diff --git a/engines/sherlock/tattoo/tattoo_darts.cpp b/engines/sherlock/tattoo/tattoo_darts.cpp index fe707a8c13..cbc3ea1fe8 100644 --- a/engines/sherlock/tattoo/tattoo_darts.cpp +++ b/engines/sherlock/tattoo/tattoo_darts.cpp @@ -163,7 +163,7 @@ void Darts::playDarts(GameType gameType) { // Show scores showStatus(playerNum); - screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1), + screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1), Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1)); screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, FIXED(DartsCurrentDart), idx + 1); @@ -178,7 +178,7 @@ void Darts::playDarts(GameType gameType) { scoredPoints = Common::String::format(FIXED(DartsScoredPoints), lastDart); } - screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, scoredPoints.c_str()); + screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", scoredPoints.c_str()); } else { Common::String hitText; @@ -213,7 +213,7 @@ void Darts::playDarts(GameType gameType) { break; } } - screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, hitText.c_str()); + screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", hitText.c_str()); } if (score != 0 && playerNum == 0 && !gameOver) @@ -267,10 +267,11 @@ void Darts::playDarts(GameType gameType) { } else { events.wait(40); } + // Clears the status part of the board - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1), Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1)); - screen.blitFrom(screen._backBuffer1); + screen.SHblitFrom(screen._backBuffer1); } playerNum ^= 1; @@ -278,9 +279,9 @@ void Darts::playDarts(GameType gameType) { ++_roundNum; if (!done) { - screen._backBuffer2.blitFrom((*_dartBoard)[0], Common::Point(0, 0)); - screen._backBuffer1.blitFrom(screen._backBuffer2); - screen.blitFrom(screen._backBuffer2); + screen._backBuffer2.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0)); + screen._backBuffer1.SHblitFrom(screen._backBuffer2); + screen.SHblitFrom(screen._backBuffer2); } } @@ -367,9 +368,9 @@ void Darts::loadDarts() { delete stream; // Load the initial background - screen._backBuffer1.blitFrom((*_dartBoard)[0], Common::Point(0, 0)); - screen._backBuffer2.blitFrom(screen._backBuffer1); - screen.blitFrom(screen._backBuffer1); + screen._backBuffer1.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0)); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); + screen.SHblitFrom(screen._backBuffer1); } void Darts::closeDarts() { @@ -399,14 +400,14 @@ void Darts::showNames(int playerNum) { screen.fillRect(Common::Rect(STATUS2_INFO_X, STATUS_INFO_Y + _spacing + 1, STATUS2_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color); - screen._backBuffer2.blitFrom(screen._backBuffer1); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); } void Darts::showStatus(int playerNum) { Screen &screen = *_vm->_screen; const char *const CRICKET_SCORE_NAME[7] = { "20", "19", "18", "17", "16", "15", FIXED(DartsBull) }; - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH, STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10)); screen.print(Common::Point(STATUS_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score1); @@ -417,12 +418,12 @@ void Darts::showStatus(int playerNum) { // "Round: x" Common::String dartsRoundStatus = Common::String::format(FIXED(DartsCurrentRound), _roundNum); - screen.print(Common::Point(STATUS_INFO_X, temp), 0, dartsRoundStatus.c_str()); + screen.print(Common::Point(STATUS_INFO_X, temp), 0, "%s", dartsRoundStatus.c_str()); if (_gameType == GAME_301) { // "Turn Total: x" Common::String dartsTotalPoints = Common::String::format(FIXED(DartsCurrentTotalPoints), _roundScore); - screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, dartsTotalPoints.c_str()); + screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, "%s", dartsTotalPoints.c_str()); } else { // Show cricket scores for (int x = 0; x < 7; ++x) { @@ -447,7 +448,7 @@ void Darts::showStatus(int playerNum) { } } - screen.blitFrom(screen._backBuffer1, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), + screen.SHblitFrom(screen._backBuffer1, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10), Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH, STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10)); } @@ -457,7 +458,7 @@ void Darts::erasePowerBars() { // Erase the old power bars and replace them with empty ones screen._backBuffer1.fillRect(Common::Rect(DART_BAR_VX, DART_HEIGHT_Y, DART_BAR_VX + 9, DART_HEIGHT_Y + DART_BAR_SIZE), 0); - screen._backBuffer1.transBlitFrom((*_dartGraphics)[0], Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1)); + screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1)); screen.slamArea(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, 10, DART_BAR_SIZE + 2); } @@ -497,7 +498,7 @@ int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, int or } screen._backBuffer1.hLine(pt.x, pt.y + DART_BAR_SIZE- 1 - idx, pt.x + 8, color); - screen._backBuffer1.transBlitFrom((*_dartGraphics)[0], Common::Point(pt.x - 1, pt.y - 1)); + screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(pt.x - 1, pt.y - 1)); screen.slamArea(pt.x, pt.y + DART_BAR_SIZE - 1 - idx, 8, 2); if (!(idx % 8)) @@ -544,7 +545,7 @@ int Darts::drawHand(int goToPower, int computer) { break; } - screen._backBuffer1.transBlitFrom((*hands)[0], pt); + screen._backBuffer1.SHtransBlitFrom((*hands)[0], pt); screen.slamArea(pt.x - 1, pt.y, _handSize.x + 1, _handSize.y); screen.restoreBackground(Common::Rect(pt.x, pt.y, pt.x + _handSize.x, pt.y + _handSize.y)); @@ -631,7 +632,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { _handSize.y = hands[idx]._offset.y + hands[idx]._height; int handCy = SHERLOCK_SCREEN_HEIGHT - _handSize.y; - screen._backBuffer1.transBlitFrom(hands[idx], Common::Point(_handX, handCy)); + screen._backBuffer1.SHtransBlitFrom(hands[idx], Common::Point(_handX, handCy)); screen.slamArea(_handX, handCy, _handSize.x + 1, _handSize.y); screen.slamArea(handOCx, handOCy, handOldxSize, handOldySize); screen.restoreBackground(Common::Rect(_handX, handCy, _handX + _handSize.x, handCy + _handSize.y)); @@ -653,7 +654,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height; // Draw dart - screen._backBuffer1.transBlitFrom((*_dartGraphics)[dartNum], drawPos); + screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], drawPos); if (drawPos.x < 0) { xSize += drawPos.x; @@ -675,7 +676,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { // Flush the erased dart area screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize); - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y), Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize)); oldDrawPos.x = drawPos.x; @@ -696,7 +697,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { if (oldDrawPos.x != -1) screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize); - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y), Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize)); cx = dartPos.x; @@ -722,7 +723,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { ocx = drawPos.x = cx - (*_dartGraphics)[dartNum]._width / 2; ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height; - screen._backBuffer1.transBlitFrom((*_dartGraphics)[dartNum], Common::Point(drawPos.x, drawPos.y)); + screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(drawPos.x, drawPos.y)); if (drawPos.x < 0) { xSize += drawPos.x; @@ -744,7 +745,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize); if (idx != 23) - screen._backBuffer1.blitFrom(screen._backBuffer2, drawPos, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos, Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize)); // erase dart events.wait(1); @@ -761,8 +762,8 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) { ySize = (*_dartGraphics)[dartNum]._height; // Draw final dart on the board - screen._backBuffer1.transBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy)); - screen._backBuffer2.transBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy)); + screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy)); + screen._backBuffer2.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy)); screen.slamArea(ocx, ocy, xSize, ySize); } @@ -935,7 +936,7 @@ int Darts::throwDart(int dartNum, int computer) { // "Dart # x" Common::String currentDart = Common::String::format(FIXED(DartsCurrentDart), dartNum); - screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, currentDart.c_str()); + screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s", currentDart.c_str()); drawDartsLeft(dartNum, computer); @@ -955,9 +956,9 @@ int Darts::throwDart(int dartNum, int computer) { } drawDartsLeft(dartNum + 1, computer); - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1), Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1)); - screen.blitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1), + screen.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1), Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1)); if (computer) { @@ -979,7 +980,7 @@ int Darts::throwDart(int dartNum, int computer) { height = 101 - height; // Copy power bars to the secondary back buffer - screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1), + screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1), Common::Rect(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, DART_BAR_VX - 1 + 10, DART_HEIGHT_Y - 1 + DART_BAR_SIZE + 2)); @@ -1023,14 +1024,14 @@ void Darts::drawDartsLeft(int dartNum, int computer) { const int DART_X2[3] = { 393, 441, 502 }; const int DART_Y2[3] = { 373, 373, 373 }; - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_X1[0], DART_Y1[0]), + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_X1[0], DART_Y1[0]), Common::Rect(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); for (int idx = 2; idx >= dartNum - 1; --idx) { if (computer) - screen._backBuffer1.transBlitFrom((*_dartsLeft)[idx + 3], Common::Point(DART_X2[idx], DART_Y2[idx])); + screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx + 3], Common::Point(DART_X2[idx], DART_Y2[idx])); else - screen._backBuffer1.transBlitFrom((*_dartsLeft)[idx], Common::Point(DART_X1[idx], DART_Y1[idx])); + screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx], Common::Point(DART_X1[idx], DART_Y1[idx])); } screen.slamArea(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH - DART_X1[0], SHERLOCK_SCREEN_HEIGHT - DART_Y1[0]); diff --git a/engines/sherlock/tattoo/tattoo_journal.cpp b/engines/sherlock/tattoo/tattoo_journal.cpp index dac0760ad2..918887f320 100644 --- a/engines/sherlock/tattoo/tattoo_journal.cpp +++ b/engines/sherlock/tattoo/tattoo_journal.cpp @@ -65,7 +65,7 @@ void TattooJournal::show() { delete stream; // Set screen to black, and set background - screen._backBuffer1.blitFrom((*_journalImages)[0], Common::Point(0, 0)); + screen._backBuffer1.SHblitFrom((*_journalImages)[0], Common::Point(0, 0)); screen.empty(); screen.setPalette(palette); @@ -461,7 +461,7 @@ void TattooJournal::loadLocations() { void TattooJournal::drawFrame() { Screen &screen = *_vm->_screen; - screen._backBuffer1.blitFrom((*_journalImages)[0], Common::Point(0, 0)); + screen._backBuffer1.SHblitFrom((*_journalImages)[0], Common::Point(0, 0)); drawControls(0); } @@ -486,10 +486,10 @@ void TattooJournal::drawControls(int mode) { screen._backBuffer1.fillRect(inner, MENU_BACKGROUND); // Draw the four corners of the info box - screen._backBuffer1.transBlitFrom(images[0], Common::Point(r.left, r.top)); - screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.top)); - screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.left, r.bottom - images[1]._height)); - screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.bottom - images[1]._height)); + screen._backBuffer1.SHtransBlitFrom(images[0], Common::Point(r.left, r.top)); + screen._backBuffer1.SHtransBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.top)); + screen._backBuffer1.SHtransBlitFrom(images[1], Common::Point(r.left, r.bottom - images[1]._height)); + screen._backBuffer1.SHtransBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.bottom - images[1]._height)); // Draw the top of the info box screen._backBuffer1.hLine(r.left + images[0]._width, r.top, r.right - images[0]._height, INFO_TOP); @@ -513,8 +513,8 @@ void TattooJournal::drawControls(int mode) { // Draw the sides of the separator bar above the scroll bar int yp = r.top + screen.fontHeight() + 7; - screen._backBuffer1.transBlitFrom(images[4], Common::Point(r.left, yp - 1)); - screen._backBuffer1.transBlitFrom(images[5], Common::Point(r.right - images[5]._width, yp - 1)); + screen._backBuffer1.SHtransBlitFrom(images[4], Common::Point(r.left, yp - 1)); + screen._backBuffer1.SHtransBlitFrom(images[5], Common::Point(r.right - images[5]._width, yp - 1)); // Draw the bar above the scroll bar screen._backBuffer1.hLine(r.left + images[4]._width, yp, r.right - images[5]._width, INFO_TOP); @@ -525,8 +525,8 @@ void TattooJournal::drawControls(int mode) { // Draw the Bars separating the Journal Commands int xp = r.right / 3; for (int idx = 0; idx < 2; ++idx) { - screen._backBuffer1.transBlitFrom(images[6], Common::Point(xp - 2, r.top + 1)); - screen._backBuffer1.transBlitFrom(images[7], Common::Point(xp - 2, yp - 1)); + screen._backBuffer1.SHtransBlitFrom(images[6], Common::Point(xp - 2, r.top + 1)); + screen._backBuffer1.SHtransBlitFrom(images[7], Common::Point(xp - 2, yp - 1)); screen._backBuffer1.hLine(xp - 1, r.top + 4, yp - 2, INFO_TOP); screen._backBuffer1.hLine(xp, r.top + 4, yp - 2, INFO_MIDDLE); @@ -779,7 +779,7 @@ int TattooJournal::getFindName(bool printError) { // Backup the area under the text entry Surface bgSurface(r.width() - 6, screen.fontHeight()); - bgSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(r.left + 3, cursorY, + bgSurface.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(r.left + 3, cursorY, r.right - 3, cursorY + screen.fontHeight())); if (printError) { @@ -810,7 +810,7 @@ int TattooJournal::getFindName(bool printError) { events.clearEvents(); // Restore the text background - screen._backBuffer1.blitFrom(bgSurface, Common::Point(r.left, cursorY)); + screen._backBuffer1.SHblitFrom(bgSurface, Common::Point(r.left, cursorY)); // If there was a name already entered, copy it to name and display it if (!_find.empty()) { @@ -846,7 +846,7 @@ int TattooJournal::getFindName(bool printError) { } else { // Erase cursor by restoring background and writing current text - screen._backBuffer1.blitFrom(bgSurface, Common::Point(r.left + 3, cursorY)); + screen._backBuffer1.SHblitFrom(bgSurface, Common::Point(r.left + 3, cursorY)); screen.gPrint(Common::Point(r.left + screen.widestChar() + 3, cursorY), COMMAND_HIGHLIGHTED, "%s", name.c_str()); screen.slamArea(r.left + 3, cursorY, r.width() - 3, screen.fontHeight()); } @@ -912,7 +912,7 @@ int TattooJournal::getFindName(bool printError) { } // Redraw the text - screen._backBuffer1.blitFrom(bgSurface, Common::Point(r.left + 3, cursorY)); + screen._backBuffer1.SHblitFrom(bgSurface, Common::Point(r.left + 3, cursorY)); screen.gPrint(Common::Point(r.left + screen.widestChar() + 3, cursorY), COMMAND_HIGHLIGHTED, "%s", name.c_str()); screen.slamArea(r.left + 3, cursorY, r.right - 3, screen.fontHeight()); diff --git a/engines/sherlock/tattoo/tattoo_map.cpp b/engines/sherlock/tattoo/tattoo_map.cpp index 4c7e8c8fef..23e8bd9739 100644 --- a/engines/sherlock/tattoo/tattoo_map.cpp +++ b/engines/sherlock/tattoo/tattoo_map.cpp @@ -105,7 +105,8 @@ int TattooMap::show() { // Load the map image and draw it to the back buffer ImageFile *map = new ImageFile("map.vgs"); screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2); - screen._backBuffer1.blitFrom((*map)[0], Common::Point(0, 0)); + screen._backBuffer1.SHblitFrom((*map)[0], Common::Point(0, 0)); + screen.activateBackBuffer1(); delete map; screen.clear(); @@ -114,7 +115,7 @@ int TattooMap::show() { // Copy the map drawn in the back buffer to the secondary back buffer screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2); - screen._backBuffer2.blitFrom(screen._backBuffer1); + screen._backBuffer2.SHblitFrom(screen._backBuffer1); // Display the built map to the screen screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); @@ -148,12 +149,12 @@ int TattooMap::show() { if (_targetScroll.x < 0) _targetScroll.x = 0; - if ((_targetScroll.x + SHERLOCK_SCREEN_WIDTH) > screen._backBuffer1.w()) - _targetScroll.x = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH; + if ((_targetScroll.x + SHERLOCK_SCREEN_WIDTH) > screen._backBuffer1.width()) + _targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH; if (_targetScroll.y < 0) _targetScroll.y = 0; - if ((_targetScroll.y + SHERLOCK_SCREEN_HEIGHT) > screen._backBuffer1.h()) - _targetScroll.y = screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT; + if ((_targetScroll.y + SHERLOCK_SCREEN_HEIGHT) > screen._backBuffer1.height()) + _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT; // Check the keyboard if (events.kbHit()) { @@ -166,8 +167,8 @@ int TattooMap::show() { break; case Common::KEYCODE_END: - _targetScroll.x = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH; - _targetScroll.y = screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT; + _targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH; + _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT; break; case Common::KEYCODE_PAGEUP: @@ -178,8 +179,8 @@ int TattooMap::show() { case Common::KEYCODE_PAGEDOWN: _targetScroll.y += SHERLOCK_SCREEN_HEIGHT; - if (_targetScroll.y > (screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT)) - _targetScroll.y = screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT; + if (_targetScroll.y > (screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT)) + _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT; break; case Common::KEYCODE_SPACE: @@ -224,6 +225,7 @@ int TattooMap::show() { // Reset the back buffers back to standard size screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); + screen.activateBackBuffer1(); return result; } @@ -304,7 +306,7 @@ void TattooMap::drawMapIcons() { if (_data[idx]._iconNum != -1 && _vm->readFlags(idx + 1)) { MapEntry &mapEntry = _data[idx]; ImageFrame &img = (*_iconImages)[mapEntry._iconNum]; - screen._backBuffer1.transBlitFrom(img._frame, Common::Point(mapEntry.x - img._width / 2, + screen._backBuffer1.SHtransBlitFrom(img._frame, Common::Point(mapEntry.x - img._width / 2, mapEntry.y - img._height / 2)); } } @@ -355,10 +357,10 @@ void TattooMap::restoreArea(const Common::Rect &bounds) { Screen &screen = *_vm->_screen; Common::Rect r = bounds; - r.clip(Common::Rect(0, 0, screen._backBuffer1.w(), screen._backBuffer1.h())); + r.clip(Common::Rect(0, 0, screen._backBuffer1.width(), screen._backBuffer1.height())); if (!r.isEmpty()) - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(r.left, r.top), r); + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(r.left, r.top), r); } void TattooMap::showCloseUp(int closeUpNum) { @@ -407,7 +409,7 @@ void TattooMap::showCloseUp(int closeUpNum) { screen._currentScroll.y + closeUp.y / 100 - picSize.y / 2); restoreArea(oldBounds); - screen._backBuffer1.transBlitFrom(pic[0], pt, false, 0, scaleVal); + screen._backBuffer1.SHtransBlitFrom(pic[0], pt, false, 0, scaleVal); screen.slamRect(oldBounds); screen.slamArea(pt.x, pt.y, picSize.x, picSize.y); @@ -426,7 +428,7 @@ void TattooMap::showCloseUp(int closeUpNum) { screen._currentScroll.y + SHERLOCK_SCREEN_HEIGHT / 2 - pic[0]._height / 2 + pic[0]._height); restoreArea(oldBounds); - screen._backBuffer1.transBlitFrom(pic[0], Common::Point(r.left, r.top)); + screen._backBuffer1.SHtransBlitFrom(pic[0], Common::Point(r.left, r.top)); screen.slamRect(oldBounds); screen.slamRect(r); diff --git a/engines/sherlock/tattoo/tattoo_people.cpp b/engines/sherlock/tattoo/tattoo_people.cpp index b83a977a77..65cc283b66 100644 --- a/engines/sherlock/tattoo/tattoo_people.cpp +++ b/engines/sherlock/tattoo/tattoo_people.cpp @@ -1042,7 +1042,7 @@ void TattooPerson::walkHolmesToNPC() { holmes._walkDest.x = MAX(_position.x / FIXED_INT_MULTIPLIER - imgFrame.sDrawXSize(scaleVal), 0); } else { holmes._walkDest.x = MIN(_position.x / FIXED_INT_MULTIPLIER + imgFrame.sDrawXSize(scaleVal) * 2, - screen._backBuffer1.w() - 1); + screen._backBuffer1.width() - 1); } // See where Holmes is with respect to the NPC (y coords) @@ -1168,7 +1168,7 @@ void TattooPerson::centerScreenOnPerson() { TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; ui._targetScroll.x = CLIP(_position.x / FIXED_INT_MULTIPLIER - SHERLOCK_SCREEN_WIDTH / 2, - 0, screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH); + 0, screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH); screen._currentScroll = ui._targetScroll; // Reset the default look position to the center of the screen @@ -1478,7 +1478,7 @@ const Common::Point TattooPeople::restrictToZone(int zoneId, const Common::Point Screen &screen = *_vm->_screen; Common::Rect &r = scene._zones[zoneId]; - if (destPos.x < 0 || destPos.x > screen._backBuffer1.w()) + if (destPos.x < 0 || destPos.x > screen._backBuffer1.width()) return destPos; else if (destPos.y < r.top && r.left < destPos.x && destPos.x < r.right) return Common::Point(destPos.x, r.top); diff --git a/engines/sherlock/tattoo/tattoo_scene.cpp b/engines/sherlock/tattoo/tattoo_scene.cpp index 27f37665dc..00015cb189 100644 --- a/engines/sherlock/tattoo/tattoo_scene.cpp +++ b/engines/sherlock/tattoo/tattoo_scene.cpp @@ -141,15 +141,15 @@ void TattooScene::drawAllShapes() { if (obj._type == ACTIVE_BG_SHAPE && obj._misc == BEHIND) { if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD) - screen._backBuffer1.blitFrom(*obj._imageFrame, obj._position); + screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position); else - screen._backBuffer1.transBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal); + screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal); } } // Draw the animation if it is behind the person if (_activeCAnim.active() && _activeCAnim._zPlacement == BEHIND) - screen._backBuffer1.transBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, + screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal); screen.resetDisplayBounds(); @@ -194,13 +194,13 @@ void TattooScene::drawAllShapes() { if (se._shape) { // it's a bg shape if (se._shape->_quickDraw && se._shape->_scaleVal == SCALE_THRESHOLD) - screen._backBuffer1.blitFrom(*se._shape->_imageFrame, se._shape->_position); + screen._backBuffer1.SHblitFrom(*se._shape->_imageFrame, se._shape->_position); else - screen._backBuffer1.transBlitFrom(*se._shape->_imageFrame, se._shape->_position, + screen._backBuffer1.SHtransBlitFrom(*se._shape->_imageFrame, se._shape->_position, se._shape->_flags & OBJ_FLIPPED, 0, se._shape->_scaleVal); } else if (se._isAnimation) { // It's an active animation - screen._backBuffer1.transBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, + screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal); } else { // Drawing person @@ -212,7 +212,7 @@ void TattooScene::drawAllShapes() { if (p._tempScaleVal == SCALE_THRESHOLD) { p._tempX += adjust.x; - screen._backBuffer1.transBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER + screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER - p.frameHeight() - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, 0, p._tempScaleVal); } else { if (adjust.x) { @@ -242,7 +242,7 @@ void TattooScene::drawAllShapes() { ++adjust.y; } - screen._backBuffer1.transBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER + screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER - p._imageFrame->sDrawYSize(p._tempScaleVal) - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, 0, p._tempScaleVal); } } @@ -255,15 +255,15 @@ void TattooScene::drawAllShapes() { if (obj._type == ACTIVE_BG_SHAPE && obj._misc == FORWARD) { if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD) - screen._backBuffer1.blitFrom(*obj._imageFrame, obj._position); + screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position); else - screen._backBuffer1.transBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal); + screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal); } } // Draw the canimation if it is set as FORWARD if (_activeCAnim.active() && _activeCAnim._zPlacement == FORWARD) - screen._backBuffer1.transBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal); + screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal); // Draw all NO_SHAPE shapes which have their flag bits clear for (uint idx = 0; idx < _bgShapes.size(); ++idx) { diff --git a/engines/sherlock/tattoo/tattoo_screen.cpp b/engines/sherlock/tattoo/tattoo_screen.cpp new file mode 100644 index 0000000000..159060b19e --- /dev/null +++ b/engines/sherlock/tattoo/tattoo_screen.cpp @@ -0,0 +1,38 @@ +/* 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 "sherlock/tattoo/tattoo_screen.h" +#include "sherlock/tattoo/tattoo.h" + +namespace Sherlock { + +namespace Tattoo { + +TattooScreen::TattooScreen(SherlockEngine *vm) : Screen(vm) { + _backBuffer1.create(640, 480); + _backBuffer2.create(640, 480); + activateBackBuffer1(); +} + +} // End of namespace Tattoo + +} // End of namespace Sherlock diff --git a/backends/graphics/opengl/extensions.h b/engines/sherlock/tattoo/tattoo_screen.h index 87452429e2..b55e9bb0dd 100644 --- a/backends/graphics/opengl/extensions.h +++ b/engines/sherlock/tattoo/tattoo_screen.h @@ -20,22 +20,25 @@ * */ -#ifndef BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H -#define BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H +#ifndef SHERLOCK_TATTOO_SCREEN_H +#define SHERLOCK_TATTOO_SCREEN_H -namespace OpenGL { +#include "sherlock/screen.h" -/** - * Checks for availability of extensions we want to use and initializes them - * when available. - */ -void initializeGLExtensions(); +namespace Sherlock { -/** - * Whether non power of two textures are supported - */ -extern bool g_extNPOTSupported; +class SherlockEngine; + +namespace Tattoo { + +class TattooScreen : public Screen { +public: + TattooScreen(SherlockEngine *vm); + virtual ~TattooScreen() {} +}; + +} // End of namespace Tattoo -} // End of namespace OpenGL +} // End of namespace Sherlock #endif diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp index ee028f89c2..677a662535 100644 --- a/engines/sherlock/tattoo/tattoo_user_interface.cpp +++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp @@ -72,7 +72,7 @@ TattooUserInterface::~TattooUserInterface() { void TattooUserInterface::initScrollVars() { Screen &screen = *_vm->_screen; - _scrollSize = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH; + _scrollSize = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH; _targetScroll = Common::Point(0, 0); screen._currentScroll = Common::Point(0, 0); } @@ -233,7 +233,7 @@ void TattooUserInterface::doJournal() { Common::copy(&lookupTable1[0], &lookupTable1[PALETTE_COUNT], &_lookupTable1[0]); // Restore the scene - screen._backBuffer1.blitFrom(screen._backBuffer2); + screen._backBuffer1.SHblitFrom(screen._backBuffer2); scene.updateBackground(); screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); } @@ -727,7 +727,7 @@ void TattooUserInterface::doBgAnimEraseBackground() { if (_mask != nullptr) { // Since a mask is active, restore the screen from the secondary back buffer prior to applying the mask - screen._backBuffer1.blitFrom(screen._backBuffer2, screen._currentScroll, Common::Rect(screen._currentScroll.x, 0, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, screen._currentScroll, Common::Rect(screen._currentScroll.x, 0, screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); switch (scene._currentScene) { @@ -757,7 +757,7 @@ void TattooUserInterface::doBgAnimEraseBackground() { case 53: if (++_maskCounter == 2) { _maskCounter = 0; - if (++_maskOffset.x == screen._backBuffer1.w()) + if (++_maskOffset.x == screen._backBuffer1.width()) _maskOffset.x = 0; } break; @@ -779,7 +779,7 @@ void TattooUserInterface::doBgAnimEraseBackground() { if ((obj._type == ACTIVE_BG_SHAPE && (obj._maxFrames > 1 || obj._delta.x != 0 || obj._delta.y != 0)) || obj._type == HIDE_SHAPE || obj._type == REMOVE) - screen._backBuffer1.blitFrom(screen._backBuffer2, obj._oldPosition, + screen._backBuffer1.SHblitFrom(screen._backBuffer2, obj._oldPosition, Common::Rect(obj._oldPosition.x, obj._oldPosition.y, obj._oldPosition.x + obj._oldSize.x, obj._oldPosition.y + obj._oldSize.y)); } @@ -793,7 +793,7 @@ void TattooUserInterface::doBgAnimEraseBackground() { Object &obj = scene._bgShapes[idx]; if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) { - screen._backBuffer1.blitFrom(screen._backBuffer2, obj._position, obj.getNoShapeBounds()); + screen._backBuffer1.SHblitFrom(screen._backBuffer2, obj._position, obj.getNoShapeBounds()); obj._oldPosition = obj._position; obj._oldSize = obj._noShapeSize; @@ -870,7 +870,7 @@ void TattooUserInterface::maskArea(Common::SeekableReadStream &mask, const Commo int pixel, len, xp, yp; for (yp = 0; yp < ySize; ++yp) { - byte *ptr = bb1.getBasePtr(pt.x, pt.y + yp); + byte *ptr = (byte *)bb1.getBasePtr(pt.x, pt.y + yp); for (xp = 0; xp < xSize;) { // The mask data consists of pairs of pixel/lengths, where all non-zero pixels means that the @@ -893,7 +893,7 @@ void TattooUserInterface::makeBGArea(const Common::Rect &r) { Screen &screen = *_vm->_screen; for (int yp = r.top; yp < r.bottom; ++yp) { - byte *ptr = screen._backBuffer1.getBasePtr(r.left, yp); + byte *ptr = (byte *)screen._backBuffer1.getBasePtr(r.left, yp); for (int xp = r.left; xp < r.right; ++xp, ++ptr) *ptr = _lookupTable[*ptr]; diff --git a/engines/sherlock/tattoo/widget_base.cpp b/engines/sherlock/tattoo/widget_base.cpp index 8f0649130a..a35f4e5d74 100644 --- a/engines/sherlock/tattoo/widget_base.cpp +++ b/engines/sherlock/tattoo/widget_base.cpp @@ -88,7 +88,7 @@ void WidgetBase::erase() { if (_oldBounds.width() > 0) { // Restore the affected area from the secondary back buffer into the first one, and then copy to screen - screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds); + screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds); screen.slamRect(_oldBounds); // Reset the old bounds so it won't be erased again @@ -111,7 +111,7 @@ void WidgetBase::draw() { drawBackground(); // Draw the widget onto the back buffer and then slam it to the screen - screen._backBuffer1.transBlitFrom(_surface, Common::Point(_bounds.left, _bounds.top)); + screen._backBuffer1.SHtransBlitFrom(_surface, Common::Point(_bounds.left, _bounds.top)); screen.slamRect(_bounds); // Store a copy of the drawn area for later erasing @@ -183,8 +183,8 @@ void WidgetBase::restrictToScreen() { _bounds.moveTo(_bounds.left, 0); if (_bounds.right > (screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH)) _bounds.moveTo(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH - _bounds.width(), _bounds.top); - if (_bounds.bottom > screen._backBuffer1.h()) - _bounds.moveTo(_bounds.left, screen._backBuffer1.h() - _bounds.height()); + if (_bounds.bottom > screen._backBuffer1.height()) + _bounds.moveTo(_bounds.left, screen._backBuffer1.height() - _bounds.height()); } void WidgetBase::makeInfoArea(Surface &s) { @@ -192,30 +192,30 @@ void WidgetBase::makeInfoArea(Surface &s) { ImageFile &images = *ui._interfaceImages; // Draw the four corners of the Info Box - s.transBlitFrom(images[0], Common::Point(0, 0)); - s.transBlitFrom(images[1], Common::Point(s.w() - images[1]._width, 0)); - s.transBlitFrom(images[2], Common::Point(0, s.h() - images[2]._height)); - s.transBlitFrom(images[3], Common::Point(s.w() - images[3]._width, s.h())); + s.SHtransBlitFrom(images[0], Common::Point(0, 0)); + s.SHtransBlitFrom(images[1], Common::Point(s.width() - images[1]._width, 0)); + s.SHtransBlitFrom(images[2], Common::Point(0, s.height() - images[2]._height)); + s.SHtransBlitFrom(images[3], Common::Point(s.width() - images[3]._width, s.height())); // Draw the top of the Info Box - s.hLine(images[0]._width, 0, s.w() - images[1]._width, INFO_TOP); - s.hLine(images[0]._width, 1, s.w() - images[1]._width, INFO_MIDDLE); - s.hLine(images[0]._width, 2, s.w() - images[1]._width, INFO_BOTTOM); + s.hLine(images[0]._width, 0, s.width() - images[1]._width, INFO_TOP); + s.hLine(images[0]._width, 1, s.width() - images[1]._width, INFO_MIDDLE); + s.hLine(images[0]._width, 2, s.width() - images[1]._width, INFO_BOTTOM); // Draw the bottom of the Info Box - s.hLine(images[0]._width, s.h()- 3, s.w() - images[1]._width, INFO_TOP); - s.hLine(images[0]._width, s.h()- 2, s.w() - images[1]._width, INFO_MIDDLE); - s.hLine(images[0]._width, s.h()- 1, s.w() - images[1]._width, INFO_BOTTOM); + s.hLine(images[0]._width, s.height()- 3, s.width() - images[1]._width, INFO_TOP); + s.hLine(images[0]._width, s.height()- 2, s.width() - images[1]._width, INFO_MIDDLE); + s.hLine(images[0]._width, s.height()- 1, s.width() - images[1]._width, INFO_BOTTOM); // Draw the left Side of the Info Box - s.vLine(0, images[0]._height, s.h()- images[2]._height, INFO_TOP); - s.vLine(1, images[0]._height, s.h()- images[2]._height, INFO_MIDDLE); - s.vLine(2, images[0]._height, s.h()- images[2]._height, INFO_BOTTOM); + s.vLine(0, images[0]._height, s.height()- images[2]._height, INFO_TOP); + s.vLine(1, images[0]._height, s.height()- images[2]._height, INFO_MIDDLE); + s.vLine(2, images[0]._height, s.height()- images[2]._height, INFO_BOTTOM); // Draw the right Side of the Info Box - s.vLine(s.w() - 3, images[0]._height, s.h()- images[2]._height, INFO_TOP); - s.vLine(s.w() - 2, images[0]._height, s.h()- images[2]._height, INFO_MIDDLE); - s.vLine(s.w() - 1, images[0]._height, s.h()- images[2]._height, INFO_BOTTOM); + s.vLine(s.width() - 3, images[0]._height, s.height()- images[2]._height, INFO_TOP); + s.vLine(s.width() - 2, images[0]._height, s.height()- images[2]._height, INFO_MIDDLE); + s.vLine(s.width() - 1, images[0]._height, s.height()- images[2]._height, INFO_BOTTOM); } void WidgetBase::makeInfoArea() { diff --git a/engines/sherlock/tattoo/widget_credits.cpp b/engines/sherlock/tattoo/widget_credits.cpp index 2b37dd304b..1c878daaf6 100644 --- a/engines/sherlock/tattoo/widget_credits.cpp +++ b/engines/sherlock/tattoo/widget_credits.cpp @@ -37,7 +37,7 @@ void WidgetCredits::initCredits() { Screen &screen = *_vm->_screen; Common::SeekableReadStream *stream = res.load("credits.txt"); int spacing = screen.fontHeight() * 2; - int yp = screen.h(); + int yp = screen.height(); _creditsActive = true; _creditLines.clear(); @@ -60,7 +60,7 @@ void WidgetCredits::initCredits() { } else { int width = screen.stringWidth(line) + 2; - _creditLines.push_back(CreditLine(line, Common::Point((screen.w() - width) / 2 + 1, yp), width)); + _creditLines.push_back(CreditLine(line, Common::Point((screen.width() - width) / 2 + 1, yp), width)); yp += spacing; } } @@ -120,10 +120,10 @@ void WidgetCredits::close() { void WidgetCredits::drawCredits() { Screen &screen = *_vm->_screen; - Common::Rect screenRect(0, 0, screen.w(), screen.h()); + Common::Rect screenRect(0, 0, screen.width(), screen.height()); Surface &bb1 = screen._backBuffer1; - for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < screen.h(); ++idx) { + for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < screen.height(); ++idx) { if (screenRect.contains(_creditLines[idx]._position)) { if (!_creditLines[idx]._line2.empty()) { int x1 = _creditLines[idx]._position.x; @@ -176,7 +176,7 @@ void WidgetCredits::drawCredits() { void WidgetCredits::blitCredits() { Screen &screen = *_vm->_screen; - Common::Rect screenRect(0, -_creditSpeed, screen.w(), screen.h() + _creditSpeed); + Common::Rect screenRect(0, -_creditSpeed, screen.width(), screen.height() + _creditSpeed); for (uint idx = 0; idx < _creditLines.size(); ++idx) { if (screenRect.contains(_creditLines[idx]._position)) { @@ -190,7 +190,7 @@ void WidgetCredits::blitCredits() { void WidgetCredits::eraseCredits() { Screen &screen = *_vm->_screen; - Common::Rect screenRect(0, -_creditSpeed, screen.w(), screen.h() + _creditSpeed); + Common::Rect screenRect(0, -_creditSpeed, screen.width(), screen.height() + _creditSpeed); for (uint idx = 0; idx < _creditLines.size(); ++idx) { if (screenRect.contains(_creditLines[idx]._position)) { diff --git a/engines/sherlock/tattoo/widget_files.cpp b/engines/sherlock/tattoo/widget_files.cpp index ff8cb83dca..7666e81480 100644 --- a/engines/sherlock/tattoo/widget_files.cpp +++ b/engines/sherlock/tattoo/widget_files.cpp @@ -107,36 +107,36 @@ void WidgetFiles::render(FilesRenderMode mode) { byte color; if (mode == RENDER_ALL) { - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); makeInfoArea(); switch (_fileMode) { case SAVEMODE_LOAD: _surface.writeString(FIXED(LoadGame), - Common::Point((_surface.w() - _surface.stringWidth(FIXED(LoadGame))) / 2, 5), INFO_TOP); + Common::Point((_surface.width() - _surface.stringWidth(FIXED(LoadGame))) / 2, 5), INFO_TOP); break; case SAVEMODE_SAVE: _surface.writeString(FIXED(SaveGame), - Common::Point((_surface.w() - _surface.stringWidth(FIXED(SaveGame))) / 2, 5), INFO_TOP); + Common::Point((_surface.width() - _surface.stringWidth(FIXED(SaveGame))) / 2, 5), INFO_TOP); break; default: break; } - _surface.hLine(3, _surface.fontHeight() + 7, _surface.w() - 4, INFO_TOP); - _surface.hLine(3, _surface.fontHeight() + 8, _surface.w() - 4, INFO_MIDDLE); - _surface.hLine(3, _surface.fontHeight() + 9, _surface.w() - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 6)); - _surface.transBlitFrom(images[5], Common::Point(_surface.w() - images[5]._width, _surface.fontHeight() + 6)); + _surface.hLine(3, _surface.fontHeight() + 7, _surface.width() - 4, INFO_TOP); + _surface.hLine(3, _surface.fontHeight() + 8, _surface.width() - 4, INFO_MIDDLE); + _surface.hLine(3, _surface.fontHeight() + 9, _surface.width() - 4, INFO_BOTTOM); + _surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 6)); + _surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, _surface.fontHeight() + 6)); - int xp = _surface.w() - BUTTON_SIZE - 6; + int xp = _surface.width() - BUTTON_SIZE - 6; _surface.vLine(xp, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_TOP); _surface.vLine(xp + 1, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_MIDDLE); _surface.vLine(xp + 2, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[6], Common::Point(xp - 1, _surface.fontHeight() + 8)); - _surface.transBlitFrom(images[7], Common::Point(xp - 1, _bounds.height() - 4)); + _surface.SHtransBlitFrom(images[6], Common::Point(xp - 1, _surface.fontHeight() + 8)); + _surface.SHtransBlitFrom(images[7], Common::Point(xp - 1, _bounds.height() - 4)); } int xp = _surface.stringWidth("00.") + _surface.widestChar() + 5; @@ -149,7 +149,7 @@ void WidgetFiles::render(FilesRenderMode mode) { color = INFO_TOP; if (mode == RENDER_NAMES_AND_SCROLLBAR) - _surface.fillRect(Common::Rect(4, yp, _surface.w() - BUTTON_SIZE - 9, yp + _surface.fontHeight()), TRANSPARENCY); + _surface.fillRect(Common::Rect(4, yp, _surface.width() - BUTTON_SIZE - 9, yp + _surface.fontHeight()), TRANSPARENCY); Common::String numStr = Common::String::format("%d.", idx + 1); _surface.writeString(numStr, Common::Point(_surface.widestChar(), yp), color); @@ -324,7 +324,7 @@ bool WidgetFiles::getFilename() { filename.setChar(' ', index); } - _surface.fillRect(Common::Rect(pt.x, pt.y, _surface.w() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY); + _surface.fillRect(Common::Rect(pt.x, pt.y, _surface.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY); _surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED); } else if ((keyState.keycode == Common::KEYCODE_LEFT && index > 0) @@ -387,7 +387,7 @@ bool WidgetFiles::getFilename() { } if ((keyState.ascii >= ' ') && (keyState.ascii <= 'z') && (index < 50)) { - if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w() - BUTTON_SIZE - 20) { + if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w - BUTTON_SIZE - 20) { if (insert) filename.insertChar(keyState.ascii, index); else diff --git a/engines/sherlock/tattoo/widget_foolscap.cpp b/engines/sherlock/tattoo/widget_foolscap.cpp index c8df71e873..8225946838 100644 --- a/engines/sherlock/tattoo/widget_foolscap.cpp +++ b/engines/sherlock/tattoo/widget_foolscap.cpp @@ -103,7 +103,7 @@ void WidgetFoolscap::show() { // Set up the window background _surface.create(_bounds.width(), _bounds.height()); - _surface.blitFrom(paperFrame, Common::Point(0, 0)); + _surface.SHblitFrom(paperFrame, Common::Point(0, 0)); // If they have already solved the puzzle, put the answer on the graphic if (_vm->readFlags(299)) { @@ -265,7 +265,7 @@ void WidgetFoolscap::handleKeyboardEvents() { void WidgetFoolscap::restoreChar() { Screen &screen = *_vm->_screen; ImageFrame &bgFrame = (*_images)[0]; - _surface.blitFrom(bgFrame, _cursorPos, Common::Rect(_cursorPos.x, _cursorPos.y, + _surface.SHblitFrom(bgFrame, _cursorPos, Common::Rect(_cursorPos.x, _cursorPos.y, _cursorPos.x + screen.widestChar(), _cursorPos.y + screen.fontHeight())); } diff --git a/engines/sherlock/tattoo/widget_inventory.cpp b/engines/sherlock/tattoo/widget_inventory.cpp index b49e30b30d..34331f0eae 100644 --- a/engines/sherlock/tattoo/widget_inventory.cpp +++ b/engines/sherlock/tattoo/widget_inventory.cpp @@ -94,7 +94,7 @@ void WidgetInventoryTooltip::setText(const Common::String &str) { // Allocate a fresh surface for the new string _bounds = Common::Rect(width, height); _surface.create(width, height); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); if (line2.empty()) { _surface.writeFancyString(str, Common::Point(0, 0), BLACK, INFO_TOP); @@ -338,7 +338,7 @@ void WidgetInventoryVerbs::load() { // Create the surface _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); makeInfoArea(); // Draw the Verb commands and the lines separating them @@ -352,8 +352,8 @@ void WidgetInventoryVerbs::load() { _surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.right - 4, INFO_MIDDLE); _surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.right - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1))); - _surface.transBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, + _surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1))); + _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, (_surface.fontHeight() + 7) * (idx + 1) - 1)); } } @@ -515,7 +515,7 @@ void WidgetInventory::load(int mode) { // Redraw the inventory menu on the widget surface _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); // Draw the window background and then the inventory on top of it makeInfoArea(_surface); @@ -531,7 +531,7 @@ void WidgetInventory::drawBars() { _surface.hLine(3, INVENTORY_YSIZE + 3, _bounds.width() - 4, INFO_TOP); _surface.hLine(3, INVENTORY_YSIZE + 4, _bounds.width() - 4, INFO_MIDDLE); _surface.hLine(3, INVENTORY_YSIZE + 5, _bounds.width() - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[4], Common::Point(0, INVENTORY_YSIZE + 2)); + _surface.SHtransBlitFrom(images[4], Common::Point(0, INVENTORY_YSIZE + 2)); for (int idx = 1; idx <= NUM_INVENTORY_SHOWN / 2; ++idx) { x = idx * (INVENTORY_XSIZE + 3); @@ -540,10 +540,10 @@ void WidgetInventory::drawBars() { _surface.vLine(x + 1, 3, _bounds.height() - 4, INFO_MIDDLE); _surface.vLine(x + 2, 3, _bounds.height() - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[6], Common::Point(x - 1, 1)); - _surface.transBlitFrom(images[7], Common::Point(x - 1, _bounds.height() - 4)); - _surface.transBlitFrom(images[6], Common::Point(x - 1, INVENTORY_YSIZE + 5)); - _surface.transBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2)); + _surface.SHtransBlitFrom(images[6], Common::Point(x - 1, 1)); + _surface.SHtransBlitFrom(images[7], Common::Point(x - 1, _bounds.height() - 4)); + _surface.SHtransBlitFrom(images[6], Common::Point(x - 1, INVENTORY_YSIZE + 5)); + _surface.SHtransBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2)); } _surface.hLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM); @@ -566,7 +566,7 @@ void WidgetInventory::drawInventory() { // Draw the item if (itemId < inv._holdings) { ImageFrame &img = (*inv._invShapes[idx])[0]; - _surface.transBlitFrom(img, Common::Point(pt.x + (INVENTORY_XSIZE - img._width) / 2, + _surface.SHtransBlitFrom(img, Common::Point(pt.x + (INVENTORY_XSIZE - img._width) / 2, pt.y + (INVENTORY_YSIZE - img._height) / 2)); } } diff --git a/engines/sherlock/tattoo/widget_options.cpp b/engines/sherlock/tattoo/widget_options.cpp index 92bd10bbf6..81f50f3bc5 100644 --- a/engines/sherlock/tattoo/widget_options.cpp +++ b/engines/sherlock/tattoo/widget_options.cpp @@ -257,17 +257,17 @@ void WidgetOptions::render(OptionRenderMode mode) { // Setup the dialog _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); makeInfoArea(); // Draw the lines separating options in the dialog int yp = _surface.fontHeight() + 7; for (int idx = 0; idx < 7; ++idx) { - _surface.transBlitFrom(images[4], Common::Point(0, yp - 1)); - _surface.transBlitFrom(images[5], Common::Point(_surface.w() - images[5]._width, yp - 1)); - _surface.hLine(3, yp, _surface.w() - 4, INFO_TOP); - _surface.hLine(3, yp + 1, _surface.w() - 4, INFO_MIDDLE); - _surface.hLine(3, yp + 2, _surface.w() - 4, INFO_BOTTOM); + _surface.SHtransBlitFrom(images[4], Common::Point(0, yp - 1)); + _surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, yp - 1)); + _surface.hLine(3, yp, _surface.width() - 4, INFO_TOP); + _surface.hLine(3, yp + 1, _surface.width() - 4, INFO_MIDDLE); + _surface.hLine(3, yp + 2, _surface.width() - 4, INFO_BOTTOM); yp += _surface.fontHeight() + 7; if (idx == 1) @@ -281,7 +281,7 @@ void WidgetOptions::render(OptionRenderMode mode) { for (int idx = 0, yp = 5; idx < 11; ++idx, yp += _surface.fontHeight() + 7) { if (mode == OP_ALL || idx == _selector || idx == _oldSelector) { if (mode == OP_NAMES) - _surface.fillRect(Common::Rect(4, yp, _surface.w() - 5, yp + _surface.fontHeight() - 1), TRANSPARENCY); + _surface.fillRect(Common::Rect(4, yp, _surface.width() - 5, yp + _surface.fontHeight() - 1), TRANSPARENCY); byte color = (idx == _selector) ? COMMAND_HIGHLIGHTED : INFO_TOP; Common::String str; @@ -302,11 +302,11 @@ void WidgetOptions::render(OptionRenderMode mode) { int num = (_surface.fontHeight() + 4) & 0xfe; int sliderY = yp + num / 2 - 8; - _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.w() - 5, + _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.width() - 5, sliderY - (num - 6) / 2 + num - 1), TRANSPARENCY); _surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2, - _surface.w() - _surface.widestChar() - 1, sliderY + 3), INFO_MIDDLE); - drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.w() - _surface.widestChar(), sliderY + 6)); + _surface.width() - _surface.widestChar() - 1, sliderY + 3), INFO_MIDDLE); + drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.width() - _surface.widestChar(), sliderY + 6)); _surface.fillRect(Common::Rect(_midiSliderX - 1, sliderY - (num - 6) / 2 + 2, _midiSliderX + 1, sliderY - (num - 6) / 2 + num - 3), INFO_MIDDLE); @@ -315,7 +315,7 @@ void WidgetOptions::render(OptionRenderMode mode) { if (_midiSliderX - 4 > _surface.widestChar()) _surface.fillRect(Common::Rect(_midiSliderX - 4, sliderY, _midiSliderX - 4, sliderY + 4), INFO_BOTTOM); - if (_midiSliderX + 4 < _surface.w() - _surface.widestChar()) + if (_midiSliderX + 4 < _surface.width() - _surface.widestChar()) _surface.fillRect(Common::Rect(_midiSliderX + 4, sliderY, _midiSliderX + 4, sliderY + 4), INFO_BOTTOM); break; } @@ -332,18 +332,18 @@ void WidgetOptions::render(OptionRenderMode mode) { int num = (_surface.fontHeight() + 4) & 0xfe; int sliderY = yp + num / 2 - 8; - _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.w() - 5, + _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.width() - 5, sliderY - (num - 6) / 2 + num - 1), TRANSPARENCY); - _surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2, _surface.w() - _surface.widestChar() - 1, + _surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2, _surface.width() - _surface.widestChar() - 1, sliderY + 3), INFO_MIDDLE); - drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.w() - _surface.widestChar(), sliderY + 6)); + drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.width() - _surface.widestChar(), sliderY + 6)); _surface.fillRect(Common::Rect(_digiSliderX - 1, sliderY - (num - 6) / 2 + 2, _digiSliderX + 1, sliderY - (num - 6) / 2 + num - 3), INFO_MIDDLE); drawDialogRect(Common::Rect(_digiSliderX - 3, sliderY - (num - 6) / 2, _digiSliderX + 4, sliderY - (num - 6) / 2 + num)); if (_digiSliderX - 4 > _surface.widestChar()) _surface.fillRect(Common::Rect(_digiSliderX - 4, sliderY, _digiSliderX - 4, sliderY + 4), INFO_BOTTOM); - if (_digiSliderX + 4 < _surface.w() - _surface.widestChar()) + if (_digiSliderX + 4 < _surface.width() - _surface.widestChar()) _surface.fillRect(Common::Rect(_digiSliderX + 4, sliderY, _digiSliderX + 4, sliderY + 4), INFO_BOTTOM); break; } @@ -375,7 +375,7 @@ void WidgetOptions::render(OptionRenderMode mode) { // Unless we're doing one of the Slider Controls, print the text for the line if (idx != 3 && idx != 6) { - int xp = (_surface.w() - _surface.stringWidth(str)) / 2; + int xp = (_surface.width() - _surface.stringWidth(str)) / 2; _surface.writeString(str, Common::Point(xp, yp), color); } } diff --git a/engines/sherlock/tattoo/widget_password.cpp b/engines/sherlock/tattoo/widget_password.cpp index 57a5e02653..2a2921026d 100644 --- a/engines/sherlock/tattoo/widget_password.cpp +++ b/engines/sherlock/tattoo/widget_password.cpp @@ -47,7 +47,7 @@ void WidgetPassword::show() { // Create the surface _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); makeInfoArea(); // Draw the header area @@ -55,8 +55,8 @@ void WidgetPassword::show() { _surface.hLine(3, _surface.fontHeight() + 7, _bounds.width() - 4, INFO_TOP); _surface.hLine(3, _surface.fontHeight() + 8, _bounds.width() - 4, INFO_MIDDLE); _surface.hLine(3, _surface.fontHeight() + 9, _bounds.width() - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 7 - 1)); - _surface.transBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, _surface.fontHeight() + 7 - 1)); + _surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 7 - 1)); + _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, _surface.fontHeight() + 7 - 1)); // Set the password entry data _cursorPos = Common::Point(_surface.widestChar(), _surface.fontHeight() + 12); diff --git a/engines/sherlock/tattoo/widget_quit.cpp b/engines/sherlock/tattoo/widget_quit.cpp index f853e7f47f..ea8f2e080c 100644 --- a/engines/sherlock/tattoo/widget_quit.cpp +++ b/engines/sherlock/tattoo/widget_quit.cpp @@ -48,22 +48,22 @@ void WidgetQuit::show() { // Create the surface _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); makeInfoArea(); // Draw the message text - _surface.writeString(FIXED(AreYouSureYou), Common::Point((_surface.w() - _surface.stringWidth(FIXED(AreYouSureYou))) / 2, 5), INFO_TOP); - _surface.writeString(FIXED(WishToQuit), Common::Point((_surface.w() - _surface.stringWidth(FIXED(WishToQuit))) / 2, + _surface.writeString(FIXED(AreYouSureYou), Common::Point((_surface.width() - _surface.stringWidth(FIXED(AreYouSureYou))) / 2, 5), INFO_TOP); + _surface.writeString(FIXED(WishToQuit), Common::Point((_surface.width() - _surface.stringWidth(FIXED(WishToQuit))) / 2, _surface.fontHeight() + 9), INFO_TOP); // Draw the horizontal bars seperating the commands and the message int yp = (_surface.fontHeight() + 4) * 2 + 3; for (int idx = 0; idx < 2; ++idx) { - _surface.transBlitFrom(images[4], Common::Point(0, yp - 1)); - _surface.transBlitFrom(images[5], Common::Point(_surface.w() - images[5]._width, yp - 1)); - _surface.hLine(3, yp, _surface.w() - 4, INFO_TOP); - _surface.hLine(3, yp + 1, _surface.w() - 4, INFO_MIDDLE); - _surface.hLine(3, yp + 2, _surface.w() - 4, INFO_BOTTOM); + _surface.SHtransBlitFrom(images[4], Common::Point(0, yp - 1)); + _surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, yp - 1)); + _surface.hLine(3, yp, _surface.width() - 4, INFO_TOP); + _surface.hLine(3, yp + 1, _surface.width() - 4, INFO_MIDDLE); + _surface.hLine(3, yp + 2, _surface.width() - 4, INFO_BOTTOM); const char *btn = (idx == 0) ? YES : NO; _surface.writeString(btn, Common::Point((_bounds.width() - _surface.stringWidth(btn)) / 2, yp + 5), INFO_TOP); @@ -129,11 +129,11 @@ void WidgetQuit::handleEvents() { if (_select != _oldSelect) { byte color = (_select == 1) ? COMMAND_HIGHLIGHTED : INFO_TOP; int yp = (_surface.fontHeight() + 4) * 2 + 8; - _surface.writeString(FIXED(Yes), Common::Point((_surface.w() - _surface.stringWidth(FIXED(Yes))) / 2, yp), color); + _surface.writeString(FIXED(Yes), Common::Point((_surface.width() - _surface.stringWidth(FIXED(Yes))) / 2, yp), color); color = (_select == 0) ? COMMAND_HIGHLIGHTED : INFO_TOP; yp += (_surface.fontHeight() + 7); - _surface.writeString(FIXED(No), Common::Point((_surface.w() - _surface.stringWidth(FIXED(No))) / 2, yp), color); + _surface.writeString(FIXED(No), Common::Point((_surface.width() - _surface.stringWidth(FIXED(No))) / 2, yp), color); } _oldSelect = _select; diff --git a/engines/sherlock/tattoo/widget_talk.cpp b/engines/sherlock/tattoo/widget_talk.cpp index 6e7bde292f..b673f32d31 100644 --- a/engines/sherlock/tattoo/widget_talk.cpp +++ b/engines/sherlock/tattoo/widget_talk.cpp @@ -100,7 +100,7 @@ void WidgetTalk::load() { // Set up the surface _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); // Form the background for the new window makeInfoArea(); @@ -389,7 +389,7 @@ void WidgetTalk::render(Highlight highlightMode) { if (highlightMode == HL_NO_HIGHLIGHTING || _statementLines[idx]._num == _selector || _statementLines[idx]._num == _oldSelector) { // Erase the line contents - _surface.fillRect(Common::Rect(3, yp, _surface.w() - BUTTON_SIZE - 3, yp + _surface.fontHeight()), TRANSPARENCY); + _surface.fillRect(Common::Rect(3, yp, _surface.width() - BUTTON_SIZE - 3, yp + _surface.fontHeight()), TRANSPARENCY); // Different coloring based on whether the option has been previously chosen or not byte color = (!talk._talkHistory[talk._converseNum][_statementLines[idx]._num]) ? diff --git a/engines/sherlock/tattoo/widget_text.cpp b/engines/sherlock/tattoo/widget_text.cpp index d8d229d277..a29cd2700f 100644 --- a/engines/sherlock/tattoo/widget_text.cpp +++ b/engines/sherlock/tattoo/widget_text.cpp @@ -166,7 +166,7 @@ void WidgetText::render(const Common::String &str) { // Allocate a surface for the window _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); // Form the background for the new window makeInfoArea(); @@ -195,7 +195,7 @@ void WidgetMessage::load(const Common::String &str, int time) { // Allocate a surface for the window _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); // Form the background for the new window and write the line of text makeInfoArea(); diff --git a/engines/sherlock/tattoo/widget_tooltip.cpp b/engines/sherlock/tattoo/widget_tooltip.cpp index b29f45f531..1560cb9a80 100644 --- a/engines/sherlock/tattoo/widget_tooltip.cpp +++ b/engines/sherlock/tattoo/widget_tooltip.cpp @@ -47,7 +47,7 @@ void WidgetTooltipBase::draw() { // Draw the widget directly onto the screen. Unlike other widgets, we don't draw to the back buffer, // since nothing should be drawing on top of tooltips, so there's no need to store in the back buffer - screen.transBlitFrom(_surface, Common::Point(_bounds.left - screen._currentScroll.x, + screen.SHtransBlitFrom(_surface, Common::Point(_bounds.left - screen._currentScroll.x, _bounds.top - screen._currentScroll.y)); // Store a copy of the drawn area for later erasing @@ -126,7 +126,7 @@ void WidgetTooltip::setText(const Common::String &str) { // Reallocate the text surface with the new size _surface.create(width, height); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); if (line2.empty()) { // Only a single line diff --git a/engines/sherlock/tattoo/widget_verbs.cpp b/engines/sherlock/tattoo/widget_verbs.cpp index 499afb2e79..5041888ffb 100644 --- a/engines/sherlock/tattoo/widget_verbs.cpp +++ b/engines/sherlock/tattoo/widget_verbs.cpp @@ -127,7 +127,7 @@ void WidgetVerbs::render() { // Create the drawing surface _surface.create(_bounds.width(), _bounds.height()); - _surface.fill(TRANSPARENCY); + _surface.clear(TRANSPARENCY); // Draw basic background makeInfoArea(); @@ -142,8 +142,8 @@ void WidgetVerbs::render() { _surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.width() - 4, INFO_MIDDLE); _surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.width() - 4, INFO_BOTTOM); - _surface.transBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1) - 1)); - _surface.transBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, + _surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1) - 1)); + _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, (_surface.fontHeight() + 7) * (idx + 1) - 1)); } } diff --git a/engines/sword25/POTFILES b/engines/sword25/POTFILES new file mode 100644 index 0000000000..f4b0e6fc27 --- /dev/null +++ b/engines/sword25/POTFILES @@ -0,0 +1 @@ +engines/sword25/detection.cpp diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp index ca6bb406de..4ca565c972 100644 --- a/engines/sword25/detection.cpp +++ b/engines/sword25/detection.cpp @@ -21,6 +21,7 @@ */ #include "base/plugins.h" +#include "common/translation.h" #include "engines/advancedDetector.h" #include "sword25/sword25.h" @@ -41,6 +42,13 @@ static const char *directoryGlobs[] = { 0 }; +static const ExtraGuiOption sword25ExtraGuiOption = { + _s("Use English speech"), + _s("Use English speech instead of German for every language other than German"), + "english_speech", + false +}; + class Sword25MetaEngine : public AdvancedMetaEngine { public: Sword25MetaEngine() : AdvancedMetaEngine(Sword25::gameDescriptions, sizeof(ADGameDescription), sword25Game) { @@ -58,6 +66,7 @@ public: virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual bool hasFeature(MetaEngineFeature f) const; + virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); } virtual SaveStateList listSaves(const char *target) const; }; @@ -74,6 +83,12 @@ bool Sword25MetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSupportsListSaves); } +const ExtraGuiOptions Sword25MetaEngine::getExtraGuiOptions(const Common::String &target) const { + ExtraGuiOptions options; + options.push_back(sword25ExtraGuiOption); + return options; +} + SaveStateList Sword25MetaEngine::listSaves(const char *target) const { Common::String pattern = target; pattern = pattern + ".???"; diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp index eb0f0390dc..62a897a332 100644 --- a/engines/sword25/fmv/movieplayer.cpp +++ b/engines/sword25/fmv/movieplayer.cpp @@ -58,6 +58,8 @@ MoviePlayer::~MoviePlayer() { } bool MoviePlayer::loadMovie(const Common::String &filename, uint z) { + if (isMovieLoaded()) + unloadMovie(); // Get the file and load it into the decoder Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename); _decoder.loadStream(in); diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp index 2db4f2da74..457dda6268 100644 --- a/engines/sword25/package/packagemanager.cpp +++ b/engines/sword25/package/packagemanager.cpp @@ -56,7 +56,8 @@ static Common::String normalizePath(const Common::String &path, const Common::St PackageManager::PackageManager(Kernel *pKernel) : Service(pKernel), _currentDirectory(PATH_SEPARATOR), - _rootFolder(ConfMan.get("path")) { + _rootFolder(ConfMan.get("path")), + _useEnglishSpeech(ConfMan.getBool("english_speech")) { if (!registerScriptBindings()) error("Script bindings could not be registered."); else @@ -71,14 +72,34 @@ PackageManager::~PackageManager() { } +Common::String PackageManager::ensureSpeechLang(const Common::String &fileName) { + if (!_useEnglishSpeech || fileName.size() < 9 || !fileName.hasPrefix("/speech/")) + return fileName; + + // Always keep German speech as a fallback in case the English speech pack is not present. + // However this means we cannot play with German text and English voice. + if (fileName.hasPrefix("/speech/de")) + return fileName; + + Common::String newFileName = "/speech/en"; + int fileIdx = 9; + while (fileIdx < fileName.size() && fileName[fileIdx] != '/') + ++fileIdx; + if (fileIdx < fileName.size()) + newFileName += fileName.c_str() + fileIdx; + + return newFileName; +} + /** * Scans through the archive list for a specified file */ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &fileName) { + Common::String fileName2 = ensureSpeechLang(fileName); // Loop through checking each archive Common::List<ArchiveEntry *>::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) { - if (!fileName.hasPrefix((*i)->_mountPath)) { + if (!fileName2.hasPrefix((*i)->_mountPath)) { // The mount path is in different subtree. Skipping continue; } @@ -87,7 +108,7 @@ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String & Common::Archive *archiveFolder = (*i)->archive; // Construct relative path - Common::String resPath(&fileName.c_str()[(*i)->_mountPath.size()]); + Common::String resPath(&fileName2.c_str()[(*i)->_mountPath.size()]); if (archiveFolder->hasFile(resPath)) { return archiveFolder->getMember(resPath); @@ -203,23 +224,29 @@ bool PackageManager::changeDirectory(const Common::String &directory) { } Common::String PackageManager::getAbsolutePath(const Common::String &fileName) { - return normalizePath(fileName, _currentDirectory); + return normalizePath(ensureSpeechLang(fileName), _currentDirectory); } bool PackageManager::fileExists(const Common::String &fileName) { // FIXME: The current Zip implementation doesn't support getting a folder entry, which is needed for detecting - // the English voick pack - if (fileName == "/speech/en") { + // the English voice pack + Common::String fileName2 = ensureSpeechLang(fileName); + if (fileName2 == "/speech/en") { // To get around this, change to detecting one of the files in the folder - return getArchiveMember(normalizePath(fileName + "/APO0001.ogg", _currentDirectory)); + bool exists = getArchiveMember(normalizePath(fileName2 + "/APO0001.ogg", _currentDirectory)); + if (!exists && _useEnglishSpeech) { + _useEnglishSpeech = false; + warning("English speech not found"); + } + return exists; } - Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory)); + Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName2, _currentDirectory)); return fileNode; } int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter) { - Common::String normalizedFilter = normalizePath(filter, _currentDirectory); + Common::String normalizedFilter = normalizePath(ensureSpeechLang(filter), _currentDirectory); int num = 0; if (path.size() > 0) diff --git a/engines/sword25/package/packagemanager.h b/engines/sword25/package/packagemanager.h index a1806a4046..5475cb02fc 100644 --- a/engines/sword25/package/packagemanager.h +++ b/engines/sword25/package/packagemanager.h @@ -87,6 +87,9 @@ private: Common::String _currentDirectory; Common::FSNode _rootFolder; Common::List<ArchiveEntry *> _archiveList; + + bool _useEnglishSpeech; + Common::String ensureSpeechLang(const Common::String &fileName); Common::ArchiveMemberPtr getArchiveMember(const Common::String &fileName); diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index d5a8b8e2dc..4dc785754c 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -545,6 +545,10 @@ Common::Error TeenAgentEngine::run() { syncSoundSettings(); + // Initialize CD audio + if (_gameDescription->flags & ADGF_CD) + g_system->getAudioCDManager()->open(); + setMusic(1); _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false); diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp index 2adddca4fd..1b733a08ba 100644 --- a/engines/tinsel/tinsel.cpp +++ b/engines/tinsel/tinsel.cpp @@ -841,9 +841,7 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) if (!scumm_stricmp(g->gameid, gameid)) _gameId = g->id; - int cd_num = ConfMan.getInt("cdrom"); - if (cd_num >= 0) - _system->getAudioCDManager()->openCD(cd_num); + _system->getAudioCDManager()->open(); _mousePos.x = 0; _mousePos.y = 0; @@ -975,7 +973,7 @@ Common::Error TinselEngine::run() { // Check for time to do next game cycle if ((g_system->getMillis() > timerVal + GAME_FRAME_DELAY)) { timerVal = g_system->getMillis(); - _system->getAudioCDManager()->updateCD(); + _system->getAudioCDManager()->update(); NextGameCycle(); } diff --git a/engines/tsage/blue_force/blueforce_dialogs.cpp b/engines/tsage/blue_force/blueforce_dialogs.cpp index 5be27c9ae7..3697ca700e 100644 --- a/engines/tsage/blue_force/blueforce_dialogs.cpp +++ b/engines/tsage/blue_force/blueforce_dialogs.cpp @@ -161,7 +161,7 @@ void RightClickDialog::execute() { } g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } // Deactivate the graphics manager used for the dialog @@ -242,7 +242,7 @@ void AmmoBeltDialog::execute() { } g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } _gfxManager.deactivate(); diff --git a/engines/tsage/blue_force/blueforce_logic.cpp b/engines/tsage/blue_force/blueforce_logic.cpp index e6e71399dc..2c5f9bd738 100644 --- a/engines/tsage/blue_force/blueforce_logic.cpp +++ b/engines/tsage/blue_force/blueforce_logic.cpp @@ -859,7 +859,7 @@ void SceneExt::endStrip() { } void SceneExt::clearScreen() { - BF_GLOBALS._screenSurface.fillRect(BF_GLOBALS._screenSurface.getBounds(), 0); + BF_GLOBALS._screen.clear(); } /*--------------------------------------------------------------------------*/ @@ -1411,7 +1411,7 @@ void SceneMessage::process(Event &event) { void SceneMessage::draw() { - GfxSurface &surface = BF_GLOBALS._screenSurface; + GfxSurface &surface = BF_GLOBALS._screen; // Clear the game area surface.fillRect(Rect(0, 0, SCREEN_WIDTH, UI_INTERFACE_Y), 0); diff --git a/engines/tsage/blue_force/blueforce_scenes6.cpp b/engines/tsage/blue_force/blueforce_scenes6.cpp index 92534d3095..0d6b5c2487 100644 --- a/engines/tsage/blue_force/blueforce_scenes6.cpp +++ b/engines/tsage/blue_force/blueforce_scenes6.cpp @@ -78,7 +78,7 @@ void Scene600::Action1::signal() { pObj->animate(ANIM_MODE_NONE, NULL); } - BF_GLOBALS._screenSurface.fillRect(BF_GLOBALS._screenSurface.getBounds(), 0); + BF_GLOBALS._screen.fillRect(BF_GLOBALS._screen.getBounds(), 0); scene->loadScene(999); setDelay(5); break; @@ -275,7 +275,7 @@ void Scene666::postInit(SceneObjectList *OwnerList) { SceneExt::postInit(); BF_GLOBALS._interfaceY = SCREEN_HEIGHT; loadScene(999); - BF_GLOBALS._screenSurface.fillRect(BF_GLOBALS._screenSurface.getBounds(), 0); + BF_GLOBALS._screen.fillRect(BF_GLOBALS._screen.getBounds(), 0); if (BF_GLOBALS._dayNumber == 0) { BF_GLOBALS._dayNumber = 1; diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp index d1faca5dac..7240c91720 100644 --- a/engines/tsage/converse.cpp +++ b/engines/tsage/converse.cpp @@ -469,7 +469,7 @@ int ConversationChoiceDialog::execute(const Common::StringArray &choiceList) { while (!g_globals->_events.getEvent(event, EVENT_KEYPRESS | EVENT_BUTTON_DOWN | EVENT_MOUSE_MOVE) && !g_vm->shouldQuit()) { g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } if (g_vm->shouldQuit()) break; diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp index d4068c25c9..985d16b031 100644 --- a/engines/tsage/core.cpp +++ b/engines/tsage/core.cpp @@ -1467,7 +1467,7 @@ void ScenePalette::fade(const byte *adjustData, bool fullAdjust, int percent) { // Set the altered palette g_system->getPaletteManager()->setPalette((const byte *)&tempPalette[0], 0, 256); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } PaletteRotation *ScenePalette::addRotation(int start, int end, int rotationMode, int duration, Action *action) { @@ -1524,11 +1524,11 @@ void ScenePalette::changeBackground(const Rect &bounds, FadeMode fadeMode) { if (g_vm->getGameID() != GType_Ringworld && g_vm->getGameID() != GType_Sherlock1) tempRect.setHeight(T2_GLOBALS._interfaceY); - g_globals->_screenSurface.copyFrom(g_globals->_sceneManager._scene->_backSurface, + g_globals->_screen.copyFrom(g_globals->_sceneManager._scene->_backSurface, tempRect, Rect(0, 0, tempRect.width(), tempRect.height()), NULL); if (g_vm->getGameID() == GType_Ringworld2 && !GLOBALS._player._uiEnabled && T2_GLOBALS._interfaceY == UI_INTERFACE_Y) { - g_globals->_screenSurface.fillRect(Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT - 1), 0); + g_globals->_screen.fillRect(Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT - 1), 0); } for (SynchronizedList<PaletteModifier *>::iterator i = tempPalette._listeners.begin(); i != tempPalette._listeners.end(); ++i) @@ -1796,7 +1796,7 @@ void SceneItem::display(int resNum, int lineNum, ...) { // Keep event on-screen until a mouse or keypress while (!g_vm->shouldQuit() && !g_globals->_events.getEvent(event, EVENT_BUTTON_DOWN | EVENT_KEYPRESS)) { - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); if ((g_vm->getGameID() == GType_Ringworld2) && (R2_GLOBALS._speechSubtitles & SPEECH_VOICE)) { @@ -2816,7 +2816,7 @@ void SceneObject::updateScreen() { destRect.translate(-sceneBounds.left, -sceneBounds.top); srcRect.translate(-g_globals->_sceneOffset.x, -g_globals->_sceneOffset.y); - g_globals->_screenSurface.copyFrom(g_globals->_sceneManager._scene->_backSurface, srcRect, destRect); + g_globals->_screen.copyFrom(g_globals->_sceneManager._scene->_backSurface, srcRect, destRect); } } diff --git a/engines/tsage/events.cpp b/engines/tsage/events.cpp index 0491c043a4..1fa17022de 100644 --- a/engines/tsage/events.cpp +++ b/engines/tsage/events.cpp @@ -50,7 +50,7 @@ bool EventsClass::pollEvent() { ++_frameNumber; // Update screen - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } if (!g_system->getEventManager()->pollEvent(_event)) return false; @@ -400,7 +400,7 @@ void EventsClass::delay(int numFrames) { _priorFrameTime = g_system->getMillis(); } - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); _prevDelayFrame = _frameNumber; _priorFrameTime = g_system->getMillis(); } diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp index b880f35007..b95bea3b23 100644 --- a/engines/tsage/globals.cpp +++ b/engines/tsage/globals.cpp @@ -59,7 +59,7 @@ static SavedObject *classFactoryProc(const Common::String &className) { /*--------------------------------------------------------------------------*/ -Globals::Globals() : _dialogCenter(160, 140), _gfxManagerInstance(_screenSurface), +Globals::Globals() : _dialogCenter(160, 140), _gfxManagerInstance(_screen), _randomSource("tsage"), _color1(0), _color2(255), _color3(255) { reset(); _stripNum = 0; @@ -119,7 +119,7 @@ Globals::Globals() : _dialogCenter(160, 140), _gfxManagerInstance(_screenSurface _color3 = 4; _dialogCenter.y = 100; } - _screenSurface.setScreenSurface(); + _gfxManagers.push_back(&_gfxManagerInstance); _sceneObjects = &_sceneObjectsInstance; diff --git a/engines/tsage/globals.h b/engines/tsage/globals.h index 1194fe8b9c..e1ebe261dc 100644 --- a/engines/tsage/globals.h +++ b/engines/tsage/globals.h @@ -30,6 +30,7 @@ #include "tsage/events.h" #include "tsage/sound.h" #include "tsage/saveload.h" +#include "tsage/screen.h" #include "tsage/user_interface.h" namespace TsAGE { @@ -38,7 +39,7 @@ class Globals : public SavedObject { private: static void dispatchSound(ASound *obj); public: - GfxSurface _screenSurface; + Screen _screen; GfxManager _gfxManagerInstance; Common::List<GfxManager *> _gfxManagers; SceneHandler *_sceneHandler; diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp index 156503fb51..58fa5b8094 100644 --- a/engines/tsage/graphics.cpp +++ b/engines/tsage/graphics.cpp @@ -229,123 +229,43 @@ void Rect::synchronize(Serializer &s) { /*--------------------------------------------------------------------------*/ -GfxSurface::GfxSurface() : _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) { +GfxSurface::GfxSurface() : Graphics::ManagedSurface(), _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) { _disableUpdates = false; _lockSurfaceCtr = 0; - _customSurface = NULL; _transColor = -1; - _trackDirtyRects = false; _flags = 0; } -GfxSurface::GfxSurface(const GfxSurface &s) { +GfxSurface::GfxSurface(const GfxSurface &s): Graphics::ManagedSurface() { _lockSurfaceCtr = 0; - _customSurface = NULL; - _trackDirtyRects = false; - *this = s; + + operator=(s); } GfxSurface::~GfxSurface() { - clear(); + // Sanity check.. GfxSurface should always be just referencing _rawSurface, + // and not directly managing it's own surface + assert(disposeAfterUse() == DisposeAfterUse::NO); } -void GfxSurface::clear() { - if (_customSurface) { - _customSurface->free(); - delete _customSurface; - _customSurface = NULL; - } -} - -/** - * Specifies that the surface will encapsulate the ScummVM screen surface - */ -void GfxSurface::setScreenSurface() { - _trackDirtyRects = true; - create(SCREEN_WIDTH, SCREEN_HEIGHT); -} - -/** - * Updates the physical screen with the screen surface buffer - */ -void GfxSurface::updateScreen() { - assert(_trackDirtyRects); - - // Merge any overlapping dirty rects - mergeDirtyRects(); - - // Loop through the dirty rect list to copy the affected areas to the sc - for (Common::List<Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) { - Rect r = *i; - - // Make sure that there is something to update. If not, skip this - // rectangle. An example case is the speedbike closeup at the beginning - // of Ringworld (third screen). - if (r.isEmpty()) - continue; - - const byte *srcP = (const byte *)_customSurface->getBasePtr(r.left, r.top); - g_system->copyRectToScreen(srcP, _customSurface->pitch, r.left, r.top, - r.width(), r.height()); - } - - // Update the physical screen - g_system->updateScreen(); - - // Now that the dirty rects have been copied, clear the dirty rect list - _dirtyRects.clear(); -} +void GfxSurface::create(uint16 width, uint16 height) { + free(); -/** - * Adds a rect to the dirty rect list - */ -void GfxSurface::addDirtyRect(const Rect &r) { - if (_trackDirtyRects) { - // Get the bounds and adjust to allow for sub-screen areas - Rect r2 = r; - r2.translate(_bounds.left, _bounds.top); - - // Add to the dirty rect list - r2.right = MIN(r2.right + 1, SCREEN_WIDTH); - r2.bottom = MIN(r2.bottom + 1, SCREEN_HEIGHT); - - if (r2.isValidRect()) - _dirtyRects.push_back(r2); - } + _rawSurface.create(width, height); + setBounds(Rect(0, 0, width, height)); } - - -/** - * Specifies that the surface should maintain it's own internal surface - */ -void GfxSurface::create(int width, int height) { - assert((width >= 0) && (height >= 0)); - - if (_customSurface) { - _customSurface->free(); - delete _customSurface; - } - _customSurface = new Graphics::Surface(); - _customSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - Common::fill((byte *)_customSurface->getPixels(), (byte *)_customSurface->getBasePtr(0, height), 0); - _bounds = Rect(0, 0, width, height); +void GfxSurface::setBounds(const Rect &bounds) { + _bounds = bounds; + Graphics::ManagedSurface::create(_rawSurface, bounds); } /** * Locks the surface for access, and returns a raw ScummVM surface to manipulate it */ -Graphics::Surface GfxSurface::lockSurface() { +Graphics::ManagedSurface &GfxSurface::lockSurface() { ++_lockSurfaceCtr; - - Graphics::Surface *src = _customSurface; - assert(src); - - // Setup the returned surface either as one pointing to the same pixels as the source, or - // as a subset of the source one based on the currently set bounds - Graphics::Surface result; - result.init(_bounds.width(), _bounds.height(), src->pitch, src->getBasePtr(_bounds.left, _bounds.top), src->format); - return result; + return *this; } /** @@ -367,69 +287,43 @@ void GfxSurface::synchronize(Serializer &s) { if (s.isSaving()) { // Save contents of the surface - if (_customSurface) { - s.syncAsSint16LE(_customSurface->w); - s.syncAsSint16LE(_customSurface->h); - s.syncBytes((byte *)_customSurface->getPixels(), _customSurface->w * _customSurface->h); + if (disposeAfterUse() == DisposeAfterUse::YES) { + s.syncAsSint16LE(this->w); + s.syncAsSint16LE(this->h); + s.syncBytes((byte *)getPixels(), this->w * this->h); } else { int zero = 0; s.syncAsSint16LE(zero); s.syncAsSint16LE(zero); } } else { - int w = 0, h = 0; - s.syncAsSint16LE(w); - s.syncAsSint16LE(h); - - if ((w == 0) || (h == 0)) { - if (_customSurface) - delete _customSurface; - _customSurface = NULL; + int xSize = 0, ySize = 0; + s.syncAsSint16LE(xSize); + s.syncAsSint16LE(ySize); + + if (xSize == 0 || ySize == 0) { + free(); } else { - create(w, h); - s.syncBytes((byte *)_customSurface->getPixels(), w * h); + create(xSize, ySize); + s.syncBytes((byte *)getPixels(), xSize * ySize); } } } -/** - * Fills a specified rectangle on the surface with the specified color - * - * @bounds Area to fill - * @color Color to use - */ -void GfxSurface::fillRect(const Rect &bounds, int color) { - Graphics::Surface surface = lockSurface(); - surface.fillRect(bounds, color); - unlockSurface(); - addDirtyRect(bounds); -} - GfxSurface &GfxSurface::operator=(const GfxSurface &s) { assert(_lockSurfaceCtr == 0); assert(s._lockSurfaceCtr == 0); - if (_customSurface) { - _customSurface->free(); - delete _customSurface; - } - - _customSurface = s._customSurface; _disableUpdates = s._disableUpdates; _bounds = s._bounds; _centroid = s._centroid; _transColor = s._transColor; _flags = s._flags; - if (_customSurface) { - // Surface owns the internal data, so replicate it so new surface owns it's own - _customSurface = new Graphics::Surface(); - _customSurface->create(s._customSurface->w, s._customSurface->h, Graphics::PixelFormat::createFormatCLUT8()); - const byte *srcP = (const byte *)s._customSurface->getPixels(); - byte *destP = (byte *)_customSurface->getPixels(); - - Common::copy(srcP, srcP + (_bounds.width() * _bounds.height()), destP); - } + // Copy the source's surface + create(s.w, s.h); + blitFrom(s); + setBounds(s.getBounds()); return *this; } @@ -474,7 +368,7 @@ bool GfxSurface::displayText(const Common::String &msg, const Common::Point &pt) /** * Loads a quarter of a screen from a resource */ -void GfxSurface::loadScreenSection(Graphics::Surface &dest, int xHalf, int yHalf, int xSection, int ySection) { +void GfxSurface::loadScreenSection(Graphics::ManagedSurface &dest, int xHalf, int yHalf, int xSection, int ySection) { int screenNum = g_globals->_sceneManager._scene->_activeScreenNumber; Rect updateRect(0, 0, 160, 100); updateRect.translate(xHalf * 160, yHalf * 100); @@ -682,50 +576,6 @@ void GfxSurface::draw(const Common::Point &pt, Rect *rect) { } } -/** - * Merges any clipping rectangles that overlap to try and reduce - * the total number of clip rectangles. - */ -void GfxSurface::mergeDirtyRects() { - if (_dirtyRects.size() <= 1) - return; - - Common::List<Rect>::iterator rOuter, rInner; - - for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { - rInner = rOuter; - while (++rInner != _dirtyRects.end()) { - - if ((*rOuter).intersects(*rInner)) { - // these two rectangles overlap or - // are next to each other - merge them - - unionRectangle(*rOuter, *rOuter, *rInner); - - // remove the inner rect from the list - _dirtyRects.erase(rInner); - - // move back to beginning of list - rInner = rOuter; - } - } - } -} - -/** - * Creates the union of two rectangles. - * Returns True if there is a union. - * @param pDest destination rectangle that is to receive the new union - * @param pSrc1 a source rectangle - * @param pSrc2 a source rectangle - */ -bool GfxSurface::unionRectangle(Common::Rect &destRect, const Rect &src1, const Rect &src2) { - destRect = src1; - destRect.extend(src2); - - return !destRect.isEmpty(); -} - /*--------------------------------------------------------------------------*/ GfxElement::GfxElement() { @@ -762,17 +612,16 @@ void GfxElement::highlight() { Rect tempRect(_bounds); tempRect.collapse(g_globals->_gfxEdgeAdjust - 1, g_globals->_gfxEdgeAdjust - 1); - for (int yp = tempRect.top; yp < tempRect.bottom; ++yp) { - byte *lineP = (byte *)surface.getBasePtr(tempRect.left, yp); - for (int xp = tempRect.left; xp < tempRect.right; ++xp, ++lineP) { + Graphics::Surface dest = surface.getSubArea(tempRect); + + for (int yp = 0; yp < dest.h; ++yp) { + byte *lineP = (byte *)dest.getBasePtr(0, yp); + for (int xp = 0; xp < tempRect.right; ++xp, ++lineP) { if (*lineP == _colors.background) *lineP = _colors.foreground; else if (*lineP == _colors.foreground) *lineP = _colors.background; } } - // Mark the affected area as dirty - gfxManager.getSurface().addDirtyRect(tempRect); - // Release the surface gfxManager.unlockSurface(); } @@ -816,10 +665,11 @@ void GfxElement::drawFrame() { // Loop through the surface area to replace each pixel // with its proper shaded replacement - Graphics::Surface surface = gfxManager.lockSurface(); - for (int y = tempRect.top; y < tempRect.bottom; ++y) { - byte *lineP = (byte *)surface.getBasePtr(tempRect.left, y); - for (int x = 0; x < tempRect.width(); ++x) { + Graphics::Surface dest = gfxManager.getSurface().getSubArea(tempRect); + + for (int y = 0; y < dest.h; ++y) { + byte *lineP = (byte *)dest.getBasePtr(0, y); + for (int x = 0; x < dest.w; ++x) { *lineP = transList[*lineP]; lineP++; } @@ -827,27 +677,24 @@ void GfxElement::drawFrame() { // Draw the edge frame // Outer frame border - surface.hLine(tempRect.left + 2, tempRect.top, tempRect.right - 2, 0); - surface.hLine(tempRect.left + 2, tempRect.bottom, tempRect.right - 2, 0); - surface.vLine(tempRect.left, tempRect.top + 2, tempRect.bottom - 2, 0); - surface.vLine(tempRect.right, tempRect.top + 2, tempRect.bottom - 2, 0); - *((byte *)surface.getBasePtr(tempRect.left + 1, tempRect.top + 1)) = 0; - *((byte *)surface.getBasePtr(tempRect.right - 1, tempRect.top + 1)) = 0; - *((byte *)surface.getBasePtr(tempRect.left + 1, tempRect.bottom - 1)) = 0; - *((byte *)surface.getBasePtr(tempRect.right - 1, tempRect.bottom - 1)) = 0; + dest.hLine(2, 0, dest.w - 2, 0); + dest.hLine(2, dest.h - 1, dest.w - 2, 0); + dest.vLine(0, 2, dest.h - 2, 0); + dest.vLine(tempRect.right, 2, dest.h - 2, 0); + *((byte *)dest.getBasePtr(1, 1)) = 0; + *((byte *)dest.getBasePtr(dest.w - 1, 1)) = 0; + *((byte *)dest.getBasePtr(1, dest.h - 1)) = 0; + *((byte *)dest.getBasePtr(dest.w - 1, dest.h - 1)) = 0; // Inner frame border - surface.hLine(tempRect.left + 2, tempRect.top + 1, tempRect.right - 2, R2_GLOBALS._frameEdgeColor); - surface.hLine(tempRect.left + 2, tempRect.bottom - 1, tempRect.right - 2, R2_GLOBALS._frameEdgeColor); - surface.vLine(tempRect.left + 1, tempRect.top + 2, tempRect.bottom - 2, R2_GLOBALS._frameEdgeColor); - surface.vLine(tempRect.right - 1, tempRect.top + 2, tempRect.bottom - 2, R2_GLOBALS._frameEdgeColor); - *((byte *)surface.getBasePtr(tempRect.left + 2, tempRect.top + 2)) = R2_GLOBALS._frameEdgeColor; - *((byte *)surface.getBasePtr(tempRect.right - 2, tempRect.top + 2)) = R2_GLOBALS._frameEdgeColor; - *((byte *)surface.getBasePtr(tempRect.left + 2, tempRect.bottom - 2)) = R2_GLOBALS._frameEdgeColor; - *((byte *)surface.getBasePtr(tempRect.right - 2, tempRect.bottom - 2)) = R2_GLOBALS._frameEdgeColor; - - gfxManager.unlockSurface(); - gfxManager.getSurface().addDirtyRect(tempRect); + dest.hLine(2, 1, dest.w - 2, R2_GLOBALS._frameEdgeColor); + dest.hLine(2, dest.h - 1, dest.w - 2, R2_GLOBALS._frameEdgeColor); + dest.vLine(1, 2, dest.h - 2, R2_GLOBALS._frameEdgeColor); + dest.vLine(dest.w - 1, 2, dest.h - 2, R2_GLOBALS._frameEdgeColor); + *((byte *)dest.getBasePtr(2, 2)) = R2_GLOBALS._frameEdgeColor; + *((byte *)dest.getBasePtr(dest.w - 2, 2)) = R2_GLOBALS._frameEdgeColor; + *((byte *)dest.getBasePtr(2, dest.h - 2)) = R2_GLOBALS._frameEdgeColor; + *((byte *)dest.getBasePtr(dest.w - 2, dest.h - 2)) = R2_GLOBALS._frameEdgeColor; } else { // Fill dialog content with specified background color @@ -1236,7 +1083,7 @@ GfxButton *GfxDialog::execute(GfxButton *defaultButton) { } g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } _gfxManager.deactivate(); @@ -1269,7 +1116,7 @@ void GfxDialog::setPalette() { /*--------------------------------------------------------------------------*/ -GfxManager::GfxManager() : _surface(g_globals->_screenSurface), _oldManager(NULL) { +GfxManager::GfxManager() : _surface(g_globals->_screen), _oldManager(NULL) { _font.setOwner(this); _font._fillFlag = false; _bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); @@ -1563,23 +1410,24 @@ int GfxFont::writeChar(const char ch) { int yOffset = (_fontData[charOffset + 1] >> 3) & 0x1f; const uint8 *dataP = &_fontData[charOffset + 2]; - // Lock the surface for access - Graphics::Surface surfacePtr = _gfxManager->lockSurface(); - Rect charRect; charRect.set(0, 0, charWidth, _fontSize.y); charRect.translate(_topLeft.x + _position.x, _topLeft.y + _position.y + yOffset); + // Get the sub-section of the screen to update + Graphics::Surface dest = _gfxManager->getSurface().getSubArea(charRect); + if (_fillFlag) - surfacePtr.fillRect(charRect, _colors.background); + dest.fillRect(charRect, _colors.background); charRect.bottom = charRect.top + charHeight; + assert(charRect.height() <= dest.h); // Display the character int bitCtr = 0; uint8 v = 0; - for (int yp = charRect.top; yp < charRect.bottom; ++yp) { - byte *destP = (byte *)surfacePtr.getBasePtr(charRect.left, yp); + for (int yp = 0; yp < charHeight; ++yp) { + byte *destP = (byte *)dest.getBasePtr(0, yp); for (int xs = 0; xs < charRect.width(); ++xs, ++destP) { // Get the next color index to use @@ -1599,13 +1447,9 @@ int GfxFont::writeChar(const char ch) { } } - // Mark the affected area as dirty - _gfxManager->getSurface().addDirtyRect(charRect); - // Move the text writing position _position.x += charWidth; - _gfxManager->unlockSurface(); return charWidth; } diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h index 25f7aea8cd..3b395b7625 100644 --- a/engines/tsage/graphics.h +++ b/engines/tsage/graphics.h @@ -28,7 +28,7 @@ #include "common/list.h" #include "common/rect.h" #include "common/system.h" -#include "graphics/surface.h" +#include "graphics/managed_surface.h" namespace TsAGE { @@ -73,20 +73,13 @@ public: enum FrameFlag { FRAME_FLIP_CENTROID_X = 4, FRAME_FLIP_CENTROID_Y = 8 }; -class GfxSurface { +class GfxSurface: virtual public Graphics::ManagedSurface { private: - Graphics::Surface *_customSurface; int _lockSurfaceCtr; + Graphics::ManagedSurface _rawSurface; bool _disableUpdates; Rect _bounds; - - bool _trackDirtyRects; - Common::List<Rect> _dirtyRects; - - void mergeDirtyRects(); - bool unionRectangle(Common::Rect &destRect, const Rect &src1, const Rect &src2); - public: Common::Point _centroid; int _transColor; @@ -95,17 +88,13 @@ public: public: GfxSurface(); GfxSurface(const GfxSurface &s); - ~GfxSurface(); + virtual ~GfxSurface(); - void setScreenSurface(); - void updateScreen(); - void addDirtyRect(const Rect &r); - Graphics::Surface lockSurface(); + Graphics::ManagedSurface &lockSurface(); void unlockSurface(); void synchronize(Serializer &s); - void create(int width, int height); - void clear(); - void setBounds(const Rect &bounds) { _bounds = bounds; } + virtual void create(uint16 width, uint16 height); + void setBounds(const Rect &bounds); const Rect &getBounds() const { return _bounds; } void copyFrom(GfxSurface &src, Rect srcBounds, Rect destBounds, @@ -119,10 +108,9 @@ public: copyFrom(src, tempRect, priorityRegion); } void draw(const Common::Point &pt, Rect *rect = NULL); - void fillRect(const Rect &bounds, int color); GfxSurface &operator=(const GfxSurface &s); - static void loadScreenSection(Graphics::Surface &dest, int xHalf, int yHalf, int xSection, int ySection); + static void loadScreenSection(Graphics::ManagedSurface &dest, int xHalf, int yHalf, int xSection, int ySection); static bool displayText(const Common::String &msg, const Common::Point &pt = Common::Point(160, 100)); }; @@ -281,7 +269,7 @@ public: void getStringBounds(const char *s, Rect &bounds, int maxWidth); void setDialogPalette(); - Graphics::Surface lockSurface() { + Graphics::ManagedSurface lockSurface() { _surface.setBounds(_bounds); return _surface.lockSurface(); } diff --git a/engines/tsage/module.mk b/engines/tsage/module.mk index e23b157a95..b58c748567 100644 --- a/engines/tsage/module.mk +++ b/engines/tsage/module.mk @@ -47,6 +47,7 @@ MODULE_OBJS := \ ringworld2/ringworld2_vampire.o \ saveload.o \ scenes.o \ + screen.o \ sherlock/sherlock_logo.o \ sound.o \ staticres.o \ diff --git a/engines/tsage/ringworld/ringworld_dialogs.cpp b/engines/tsage/ringworld/ringworld_dialogs.cpp index 1dd3bc158b..9fa17f3920 100644 --- a/engines/tsage/ringworld/ringworld_dialogs.cpp +++ b/engines/tsage/ringworld/ringworld_dialogs.cpp @@ -181,7 +181,7 @@ void RightClickDialog::execute() { } g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } _gfxManager.deactivate(); @@ -391,7 +391,7 @@ void InventoryDialog::execute() { Event event; while (!g_globals->_events.getEvent(event) && !g_vm->shouldQuit()) { g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } if (g_vm->shouldQuit()) break; @@ -439,7 +439,7 @@ void InventoryDialog::execute() { // Inventory item selected InvObject *invObject = static_cast<GfxInvImage *>(hiliteObj)->_invObject; if (lookFlag) { - g_globals->_screenSurface.displayText(invObject->_description); + g_globals->_screen.displayText(invObject->_description); } else { RING_INVENTORY._selectedItem = invObject; invObject->setCursor(); diff --git a/engines/tsage/ringworld/ringworld_logic.cpp b/engines/tsage/ringworld/ringworld_logic.cpp index 1d8293cffd..354c86abfc 100644 --- a/engines/tsage/ringworld/ringworld_logic.cpp +++ b/engines/tsage/ringworld/ringworld_logic.cpp @@ -320,7 +320,7 @@ void SceneArea::wait() { // Wait until a mouse or keypress Event event; while (!g_vm->shouldQuit() && !g_globals->_events.getEvent(event)) { - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); } diff --git a/engines/tsage/ringworld/ringworld_scenes3.cpp b/engines/tsage/ringworld/ringworld_scenes3.cpp index a515224964..a9ed7af870 100644 --- a/engines/tsage/ringworld/ringworld_scenes3.cpp +++ b/engines/tsage/ringworld/ringworld_scenes3.cpp @@ -532,7 +532,7 @@ void Scene2100::Action1::signal() { // Wait for an event Event event; if (!g_globals->_events.getEvent(event)) { - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); continue; } @@ -2265,7 +2265,7 @@ void Scene2150::Action1::signal() { // Wait for an event Event event; if (!g_globals->_events.getEvent(event)) { - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); continue; } @@ -5119,7 +5119,7 @@ void Scene2320::Action3::signal() { // Wait for an event Event event; if (!g_globals->_events.getEvent(event)) { - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); continue; } diff --git a/engines/tsage/ringworld/ringworld_scenes5.cpp b/engines/tsage/ringworld/ringworld_scenes5.cpp index cb8a89de80..98859f32ee 100644 --- a/engines/tsage/ringworld/ringworld_scenes5.cpp +++ b/engines/tsage/ringworld/ringworld_scenes5.cpp @@ -2813,7 +2813,7 @@ void Scene4150::Action1::signal() { case 4: { for (int idx = 100; idx >= 0; idx -= 5) { g_globals->_scenePalette.fade(adjustData, false, idx); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); } @@ -2841,7 +2841,7 @@ void Scene4150::Action1::signal() { case 7: for (int idx = 100; idx >= 0; idx -= 5) { g_globals->_scenePalette.fade(adjustData, false, idx); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); } diff --git a/engines/tsage/ringworld2/ringworld2_dialogs.cpp b/engines/tsage/ringworld2/ringworld2_dialogs.cpp index 99f88a1687..027fb558db 100644 --- a/engines/tsage/ringworld2/ringworld2_dialogs.cpp +++ b/engines/tsage/ringworld2/ringworld2_dialogs.cpp @@ -154,7 +154,7 @@ int RightClickDialog::execute() { } g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } // Execute the specified action diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index d24541932f..ecaa671bd7 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -351,7 +351,7 @@ SceneExt::SceneExt(): Scene() { _preventSaving = false; // Reset screen clipping area - R2_GLOBALS._screenSurface._clipRect = Rect(); + R2_GLOBALS._screen._clipRect = Rect(); // WORKAROUND: In the original, playing animations don't reset the global _animationCtr // counter as scene changes unless the playing animation explicitly finishes. For now, @@ -513,7 +513,7 @@ void SceneExt::endStrip() { } void SceneExt::clearScreen() { - R2_GLOBALS._screenSurface.fillRect(R2_GLOBALS._screenSurface.getBounds(), 0); + R2_GLOBALS._screen.fillRect(R2_GLOBALS._screen.getBounds(), 0); } void SceneExt::refreshBackground(int xAmount, int yAmount) { @@ -543,15 +543,12 @@ void SceneExt::refreshBackground(int xAmount, int yAmount) { int screenSize = g_vm->_memoryManager.getSize(dataP); // Lock the background for update - Graphics::Surface s = _backSurface.lockSurface(); - assert(screenSize == (s.w * s.h)); + assert(screenSize == (_backSurface.w * _backSurface.h)); + Graphics::Surface s = _backSurface.getSubArea(Common::Rect(0, 0, _backSurface.w, _backSurface.h)); - // Copy the data + // Copy the data into the surface byte *destP = (byte *)s.getPixels(); Common::copy(dataP, dataP + (s.w * s.h), destP); - _backSurface.unlockSurface(); - - R2_GLOBALS._screenSurface.addDirtyRect(_backSurface.getBounds()); // Free the resource data DEALLOCATE(dataP); @@ -601,7 +598,7 @@ void SceneExt::loadBlankScene() { _backSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT * 3 / 2); _backSurface.fillRect(_backSurface.getBounds(), 0); - R2_GLOBALS._screenSurface.fillRect(R2_GLOBALS._screenSurface.getBounds(), 0); + R2_GLOBALS._screen.fillRect(R2_GLOBALS._screen.getBounds(), 0); } /*--------------------------------------------------------------------------*/ @@ -1966,10 +1963,9 @@ void AnimationPlayer::drawFrame(int sliceIndex) { byte *sliceData1 = sliceDataStart; Rect playerBounds = _screenBounds; - int y = _screenBounds.top; - R2_GLOBALS._screenSurface.addDirtyRect(playerBounds); - Graphics::Surface surface = R2_GLOBALS._screenSurface.lockSurface(); + Graphics::Surface dest = R2_GLOBALS._screen.getSubArea(playerBounds); + int y = 0; // Handle different drawing modes switch (slice._drawMode) { @@ -1980,7 +1976,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) { // TODO: Check of _subData._drawType was done for two different kinds of // line slice drawing in original const byte *pSrc = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData1 + sliceNum * 2); - byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, y++); + byte *pDest = (byte *)dest.getBasePtr(0, y++); Common::copy(pSrc, pSrc + _subData._sliceSize, pDest); } @@ -1997,7 +1993,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) { if (offset) { const byte *pSrc = (const byte *)sliceDataStart + offset; - byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, playerBounds.top); + byte *pDest = (byte *)dest.getBasePtr(0, 0); //Common::copy(pSrc, pSrc + playerBounds.width(), pDest); rleDecode(pSrc, pDest, playerBounds.width()); @@ -2012,7 +2008,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) { // TODO: Check of _subData._drawType was done for two different kinds of // line slice drawing in original const byte *pSrc = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData1 + sliceNum * 2); - byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, playerBounds.top); + byte *pDest = (byte *)dest.getBasePtr(0, 0); rleDecode(pSrc, pDest, _subData._sliceSize); } @@ -2027,7 +2023,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) { for (int yIndex = 0; yIndex < _sliceHeight; ++yIndex) { const byte *pSrc1 = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData2 + sliceNum * 2); const byte *pSrc2 = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData1 + sliceNum * 2); - byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, y++); + byte *pDest = (byte *)dest.getBasePtr(0, y++); if (slice2._drawMode == 0) { // Uncompressed background, foreground compressed @@ -2047,17 +2043,14 @@ void AnimationPlayer::drawFrame(int sliceIndex) { break; } - // Unlock the screen surface - R2_GLOBALS._screenSurface.unlockSurface(); - if (_objectMode == ANIMOBJMODE_42) { _screenBounds.expandPanes(); // Copy the drawn frame to the back surface - Rect srcRect = R2_GLOBALS._screenSurface.getBounds(); + Rect srcRect = R2_GLOBALS._screen.getBounds(); Rect destRect = srcRect; destRect.translate(-g_globals->_sceneOffset.x, -g_globals->_sceneOffset.y); - R2_GLOBALS._sceneManager._scene->_backSurface.copyFrom(R2_GLOBALS._screenSurface, + R2_GLOBALS._sceneManager._scene->_backSurface.copyFrom(R2_GLOBALS._screen, srcRect, destRect); // Draw any objects into the scene diff --git a/engines/tsage/ringworld2/ringworld2_outpost.cpp b/engines/tsage/ringworld2/ringworld2_outpost.cpp index cad21b4623..8c64970bda 100644 --- a/engines/tsage/ringworld2/ringworld2_outpost.cpp +++ b/engines/tsage/ringworld2/ringworld2_outpost.cpp @@ -4689,7 +4689,7 @@ GfxButton *Scene1337::OptionsDialog::execute(GfxButton *defaultButton) { } g_system->delayMillis(10); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); } _gfxManager.deactivate(); diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp index 573cbbb29a..63879b0366 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp @@ -1613,7 +1613,7 @@ void Scene180::signal() { case 43: case 47: _helpEnabled = false; - R2_GLOBALS._screenSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); + R2_GLOBALS._screen.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); _palette.loadPalette(0); _palette.loadPalette(9998); R2_GLOBALS._scenePalette.addFader(_palette._palette, 256, 8, this); @@ -1815,7 +1815,7 @@ void Scene180::signal() { _shipDisplay.remove(); _backSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); - R2_GLOBALS._screenSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); + R2_GLOBALS._screen.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); R2_GLOBALS._sound2.fadeOut2(NULL); R2_GLOBALS._sound1.fadeOut2(this); break; @@ -1880,7 +1880,7 @@ void Scene180::signal() { R2_GLOBALS._paneRefreshFlag[0] = 3; _backSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); - R2_GLOBALS._screenSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); + R2_GLOBALS._screen.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); setSceneDelay(1); break; diff --git a/engines/tsage/saveload.cpp b/engines/tsage/saveload.cpp index 9954b929b2..3cb8e52692 100644 --- a/engines/tsage/saveload.cpp +++ b/engines/tsage/saveload.cpp @@ -289,10 +289,10 @@ void Saver::writeSavegameHeader(Common::OutSaveFile *out, tSageSavegameHeader &h // Create a thumbnail and save it Graphics::Surface *thumb = new Graphics::Surface(); - Graphics::Surface s = g_globals->_screenSurface.lockSurface(); + Graphics::Surface s = g_globals->_screen.lockSurface(); ::createThumbnail(thumb, (const byte *)s.getPixels(), SCREEN_WIDTH, SCREEN_HEIGHT, thumbPalette); Graphics::saveThumbnail(*out, *thumb); - g_globals->_screenSurface.unlockSurface(); + g_globals->_screen.unlockSurface(); thumb->free(); delete thumb; diff --git a/engines/tsage/scenes.cpp b/engines/tsage/scenes.cpp index 80ce1e3ecc..095c0d7ab5 100644 --- a/engines/tsage/scenes.cpp +++ b/engines/tsage/scenes.cpp @@ -139,7 +139,7 @@ void SceneManager::fadeInIfNecessary() { percent = 100; g_globals->_scenePalette.fade((const byte *)&adjustData, false, percent); - GLOBALS._screenSurface.updateScreen(); + GLOBALS._screen.update(); g_system->delayMillis(10); } @@ -175,7 +175,7 @@ void SceneManager::changeScene(int newSceneNumber) { } // Blank out the screen - g_globals->_screenSurface.fillRect(g_globals->_screenSurface.getBounds(), 0); + g_globals->_screen.fillRect(g_globals->_screen.getBounds(), 0); // If there are any fading sounds, wait until fading is complete while (g_globals->_soundManager.isFading()) { @@ -463,7 +463,7 @@ void Scene::refreshBackground(int xAmount, int yAmount) { // Check if the section is already loaded if ((_enabledSections[xp * 16 + yp] == 0xffff) || ((xAmount == 0) && (yAmount == 0))) { // Chunk isn't loaded, so load it in - Graphics::Surface s = _backSurface.lockSurface(); + Graphics::ManagedSurface s = _backSurface.lockSurface(); GfxSurface::loadScreenSection(s, xp - xHalfOffset, yp - yHalfOffset, xp, yp); _backSurface.unlockSurface(); changedFlag = true; diff --git a/engines/tsage/screen.cpp b/engines/tsage/screen.cpp new file mode 100644 index 0000000000..f11c384797 --- /dev/null +++ b/engines/tsage/screen.cpp @@ -0,0 +1,46 @@ +/* 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 "common/scummsys.h" +#include "tsage/screen.h" + +namespace TsAGE { + +Screen::Screen(): GfxSurface(), Graphics::Screen() { + create(SCREEN_WIDTH, SCREEN_HEIGHT); +} + +void Screen::update() { + // When dialogs are active, the screen surface may be remapped to + // sub-sections of the screen. But for drawing we'll need to temporarily + // remove any such remappings and use the entirety of the screen + Rect clipBounds = getBounds(); + setBounds(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + // Update the screen + Graphics::Screen::update(); + + // Reset the clipping + setBounds(clipBounds); +} + +} // End of namespace TsAGE diff --git a/engines/tsage/screen.h b/engines/tsage/screen.h new file mode 100644 index 0000000000..bf5057e4d6 --- /dev/null +++ b/engines/tsage/screen.h @@ -0,0 +1,59 @@ +/* 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 TSAGE_SCREEN_H +#define TSAGE_SCREEN_H + +#include "common/scummsys.h" +#include "common/array.h" +#include "graphics/screen.h" +#include "tsage/graphics.h" + +namespace TsAGE { + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 200 +#define SCREEN_CENTER_X 160 +#define SCREEN_CENTER_Y 100 +#define UI_INTERFACE_Y 168 + +class Screen : virtual public Graphics::Screen, virtual public GfxSurface { +public: + /** + * Constructor + */ + Screen(); + + /** + * Destructor + */ + virtual ~Screen() {} + + /** + * Update the screen + */ + virtual void update(); +}; + +} // End of namespace TsAGE + +#endif /* MADS_SCREEN_H */ diff --git a/engines/tsage/tsage.h b/engines/tsage/tsage.h index 667a8daa59..1a29700a10 100644 --- a/engines/tsage/tsage.h +++ b/engines/tsage/tsage.h @@ -62,12 +62,6 @@ enum { struct tSageGameDescription; -#define SCREEN_WIDTH 320 -#define SCREEN_HEIGHT 200 -#define SCREEN_CENTER_X 160 -#define SCREEN_CENTER_Y 100 -#define UI_INTERFACE_Y 168 - class TSageEngine : public Engine { private: const tSageGameDescription *_gameDescription; diff --git a/engines/tsage/user_interface.cpp b/engines/tsage/user_interface.cpp index 3ee585d5ef..fffc0dc16c 100644 --- a/engines/tsage/user_interface.cpp +++ b/engines/tsage/user_interface.cpp @@ -253,7 +253,7 @@ void UICollection::show() { void UICollection::erase() { if (_clearScreen) { Rect tempRect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT); - GLOBALS._screenSurface.fillRect(tempRect, 0); + GLOBALS._screen.fillRect(tempRect, 0); GLOBALS._sceneManager._scene->_backSurface.fillRect(tempRect, 0); _clearScreen = false; } @@ -274,7 +274,7 @@ void UICollection::draw() { _objList[idx]->draw(); // Draw the resulting UI onto the screen - GLOBALS._screenSurface.copyFrom(GLOBALS._sceneManager._scene->_backSurface, + GLOBALS._screen.copyFrom(GLOBALS._sceneManager._scene->_backSurface, Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT), Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT)); @@ -293,12 +293,12 @@ void UICollection::r2rDrawFrame() { GfxSurface vertLineRight = visage.getFrame(3); GfxSurface horizLine = visage.getFrame(2); - GLOBALS._screenSurface.copyFrom(horizLine, 0, 0); - GLOBALS._screenSurface.copyFrom(vertLineLeft, 0, 3); - GLOBALS._screenSurface.copyFrom(vertLineRight, SCREEN_WIDTH - 4, 3); + GLOBALS._screen.copyFrom(horizLine, 0, 0); + GLOBALS._screen.copyFrom(vertLineLeft, 0, 3); + GLOBALS._screen.copyFrom(vertLineRight, SCREEN_WIDTH - 4, 3); // Restrict drawing area to exclude the borders at the edge of the screen - R2_GLOBALS._screenSurface._clipRect = Rect(4, 3, SCREEN_WIDTH - 4, + R2_GLOBALS._screen._clipRect = Rect(4, 3, SCREEN_WIDTH - 4, SCREEN_HEIGHT - 3); } diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp index 9cba7b523d..d7b75e39c1 100644 --- a/engines/tucker/resource.cpp +++ b/engines/tucker/resource.cpp @@ -662,9 +662,11 @@ void TuckerEngine::loadData3() { void TuckerEngine::loadData4() { loadFile("data4.c", _loadTempBuf); DataTokenizer t(_loadTempBuf, _fileLoadSize); - t.findNextToken(kDataTokenDw); - _gameDebug = t.getNextInteger() != 0; - _displayGameHints = t.getNextInteger() != 0; + if ((_gameFlags & kGameFlagDemo) == 0) { + t.findNextToken(kDataTokenDw); + _gameDebug = t.getNextInteger() != 0; + _displayGameHints = t.getNextInteger() != 0; + } _locationObjectsCount = 0; if (t.findIndex(_locationNum)) { while (t.findNextToken(kDataTokenDw)) { diff --git a/engines/voyeur/animation.cpp b/engines/voyeur/animation.cpp index 62b37346da..d5d58a2fd3 100644 --- a/engines/voyeur/animation.cpp +++ b/engines/voyeur/animation.cpp @@ -470,7 +470,7 @@ void RL2Decoder::play(VoyeurEngine *vm, int resourceOffset, if (hasDirtyPalette()) { const byte *palette = getPalette(); - vm->_graphicsManager->setPalette128(palette, paletteStart, paletteCount); + vm->_screen->setPalette128(palette, paletteStart, paletteCount); } if (needsUpdate()) { @@ -482,15 +482,14 @@ void RL2Decoder::play(VoyeurEngine *vm, int resourceOffset, Common::Point pt(READ_LE_UINT16(imgPos + 4 * picCtr) - 32, READ_LE_UINT16(imgPos + 4 * picCtr + 2) - 20); - vm->_graphicsManager->sDrawPic(newPic, &videoFrame, pt); + vm->_screen->sDrawPic(newPic, &videoFrame, pt); ++picCtr; } } // Decode the next frame and display const Graphics::Surface *frame = decodeNextFrame(); - Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200, - (byte *)vm->_graphicsManager->_screenSurface.getPixels()); + vm->_screen->blitFrom(*frame); } vm->_eventsManager->getMouseInfo(); diff --git a/engines/voyeur/data.cpp b/engines/voyeur/data.cpp index b8c987f18b..4d6e32436d 100644 --- a/engines/voyeur/data.cpp +++ b/engines/voyeur/data.cpp @@ -240,10 +240,10 @@ void SVoy::reviewAnEvidEvent(int eventIndex) { int frameOff = e._computerOff; if (_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId)) { - _vm->_graphicsManager->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource; - _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource; - _vm->_graphicsManager->_vPort->setupViewPort(_vm->_graphicsManager->_backgroundPage); - _vm->_graphicsManager->_backColors->startFade(); + _vm->_screen->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource; + _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource; + _vm->_screen->_vPort->setupViewPort(_vm->_screen->_backgroundPage); + _vm->_screen->_backColors->startFade(); _vm->doEvidDisplay(frameOff, e._dead); _vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId); @@ -262,10 +262,10 @@ void SVoy::reviewComputerEvent(int eventIndex) { _computerTextId = e._computerOn; if (_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId)) { - _vm->_graphicsManager->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource; - _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource; - _vm->_graphicsManager->_vPort->setupViewPort(_vm->_graphicsManager->_backgroundPage); - _vm->_graphicsManager->_backColors->startFade(); + _vm->_screen->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource; + _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource; + _vm->_screen->_vPort->setupViewPort(_vm->_screen->_backgroundPage); + _vm->_screen->_backColors->startFade(); _vm->flipPageAndWaitForFade(); _vm->getComputerBrush(); diff --git a/engines/voyeur/debugger.cpp b/engines/voyeur/debugger.cpp index e9a12180da..ebfa123eb6 100644 --- a/engines/voyeur/debugger.cpp +++ b/engines/voyeur/debugger.cpp @@ -21,7 +21,7 @@ */ #include "voyeur/debugger.h" -#include "voyeur/graphics.h" +#include "voyeur/screen.h" #include "voyeur/voyeur.h" #include "voyeur/staticres.h" diff --git a/engines/voyeur/events.cpp b/engines/voyeur/events.cpp index 34ef507ad3..020fe4b692 100644 --- a/engines/voyeur/events.cpp +++ b/engines/voyeur/events.cpp @@ -111,18 +111,18 @@ void EventsManager::mainVoyeurIntFunc() { } void EventsManager::sWaitFlip() { - Common::Array<ViewPortResource *> &viewPorts = _vm->_graphicsManager->_viewPortListPtr->_entries; + Common::Array<ViewPortResource *> &viewPorts = _vm->_screen->_viewPortListPtr->_entries; for (uint idx = 0; idx < viewPorts.size(); ++idx) { ViewPortResource &viewPort = *viewPorts[idx]; - if (_vm->_graphicsManager->_saveBack && (viewPort._flags & DISPFLAG_40)) { - Common::Rect *clipPtr = _vm->_graphicsManager->_clipPtr; - _vm->_graphicsManager->_clipPtr = &viewPort._clipRect; + if (_vm->_screen->_saveBack && (viewPort._flags & DISPFLAG_40)) { + Common::Rect *clipPtr = _vm->_screen->_clipPtr; + _vm->_screen->_clipPtr = &viewPort._clipRect; if (viewPort._restoreFn) - (_vm->_graphicsManager->*viewPort._restoreFn)(&viewPort); + (_vm->_screen->*viewPort._restoreFn)(&viewPort); - _vm->_graphicsManager->_clipPtr = clipPtr; + _vm->_screen->_clipPtr = clipPtr; viewPort._rectListCount[viewPort._pageIndex] = 0; viewPort._rectListPtr[viewPort._pageIndex]->clear(); viewPort._flags &= ~DISPFLAG_40; @@ -158,9 +158,7 @@ void EventsManager::checkForNextFrameCounter() { showMousePosition(); // Display the frame - g_system->copyRectToScreen((byte *)_vm->_graphicsManager->_screenSurface.getPixels(), - SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - g_system->updateScreen(); + _vm->_screen->update(); // Signal the ScummVM debugger _vm->_debugger->onFrame(); @@ -178,10 +176,8 @@ void EventsManager::showMousePosition() { mousePos += Common::String::format(" - (%d,%d)", pt.x, pt.y); } - _vm->_graphicsManager->_screenSurface.fillRect( - Common::Rect(0, 0, 110, font.getFontHeight()), 0); - font.drawString(&_vm->_graphicsManager->_screenSurface, mousePos, - 0, 0, 110, 63); + _vm->_screen->fillRect(Common::Rect(0, 0, 110, font.getFontHeight()), 0); + font.drawString(_vm->_screen, mousePos, 0, 0, 110, 63); } void EventsManager::voyeurTimer() { @@ -299,11 +295,11 @@ void EventsManager::startFade(CMapResource *cMap) { if (cMap->_steps > 0) { _fadeStatus = cMap->_fadeStatus | 1; - byte *vgaP = &_vm->_graphicsManager->_VGAColors[_fadeFirstCol * 3]; + byte *vgaP = &_vm->_screen->_VGAColors[_fadeFirstCol * 3]; int mapIndex = 0; for (int idx = _fadeFirstCol; idx <= _fadeLastCol; ++idx, vgaP += 3) { - ViewPortPalEntry &palEntry = _vm->_graphicsManager->_viewPortListPtr->_palette[idx]; + ViewPortPalEntry &palEntry = _vm->_screen->_viewPortListPtr->_palette[idx]; palEntry._rEntry = vgaP[0] << 8; int rDiff = (cMap->_entries[mapIndex * 3] << 8) - palEntry._rEntry; palEntry._rChange = rDiff / cMap->_steps; @@ -325,7 +321,7 @@ void EventsManager::startFade(CMapResource *cMap) { _intPtr._skipFading = true; _fadeIntNode._flags &= ~1; } else { - byte *vgaP = &_vm->_graphicsManager->_VGAColors[_fadeFirstCol * 3]; + byte *vgaP = &_vm->_screen->_VGAColors[_fadeFirstCol * 3]; int mapIndex = 0; for (int idx = _fadeFirstCol; idx <= _fadeLastCol; ++idx, vgaP += 3) { @@ -371,8 +367,8 @@ void EventsManager::vDoFadeInt() { } for (int i = _fadeFirstCol; i <= _fadeLastCol; ++i) { - ViewPortPalEntry &palEntry = _vm->_graphicsManager->_viewPortListPtr->_palette[i]; - byte *vgaP = &_vm->_graphicsManager->_VGAColors[palEntry._palIndex * 3]; + ViewPortPalEntry &palEntry = _vm->_screen->_viewPortListPtr->_palette[i]; + byte *vgaP = &_vm->_screen->_VGAColors[palEntry._palIndex * 3]; palEntry._rEntry += palEntry._rChange; palEntry._gEntry += palEntry._gChange; @@ -395,7 +391,7 @@ void EventsManager::vDoCycleInt() { for (int idx = 3; idx >= 0; --idx) { if (_cyclePtr->_type[idx] && --_cycleTime[idx] <= 0) { byte *pSrc = _cycleNext[idx]; - byte *pPal = _vm->_graphicsManager->_VGAColors; + byte *pPal = _vm->_screen->_VGAColors; if (_cyclePtr->_type[idx] != 1) { // New palette data being specified - loop to set entries @@ -521,7 +517,7 @@ void EventsManager::setCursor(PictureResource *pic) { cursor._bounds = pic->_bounds; cursor._flags = DISPFLAG_CURSOR; - _vm->_graphicsManager->sDrawPic(pic, &cursor, Common::Point()); + _vm->_screen->sDrawPic(pic, &cursor, Common::Point()); } void EventsManager::setCursor(byte *cursorData, int width, int height, int keyColor) { @@ -531,16 +527,16 @@ void EventsManager::setCursor(byte *cursorData, int width, int height, int keyCo void EventsManager::setCursorColor(int idx, int mode) { switch (mode) { case 0: - _vm->_graphicsManager->setColor(idx, 90, 90, 232); + _vm->_screen->setColor(idx, 90, 90, 232); break; case 1: - _vm->_graphicsManager->setColor(idx, 232, 90, 90); + _vm->_screen->setColor(idx, 232, 90, 90); break; case 2: - _vm->_graphicsManager->setColor(idx, 90, 232, 90); + _vm->_screen->setColor(idx, 90, 232, 90); break; case 3: - _vm->_graphicsManager->setColor(idx, 90, 232, 232); + _vm->_screen->setColor(idx, 90, 232, 232); break; default: break; @@ -564,12 +560,12 @@ void EventsManager::getMouseInfo() { if (_cursorBlinked) { _cursorBlinked = false; - _vm->_graphicsManager->setOneColor(128, 220, 20, 20); - _vm->_graphicsManager->setColor(128, 220, 20, 20); + _vm->_screen->setOneColor(128, 220, 20, 20); + _vm->_screen->setColor(128, 220, 20, 20); } else { _cursorBlinked = true; - _vm->_graphicsManager->setOneColor(128, 220, 220, 220); - _vm->_graphicsManager->setColor(128, 220, 220, 220); + _vm->_screen->setOneColor(128, 220, 220, 220); + _vm->_screen->setColor(128, 220, 220, 220); } } } @@ -585,11 +581,11 @@ void EventsManager::getMouseInfo() { void EventsManager::startCursorBlink() { if (_vm->_voy->_eventFlags & EVTFLAG_RECORDING) { - _vm->_graphicsManager->setOneColor(128, 55, 5, 5); - _vm->_graphicsManager->setColor(128, 220, 20, 20); + _vm->_screen->setOneColor(128, 55, 5, 5); + _vm->_screen->setColor(128, 220, 20, 20); _intPtr._hasPalette = true; - _vm->_graphicsManager->drawDot(); + _vm->_screen->drawDot(); //copySection(); } } diff --git a/engines/voyeur/files.cpp b/engines/voyeur/files.cpp index 300e086f75..46b195ecaf 100644 --- a/engines/voyeur/files.cpp +++ b/engines/voyeur/files.cpp @@ -21,7 +21,7 @@ */ #include "voyeur/files.h" -#include "voyeur/graphics.h" +#include "voyeur/screen.h" #include "voyeur/voyeur.h" #include "voyeur/staticres.h" @@ -359,7 +359,7 @@ void BoltFile::resolveIt(uint32 id, byte **p) { } } -void BoltFile::resolveFunction(uint32 id, GraphicMethodPtr *fn) { +void BoltFile::resolveFunction(uint32 id, ScreenMethodPtr *fn) { if ((int32)id == -1) *fn = NULL; else @@ -485,8 +485,8 @@ void BVoyBoltFile::initViewPortList() { _state._curMemberPtr->_viewPortListResource = res = new ViewPortListResource( _state, _state._curMemberPtr->_data); - _state._vm->_graphicsManager->_viewPortListPtr = res; - _state._vm->_graphicsManager->_vPort = res->_entries[0]; + _state._vm->_screen->_viewPortListPtr = res; + _state._vm->_screen->_vPort = res->_entries[0]; } void BVoyBoltFile::initFontInfo() { @@ -752,24 +752,24 @@ DisplayResource::DisplayResource(VoyeurEngine *vm) { void DisplayResource::sFillBox(int width, int height) { assert(_vm); - bool saveBack = _vm->_graphicsManager->_saveBack; - _vm->_graphicsManager->_saveBack = false; + bool saveBack = _vm->_screen->_saveBack; + _vm->_screen->_saveBack = false; PictureResource pr; pr._flags = DISPFLAG_1; pr._select = 0xff; pr._pick = 0; - pr._onOff = _vm->_graphicsManager->_drawPtr->_penColor; + pr._onOff = _vm->_screen->_drawPtr->_penColor; pr._bounds = Common::Rect(0, 0, width, height); - _vm->_graphicsManager->sDrawPic(&pr, this, _vm->_graphicsManager->_drawPtr->_pos); - _vm->_graphicsManager->_saveBack = saveBack; + _vm->_screen->sDrawPic(&pr, this, _vm->_screen->_drawPtr->_pos); + _vm->_screen->_saveBack = saveBack; } bool DisplayResource::clipRect(Common::Rect &rect) { Common::Rect clippingRect; - if (_vm->_graphicsManager->_clipPtr) { - clippingRect = *_vm->_graphicsManager->_clipPtr; + if (_vm->_screen->_clipPtr) { + clippingRect = *_vm->_screen->_clipPtr; } else if (_flags & DISPFLAG_VIEWPORT) { clippingRect = ((ViewPortResource *)this)->_clipRect; } else { @@ -804,18 +804,18 @@ bool DisplayResource::clipRect(Common::Rect &rect) { } int DisplayResource::drawText(const Common::String &msg) { - GraphicsManager &gfxManager = *_vm->_graphicsManager; - assert(gfxManager._fontPtr); - assert(gfxManager._fontPtr->_curFont); - FontInfoResource &fontInfo = *gfxManager._fontPtr; - PictureResource &fontChar = *_vm->_graphicsManager->_fontChar; + Screen &screen = *_vm->_screen; + assert(screen._fontPtr); + assert(screen._fontPtr->_curFont); + FontInfoResource &fontInfo = *screen._fontPtr; + PictureResource &fontChar = *_vm->_screen->_fontChar; FontResource &fontData = *fontInfo._curFont; int xShadows[9] = { 0, 1, 1, 1, 0, -1, -1, -1, 0 }; int yShadows[9] = { 0, 1, 0, -1, -1, -1, 0, 1, 1 }; - Common::Rect *clipPtr = gfxManager._clipPtr; + Common::Rect *clipPtr = screen._clipPtr; if (!(fontInfo._picFlags & DISPFLAG_1)) - gfxManager._clipPtr = NULL; + screen._clipPtr = NULL; int minChar = fontData._minChar; int padding = fontData._padding; @@ -834,7 +834,7 @@ int DisplayResource::drawText(const Common::String &msg) { (ViewPortResource *)this; if ((fontInfo._fontFlags & DISPFLAG_1) || fontInfo._justify || - (gfxManager._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT))) { + (screen._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT))) { msgWidth = viewPort->textWidth(msg); yp = pos.y; xp = pos.x; @@ -898,18 +898,18 @@ int DisplayResource::drawText(const Common::String &msg) { } } - if (gfxManager._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT)) { + if (screen._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT)) { viewPort->addSaveRect(viewPort->_pageIndex, viewPort->_fontRect); } if (fontInfo._fontFlags & DISPFLAG_1) { - gfxManager._drawPtr->_pos = Common::Point(viewPort->_fontRect.left, viewPort->_fontRect.top); - gfxManager._drawPtr->_penColor = fontInfo._backColor; + screen._drawPtr->_pos = Common::Point(viewPort->_fontRect.left, viewPort->_fontRect.top); + screen._drawPtr->_penColor = fontInfo._backColor; sFillBox(viewPort->_fontRect.width(), viewPort->_fontRect.height()); } - bool saveBack = gfxManager._saveBack; - gfxManager._saveBack = false; + bool saveBack = screen._saveBack; + screen._saveBack = false; int count = 0; if (fontInfo._fontFlags & DISPFLAG_4) @@ -970,7 +970,7 @@ int DisplayResource::drawText(const Common::String &msg) { uint16 offset = READ_LE_UINT16(fontData._charOffsets + charValue * 2); fontChar._imgData = fontData._charImages + offset * 2; - gfxManager.sDrawPic(&fontChar, this, Common::Point(xp, yp)); + screen.sDrawPic(&fontChar, this, Common::Point(xp, yp)); fontChar._imgData = NULL; xp += charWidth + padding; @@ -982,8 +982,8 @@ int DisplayResource::drawText(const Common::String &msg) { if (fontInfo._justify == ALIGN_LEFT) fontInfo._pos.x = xp; - gfxManager._saveBack = saveBack; - gfxManager._clipPtr = clipPtr; + screen._saveBack = saveBack; + screen._clipPtr = clipPtr; return msgWidth; } @@ -993,7 +993,7 @@ int DisplayResource::textWidth(const Common::String &msg) { return 0; const char *msgP = msg.c_str(); - FontResource &fontData = *_vm->_graphicsManager->_fontPtr->_curFont; + FontResource &fontData = *_vm->_screen->_fontPtr->_curFont; int minChar = fontData._minChar; int maxChar = fontData._maxChar; int padding = fontData._padding; @@ -1085,9 +1085,9 @@ PictureResource::PictureResource(BoltFilesState &state, const byte *src): mode = 226; } - if (mode != state._vm->_graphicsManager->_SVGAMode) { - state._vm->_graphicsManager->_SVGAMode = mode; - state._vm->_graphicsManager->clearPalette(); + if (mode != state._vm->_screen->_SVGAMode) { + state._vm->_screen->_SVGAMode = mode; + state._vm->_screen->clearPalette(); } int screenOffset = READ_LE_UINT32(&src[18]) & 0xffff; @@ -1096,13 +1096,14 @@ PictureResource::PictureResource(BoltFilesState &state, const byte *src): if (_flags & PICFLAG_CLEAR_SCREEN) { // Clear screen picture. That's right. This game actually has a picture // resource flag to clear the screen! Bizarre. - Graphics::Surface &s = state._vm->_graphicsManager->_screenSurface; - s.fillRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0); + state._vm->_screen->clear(); } else { // Direct screen loading picture. In this case, the raw data of the resource // is directly decompressed into the screen surface. Again, bizarre. - byte *pDest = (byte *)state._vm->_graphicsManager->_screenSurface.getPixels(); + Screen &screen = *state._vm->_screen; + byte *pDest = (byte *)screen.getPixels(); state.decompress(pDest, SCREEN_WIDTH * SCREEN_HEIGHT, state._curMemberPtr->_mode); + screen.markAllDirty(); } } else { if (_flags & PICFLAG_CLEAR_SCREEN00) { @@ -1249,13 +1250,13 @@ ViewPortResource::ViewPortResource(BoltFilesState &state, const byte *src): ys + READ_LE_UINT16(src + 0x4C)); state._curLibPtr->resolveIt(READ_LE_UINT32(src + 0x7A), &dummy); - state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x7E), (GraphicMethodPtr *)&_fn1); - state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x82), (GraphicMethodPtr *)&_setupFn); - state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x86), (GraphicMethodPtr *)&_addFn); - state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x8A), (GraphicMethodPtr *)&_restoreFn); + state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x7E), (ScreenMethodPtr *)&_fn1); + state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x82), (ScreenMethodPtr *)&_setupFn); + state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x86), (ScreenMethodPtr *)&_addFn); + state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x8A), (ScreenMethodPtr *)&_restoreFn); if (!_restoreFn && _addFn) - _addFn = &GraphicsManager::addRectNoSaveBack; + _addFn = &Screen::addRectNoSaveBack; } ViewPortResource::~ViewPortResource() { @@ -1327,19 +1328,19 @@ void ViewPortResource::setupViewPort(PictureResource *page, Common::Rect *clippi _restoreFn = restoreFn; if (setupFn) - (_state._vm->_graphicsManager->*setupFn)(this); + (_state._vm->_screen->*setupFn)(this); } void ViewPortResource::setupViewPort() { - setupViewPort(_state._vm->_graphicsManager->_backgroundPage, NULL, - &GraphicsManager::setupMCGASaveRect, &GraphicsManager::addRectOptSaveRect, - &GraphicsManager::restoreMCGASaveRect); + setupViewPort(_state._vm->_screen->_backgroundPage, NULL, + &Screen::setupMCGASaveRect, &Screen::addRectOptSaveRect, + &Screen::restoreMCGASaveRect); } void ViewPortResource::setupViewPort(PictureResource *pic, Common::Rect *clippingRect) { setupViewPort(pic, clippingRect, - &GraphicsManager::setupMCGASaveRect, &GraphicsManager::addRectOptSaveRect, - &GraphicsManager::restoreMCGASaveRect); + &Screen::setupMCGASaveRect, &Screen::addRectOptSaveRect, + &Screen::restoreMCGASaveRect); } void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) { @@ -1347,7 +1348,7 @@ void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) { if (clipRect(rect)) { if (_addFn) { - (_state._vm->_graphicsManager->*_addFn)(this, pageIndex, rect); + (_state._vm->_screen->*_addFn)(this, pageIndex, rect); } else if (_rectListCount[pageIndex] != -1) { _rectListPtr[pageIndex]->push_back(rect); } @@ -1355,26 +1356,26 @@ void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) { } void ViewPortResource::fillPic(byte onOff) { - _state._vm->_graphicsManager->fillPic(this, onOff); + _state._vm->_screen->fillPic(this, onOff); } void ViewPortResource::drawIfaceTime() { // Hour display - _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort, + _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort, (_state._vm->_gameHour / 10) == 0 ? 10 : _state._vm->_gameHour / 10, Common::Point(161, 25)); - _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort, + _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort, _state._vm->_gameHour % 10, Common::Point(172, 25)); // Minute display - _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort, + _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort, _state._vm->_gameMinute / 10, Common::Point(190, 25)); - _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort, + _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort, _state._vm->_gameMinute % 10, Common::Point(201, 25)); // AM/PM indicator PictureResource *pic = _state._vm->_bVoy->boltEntry(_state._vm->_voy->_isAM ? 272 : 273)._picResource; - _state._vm->_graphicsManager->sDrawPic(pic, _state._vm->_graphicsManager->_vPort, + _state._vm->_screen->sDrawPic(pic, _state._vm->_screen->_vPort, Common::Point(215, 27)); } @@ -1382,9 +1383,9 @@ void ViewPortResource::drawPicPerm(PictureResource *pic, const Common::Point &pt Common::Rect bounds = pic->_bounds; bounds.translate(pt.x, pt.y); - bool saveBack = _state._vm->_graphicsManager->_saveBack; - _state._vm->_graphicsManager->_saveBack = false; - _state._vm->_graphicsManager->sDrawPic(pic, this, pt); + bool saveBack = _state._vm->_screen->_saveBack; + _state._vm->_screen->_saveBack = false; + _state._vm->_screen->sDrawPic(pic, this, pt); clipRect(bounds); for (int pageIndex = 0; pageIndex < _pageCount; ++pageIndex) { @@ -1393,7 +1394,7 @@ void ViewPortResource::drawPicPerm(PictureResource *pic, const Common::Point &pt } } - _state._vm->_graphicsManager->_saveBack = saveBack; + _state._vm->_screen->_saveBack = saveBack; } /*------------------------------------------------------------------------*/ @@ -1526,7 +1527,7 @@ CMapResource::CMapResource(BoltFilesState &state, const byte *src): _vm(state._v _entries = new byte[count * 3]; Common::copy(src + 6, src + 6 + 3 * count, _entries); - int palIndex = state._vm->_graphicsManager->_viewPortListPtr->_palIndex; + int palIndex = state._vm->_screen->_viewPortListPtr->_palIndex; if (_end > palIndex) _end = palIndex; if (_start > palIndex) diff --git a/engines/voyeur/files.h b/engines/voyeur/files.h index eef5df497c..8726b38ddf 100644 --- a/engines/voyeur/files.h +++ b/engines/voyeur/files.h @@ -27,7 +27,7 @@ #include "common/file.h" #include "common/rect.h" #include "common/str.h" -#include "voyeur/graphics.h" +#include "voyeur/screen.h" namespace Voyeur { @@ -112,7 +112,7 @@ public: byte *memberAddr(uint32 id); byte *memberAddrOffset(uint32 id); void resolveIt(uint32 id, byte **p); - void resolveFunction(uint32 id, GraphicMethodPtr *fn); + void resolveFunction(uint32 id, ScreenMethodPtr *fn); BoltEntry &boltEntry(uint16 id); BoltEntry &getBoltEntryFromLong(uint32 id); @@ -340,7 +340,7 @@ public: int _rectListCount[3]; Common::Rect _clipRect; - GraphicMethodPtr _fn1; + ScreenMethodPtr _fn1; ViewPortSetupPtr _setupFn; ViewPortAddPtr _addFn; ViewPortRestorePtr _restoreFn; diff --git a/engines/voyeur/files_threads.cpp b/engines/voyeur/files_threads.cpp index 9908324043..bbd3dfe4e9 100644 --- a/engines/voyeur/files_threads.cpp +++ b/engines/voyeur/files_threads.cpp @@ -21,7 +21,7 @@ */ #include "voyeur/files.h" -#include "voyeur/graphics.h" +#include "voyeur/screen.h" #include "voyeur/voyeur.h" #include "voyeur/staticres.h" @@ -461,7 +461,7 @@ void ThreadResource::parsePlayCommands() { pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2)._picResource; pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2 + 1)._cMapResource; - _vm->_graphicsManager->_vPort->setupViewPort(pic); + _vm->_screen->_vPort->setupViewPort(pic); pal->startFade(); _vm->flipPageAndWaitForFade(); @@ -980,10 +980,10 @@ int ThreadResource::doApt() { _vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId)); _vm->_currentVocId = 151; - _vm->_graphicsManager->setColor(129, 82, 82, 82); - _vm->_graphicsManager->setColor(130, 112, 112, 112); - _vm->_graphicsManager->setColor(131, 215, 215, 215); - _vm->_graphicsManager->setColor(132, 235, 235, 235); + _vm->_screen->setColor(129, 82, 82, 82); + _vm->_screen->setColor(130, 112, 112, 112); + _vm->_screen->setColor(131, 215, 215, 215); + _vm->_screen->setColor(132, 235, 235, 235); _vm->_eventsManager->_intPtr._hasPalette = true; @@ -1044,7 +1044,7 @@ int ThreadResource::doApt() { // Draw the text description for the highlighted hotspot pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + hotspotId + 6)._picResource; - _vm->_graphicsManager->sDrawPic(pic, _vm->_graphicsManager->_vPort, + _vm->_screen->sDrawPic(pic, _vm->_screen->_vPort, Common::Point(106, 200)); } @@ -1112,10 +1112,10 @@ void ThreadResource::doRoom() { if (!vm._bVoy->getBoltGroup(vm._playStampGroupId)) return; - vm._graphicsManager->_backColors = vm._bVoy->boltEntry(vm._playStampGroupId + 1)._cMapResource; - vm._graphicsManager->_backgroundPage = vm._bVoy->boltEntry(vm._playStampGroupId)._picResource; - vm._graphicsManager->_vPort->setupViewPort(vm._graphicsManager->_backgroundPage); - vm._graphicsManager->_backColors->startFade(); + vm._screen->_backColors = vm._bVoy->boltEntry(vm._playStampGroupId + 1)._cMapResource; + vm._screen->_backgroundPage = vm._bVoy->boltEntry(vm._playStampGroupId)._picResource; + vm._screen->_vPort->setupViewPort(vm._screen->_backgroundPage); + vm._screen->_backColors->startFade(); voy._fadingStep1 = 2; voy._fadingStep2 = 0; @@ -1144,7 +1144,7 @@ void ThreadResource::doRoom() { bool breakFlag = false; while (!vm.shouldQuit() && !breakFlag) { _vm->_voyeurArea = AREA_ROOM; - vm._graphicsManager->setColor(128, 0, 255, 0); + vm._screen->setColor(128, 0, 255, 0); vm._eventsManager->_intPtr._hasPalette = true; do { @@ -1186,7 +1186,7 @@ void ThreadResource::doRoom() { } vm._eventsManager->_intPtr._hasPalette = true; - vm._graphicsManager->flipPage(); + vm._screen->flipPage(); vm._eventsManager->sWaitFlip(); } while (!vm.shouldQuit() && !vm._eventsManager->_mouseClicked); @@ -1234,13 +1234,13 @@ void ThreadResource::doRoom() { // WORKAROUND: Skipped code from the original, that freed the group, // reloaded it, and reloaded the cursors - vm._graphicsManager->_backColors = vm._bVoy->boltEntry( + vm._screen->_backColors = vm._bVoy->boltEntry( vm._playStampGroupId + 1)._cMapResource; - vm._graphicsManager->_backgroundPage = vm._bVoy->boltEntry( + vm._screen->_backgroundPage = vm._bVoy->boltEntry( vm._playStampGroupId)._picResource; - vm._graphicsManager->_vPort->setupViewPort(); - vm._graphicsManager->_backColors->startFade(); + vm._screen->_vPort->setupViewPort(); + vm._screen->_backColors->startFade(); _vm->flipPageAndWait(); while (!vm.shouldQuit() && (vm._eventsManager->_fadeStatus & 1)) @@ -1265,7 +1265,7 @@ void ThreadResource::doRoom() { _vm->flipPageAndWait(); - vm._graphicsManager->fadeUpICF1(); + vm._screen->fadeUpICF1(); voy._eventFlags &= EVTFLAG_RECORDING; vm._eventsManager->showCursor(); } @@ -1350,7 +1350,7 @@ int ThreadResource::doInterface() { _vm->_soundManager->startVOCPlay(fname); _vm->_eventsManager->getMouseInfo(); - _vm->_graphicsManager->setColor(240, 220, 220, 220); + _vm->_screen->setColor(240, 220, 220, 220); _vm->_eventsManager->_intPtr._hasPalette = true; _vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED; @@ -1424,20 +1424,20 @@ int ThreadResource::doInterface() { // Regularly update the time display if (_vm->_voy->_RTANum & 2) { - _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort, + _vm->_screen->drawANumber(_vm->_screen->_vPort, _vm->_gameMinute / 10, Common::Point(190, 25)); - _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort, + _vm->_screen->drawANumber(_vm->_screen->_vPort, _vm->_gameMinute % 10, Common::Point(201, 25)); if (_vm->_voy->_RTANum & 4) { int v = _vm->_gameHour / 10; - _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort, + _vm->_screen->drawANumber(_vm->_screen->_vPort, v == 0 ? 10 : v, Common::Point(161, 25)); - _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort, + _vm->_screen->drawANumber(_vm->_screen->_vPort, _vm->_gameHour % 10, Common::Point(172, 25)); pic = _vm->_bVoy->boltEntry(_vm->_voy->_isAM ? 272 : 273)._picResource; - _vm->_graphicsManager->sDrawPic(pic, _vm->_graphicsManager->_vPort, + _vm->_screen->sDrawPic(pic, _vm->_screen->_vPort, Common::Point(215, 27)); } } @@ -1605,16 +1605,16 @@ void ThreadResource::loadTheApt() { _vm->_bVoy->getBoltGroup(_vm->_playStampGroupId); _vm->_voy->_aptLoadMode = -1; - _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry( + _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry( _vm->_playStampGroupId + 5)._picResource; - _vm->_graphicsManager->_vPort->setupViewPort( - _vm->_graphicsManager->_backgroundPage); + _vm->_screen->_vPort->setupViewPort( + _vm->_screen->_backgroundPage); } else { _vm->_bVoy->getBoltGroup(_vm->_playStampGroupId); - _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry( + _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry( _vm->_playStampGroupId + 5)._picResource; - _vm->_graphicsManager->_vPort->setupViewPort( - _vm->_graphicsManager->_backgroundPage); + _vm->_screen->_vPort->setupViewPort( + _vm->_screen->_backgroundPage); } CMapResource *pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 4)._cMapResource; @@ -1624,10 +1624,10 @@ void ThreadResource::loadTheApt() { } void ThreadResource::freeTheApt() { - _vm->_graphicsManager->fadeDownICF1(5); + _vm->_screen->fadeDownICF1(5); _vm->flipPageAndWaitForFade(); - _vm->_graphicsManager->fadeUpICF1(); + _vm->_screen->fadeUpICF1(); if (_vm->_currentVocId != -1) { _vm->_soundManager->stopVOCPlay(); @@ -1635,17 +1635,17 @@ void ThreadResource::freeTheApt() { } if (_vm->_voy->_aptLoadMode == -1) { - _vm->_graphicsManager->fadeDownICF(6); + _vm->_screen->fadeDownICF(6); } else { doAptAnim(2); } if (_vm->_voy->_aptLoadMode == 140) { - _vm->_graphicsManager->screenReset(); - _vm->_graphicsManager->resetPalette(); + _vm->_screen->screenReset(); + _vm->_screen->resetPalette(); } - _vm->_graphicsManager->_vPort->setupViewPort(nullptr); + _vm->_screen->_vPort->setupViewPort(nullptr); _vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId); _vm->_playStampGroupId = -1; _vm->_voy->_viewBounds = nullptr; @@ -1705,7 +1705,7 @@ void ThreadResource::doAptAnim(int mode) { for (int idx = 0; (idx < 6) && !_vm->shouldQuit(); ++idx) { PictureResource *pic = _vm->_bVoy->boltEntry(id + idx + 1)._picResource; - _vm->_graphicsManager->_vPort->setupViewPort(pic); + _vm->_screen->_vPort->setupViewPort(pic); pal->startFade(); _vm->flipPageAndWait(); diff --git a/engines/voyeur/module.mk b/engines/voyeur/module.mk index aab254cf36..a38bdd9ab2 100644 --- a/engines/voyeur/module.mk +++ b/engines/voyeur/module.mk @@ -8,7 +8,7 @@ MODULE_OBJS := \ events.o \ files.o \ files_threads.o \ - graphics.o \ + screen.o \ sound.o \ staticres.o \ voyeur.o \ diff --git a/engines/voyeur/graphics.cpp b/engines/voyeur/screen.cpp index a20e9f6006..62f609c5c7 100644 --- a/engines/voyeur/graphics.cpp +++ b/engines/voyeur/screen.cpp @@ -20,7 +20,7 @@ * */ -#include "voyeur/graphics.h" +#include "voyeur/screen.h" #include "voyeur/voyeur.h" #include "voyeur/staticres.h" #include "engines/util.h" @@ -38,7 +38,8 @@ DrawInfo::DrawInfo(int penColor, const Common::Point &pos) { /*------------------------------------------------------------------------*/ -GraphicsManager::GraphicsManager(VoyeurEngine *vm) : _defaultDrawInfo(1, Common::Point()), _drawPtr(&_defaultDrawInfo), _vm(vm) { +Screen::Screen(VoyeurEngine *vm) : Graphics::Screen(), _vm(vm), _drawPtr(&_defaultDrawInfo), + _defaultDrawInfo(1, Common::Point()) { _SVGAMode = 0; _planeSelect = 0; _saveBack = true; @@ -52,18 +53,17 @@ GraphicsManager::GraphicsManager(VoyeurEngine *vm) : _defaultDrawInfo(1, Common: _backColors = nullptr; } -void GraphicsManager::sInitGraphics() { +void Screen::sInitGraphics() { initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false); - _screenSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8()); + create(SCREEN_WIDTH, SCREEN_HEIGHT); clearPalette(); } -GraphicsManager::~GraphicsManager() { - _screenSurface.free(); +Screen::~Screen() { delete _fontChar; } -void GraphicsManager::setupMCGASaveRect(ViewPortResource *viewPort) { +void Screen::setupMCGASaveRect(ViewPortResource *viewPort) { if (viewPort->_activePage) { viewPort->_activePage->_flags |= DISPFLAG_1; Common::Rect *clipRect = _clipPtr; @@ -77,7 +77,7 @@ void GraphicsManager::setupMCGASaveRect(ViewPortResource *viewPort) { viewPort->_rectListCount[1] = -1; } -void GraphicsManager::addRectOptSaveRect(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) { +void Screen::addRectOptSaveRect(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) { if (viewPort->_rectListCount[idx] == -1) return; @@ -86,7 +86,7 @@ void GraphicsManager::addRectOptSaveRect(ViewPortResource *viewPort, int idx, co ++viewPort->_rectListCount[idx]; } -void GraphicsManager::restoreMCGASaveRect(ViewPortResource *viewPort) { +void Screen::restoreMCGASaveRect(ViewPortResource *viewPort) { if (viewPort->_rectListCount[0] != -1) { for (int i = 0; i < viewPort->_rectListCount[0]; ++i) { addRectOptSaveRect(viewPort, 1, (*viewPort->_rectListPtr[0])[i]); @@ -106,11 +106,11 @@ void GraphicsManager::restoreMCGASaveRect(ViewPortResource *viewPort) { viewPort->_rectListCount[1] = count; } -void GraphicsManager::addRectNoSaveBack(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) { +void Screen::addRectNoSaveBack(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) { // Stubbed/dummy method in the original. } -void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *destDisplay, +void Screen::sDrawPic(DisplayResource *srcDisplay, DisplayResource *destDisplay, const Common::Point &initialOffset) { int width1, width2; int widthDiff, widthDiff2; @@ -128,7 +128,8 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des int runLength; byte *srcImgData, *destImgData; - byte *srcP, *destP; + const byte *srcP; + byte *destP; byte byteVal, byteVal2; PictureResource *srcPic; @@ -292,7 +293,7 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des // loc_2566F if (srcFlags & DISPFLAG_2) { // loc_256FA - srcP = (byte *)_screenSurface.getPixels() + srcOffset; + srcP = (const byte *)getPixels() + srcOffset; for (int yp = 0; yp < height1; ++yp) { for (int xp = 0; xp < width2; ++xp, ++srcP, ++destP) { @@ -325,13 +326,16 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des } } else { // loc_25829 - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { Common::copy(srcP, srcP + width2, destP); srcP += width2 + widthDiff; destP += width2 + widthDiff2; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } } } else { @@ -341,13 +345,16 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des error("TODO: sDrawPic variation"); } else { // loc_2606D - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { Common::copy(srcP, srcP + width2, destP); destP += width2 + widthDiff2; srcP += width2 + widthDiff; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } } } else { @@ -530,11 +537,14 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des // loc_27477 if (destFlags & DISPFLAG_8) { // loc_27481 - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { Common::fill(destP, destP + width2, onOff); destP += width2 + widthDiff2; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } else { // loc_2753C destP = destImgData + screenOffset; @@ -561,7 +571,7 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des if (srcFlags & PICFLAG_100) { if (isClipped) { // loc_266E3 - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; tmpWidth = (tmpWidth < 0) ? -tmpWidth : 0; int xMax = tmpWidth + width2; tmpHeight = (tmpHeight < 0) ? -tmpHeight : 0; @@ -592,9 +602,12 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des if (yp >= tmpHeight) destP += widthDiff2; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } else { // loc_26815 - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { for (int xi = 0; xi < width2; ++xi, ++destP) { @@ -618,10 +631,13 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des destP += widthDiff2; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } } else { // Direct screen write - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { for (int xp = 0; xp < width2; ++xp, ++srcP, ++destP) { @@ -631,6 +647,9 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des destP += widthDiff2; srcP += widthDiff; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } } else if (srcFlags & PICFLAG_100) { srcP = srcImgData; @@ -663,7 +682,7 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des } } else { // loc_26BD5 - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { byteVal2 = 0; @@ -684,10 +703,13 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des destP += widthDiff2; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } } else { // loc_26C9A - destP = (byte *)_screenSurface.getPixels() + screenOffset; + destP = (byte *)getPixels() + screenOffset; for (int yp = 0; yp < height1; ++yp) { for (int xp = 0; xp < width2; ++xp, ++srcP, ++destP) { @@ -696,6 +718,9 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des destP += widthDiff2; srcP += widthDiff; } + + addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2, + offset.y + height1)); } } else { // loc_26D2F @@ -850,12 +875,12 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des } } -void GraphicsManager::drawANumber(DisplayResource *display, int num, const Common::Point &pt) { +void Screen::drawANumber(DisplayResource *display, int num, const Common::Point &pt) { PictureResource *pic = _vm->_bVoy->boltEntry(num + 261)._picResource; sDrawPic(pic, display, pt); } -void GraphicsManager::fillPic(DisplayResource *display, byte onOff) { +void Screen::fillPic(DisplayResource *display, byte onOff) { PictureResource *pic; if (display->_flags & DISPFLAG_VIEWPORT) { pic = ((ViewPortResource *)display)->_currentPic; @@ -876,11 +901,11 @@ void GraphicsManager::fillPic(DisplayResource *display, byte onOff) { /** * Queues the given picture for display */ -void GraphicsManager::sDisplayPic(PictureResource *pic) { +void Screen::sDisplayPic(PictureResource *pic) { _vm->_eventsManager->_intPtr._flipWait = true; } -void GraphicsManager::flipPage() { +void Screen::flipPage() { Common::Array<ViewPortResource *> &viewPorts = _viewPortListPtr->_entries; bool flipFlag = false; @@ -907,7 +932,7 @@ void GraphicsManager::flipPage() { } } -void GraphicsManager::restoreBack(Common::Array<Common::Rect> &rectList, int rectListCount, +void Screen::restoreBack(Common::Array<Common::Rect> &rectList, int rectListCount, PictureResource *srcPic, PictureResource *destPic) { // WORKAROUND: Since _backgroundPage can point to a resource freed at the end of display methods, // I'm now explicitly resetting it to null in screenReset(), so at this point it can be null @@ -929,33 +954,26 @@ void GraphicsManager::restoreBack(Common::Array<Common::Rect> &rectList, int rec _saveBack = saveBack; } -void GraphicsManager::clearPalette() { - byte palette[768]; - Common::fill(&palette[0], &palette[768], 0); - g_system->getPaletteManager()->setPalette(&palette[0], 0, 256); -} - -void GraphicsManager::setPalette(const byte *palette, int start, int count) { - g_system->getPaletteManager()->setPalette(palette, start, count); +void Screen::setPalette(const byte *palette, int start, int count) { + Graphics::Screen::setPalette(palette, start, count); _vm->_eventsManager->_gameData._hasPalette = false; } -void GraphicsManager::setPalette128(const byte *palette, int start, int count) { +void Screen::setPalette128(const byte *palette, int start, int count) { byte rgb[3]; - g_system->getPaletteManager()->grabPalette(&rgb[0], 128, 1); - g_system->getPaletteManager()->setPalette(palette, start, count); - g_system->getPaletteManager()->setPalette(&rgb[0], 128, 1); + getPalette(&rgb[0], 128, 1); + Graphics::Screen::setPalette(palette, start, count); + Graphics::Screen::setPalette(&rgb[0], 128, 1); } - -void GraphicsManager::resetPalette() { +void Screen::resetPalette() { for (int i = 0; i < 256; ++i) setColor(i, 0, 0, 0); _vm->_eventsManager->_intPtr._hasPalette = true; } -void GraphicsManager::setColor(int idx, byte r, byte g, byte b) { +void Screen::setColor(int idx, byte r, byte g, byte b) { byte *vgaP = &_VGAColors[idx * 3]; vgaP[0] = r; vgaP[1] = g; @@ -965,7 +983,7 @@ void GraphicsManager::setColor(int idx, byte r, byte g, byte b) { _vm->_eventsManager->_intPtr._palEndIndex = MAX(_vm->_eventsManager->_intPtr._palEndIndex, idx); } -void GraphicsManager::setOneColor(int idx, byte r, byte g, byte b) { +void Screen::setOneColor(int idx, byte r, byte g, byte b) { byte palEntry[3]; palEntry[0] = r; palEntry[1] = g; @@ -973,7 +991,7 @@ void GraphicsManager::setOneColor(int idx, byte r, byte g, byte b) { g_system->getPaletteManager()->setPalette(&palEntry[0], idx, 1); } -void GraphicsManager::setColors(int start, int count, const byte *pal) { +void Screen::setColors(int start, int count, const byte *pal) { for (int i = 0; i < count; ++i) { if ((i + start) != 128) { const byte *rgb = pal + i * 3; @@ -984,7 +1002,7 @@ void GraphicsManager::setColors(int start, int count, const byte *pal) { _vm->_eventsManager->_intPtr._hasPalette = true; } -void GraphicsManager::screenReset() { +void Screen::screenReset() { resetPalette(); _backgroundPage = NULL; @@ -994,7 +1012,7 @@ void GraphicsManager::screenReset() { _vm->flipPageAndWait(); } -void GraphicsManager::fadeDownICF1(int steps) { +void Screen::fadeDownICF1(int steps) { if (steps > 0) { int stepAmount = _vm->_voy->_fadingAmount2 / steps; @@ -1007,7 +1025,7 @@ void GraphicsManager::fadeDownICF1(int steps) { _vm->_voy->_fadingAmount2 = 0; } -void GraphicsManager::fadeUpICF1(int steps) { +void Screen::fadeUpICF1(int steps) { if (steps > 0) { int stepAmount = (63 - _vm->_voy->_fadingAmount2) / steps; @@ -1020,7 +1038,7 @@ void GraphicsManager::fadeUpICF1(int steps) { _vm->_voy->_fadingAmount2 = 63; } -void GraphicsManager::fadeDownICF(int steps) { +void Screen::fadeDownICF(int steps) { if (steps > 0) { _vm->_eventsManager->hideCursor(); int stepAmount1 = _vm->_voy->_fadingAmount1 / steps; @@ -1037,14 +1055,19 @@ void GraphicsManager::fadeDownICF(int steps) { _vm->_voy->_fadingAmount2 = 0; } -void GraphicsManager::drawDot() { - for (int y = 0; y < 9; ++y) { - byte *pDest = (byte *)_screenSurface.getPixels() + DOT_LINE_START[y] + DOT_LINE_OFFSET[y]; - Common::fill(pDest, pDest + DOT_LINE_LENGTH[y], 0x80); +void Screen::drawDot() { + for (int idx = 0; idx < 9; ++idx) { + uint offset = DOT_LINE_START[idx] + DOT_LINE_OFFSET[idx]; + int xp = offset % SCREEN_WIDTH; + int yp = offset / SCREEN_WIDTH; + + byte *pDest = (byte *)getPixels() + offset; + Common::fill(pDest, pDest + DOT_LINE_LENGTH[idx], 0x80); + addDirtyRect(Common::Rect(xp, yp, xp + DOT_LINE_LENGTH[idx], yp + 1)); } } -void GraphicsManager::synchronize(Common::Serializer &s) { +void Screen::synchronize(Common::Serializer &s) { s.syncBytes(&_VGAColors[0], PALETTE_SIZE); } diff --git a/engines/voyeur/graphics.h b/engines/voyeur/screen.h index e4d0b38650..aaf61747a4 100644 --- a/engines/voyeur/graphics.h +++ b/engines/voyeur/screen.h @@ -27,17 +27,15 @@ #include "common/array.h" #include "common/rect.h" #include "common/serializer.h" -#include "graphics/surface.h" +#include "graphics/screen.h" namespace Voyeur { #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 200 -#define PALETTE_COUNT 256 -#define PALETTE_SIZE (256 * 3) class VoyeurEngine; -class GraphicsManager; +class Screen; class DisplayResource; class PictureResource; class ViewPortResource; @@ -54,12 +52,12 @@ public: DrawInfo(int penColor, const Common::Point &pos); }; -typedef void (GraphicsManager::*GraphicMethodPtr)(); -typedef void (GraphicsManager::*ViewPortSetupPtr)(ViewPortResource *); -typedef void (GraphicsManager::*ViewPortAddPtr)(ViewPortResource *, int idx, const Common::Rect &bounds); -typedef void (GraphicsManager::*ViewPortRestorePtr)(ViewPortResource *); +typedef void (Screen::*ScreenMethodPtr)(); +typedef void (Screen::*ViewPortSetupPtr)(ViewPortResource *); +typedef void (Screen::*ViewPortAddPtr)(ViewPortResource *, int idx, const Common::Rect &bounds); +typedef void (Screen::*ViewPortRestorePtr)(ViewPortResource *); -class GraphicsManager { +class Screen: public Graphics::Screen { public: byte _VGAColors[PALETTE_SIZE]; PictureResource *_backgroundPage; @@ -69,7 +67,6 @@ public: bool _saveBack; Common::Rect *_clipPtr; uint _planeSelect; - Graphics::Surface _screenSurface; CMapResource *_backColors; FontInfoResource *_fontPtr; PictureResource *_fontChar; @@ -81,8 +78,8 @@ private: void restoreBack(Common::Array<Common::Rect> &rectList, int rectListCount, PictureResource *srcPic, PictureResource *destPic); public: - GraphicsManager(VoyeurEngine *vm); - ~GraphicsManager(); + Screen(VoyeurEngine *vm); + virtual ~Screen(); void sInitGraphics(); @@ -96,7 +93,6 @@ public: void sDisplayPic(PictureResource *pic); void drawANumber(DisplayResource *display, int num, const Common::Point &pt); void flipPage(); - void clearPalette(); void setPalette(const byte *palette, int start, int count); void setPalette128(const byte *palette, int start, int count); void resetPalette(); diff --git a/engines/voyeur/voyeur.cpp b/engines/voyeur/voyeur.cpp index cbb6846340..01b76a72d1 100644 --- a/engines/voyeur/voyeur.cpp +++ b/engines/voyeur/voyeur.cpp @@ -22,7 +22,7 @@ #include "voyeur/voyeur.h" #include "voyeur/animation.h" -#include "voyeur/graphics.h" +#include "voyeur/screen.h" #include "voyeur/staticres.h" #include "common/scummsys.h" #include "common/config-manager.h" @@ -40,7 +40,7 @@ VoyeurEngine::VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc) _debugger = nullptr; _eventsManager = nullptr; _filesManager = nullptr; - _graphicsManager = nullptr; + _screen = nullptr; _soundManager = nullptr; _voy = nullptr; _bVoy = NULL; @@ -65,13 +65,6 @@ VoyeurEngine::VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc) DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts"); - _debugger = new Debugger(this); - _eventsManager = new EventsManager(this); - _filesManager = new FilesManager(this); - _graphicsManager = new GraphicsManager(this); - _soundManager = new SoundManager(_mixer); - _voy = new SVoy(this); - _stampLibPtr = nullptr; _controlGroupPtr = nullptr; _stampData = nullptr; @@ -88,7 +81,7 @@ VoyeurEngine::~VoyeurEngine() { delete _bVoy; delete _voy; delete _soundManager; - delete _graphicsManager; + delete _screen; delete _filesManager; delete _eventsManager; delete _debugger; @@ -126,15 +119,22 @@ void VoyeurEngine::ESP_Init() { } void VoyeurEngine::globalInitBolt() { + _debugger = new Debugger(this); + _eventsManager = new EventsManager(this); + _filesManager = new FilesManager(this); + _screen = new Screen(this); + _soundManager = new SoundManager(_mixer); + _voy = new SVoy(this); + initBolt(); _filesManager->openBoltLib("bvoy.blt", _bVoy); _bVoy->getBoltGroup(0x000); _bVoy->getBoltGroup(0x100); - _graphicsManager->_fontPtr = &_defaultFontInfo; - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; - assert(_graphicsManager->_fontPtr->_curFont); + _screen->_fontPtr = &_defaultFontInfo; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; + assert(_screen->_fontPtr->_curFont); // Setup default flags _voy->_viewBounds = nullptr; @@ -144,13 +144,13 @@ void VoyeurEngine::globalInitBolt() { void VoyeurEngine::initBolt() { vInitInterrupts(); - _graphicsManager->sInitGraphics(); + _screen->sInitGraphics(); _eventsManager->vInitColor(); initInput(); } void VoyeurEngine::vInitInterrupts() { - _eventsManager->_intPtr._palette = &_graphicsManager->_VGAColors[0]; + _eventsManager->_intPtr._palette = &_screen->_VGAColors[0]; } void VoyeurEngine::initInput() { @@ -213,8 +213,8 @@ bool VoyeurEngine::doHeadTitle() { } void VoyeurEngine::showConversionScreen() { - _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x502)._picResource; - _graphicsManager->_vPort->setupViewPort(); + _screen->_backgroundPage = _bVoy->boltEntry(0x502)._picResource; + _screen->_vPort->setupViewPort(); flipPageAndWait(); // Immediate palette load to show the initial screen @@ -237,7 +237,7 @@ void VoyeurEngine::showConversionScreen() { flipPageAndWaitForFade(); - _graphicsManager->screenReset(); + _screen->screenReset(); } bool VoyeurEngine::doLock() { @@ -249,28 +249,28 @@ bool VoyeurEngine::doLock() { if (_bVoy->getBoltGroup(0x700)) { Common::String password = "3333"; - _graphicsManager->_backgroundPage = _bVoy->getPictureResource(0x700); - _graphicsManager->_backColors = _bVoy->getCMapResource(0x701); + _screen->_backgroundPage = _bVoy->getPictureResource(0x700); + _screen->_backColors = _bVoy->getCMapResource(0x701); PictureResource *cursorPic = _bVoy->getPictureResource(0x702); _voy->_viewBounds = _bVoy->boltEntry(0x704)._rectResource; Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(0x705)._rectResource->_entries; assert(cursorPic); - _graphicsManager->_vPort->setupViewPort(); + _screen->_vPort->setupViewPort(); - _graphicsManager->_backColors->startFade(); - _graphicsManager->_vPort->_parent->_flags |= DISPFLAG_8; - _graphicsManager->flipPage(); + _screen->_backColors->startFade(); + _screen->_vPort->_parent->_flags |= DISPFLAG_8; + _screen->flipPage(); _eventsManager->sWaitFlip(); while (!shouldQuit() && (_eventsManager->_fadeStatus & 1)) _eventsManager->delay(1); _eventsManager->setCursorColor(127, 0); - _graphicsManager->setColor(1, 64, 64, 64); - _graphicsManager->setColor(2, 96, 96, 96); - _graphicsManager->setColor(3, 160, 160, 160); - _graphicsManager->setColor(4, 224, 224, 224); + _screen->setColor(1, 64, 64, 64); + _screen->setColor(2, 96, 96, 96); + _screen->setColor(3, 160, 160, 160); + _screen->setColor(4, 224, 224, 224); // Set up the cursor _eventsManager->setCursor(cursorPic); @@ -278,9 +278,9 @@ bool VoyeurEngine::doLock() { _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x708)._fontResource; - _graphicsManager->_fontPtr->_fontSaveBack = 0; - _graphicsManager->_fontPtr->_fontFlags = DISPFLAG_NONE; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x708)._fontResource; + _screen->_fontPtr->_fontSaveBack = 0; + _screen->_fontPtr->_fontFlags = DISPFLAG_NONE; Common::String dateString = "ScummVM"; Common::String displayString = Common::String::format("Last Play %s", dateString.c_str()); @@ -288,16 +288,16 @@ bool VoyeurEngine::doLock() { bool firstLoop = true; bool breakFlag = false; while (!breakFlag && !shouldQuit()) { - _graphicsManager->_vPort->setupViewPort(); + _screen->_vPort->setupViewPort(); flipPageAndWait(); // Display the last play time - _graphicsManager->_fontPtr->_pos = Common::Point(0, 97); - _graphicsManager->_fontPtr->_justify = ALIGN_CENTER; - _graphicsManager->_fontPtr->_justifyWidth = 384; - _graphicsManager->_fontPtr->_justifyHeight = 97; + _screen->_fontPtr->_pos = Common::Point(0, 97); + _screen->_fontPtr->_justify = ALIGN_CENTER; + _screen->_fontPtr->_justifyWidth = 384; + _screen->_fontPtr->_justifyHeight = 97; - _graphicsManager->_vPort->drawText(displayString); + _screen->_vPort->drawText(displayString); flipPageAndWait(); if (firstLoop) { @@ -356,7 +356,7 @@ bool VoyeurEngine::doLock() { } else if (key == 11) { // New code if ((password.empty() && displayString.empty()) || (password != displayString)) { - _graphicsManager->_vPort->setupViewPort(); + _screen->_vPort->setupViewPort(); password = displayString; displayString = ""; continue; @@ -373,9 +373,9 @@ bool VoyeurEngine::doLock() { _soundManager->playVOCMap(wrongVoc, wrongVocSize); } - _graphicsManager->fillPic(_graphicsManager->_vPort, 0); + _screen->fillPic(_screen->_vPort, 0); flipPageAndWait(); - _graphicsManager->resetPalette(); + _screen->resetPalette(); _voy->_viewBounds = nullptr; _bVoy->freeBoltGroup(0x700); @@ -393,9 +393,9 @@ void VoyeurEngine::showTitleScreen() { if (!_bVoy->getBoltGroup(0x500)) return; - _graphicsManager->_backgroundPage = _bVoy->getPictureResource(0x500); + _screen->_backgroundPage = _bVoy->getPictureResource(0x500); - _graphicsManager->_vPort->setupViewPort(); + _screen->_vPort->setupViewPort(); flipPageAndWait(); // Immediate palette load to show the initial screen @@ -422,18 +422,18 @@ void VoyeurEngine::showTitleScreen() { return; } - _graphicsManager->screenReset(); + _screen->screenReset(); _eventsManager->delayClick(200); // Voyeur title playRL2Video("a1100100.rl2"); - _graphicsManager->screenReset(); + _screen->screenReset(); _bVoy->freeBoltGroup(0x500); } void VoyeurEngine::doOpening() { - _graphicsManager->screenReset(); + _screen->screenReset(); if (!_bVoy->getBoltGroup(0x200)) return; @@ -459,10 +459,10 @@ void VoyeurEngine::doOpening() { _voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED; for (int i = 0; i < 256; ++i) - _graphicsManager->setColor(i, 8, 8, 8); + _screen->setColor(i, 8, 8, 8); _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_vPort->setupViewPort(); + _screen->_vPort->setupViewPort(); flipPageAndWait(); RL2Decoder decoder; @@ -472,14 +472,12 @@ void VoyeurEngine::doOpening() { while (!shouldQuit() && !decoder.endOfVideo() && !_eventsManager->_mouseClicked) { if (decoder.hasDirtyPalette()) { const byte *palette = decoder.getPalette(); - _graphicsManager->setPalette(palette, 0, 256); + _screen->setPalette(palette, 0, 256); } if (decoder.needsUpdate()) { const Graphics::Surface *frame = decoder.decodeNextFrame(); - - Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200, - (byte *)_graphicsManager->_screenSurface.getPixels()); + _screen->blitFrom(*frame); if (decoder.getCurFrame() >= (int32)READ_LE_UINT32(frameTable + frameIndex * 4)) { if (creditShow) { @@ -499,7 +497,7 @@ void VoyeurEngine::doOpening() { } if (textPic) { - _graphicsManager->sDrawPic(textPic, _graphicsManager->_vPort, textPos); + _screen->sDrawPic(textPic, _screen->_vPort, textPos); } flipPageAndWait(); @@ -527,14 +525,12 @@ void VoyeurEngine::playRL2Video(const Common::String &filename) { while (!shouldQuit() && !decoder.endOfVideo() && !_eventsManager->_mouseClicked) { if (decoder.hasDirtyPalette()) { const byte *palette = decoder.getPalette(); - _graphicsManager->setPalette(palette, 0, 256); + _screen->setPalette(palette, 0, 256); } if (decoder.needsUpdate()) { const Graphics::Surface *frame = decoder.decodeNextFrame(); - - Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200, - (byte *)_graphicsManager->_screenSurface.getPixels()); + _screen->blitFrom(*frame); } _eventsManager->getMouseInfo(); @@ -573,17 +569,16 @@ void VoyeurEngine::playAVideoDuration(int videoId, int duration) { (decoder.getCurFrame() < endFrame)) { if (decoder.needsUpdate()) { const Graphics::Surface *frame = decoder.decodeNextFrame(); + _screen->blitFrom(*frame); - Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200, - (byte *)_graphicsManager->_screenSurface.getPixels()); if (_voy->_eventFlags & EVTFLAG_RECORDING) - _graphicsManager->drawDot(); + _screen->drawDot(); } if (decoder.hasDirtyPalette()) { const byte *palette = decoder.getPalette(); - _graphicsManager->setPalette(palette, 0, decoder.getPaletteCount()); - _graphicsManager->setOneColor(128, 220, 20, 20); + _screen->setPalette(palette, 0, decoder.getPaletteCount()); + _screen->setOneColor(128, 220, 20, 20); } _eventsManager->getMouseInfo(); @@ -591,13 +586,13 @@ void VoyeurEngine::playAVideoDuration(int videoId, int duration) { } // RL2 finished - _graphicsManager->screenReset(); + _screen->screenReset(); _voy->_eventFlags &= ~EVTFLAG_RECORDING; if (_voy->_eventFlags & EVTFLAG_8) { assert(pic); - byte *imgData = _graphicsManager->_vPort->_currentPic->_imgData; - _graphicsManager->_vPort->_currentPic->_imgData = pic->_imgData; + byte *imgData = _screen->_vPort->_currentPic->_imgData; + _screen->_vPort->_currentPic->_imgData = pic->_imgData; pic->_imgData = imgData; _voy->_eventFlags &= ~EVTFLAG_8; } @@ -608,13 +603,13 @@ void VoyeurEngine::playAVideoDuration(int videoId, int duration) { void VoyeurEngine::playAudio(int audioId) { _bVoy->getBoltGroup(0x7F00); - _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x7F00 + + _screen->_backgroundPage = _bVoy->boltEntry(0x7F00 + BLIND_TABLE[audioId] * 2)._picResource; - _graphicsManager->_backColors = _bVoy->boltEntry(0x7F01 + + _screen->_backColors = _bVoy->boltEntry(0x7F01 + BLIND_TABLE[audioId] * 2)._cMapResource; - _graphicsManager->_vPort->setupViewPort(); - _graphicsManager->_backColors->startFade(); + _screen->_vPort->setupViewPort(); + _screen->_backColors->startFade(); flipPageAndWaitForFade(); _voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED; @@ -633,26 +628,26 @@ void VoyeurEngine::playAudio(int audioId) { _soundManager->stopVOCPlay(); _bVoy->freeBoltGroup(0x7F00); - _graphicsManager->_vPort->setupViewPort(NULL); + _screen->_vPort->setupViewPort(NULL); _voy->_eventFlags &= ~EVTFLAG_RECORDING; _voy->_playStampMode = 129; } void VoyeurEngine::doTransitionCard(const Common::String &time, const Common::String &location) { - _graphicsManager->setColor(128, 16, 16, 16); - _graphicsManager->setColor(224, 220, 220, 220); + _screen->setColor(128, 16, 16, 16); + _screen->setColor(224, 220, 220, 220); _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_vPort->setupViewPort(NULL); - _graphicsManager->_vPort->fillPic(0x80); - _graphicsManager->flipPage(); + _screen->_vPort->setupViewPort(NULL); + _screen->_vPort->fillPic(0x80); + _screen->flipPage(); _eventsManager->sWaitFlip(); flipPageAndWait(); - _graphicsManager->_vPort->fillPic(0x80); + _screen->_vPort->fillPic(0x80); - FontInfoResource &fi = *_graphicsManager->_fontPtr; + FontInfoResource &fi = *_screen->_fontPtr; fi._curFont = _bVoy->boltEntry(257)._fontResource; fi._foreColor = 224; fi._fontSaveBack = 0; @@ -661,7 +656,7 @@ void VoyeurEngine::doTransitionCard(const Common::String &time, const Common::St fi._justifyWidth = 384; fi._justifyHeight = 120; - _graphicsManager->_vPort->drawText(time); + _screen->_vPort->drawText(time); if (!location.empty()) { fi._pos = Common::Point(0, 138); @@ -669,7 +664,7 @@ void VoyeurEngine::doTransitionCard(const Common::String &time, const Common::St fi._justifyWidth = 384; fi._justifyHeight = 140; - _graphicsManager->_vPort->drawText(location); + _screen->_vPort->drawText(location); } flipPageAndWait(); @@ -680,8 +675,8 @@ void VoyeurEngine::saveLastInplay() { } void VoyeurEngine::flipPageAndWait() { - _graphicsManager->_vPort->_flags |= DISPFLAG_8; - _graphicsManager->flipPage(); + _screen->_vPort->_flags |= DISPFLAG_8; + _screen->flipPage(); _eventsManager->sWaitFlip(); } @@ -702,7 +697,7 @@ void VoyeurEngine::showEndingNews() { PictureResource *pic = _bVoy->boltEntry(_playStampGroupId)._picResource; CMapResource *pal = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource; - _graphicsManager->_vPort->setupViewPort(pic); + _screen->_vPort->setupViewPort(pic); pal->startFade(); flipPageAndWaitForFade(); @@ -717,7 +712,7 @@ void VoyeurEngine::showEndingNews() { pal = _bVoy->boltEntry(_playStampGroupId + idx * 2 + 1)._cMapResource; } - _graphicsManager->_vPort->setupViewPort(pic); + _screen->_vPort->setupViewPort(pic); pal->startFade(); flipPageAndWaitForFade(); @@ -852,7 +847,7 @@ void VoyeurEngine::synchronize(Common::Serializer &s) { // Sub-systems _voy->synchronize(s); - _graphicsManager->synchronize(s); + _screen->synchronize(s); _mainThread->synchronize(s); _controlPtr->_state->synchronize(s); } @@ -906,8 +901,8 @@ void VoyeurSavegameHeader::write(Common::OutSaveFile *f, VoyeurEngine *vm, const // Create a thumbnail and save it Graphics::Surface *thumb = new Graphics::Surface(); - ::createThumbnail(thumb, (byte *)vm->_graphicsManager->_screenSurface.getPixels(), - SCREEN_WIDTH, SCREEN_HEIGHT, vm->_graphicsManager->_VGAColors); + ::createThumbnail(thumb, (const byte *)vm->_screen->getPixels(), + SCREEN_WIDTH, SCREEN_HEIGHT, vm->_screen->_VGAColors); Graphics::saveThumbnail(*f, *thumb); thumb->free(); delete thumb; diff --git a/engines/voyeur/voyeur.h b/engines/voyeur/voyeur.h index e0bb734fa8..9cda85fd51 100644 --- a/engines/voyeur/voyeur.h +++ b/engines/voyeur/voyeur.h @@ -27,7 +27,7 @@ #include "voyeur/data.h" #include "voyeur/events.h" #include "voyeur/files.h" -#include "voyeur/graphics.h" +#include "voyeur/screen.h" #include "voyeur/sound.h" #include "common/scummsys.h" #include "common/system.h" @@ -164,7 +164,7 @@ public: Debugger *_debugger; EventsManager *_eventsManager; FilesManager *_filesManager; - GraphicsManager *_graphicsManager; + Screen *_screen; SoundManager *_soundManager; SVoy *_voy; diff --git a/engines/voyeur/voyeur_game.cpp b/engines/voyeur/voyeur_game.cpp index 13ef31839a..e9591955fc 100644 --- a/engines/voyeur/voyeur_game.cpp +++ b/engines/voyeur/voyeur_game.cpp @@ -149,8 +149,8 @@ void VoyeurEngine::playStamp() { case 130: { // user selected to send the tape if (_bVoy->getBoltGroup(_playStampGroupId)) { - _graphicsManager->_backgroundPage = _bVoy->boltEntry(_playStampGroupId)._picResource; - _graphicsManager->_backColors = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource; + _screen->_backgroundPage = _bVoy->boltEntry(_playStampGroupId)._picResource; + _screen->_backColors = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource; buttonId = getChooseButton(); if (_eventsManager->_rightClick) @@ -158,7 +158,7 @@ void VoyeurEngine::playStamp() { buttonId = 4; _bVoy->freeBoltGroup(_playStampGroupId); - _graphicsManager->screenReset(); + _screen->screenReset(); _playStampGroupId = -1; flag = true; @@ -232,8 +232,8 @@ void VoyeurEngine::closeStamp() { } void VoyeurEngine::doTailTitle() { - _graphicsManager->_vPort->setupViewPort(NULL); - _graphicsManager->screenReset(); + _screen->_vPort->setupViewPort(NULL); + _screen->screenReset(); if (_bVoy->getBoltGroup(0x600)) { RL2Decoder decoder; @@ -245,12 +245,12 @@ void VoyeurEngine::doTailTitle() { doClosingCredits(); if (!shouldQuit() && !_eventsManager->_mouseClicked) { - _graphicsManager->screenReset(); + _screen->screenReset(); PictureResource *pic = _bVoy->boltEntry(0x602)._picResource; CMapResource *pal = _bVoy->boltEntry(0x603)._cMapResource; - _graphicsManager->_vPort->setupViewPort(pic); + _screen->_vPort->setupViewPort(pic); pal->startFade(); flipPageAndWaitForFade(); _eventsManager->delayClick(300); @@ -258,7 +258,7 @@ void VoyeurEngine::doTailTitle() { pic = _bVoy->boltEntry(0x604)._picResource; pal = _bVoy->boltEntry(0x605)._cMapResource; - _graphicsManager->_vPort->setupViewPort(pic); + _screen->_vPort->setupViewPort(pic); pal->startFade(); flipPageAndWaitForFade(); _eventsManager->delayClick(120); @@ -283,26 +283,26 @@ void VoyeurEngine::doClosingCredits() { const char *msg = (const char *)_bVoy->memberAddr(0x404); const byte *creditList = (const byte *)_bVoy->memberAddr(0x405); - _graphicsManager->_vPort->setupViewPort(NULL); - _graphicsManager->setColor(1, 180, 180, 180); - _graphicsManager->setColor(2, 200, 200, 200); + _screen->_vPort->setupViewPort(NULL); + _screen->setColor(1, 180, 180, 180); + _screen->setColor(2, 200, 200, 200); _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x402)._fontResource; - _graphicsManager->_fontPtr->_foreColor = 2; - _graphicsManager->_fontPtr->_backColor = 2; - _graphicsManager->_fontPtr->_fontSaveBack = false; - _graphicsManager->_fontPtr->_fontFlags = DISPFLAG_NONE; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x402)._fontResource; + _screen->_fontPtr->_foreColor = 2; + _screen->_fontPtr->_backColor = 2; + _screen->_fontPtr->_fontSaveBack = false; + _screen->_fontPtr->_fontFlags = DISPFLAG_NONE; _soundManager->startVOCPlay(152); - FontInfoResource &fi = *_graphicsManager->_fontPtr; + FontInfoResource &fi = *_screen->_fontPtr; for (int idx = 0; idx < 78; ++idx) { const byte *entry = creditList + idx * 6; int flags = READ_LE_UINT16(entry + 4); if (flags & 0x10) - _graphicsManager->_vPort->fillPic(0); + _screen->_vPort->fillPic(0); if (flags & 1) { fi._foreColor = 1; @@ -312,7 +312,7 @@ void VoyeurEngine::doClosingCredits() { fi._justifyHeight = 240; fi._pos = Common::Point(0, READ_LE_UINT16(entry)); - _graphicsManager->_vPort->drawText(msg); + _screen->_vPort->drawText(msg); msg += strlen(msg) + 1; } @@ -324,7 +324,7 @@ void VoyeurEngine::doClosingCredits() { fi._justifyHeight = 240; fi._pos = Common::Point(0, READ_LE_UINT16(entry)); - _graphicsManager->_vPort->drawText(msg); + _screen->_vPort->drawText(msg); msg += strlen(msg) + 1; } @@ -336,7 +336,7 @@ void VoyeurEngine::doClosingCredits() { fi._justifyHeight = 240; fi._pos = Common::Point(38, READ_LE_UINT16(entry)); - _graphicsManager->_vPort->drawText(msg); + _screen->_vPort->drawText(msg); msg += strlen(msg) + 1; fi._foreColor = 2; @@ -345,7 +345,7 @@ void VoyeurEngine::doClosingCredits() { fi._justifyHeight = 240; fi._pos = Common::Point(198, READ_LE_UINT16(entry)); - _graphicsManager->_vPort->drawText(msg); + _screen->_vPort->drawText(msg); msg += strlen(msg) + 1; } @@ -357,7 +357,7 @@ void VoyeurEngine::doClosingCredits() { fi._justifyHeight = 240; fi._pos = Common::Point(0, READ_LE_UINT16(entry)); - _graphicsManager->_vPort->drawText(msg); + _screen->_vPort->drawText(msg); msg += strlen(msg) + 1; fi._foreColor = 2; @@ -367,7 +367,7 @@ void VoyeurEngine::doClosingCredits() { fi._justifyHeight = 240; fi._pos = Common::Point(0, READ_LE_UINT16(entry) + 13); - _graphicsManager->_vPort->drawText(msg); + _screen->_vPort->drawText(msg); msg += strlen(msg) + 1; } @@ -381,19 +381,19 @@ void VoyeurEngine::doClosingCredits() { } _soundManager->stopVOCPlay(); - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; _bVoy->freeBoltGroup(0x400); } void VoyeurEngine::doPiracy() { - _graphicsManager->screenReset(); - _graphicsManager->setColor(1, 0, 0, 0); - _graphicsManager->setColor(2, 255, 255, 255); + _screen->screenReset(); + _screen->setColor(1, 0, 0, 0); + _screen->setColor(2, 255, 255, 255); _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_vPort->setupViewPort(NULL); - _graphicsManager->_vPort->fillPic(1); + _screen->_vPort->setupViewPort(NULL); + _screen->_vPort->fillPic(1); - FontInfoResource &fi = *_graphicsManager->_fontPtr; + FontInfoResource &fi = *_screen->_fontPtr; fi._curFont = _bVoy->boltEntry(0x101)._fontResource; fi._foreColor = 2; fi._backColor = 2; @@ -406,7 +406,7 @@ void VoyeurEngine::doPiracy() { // Loop through the piracy message array to draw each line for (int idx = 0, yp = 33; idx < 10; ++idx) { fi._pos = Common::Point(0, yp); - _graphicsManager->_vPort->drawText(PIRACY_MESSAGE[idx]); + _screen->_vPort->drawText(PIRACY_MESSAGE[idx]); yp += fi._curFont->_fontHeight + 4; } @@ -439,27 +439,27 @@ void VoyeurEngine::reviewTape() { _voy->_viewBounds = _bVoy->boltEntry(0x907)._rectResource; Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(0x906)._rectResource->_entries; - _graphicsManager->_backColors = _bVoy->boltEntry(0x902)._cMapResource; - _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x901)._picResource; - _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage); - _graphicsManager->_backColors->startFade(); + _screen->_backColors = _bVoy->boltEntry(0x902)._cMapResource; + _screen->_backgroundPage = _bVoy->boltEntry(0x901)._picResource; + _screen->_vPort->setupViewPort(_screen->_backgroundPage); + _screen->_backColors->startFade(); flipPageAndWaitForFade(); - _graphicsManager->setColor(1, 32, 32, 32); - _graphicsManager->setColor(2, 96, 96, 96); - _graphicsManager->setColor(3, 160, 160, 160); - _graphicsManager->setColor(4, 224, 224, 224); - _graphicsManager->setColor(9, 24, 64, 24); - _graphicsManager->setColor(10, 64, 132, 64); - _graphicsManager->setColor(11, 100, 192, 100); - _graphicsManager->setColor(12, 120, 248, 120); + _screen->setColor(1, 32, 32, 32); + _screen->setColor(2, 96, 96, 96); + _screen->setColor(3, 160, 160, 160); + _screen->setColor(4, 224, 224, 224); + _screen->setColor(9, 24, 64, 24); + _screen->setColor(10, 64, 132, 64); + _screen->setColor(11, 100, 192, 100); + _screen->setColor(12, 120, 248, 120); _eventsManager->setCursorColor(128, 1); _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x909)._fontResource; - _graphicsManager->_fontPtr->_fontSaveBack = false; - _graphicsManager->_fontPtr->_fontFlags = DISPFLAG_NONE; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x909)._fontResource; + _screen->_fontPtr->_fontSaveBack = false; + _screen->_fontPtr->_fontFlags = DISPFLAG_NONE; _eventsManager->getMouseInfo(); if (newX == -1) { @@ -481,37 +481,37 @@ void VoyeurEngine::reviewTape() { needRedraw = false; flipPageAndWait(); - _graphicsManager->_drawPtr->_penColor = 0; - _graphicsManager->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top); - _graphicsManager->_backgroundPage->sFillBox(tempRect.width(), tempRect.height()); + _screen->_drawPtr->_penColor = 0; + _screen->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top); + _screen->_backgroundPage->sFillBox(tempRect.width(), tempRect.height()); int yp = 45; int eventNum = eventStart; for (int lineNum = 0; lineNum < 8 && eventNum < _voy->_eventCount; ++lineNum, ++eventNum) { - _graphicsManager->_fontPtr->_picFlags = DISPFLAG_NONE; - _graphicsManager->_fontPtr->_picSelect = 0xff; - _graphicsManager->_fontPtr->_picPick = 7; - _graphicsManager->_fontPtr->_picOnOff = (lineNum == eventLine) ? 8 : 0; - _graphicsManager->_fontPtr->_pos = Common::Point(68, yp); - _graphicsManager->_fontPtr->_justify = ALIGN_LEFT; - _graphicsManager->_fontPtr->_justifyWidth = 0; - _graphicsManager->_fontPtr->_justifyHeight = 0; + _screen->_fontPtr->_picFlags = DISPFLAG_NONE; + _screen->_fontPtr->_picSelect = 0xff; + _screen->_fontPtr->_picPick = 7; + _screen->_fontPtr->_picOnOff = (lineNum == eventLine) ? 8 : 0; + _screen->_fontPtr->_pos = Common::Point(68, yp); + _screen->_fontPtr->_justify = ALIGN_LEFT; + _screen->_fontPtr->_justifyWidth = 0; + _screen->_fontPtr->_justifyHeight = 0; Common::String msg = _eventsManager->getEvidString(eventNum); - _graphicsManager->_backgroundPage->drawText(msg); + _screen->_backgroundPage->drawText(msg); yp += 15; } - _graphicsManager->_vPort->addSaveRect( - _graphicsManager->_vPort->_lastPage, tempRect); + _screen->_vPort->addSaveRect( + _screen->_vPort->_lastPage, tempRect); flipPageAndWait(); - _graphicsManager->_vPort->addSaveRect( - _graphicsManager->_vPort->_lastPage, tempRect); + _screen->_vPort->addSaveRect( + _screen->_vPort->_lastPage, tempRect); } - _graphicsManager->sDrawPic(cursor, _graphicsManager->_vPort, + _screen->sDrawPic(cursor, _screen->_vPort, _eventsManager->getMousePos()); flipPageAndWait(); @@ -543,34 +543,34 @@ void VoyeurEngine::reviewTape() { flipPageAndWait(); - _graphicsManager->_drawPtr->_penColor = 0; - _graphicsManager->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top); - _graphicsManager->_backgroundPage->sFillBox(tempRect.width(), tempRect.height()); + _screen->_drawPtr->_penColor = 0; + _screen->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top); + _screen->_backgroundPage->sFillBox(tempRect.width(), tempRect.height()); int yp = 45; int eventNum = eventStart; for (int idx = 0; idx < 8 && eventNum < _voy->_eventCount; ++idx, ++eventNum) { - _graphicsManager->_fontPtr->_picFlags = DISPFLAG_NONE; - _graphicsManager->_fontPtr->_picSelect = 0xff; - _graphicsManager->_fontPtr->_picPick = 7; - _graphicsManager->_fontPtr->_picOnOff = (idx == eventLine) ? 8 : 0; - _graphicsManager->_fontPtr->_pos = Common::Point(68, yp); - _graphicsManager->_fontPtr->_justify = ALIGN_LEFT; - _graphicsManager->_fontPtr->_justifyWidth = 0; - _graphicsManager->_fontPtr->_justifyHeight = 0; + _screen->_fontPtr->_picFlags = DISPFLAG_NONE; + _screen->_fontPtr->_picSelect = 0xff; + _screen->_fontPtr->_picPick = 7; + _screen->_fontPtr->_picOnOff = (idx == eventLine) ? 8 : 0; + _screen->_fontPtr->_pos = Common::Point(68, yp); + _screen->_fontPtr->_justify = ALIGN_LEFT; + _screen->_fontPtr->_justifyWidth = 0; + _screen->_fontPtr->_justifyHeight = 0; Common::String msg = _eventsManager->getEvidString(eventNum); - _graphicsManager->_backgroundPage->drawText(msg); + _screen->_backgroundPage->drawText(msg); yp += 15; } - _graphicsManager->_vPort->addSaveRect( - _graphicsManager->_vPort->_lastPage, tempRect); + _screen->_vPort->addSaveRect( + _screen->_vPort->_lastPage, tempRect); flipPageAndWait(); - _graphicsManager->_vPort->addSaveRect( - _graphicsManager->_vPort->_lastPage, tempRect); + _screen->_vPort->addSaveRect( + _screen->_vPort->_lastPage, tempRect); flipPageAndWait(); _eventsManager->getMouseInfo(); @@ -650,7 +650,7 @@ void VoyeurEngine::reviewTape() { newY = _eventsManager->getMousePos().y; _voy->_fadingType = 0; _voy->_viewBounds = nullptr; - _graphicsManager->_vPort->setupViewPort(NULL); + _screen->_vPort->setupViewPort(NULL); if (_currentVocId != -1) { _voy->_vocSecondsOffset = _voy->_RTVNum - _voy->_musicStartTime; @@ -673,13 +673,13 @@ void VoyeurEngine::reviewTape() { _voy->_vocSecondsOffset = e._computerOn; _bVoy->getBoltGroup(0x7F00); - _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x7F00 + + _screen->_backgroundPage = _bVoy->boltEntry(0x7F00 + BLIND_TABLE[_audioVideoId])._picResource; - _graphicsManager->_backColors = _bVoy->boltEntry(0x7F01 + + _screen->_backColors = _bVoy->boltEntry(0x7F01 + BLIND_TABLE[_audioVideoId])._cMapResource; - _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage); - _graphicsManager->_backColors->startFade(); + _screen->_vPort->setupViewPort(_screen->_backgroundPage); + _screen->_backColors->startFade(); flipPageAndWaitForFade(); _eventsManager->_intPtr._flashStep = 1; @@ -725,16 +725,16 @@ void VoyeurEngine::reviewTape() { } } - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; - _graphicsManager->_vPort->fillPic(0); + _screen->_vPort->fillPic(0); flipPageAndWait(); _bVoy->freeBoltGroup(0x900); } void VoyeurEngine::doGossip() { - _graphicsManager->resetPalette(); - _graphicsManager->screenReset(); + _screen->resetPalette(); + _screen->screenReset(); if (!_bVoy->getBoltGroup(0x300)) return; @@ -752,7 +752,7 @@ void VoyeurEngine::doGossip() { // Transfer initial background to video decoder PictureResource videoFrame(decoder.getRL2VideoTrack()->getBackSurface()); bgPic->_bounds.moveTo(0, 0); - _graphicsManager->sDrawPic(bgPic, &videoFrame, Common::Point(0, 0)); + _screen->sDrawPic(bgPic, &videoFrame, Common::Point(0, 0)); byte *frameNumsP = _bVoy->memberAddr(0x309); byte *posP = _bVoy->boltEntry(0x30A)._data; @@ -762,8 +762,8 @@ void VoyeurEngine::doGossip() { decoder.close(); // Reset the palette and clear the screen - _graphicsManager->resetPalette(); - _graphicsManager->screenReset(); + _screen->resetPalette(); + _screen->screenReset(); // Play interview video RL2Decoder decoder2; @@ -775,7 +775,7 @@ void VoyeurEngine::doGossip() { decoder2.close(); _bVoy->freeBoltGroup(0x300); - _graphicsManager->screenReset(); + _screen->screenReset(); } void VoyeurEngine::doTapePlaying() { @@ -783,14 +783,14 @@ void VoyeurEngine::doTapePlaying() { return; _eventsManager->getMouseInfo(); - _graphicsManager->_backColors = _bVoy->boltEntry(0xA01)._cMapResource; - _graphicsManager->_backgroundPage = _bVoy->boltEntry(0xA00)._picResource; + _screen->_backColors = _bVoy->boltEntry(0xA01)._cMapResource; + _screen->_backgroundPage = _bVoy->boltEntry(0xA00)._picResource; PictureResource *pic = _bVoy->boltEntry(0xA02)._picResource; VInitCycleResource *cycle = _bVoy->boltEntry(0xA05)._vInitCycleResource; - _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage); - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(57, 30)); - _graphicsManager->_backColors->startFade(); + _screen->_vPort->setupViewPort(_screen->_backgroundPage); + _screen->sDrawPic(pic, _screen->_vPort, Common::Point(57, 30)); + _screen->_backColors->startFade(); flipPageAndWaitForFade(); cycle->vStartCycle(); @@ -932,9 +932,9 @@ int VoyeurEngine::getChooseButton() { + 6)._rectResource->_entries; int selectedIndex = -1; - _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage); - _graphicsManager->_backColors->_steps = 0; - _graphicsManager->_backColors->startFade(); + _screen->_vPort->setupViewPort(_screen->_backgroundPage); + _screen->_backColors->_steps = 0; + _screen->_backColors->startFade(); flipPageAndWait(); _voy->_viewBounds = _bVoy->boltEntry(_playStampGroupId + 7)._rectResource; @@ -955,7 +955,7 @@ int VoyeurEngine::getChooseButton() { selectedIndex = idx; if (selectedIndex != prevIndex) { PictureResource *btnPic = _bVoy->boltEntry(_playStampGroupId + 8 + idx)._picResource; - _graphicsManager->sDrawPic(btnPic, _graphicsManager->_vPort, + _screen->sDrawPic(btnPic, _screen->_vPort, Common::Point(106, 200)); cursorPic = _bVoy->boltEntry(_playStampGroupId + 4)._picResource; @@ -967,11 +967,11 @@ int VoyeurEngine::getChooseButton() { if (selectedIndex == -1) { cursorPic = _bVoy->boltEntry(_playStampGroupId + 2)._picResource; PictureResource *btnPic = _bVoy->boltEntry(_playStampGroupId + 12)._picResource; - _graphicsManager->sDrawPic(btnPic, _graphicsManager->_vPort, + _screen->sDrawPic(btnPic, _screen->_vPort, Common::Point(106, 200)); } - _graphicsManager->sDrawPic(cursorPic, _graphicsManager->_vPort, + _screen->sDrawPic(cursorPic, _screen->_vPort, Common::Point(pt.x + 13, pt.y - 12)); flipPageAndWait(); @@ -982,9 +982,9 @@ int VoyeurEngine::getChooseButton() { } void VoyeurEngine::makeViewFinder() { - _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x103)._picResource; - _graphicsManager->sDrawPic(_graphicsManager->_backgroundPage, - _graphicsManager->_vPort, Common::Point(0, 0)); + _screen->_backgroundPage = _bVoy->boltEntry(0x103)._picResource; + _screen->sDrawPic(_screen->_backgroundPage, + _screen->_vPort, Common::Point(0, 0)); CMapResource *pal = _bVoy->boltEntry(0x104)._cMapResource; int palOffset = 0; @@ -1016,22 +1016,22 @@ void VoyeurEngine::makeViewFinder() { break; } - _graphicsManager->_vPort->drawIfaceTime(); + _screen->_vPort->drawIfaceTime(); doTimeBar(); pal->startFade(); flipPageAndWaitForFade(); - _graphicsManager->setColor(241, 105, 105, 105); - _graphicsManager->setColor(242, 105, 105, 105); - _graphicsManager->setColor(243, 105, 105, 105); - _graphicsManager->setColor(palOffset + 241, 219, 235, 235); + _screen->setColor(241, 105, 105, 105); + _screen->setColor(242, 105, 105, 105); + _screen->setColor(243, 105, 105, 105); + _screen->setColor(palOffset + 241, 219, 235, 235); _eventsManager->_intPtr._hasPalette = true; } void VoyeurEngine::makeViewFinderP() { - _graphicsManager->screenReset(); + _screen->screenReset(); } void VoyeurEngine::initIFace() { @@ -1077,7 +1077,7 @@ void VoyeurEngine::initIFace() { void VoyeurEngine::doScroll(const Common::Point &pt) { Common::Rect clipRect(72, 47, 72 + 240, 47 + 148); - _graphicsManager->_vPort->setupViewPort(NULL, &clipRect); + _screen->_vPort->setupViewPort(NULL, &clipRect); int base = 0; switch (_voy->_transitionId) { @@ -1101,18 +1101,18 @@ void VoyeurEngine::doScroll(const Common::Point &pt) { if (base) { PictureResource *pic = _bVoy->boltEntry(base + 3)._picResource; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 104)); + _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 104)); pic = _bVoy->boltEntry(base + 4)._picResource; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 44)); + _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 44)); pic = _bVoy->boltEntry(base + 5)._picResource; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 16)); + _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 16)); pic = _bVoy->boltEntry(base + 6)._picResource; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 76)); + _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 76)); pic = _bVoy->boltEntry(base + 7)._picResource; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 136)); + _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 136)); } - _graphicsManager->_vPort->setupViewPort(NULL); + _screen->_vPort->setupViewPort(NULL); } void VoyeurEngine::checkTransition() { @@ -1124,7 +1124,7 @@ void VoyeurEngine::checkTransition() { // Only proceed if a valid day string was returned if (!day.empty()) { - _graphicsManager->fadeDownICF(6); + _screen->fadeDownICF(6); // Get the time of day string time = getTimeOfDay(); @@ -1163,7 +1163,7 @@ Common::String VoyeurEngine::getTimeOfDay() { } int VoyeurEngine::doComputerText(int maxLen) { - FontInfoResource &font = *_graphicsManager->_fontPtr; + FontInfoResource &font = *_screen->_fontPtr; int totalChars = 0; font._curFont = _bVoy->boltEntry(0x4910)._fontResource; @@ -1180,7 +1180,7 @@ int VoyeurEngine::doComputerText(int maxLen) { font._justifyWidth = 384; font._justifyHeight = 100; font._pos = Common::Point(128, 100); - _graphicsManager->_vPort->drawText(END_OF_MESSAGE); + _screen->_vPort->drawText(END_OF_MESSAGE); } else if (_voy->_RTVNum < _voy->_computerTimeMin && maxLen == 9999) { if (_currentVocId != -1) _soundManager->startVOCPlay(_currentVocId); @@ -1188,7 +1188,7 @@ int VoyeurEngine::doComputerText(int maxLen) { font._justifyWidth = 384; font._justifyHeight = 100; font._pos = Common::Point(120, 100); - _graphicsManager->_vPort->drawText(START_OF_MESSAGE); + _screen->_vPort->drawText(START_OF_MESSAGE); } else { char *msg = (char *)_bVoy->memberAddr(0x4900 + _voy->_computerTextId); font._pos = Common::Point(96, 60); @@ -1206,14 +1206,14 @@ int VoyeurEngine::doComputerText(int maxLen) { if (c == '\0') { if (showEnd) { _eventsManager->delay(90); - _graphicsManager->_drawPtr->_pos = Common::Point(96, 54); - _graphicsManager->_drawPtr->_penColor = 254; - _graphicsManager->_vPort->sFillBox(196, 124); - _graphicsManager->_fontPtr->_justify = ALIGN_LEFT; - _graphicsManager->_fontPtr->_justifyWidth = 384; - _graphicsManager->_fontPtr->_justifyHeight = 100; - _graphicsManager->_fontPtr->_pos = Common::Point(128, 100); - _graphicsManager->_vPort->drawText(END_OF_MESSAGE); + _screen->_drawPtr->_pos = Common::Point(96, 54); + _screen->_drawPtr->_penColor = 254; + _screen->_vPort->sFillBox(196, 124); + _screen->_fontPtr->_justify = ALIGN_LEFT; + _screen->_fontPtr->_justifyWidth = 384; + _screen->_fontPtr->_justifyHeight = 100; + _screen->_fontPtr->_pos = Common::Point(128, 100); + _screen->_vPort->drawText(END_OF_MESSAGE); } break; } @@ -1223,20 +1223,20 @@ int VoyeurEngine::doComputerText(int maxLen) { yp += 10; } else { _eventsManager->delay(90); - _graphicsManager->_drawPtr->_pos = Common::Point(96, 54); - _graphicsManager->_drawPtr->_penColor = 255; - _graphicsManager->_vPort->sFillBox(196, 124); + _screen->_drawPtr->_pos = Common::Point(96, 54); + _screen->_drawPtr->_penColor = 255; + _screen->_vPort->sFillBox(196, 124); yp = 60; } - _graphicsManager->_fontPtr->_pos = Common::Point(96, yp); + _screen->_fontPtr->_pos = Common::Point(96, yp); } else if (c == '_') { showEnd = false; } else { - _graphicsManager->_fontPtr->_justify = ALIGN_LEFT; - _graphicsManager->_fontPtr->_justifyWidth = 0; - _graphicsManager->_fontPtr->_justifyHeight = 0; - _graphicsManager->_vPort->drawText(Common::String(c)); + _screen->_fontPtr->_justify = ALIGN_LEFT; + _screen->_fontPtr->_justifyWidth = 0; + _screen->_fontPtr->_justifyHeight = 0; + _screen->_vPort->drawText(Common::String(c)); _eventsManager->delay(4); } @@ -1251,7 +1251,7 @@ int VoyeurEngine::doComputerText(int maxLen) { flipPageAndWait(); - _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; + _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource; return totalChars; } @@ -1263,7 +1263,7 @@ void VoyeurEngine::getComputerBrush() { int xp = (384 - pic->_bounds.width()) / 2; int yp = (240 - pic->_bounds.height()) / 2 - 4; - _graphicsManager->_vPort->drawPicPerm(pic, Common::Point(xp, yp)); + _screen->_vPort->drawPicPerm(pic, Common::Point(xp, yp)); CMapResource *pal = _bVoy->boltEntry(0x490F)._cMapResource; pal->startFade(); @@ -1280,17 +1280,17 @@ void VoyeurEngine::doTimeBar() { int height = ((_voy->_RTVLimit - _voy->_RTVNum) * 59) / _voy->_RTVLimit; int fullHeight = MAX(151 - height, 93); - _graphicsManager->_drawPtr->_penColor = 134; - _graphicsManager->_drawPtr->_pos = Common::Point(39, 92); + _screen->_drawPtr->_penColor = 134; + _screen->_drawPtr->_pos = Common::Point(39, 92); - _graphicsManager->_vPort->sFillBox(6, fullHeight - 92); + _screen->_vPort->sFillBox(6, fullHeight - 92); if (height > 0) { - _graphicsManager->setColor(215, 238, 238, 238); + _screen->setColor(215, 238, 238, 238); _eventsManager->_intPtr._hasPalette = true; - _graphicsManager->_drawPtr->_penColor = 215; - _graphicsManager->_drawPtr->_pos = Common::Point(39, fullHeight); - _graphicsManager->_vPort->sFillBox(6, height); + _screen->_drawPtr->_penColor = 215; + _screen->_drawPtr->_pos = Common::Point(39, fullHeight); + _screen->_vPort->sFillBox(6, height); } } } @@ -1303,9 +1303,9 @@ void VoyeurEngine::flashTimeBar() { _flashTimeVal = _eventsManager->_intPtr._flashTimer; if (_flashTimeFlag) - _graphicsManager->setColor(240, 220, 20, 20); + _screen->setColor(240, 220, 20, 20); else - _graphicsManager->setColor(240, 220, 220, 220); + _screen->setColor(240, 220, 220, 220); _eventsManager->_intPtr._hasPalette = true; _flashTimeFlag = !_flashTimeFlag; @@ -1343,7 +1343,7 @@ void VoyeurEngine::doEvidDisplay(int evidId, int eventId) { _bVoy->getBoltGroup(_voy->_boltGroupId2); PictureResource *pic = _bVoy->boltEntry(_voy->_boltGroupId2 + evidId * 2)._picResource; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point( + _screen->sDrawPic(pic, _screen->_vPort, Common::Point( (384 - pic->_bounds.width()) / 2, (240 - pic->_bounds.height()) / 2)); _bVoy->freeBoltMember(_voy->_boltGroupId2 + evidId * 2); @@ -1394,7 +1394,7 @@ void VoyeurEngine::doEvidDisplay(int evidId, int eventId) { continue; pic = _voy->_evPicPtrs[arrIndex]; - _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, + _screen->sDrawPic(pic, _screen->_vPort, Common::Point((384 - pic->_bounds.width()) / 2, (240 - pic->_bounds.height()) / 2)); _voy->_evCmPtrs[arrIndex]->startFade(); diff --git a/engines/wage/debugger.cpp b/engines/wage/debugger.cpp new file mode 100644 index 0000000000..7d01b0b85e --- /dev/null +++ b/engines/wage/debugger.cpp @@ -0,0 +1,97 @@ +/* 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 "common/file.h" +#include "wage/wage.h" +#include "wage/debugger.h" +#include "wage/entities.h" +#include "wage/script.h" +#include "wage/world.h" + +namespace Wage { + +Debugger::Debugger(WageEngine *engine) : GUI::Debugger(), _engine(engine) { + registerCmd("continue", WRAP_METHOD(Debugger, cmdExit)); + registerCmd("scenes", WRAP_METHOD(Debugger, Cmd_ListScenes)); + registerCmd("script", WRAP_METHOD(Debugger, Cmd_Script)); +} + +Debugger::~Debugger() { +} + +static int strToInt(const char *s) { + if (!*s) + // No string at all + return 0; + else if (toupper(s[strlen(s) - 1]) != 'H') + // Standard decimal string + return atoi(s); + + // Hexadecimal string + uint tmp = 0; + int read = sscanf(s, "%xh", &tmp); + + if (read < 1) + error("strToInt failed on string \"%s\"", s); + return (int)tmp; +} + +bool Debugger::Cmd_ListScenes(int argc, const char **argv) { + int currentScene; + + for (uint i = 1; i < _engine->_world->_orderedScenes.size(); i++) { // #0 is STORAGE@ + if (_engine->_world->_player->_currentScene == _engine->_world->_orderedScenes[i]) + currentScene = i; + + debugPrintf("%d: %s\n", i, _engine->_world->_orderedScenes[i]->_name.c_str()); + } + + debugPrintf("\nCurrent scene is #%d: %s\n", currentScene, _engine->_world->_orderedScenes[currentScene]->_name.c_str()); + + return true; +} + +bool Debugger::Cmd_Script(int argc, const char **argv) { + Script *script = _engine->_world->_player->_currentScene->_script; + + if (argc >= 2) { + int scriptNum = strToInt(argv[1]); + + if (scriptNum) + script = _engine->_world->_orderedScenes[scriptNum]->_script; + else + script = _engine->_world->_globalScript; + } + + if (script == NULL) { + debugPrintf("There is no script for current scene\n"); + return true; + } + + for (uint i = 0; i < script->_scriptText.size(); i++) { + debugPrintf("%d [%04x]: %s\n", i, script->_scriptText[i]->offset, script->_scriptText[i]->line.c_str()); + } + + return true; +} + +} // End of namespace Wage diff --git a/engines/wage/debugger.h b/engines/wage/debugger.h new file mode 100644 index 0000000000..90687760cb --- /dev/null +++ b/engines/wage/debugger.h @@ -0,0 +1,47 @@ +/* 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 WAGE_DEBUGGER_H +#define WAGE_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace Wage { + +class WageEngine; + +class Debugger : public GUI::Debugger { +protected: + WageEngine *_engine; + + bool Cmd_ListScenes(int argc, const char **argv); + bool Cmd_Script(int argc, const char **argv); + +public: + Debugger(WageEngine *engine); + virtual ~Debugger(); +}; + +} // End of namespace Wage + +#endif diff --git a/engines/wage/design.cpp b/engines/wage/design.cpp index a3dbd9700b..907a1ec435 100644 --- a/engines/wage/design.cpp +++ b/engines/wage/design.cpp @@ -56,9 +56,10 @@ struct PlotData { Patterns *patterns; uint fillType; int thickness; + Design *design; - PlotData(Graphics::Surface *s, Patterns *p, int f, int t) : - surface(s), patterns(p), fillType(f), thickness(t) {} + PlotData(Graphics::Surface *s, Patterns *p, int f, int t, Design *d) : + surface(s), patterns(p), fillType(f), thickness(t), design(d) {} }; void drawPixel(int x, int y, int color, void *data); @@ -71,6 +72,8 @@ Design::Design(Common::SeekableReadStream *data) { _surface = NULL; _bounds = NULL; + + _boundsCalculationMode = false; } Design::~Design() { @@ -81,20 +84,30 @@ Design::~Design() { } void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) { - Common::MemoryReadStream in(_data, _len); - Common::Rect r(0, 0, _bounds->width(), _bounds->height()); bool needRender = false; if (_surface == NULL) { + _boundsCalculationMode = true; + _bounds->debugPrint(4, "Internal bounds:"); + render(patterns); + _boundsCalculationMode = false; + if (_bounds->right == -10000) { + _bounds->left = _bounds->top = _bounds->right = _bounds->bottom = 0; + } + _bounds->debugPrint(4, "Calculated bounds:"); + _surface = new Graphics::Surface; _surface->create(_bounds->width(), _bounds->height(), Graphics::PixelFormat::createFormatCLUT8()); + + Common::Rect r(0, 0, _bounds->width(), _bounds->height()); _surface->fillRect(r, kColorGreen); needRender = true; } + _bounds->debugPrint(4, "Using bounds:"); #if 0 - PlotData pd(_surface, &patterns, 8, 1); + PlotData pd(_surface, &patterns, 8, 1, this); int x1 = 50, y1 = 50, x2 = 200, y2 = 200, borderThickness = 30; Common::Rect inn(x1-5, y1-5, x2+5, y2+5); drawRoundRect(inn, 6, kColorGray, false, drawPixelPlain, &pd); @@ -115,6 +128,28 @@ void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) return; #endif + if (needRender) + render(patterns); + + if (_bounds->width() && _bounds->height()) { + const int padding = 3; + for (int i = padding; i < _bounds->height() - 2 * padding; i++) { + const byte *src = (const byte *)_surface->getBasePtr(padding, i); + byte *dst = (byte *)surface->getBasePtr(x + padding, y+i); + for (int j = padding; j < _bounds->width() - 2 * padding; j++) { + if (*src != kColorGreen) + *dst = *src; + src++; + dst++; + } + } + } +} + +void Design::render(Patterns &patterns) { + Common::MemoryReadStream in(_data, _len); + bool needRender = true; + while (needRender) { byte fillType = in.readByte(); byte borderThickness = in.readByte(); @@ -152,18 +187,6 @@ void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) //g_system->updateScreen(); //g_system->delayMillis(500); } - - const int padding = 3; - for (int i = padding; i < _bounds->height() - 2 * padding; i++) { - const byte *src = (const byte *)_surface->getBasePtr(padding, i); - byte *dst = (byte *)surface->getBasePtr(x + padding, y+i); - for (int j = padding; j < _bounds->width() - 2 * padding; j++) { - if (*src != kColorGreen) - *dst = *src; - src++; - dst++; - } - } } bool Design::isPointOpaque(int x, int y) { @@ -175,18 +198,43 @@ bool Design::isPointOpaque(int x, int y) { return pixel != kColorGreen; } +void Design::adjustBounds(int16 x, int16 y) { + _bounds->right = MAX(x, _bounds->right); + _bounds->bottom = MAX(y, _bounds->bottom); +} + void drawPixel(int x, int y, int color, void *data) { PlotData *p = (PlotData *)data; if (p->fillType > p->patterns->size()) return; + if (p->design && p->design->isBoundsCalculation()) { + if (x < 0 || y < 0) + return; + if (p->thickness == 1) { + p->design->adjustBounds(x, y); + } else { + int x1 = x - p->thickness / 2; + int x2 = x1 + p->thickness; + int y1 = y - p->thickness / 2; + int y2 = y1 + p->thickness; + + for (y = y1; y < y2; y++) + for (x = x1; x < x2; x++) + p->design->adjustBounds(x, y); + } + + return; + } + byte *pat = p->patterns->operator[](p->fillType - 1); if (p->thickness == 1) { if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) { uint xu = (uint)x; // for letting compiler optimize it uint yu = (uint)y; + *((byte *)p->surface->getBasePtr(xu, yu)) = (pat[yu % 8] & (1 << (7 - xu % 8))) ? color : kColorWhite; @@ -212,6 +260,11 @@ void drawPixel(int x, int y, int color, void *data) { void drawPixelPlain(int x, int y, int color, void *data) { PlotData *p = (PlotData *)data; + if (p->design && p->design->isBoundsCalculation()) { + p->design->adjustBounds(x, y); + return; + } + if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) *((byte *)p->surface->getBasePtr(x, y)) = (byte)color; } @@ -229,7 +282,7 @@ void Design::drawRect(Graphics::Surface *surface, Common::ReadStream &in, SWAP(y1, y2); Common::Rect r(x1, y1, x2, y2); - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) Graphics::drawFilledRect(r, kColorBlack, drawPixel, &pd); @@ -259,16 +312,16 @@ void Design::drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, SWAP(y1, y2); Common::Rect r(x1, y1, x2, y2); - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) - Graphics::drawRoundRect(r, arc/2, kColorBlack, true, drawPixel, &pd); + Graphics::drawRoundRect(r, arc / 2, kColorBlack, true, drawPixel, &pd); pd.fillType = borderFillType; pd.thickness = borderThickness; if (borderThickness > 0 && borderFillType <= patterns.size()) - Graphics::drawRoundRect(r, arc/2, kColorBlack, false, drawPixel, &pd); + Graphics::drawRoundRect(r, arc / 2, kColorBlack, false, drawPixel, &pd); } void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, @@ -331,7 +384,7 @@ void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, ypoints[i] = ycoords[i]; } - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) { Graphics::drawPolygonScan(xpoints, ypoints, npoints, bbox, kColorBlack, drawPixel, &pd); @@ -354,7 +407,7 @@ void Design::drawOval(Graphics::Surface *surface, Common::ReadStream &in, int16 x1 = in.readSint16BE(); int16 y2 = in.readSint16BE(); int16 x2 = in.readSint16BE(); - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) Graphics::drawEllipse(x1, y1, x2-1, y2-1, kColorBlack, true, drawPixel, &pd); @@ -410,7 +463,9 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & color = b; for (int c = 0; c < 8; c++) { - if (x1 + x >= 0 && x1 + x < surface->w && y1 + y >= 0 && y1 + y < surface->h) + if (_boundsCalculationMode) { + adjustBounds(x1 + x, y1 + y); + } else if (x1 + x >= 0 && x1 + x < surface->w && y1 + y >= 0 && y1 + y < surface->h) *((byte *)tmp.getBasePtr(x, y)) = (color & (1 << (7 - c % 8))) ? kColorBlack : kColorWhite; x++; if (x == w) { @@ -424,6 +479,9 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & in.skip(numBytes); + if (_boundsCalculationMode) + return; + FloodFill ff(&tmp, kColorWhite, kColorGreen); for (int yy = 0; yy < h; yy++) { ff.addSeed(0, yy); @@ -454,7 +512,7 @@ void Design::drawRect(Graphics::Surface *surface, Common::Rect &rect, int thickn } void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, thickness); + PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawLine(x1, y1, x2, y1, kColorBlack, drawPixel, &pd); Graphics::drawLine(x2, y1, x2, y2, kColorBlack, drawPixel, &pd); @@ -464,26 +522,26 @@ void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2 void Design::drawFilledRect(Graphics::Surface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, nullptr); for (int y = rect.top; y <= rect.bottom; y++) Graphics::drawHLine(rect.left, rect.right, y, color, drawPixel, &pd); } void Design::drawFilledRoundRect(Graphics::Surface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, nullptr); Graphics::drawRoundRect(rect, arc, color, true, drawPixel, &pd); } void Design::drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, thickness); + PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawHLine(x1, x2, y, color, drawPixel, &pd); } void Design::drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, thickness); + PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawVLine(x, y1, y2, color, drawPixel, &pd); } diff --git a/engines/wage/design.h b/engines/wage/design.h index baa99730fe..e8f42f4e04 100644 --- a/engines/wage/design.h +++ b/engines/wage/design.h @@ -76,14 +76,18 @@ public: static void drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType); static void drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType); + bool isBoundsCalculation() { return _boundsCalculationMode; } + void adjustBounds(int16 x, int16 y); private: byte *_data; int _len; Common::Rect *_bounds; Graphics::Surface *_surface; + bool _boundsCalculationMode; private: + void render(Patterns &patterns); void drawRect(Graphics::Surface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType); void drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp index fcecb8d2b0..512d432e54 100644 --- a/engines/wage/detection.cpp +++ b/engines/wage/detection.cpp @@ -41,6 +41,7 @@ static const PlainGameDescriptor wageGames[] = { {"afm", "Another Fine Mess"}, {"amot", "A Mess O' Trouble"}, {"cantitoe", "Camp Cantitoe"}, + {"drakmythcastle", "Drakmyth Castle"}, {"raysmaze", "Ray's Maze"}, {"scepters", "Enchanted Scepters"}, {"twisted", "Twisted!"}, diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h index fcd03d3e05..530b56c962 100644 --- a/engines/wage/detection_tables.h +++ b/engines/wage/detection_tables.h @@ -22,63 +22,123 @@ namespace Wage { -#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM) -#define ADGF_GENERIC (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_USEEXTRAASTITLE|ADGF_AUTOGENTARGET) +#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_MACRESFORK) +#define ADGF_GENERIC (ADGF_DEFAULT|ADGF_USEEXTRAASTITLE|ADGF_AUTOGENTARGET) +#define ADGF_DEMO (ADGF_GENERIC|ADGF_DEMO) #define FANGAME(n,m,s) { "wage",n,AD_ENTRY1s(n,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} #define FANGAMEN(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} +#define FANGAMEND(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEMO,GUIO0()} #define BIGGAME(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT,GUIO0()} static const ADGameDescription gameDescriptions[] = { - FANGAME("3rd Floor", "a107d7a177970b2259e32681bd8b47c9", 285056), - BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "8e5aa915f3253efb2aab52435647b25e", 1456000), - BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "b3ef53afed282671b704e45df829350c", 1895552), - FANGAME("Bug Hunt", "2ebd3515a87941063ad66c3cf93c5e78", 200064), + FANGAME("3rd Floor", "913812a1ac7a6b0e48dadd1afa1c7763", 281409), + BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "94a9c4f8b3dabd1846d76215a49bd221", 1420723), + BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "26207bdf0bb539464f136f0669af885f", 1843104), + // No Next on the first screen? + FANGAME("Brownie's Dream", "94a9c4f8b3dabd1846d76215a49bd221", 440704), + FANGAMEN("Brownie's Time Travels", "Brownie's Time Travels v1.2", "94a9c4f8b3dabd1846d76215a49bd221", 471589), + FANGAME("Bug Hunt", "595117cbed33e8de1ab3714b33880205", 195699), + BIGGAME("cantitoe", "", "Camp Cantitoe", "913812a1ac7a6b0e48dadd1afa1c7763", 616985), // Problems with letter rendering - FANGAME("Canal District", "8856bc699a20fc5b7fc67accee12cac7", 658176), - BIGGAME("cantitoe", "", "Camp Cantitoe", "098aa5c11c58e1ef274a30a9e01b4755", 621440), + FANGAME("Canal District", "a56aa3cd4a6e070e15ce1d5815c7be0a", 641470), + FANGAME("Carbon Copy", "913812a1ac7a6b0e48dadd1afa1c7763", 519445), // Invalid rect in scene "FINALE" - FANGAME("Castle of Ert", "85f56731635e825e49a672c4fb0490dd", 205312), - FANGAME("Deep Angst", "635f62bbc569e72b03cab9107927d03d", 335232), - FANGAMEN("Dungeon World II", "DungeonWorld2", "e10c5e3cc17879c298b1551f33571b15", 234880), + FANGAME("Castle of Ert", "327610eb2298a9427a566288312df040", 198955), + FANGAME("Deep Angst", "b130b3c811cd89024dd5fdd2b71f70b8", 329550), + FANGAME("Deep Ennui", "913812a1ac7a6b0e48dadd1afa1c7763", 86075), // Polygons with ignored byte 1 - FANGAME("Double Trouble", "5e9ee13d09ac54918ed111fa9727ac1c", 557184), - FANGAME("Eidisi I", "299d1de4baccf1c66118396519953652", 180480), + FANGAME("Double Trouble", "1652e36857a04c01dc560234c4818619", 542371), + BIGGAME("drakmythcastle", "disk I", "Drakmyth Castle disk I of II", "94a9c4f8b3dabd1846d76215a49bd221", 793784), + BIGGAME("drakmythcastle", "disk II", "Drakmyth Castle II", "cc978cc9a5256724702463cb5aaaffa0", 1685659), + // Crash at start in GUI rendering + FANGAME("Dune Eternity", "94a9c4f8b3dabd1846d76215a49bd221", 290201), // Original file name is "***DUNE ETERNITY*** " + FANGAMEN("Dungeon World II", "DungeonWorld2", "0154ea11d3cbb536c13b4ae9e6902d48", 230199), + FANGAME("Edg's World", "913812a1ac7a6b0e48dadd1afa1c7763", 106769), + FANGAME("Eidisi I", "595117cbed33e8de1ab3714b33880205", 172552), // Problems(?) with text on the first screen - FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "35514583fe7ab36fad2569fc87bd887b", 414464), - FANGAME("Escape from School!", "a854be48d4af20126d18a9cad93a969b", 51840), - FANGAME("Exploration Zeta!", "b9fbb704017d7ea9613b0160f86527bb", 370944), + FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "595117cbed33e8de1ab3714b33880205", 408913), + FANGAME("Escape from School!", "913812a1ac7a6b0e48dadd1afa1c7763", 50105), + FANGAME("Everyman 1", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 335705), + FANGAME("Exploration Zeta!", "c477921aeee6ed0f8997ba44447eb2d0", 366599), // Crash in console rendering on the first scene - FANGAME("Fantasy Quest", "599f0b2c7ecce65c39646c05f2c19c1b", 782848), - FANGAME("Find the Heart", "7ae36ffa295651cd6d2d56981d6b5ff7", 108928), // From Joshua's Worlds 1.0 - FANGAME("Lost Crystal", "4f21ba8ee64f8d655b9eeb1e3ffd50f7", 792064), - FANGAME("Magic Rings", "6e0d1dd561d3dad8f9a7a20ed1f09b16", 112000), - FANGAME("Midnight Snack", "346982a32fc701f53bb19771d72063d0", 69504), - FANGAME("Puzzle Piece Search", "51885fe2effeaa14b2b8b08a53931805", 252928), // From Joshua's Worlds 1.0 + FANGAME("Fantasy Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 762754), + FANGAME("Find the Heart", "595117cbed33e8de1ab3714b33880205", 106235), // From Joshua's Worlds 1.0 + FANGAMEN("Fortune Teller", "Fortune Teller 1.1", "e5df11bfec42dd12b675ad4d98479ef3", 73931), + // Cropped graphics on first scene + FANGAME("Intro to Gothic", "d81f2d03a1e863f04fb1e3a5495b720e", 208067), + // No Next button in intro + FANGAME("Jamie the Demon Slayer", "94a9c4f8b3dabd1846d76215a49bd221", 232789), + // Problems with window overlay + FANGAMEN("Jumble", "LSJUMBLE", "e12ec4d76d48bdc86567c5e63750547e", 647339), // Original file name is "LSJUMBLE†" + FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96711), + FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96960), // Alternative version + FANGAME("Karth of the Jungle II", "c106835ab4436de054e03aec3ce904ce", 201053), + FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "94a9c4f8b3dabd1846d76215a49bd221", 628821), + FANGAME("Lost Crystal", "8174c81ea1858d0079ae040dae2cefd3", 771072), + // Crash in design drawing on startup + FANGAMEN("Lost In Kookyville", "Lost In Kookyville 1.2.4", "e6cea2234cee9d0dba7be10bc1ad6055", 721569), + FANGAME("Magic Rings", "913812a1ac7a6b0e48dadd1afa1c7763", 109044), + // No way to click on the house + FANGAME("Messy House", "913812a1ac7a6b0e48dadd1afa1c7763", 177120), + FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67952), + FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67966), // Alt version + FANGAME("Minitorian", "913812a1ac7a6b0e48dadd1afa1c7763", 586464), + FANGAME("M'Lord's Warrior", "7d30b6e68ecf197b2d15492630bdeb89", 465639), // Original file name is "M'Lord's Warrior †" + // Unhandled comparison case + FANGAME("Mountain of Mayhem", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 750003), // Original file name "Mountain of Mayhem †" + // No way to pass through the first screen + FANGAME("Nightcrawler Ned", "94a9c4f8b3dabd1846d76215a49bd221", 366542), + // Crash on startup + FANGAMEN("Parrot Talk", "PARROT TALK V1", "d81f2d03a1e863f04fb1e3a5495b720e", 118936), + // Crash on startup + FANGAMEN("Parrot Talk", "PARROT TALKV2", "d81f2d03a1e863f04fb1e3a5495b720e", 118884), + FANGAME("Pavilion", "4d991d7d1534d48d90598d86ea6d5d97", 231687), + FANGAMEN("Pencils", "Pencils.99", "913812a1ac7a6b0e48dadd1afa1c7763", 408551), + // Polygons with byte 1 + FANGAME("Periapt", "913812a1ac7a6b0e48dadd1afa1c7763", 406006), + FANGAME("Puzzle Piece Search", "595117cbed33e8de1ab3714b33880205", 247693), // From Joshua's Worlds 1.0 // Empty(?) first scene - FANGAME("Pyramid of No Return", "48a9c668ce69206f57e11e1a85970d02", 392192), - FANGAME("Queen Quest", "730605d312efedb5e3ff108522fcac18", 59776), + FANGAME("Pyramid of No Return", "77a55a45f794b4d4a56703d3acce871e", 385145), + FANGAME("Queen Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 57026), + FANGAME("Quest for T-Rex", "913812a1ac7a6b0e48dadd1afa1c7763", 592584), // Crash in console rendering on the initial scene - FANGAME("Quest for the Dark Sword", "7e4e712d151f6c686f6024b0dedf5d34", 590720), - FANGAME("Quest for the Dark Sword", "308cf0fdfa129fa46b325b307f7e88c6", 590720), // Alteranative version - BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "e5a3e25dddfffbed461bca3c26073117", 1437184), - BIGGAME("scepters", "", "Scepters", "b80bff315897776dda7689cdf829fab4", 360832), + FANGAME("Quest for the Dark Sword", "b35dd0c078da9f35fc25a455f56bb129", 572576), + FANGAME("Radical Castle", "677bfee4afeca2f7152eb8b76c85ca8d", 355601), + FANGAME("Radical Castle 1.0", "677bfee4afeca2f7152eb8b76c85ca8d", 347278), + BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "064b16d8c20724f8debbbdc3aafde538", 1408516), + BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "92cca777800c3d31a77b5ed7f6ee49ad", 1408516), + // Unhandled comparison case + FANGAME("Sands of Time", "913812a1ac7a6b0e48dadd1afa1c7763", 122672), // Original file name "Sands of Time†" + BIGGAME("scepters", "", "Scepters", "3311deef8bf82f0b4b1cfa15a3b3289d", 346595), // ??? problems with dog bitmap? - FANGAMEN("Space Adventure", "SpaceAdventure", "e5b0d8ad6d235ede2f08583342642dfa", 158720), - FANGAME("Star Trek", "2395856df8dbefe9c0609caa985edf73", 55296), - // Crash in bitmap drawing on the first scene - FANGAME("Strange Disappearance", "f9eba5b315853a5599927db2a73c87d3", 781312), - FANGAME("Time Bomb", "2df84b636237686b624e736a698a16c4", 66432), + FANGAMEN("Space Adventure", "SpaceAdventure", "f9f3f1c419f56955f7966355b34ea5c8", 155356), + FANGAMEN("Spear of Destiny", "SpearOfDestiny", "913812a1ac7a6b0e48dadd1afa1c7763", 333665), // Original file name "SpearOfDestiny†" + FANGAME("Star Trek", "44aaef4806578700429de5aaf95c266e", 53320), + FANGAME("Strange Disappearance", "d81f2d03a1e863f04fb1e3a5495b720e", 772282), + // Code 0x03 in text + FANGAME("Swamp Witch", "913812a1ac7a6b0e48dadd1afa1c7763", 739781), // Original file name "Swamp Witch†" + FANGAME("Sweetspace Now!", "e12ec4d76d48bdc86567c5e63750547e", 123813), // Comes with Jumble + // Wrong scrolling in the first console text + FANGAMEN("Sword of Siegfried", "Sword of Siegfried 1.0", "913812a1ac7a6b0e48dadd1afa1c7763", 234763), + FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64564), + FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64578), // Alt version + FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "913812a1ac7a6b0e48dadd1afa1c7763", 145023), // Original file name "The Ashland Revolution Demo†" + FANGAME("The Axe-orcist", "94a9c4f8b3dabd1846d76215a49bd221", 308764), + FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "595117cbed33e8de1ab3714b33880205", 231969), // Invalid rect in scene "Access Tube 1" - FANGAMEN("The Phoenix v1.2", "The Phoenix", "7fa2a2ac740f22572516843922b7c630", 434560), - FANGAME("The Sultan's Palace", "589aebf6c14bb5c63d9b4b2c37f31e16", 468096), + FANGAMEN("The Phoenix v1.2", "The Phoenix", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 431640), + FANGAME("The Sultan's Palace", "358799d446ee4fc12f793febd6c94b95", 456855), // Admission for on 3rd screen is messed up - FANGAME("The Tower", "75eba57a12ed181e07f34eaf6aa9d2c4", 568320), + FANGAME("The Tower", "435f420b9dff895ae1ddf1338040c51d", 556539), + // Polygons with ignored byte 1 and 2 on second scene + FANGAME("The Village", "913812a1ac7a6b0e48dadd1afa1c7763", 314828), // Doesn't go past first scene - BIGGAME("Twisted!", "", "Twisted! 1.6", "8ea8cc13d26d7975dc43ea7e6c193217", 992896), - FANGAME("Wishing Well", "3ee884d0d1a168d088cf2250d3a83c73", 105600), - FANGAME("ZikTuria", "e793155bed1a70fa2074a3fcd696b751", 54784), - FANGAME("Zoony", "e6cc8a914a4215dafbcce6315dd12cf5", 160256), + BIGGAME("twisted", "", "Twisted! 1.6", "26207bdf0bb539464f136f0669af885f", 960954), + FANGAME("Wishing Well", "913812a1ac7a6b0e48dadd1afa1c7763", 103688), + FANGAME("Wizard's Warehouse", "913812a1ac7a6b0e48dadd1afa1c7763", 159748), + FANGAME("ZikTuria", "418e74ca71029a1e9db80d0eb30c0843", 52972), + FANGAME("Zoony", "539a64151426edc92da5eedadf39f23c", 154990), // original filename "Zoony™" AD_TABLE_END_MARKER }; diff --git a/engines/wage/gui.cpp b/engines/wage/gui.cpp index 387731cc18..15d82a68fb 100644 --- a/engines/wage/gui.cpp +++ b/engines/wage/gui.cpp @@ -50,6 +50,7 @@ #include "graphics/cursorman.h" #include "graphics/fonts/bdf.h" #include "graphics/palette.h" +#include "graphics/primitives.h" #include "wage/wage.h" #include "wage/design.h" @@ -294,10 +295,10 @@ void Gui::drawBox(Graphics::Surface *g, int x, int y, int w, int h) { g->frameRect(r, kColorBlack); } -void Gui::fillRect(Graphics::Surface *g, int x, int y, int w, int h) { +void Gui::fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color) { Common::Rect r(x, y, x + w, y + h); - g->fillRect(r, kColorBlack); + g->fillRect(r, color); } #define ARROW_W 12 @@ -310,8 +311,18 @@ const int arrowPixels[ARROW_H][ARROW_W] = { {0,1,1,1,1,1,1,1,1,1,1,0}, {1,1,1,1,1,1,1,1,1,1,1,1}}; -void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType) { - bool active = false, scrollable = false, closeable = false, closeBoxPressed = false, drawTitle = false; +static void drawPixelInverted(int x, int y, int color, void *data) { + Graphics::Surface *surface = (Graphics::Surface *)data; + + if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) { + byte *p = (byte *)surface->getBasePtr(x, y); + + *p = *p == kColorWhite ? kColorBlack : kColorWhite; + } +} + +void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart, float scrollPos, float scrollSize) { + bool active = false, scrollable = false, closeable = false, drawTitle = false; const int size = kBorderWidth; int x = r.left - size; int y = r.top - size; @@ -323,14 +334,12 @@ void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowTy active = _sceneIsActive; scrollable = false; closeable = _sceneIsActive; - closeBoxPressed = false; drawTitle = true; break; case kWindowConsole: active = !_sceneIsActive; scrollable = true; closeable = !_sceneIsActive; - closeBoxPressed = false; drawTitle = false; break; } @@ -353,29 +362,32 @@ void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowTy } else { int x1 = x + width - 15; int y1 = y + size + 1; + for (int yy = 0; yy < ARROW_H; yy++) { - for (int xx = 0; xx < ARROW_W; xx++) { - if (arrowPixels[yy][xx] != 0) { - g->hLine(x1 + xx, y1 + yy, x1 + xx, kColorBlack); - } else { - g->hLine(x1 + xx, y1 + yy, x1 + xx, kColorWhite); - } - } + for (int xx = 0; xx < ARROW_W; xx++) + g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[yy][xx] != 0 ? kColorBlack : kColorWhite)); } + fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2); + y1 += height - 2 * size - ARROW_H - 2; for (int yy = 0; yy < ARROW_H; yy++) { - for (int xx = 0; xx < ARROW_W; xx++) { - if (arrowPixels[ARROW_H - yy - 1][xx] != 0) { - g->hLine(x1 + xx, y1 + yy, x1 + xx, kColorBlack); - } else { - g->hLine(x1 + xx, y1 + yy, x1 + xx, kColorWhite); - } - } + for (int xx = 0; xx < ARROW_W; xx++) + g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[ARROW_H - yy - 1][xx] != 0 ? kColorBlack : kColorWhite)); + } + + if (highlightedPart == kBorderScrollUp || highlightedPart == kBorderScrollDown) { + int rx1 = x + width - kBorderWidth + 2; + int ry1 = y + size + r.height() * scrollPos; + int rx2 = rx1 + size - 4; + int ry2 = ry1 + r.height() * scrollSize; + Common::Rect rr(rx1, ry1, rx2, ry2); + + Graphics::drawFilledRect(rr, kColorBlack, drawPixelInverted, g); } } if (closeable) { - if (closeBoxPressed) { + if (highlightedPart == kBorderCloseButton) { fillRect(g, x + 6, y + 6, 6, 6); } else { drawBox(g, x + 5, y + 5, 7, 7); @@ -499,6 +511,26 @@ void Gui::popCursor() { CursorMan.popCursor(); } +static int isInBorder(Common::Rect &rect, int x, int y) { + if (x >= rect.left - kBorderWidth && x < rect.left && y >= rect.top - kBorderWidth && y < rect.top) + return kBorderCloseButton; + + if (x >= rect.right && x < rect.right + kBorderWidth) { + if (y < rect.top - kBorderWidth) + return kBorderNone; + + if (y >= rect.bottom + kBorderWidth) + return kBorderNone; + + if (y >= rect.top + rect.height() / 2) + return kBorderScrollDown; + + return kBorderScrollUp; + } + + return kBorderNone; +} + Designed *Gui::mouseUp(int x, int y) { if (_menu->_menuActivated) { if (_menu->mouseRelease(x, y)) { @@ -532,6 +564,8 @@ Designed *Gui::mouseUp(int x, int y) { } } + int borderClick; + if (_sceneArea.contains(x, y)) { if (!_sceneIsActive) { _sceneIsActive = true; @@ -552,16 +586,44 @@ Designed *Gui::mouseUp(int x, int y) { _sceneIsActive = false; _bordersDirty = true; } + } else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) { + _bordersDirty = true; + int _oldScrollPos = _scrollPos; + + switch (borderClick) { + case kBorderScrollUp: + _scrollPos = MAX<int>(0, _scrollPos - _consoleLineHeight); + undrawCursor(); + _cursorY -= (_scrollPos - _oldScrollPos); + _consoleDirty = true; + _consoleFullRedraw = true; + break; + case kBorderScrollDown: + _scrollPos = MIN<int>((_lines.size() - 2) * _consoleLineHeight, _scrollPos + _consoleLineHeight); + undrawCursor(); + _cursorY -= (_scrollPos - _oldScrollPos); + _consoleDirty = true; + _consoleFullRedraw = true; + break; + } } return NULL; } void Gui::mouseDown(int x, int y) { + int borderClick; + if (_menu->mouseClick(x, y)) { _menuDirty = true; } else if (_consoleTextArea.contains(x, y)) { startMarking(x, y); + } else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) { + int textFullSize = _lines.size() * _consoleLineHeight + _consoleTextArea.height(); + float scrollPos = (float)_scrollPos / textFullSize; + float scrollSize = (float)_consoleTextArea.height() / textFullSize; + + paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick, scrollPos, scrollSize); } } diff --git a/engines/wage/gui.h b/engines/wage/gui.h index 61f8c31a07..c136163951 100644 --- a/engines/wage/gui.h +++ b/engines/wage/gui.h @@ -84,6 +84,13 @@ enum { kPatternCheckers2 = 4 }; +enum { + kBorderNone = 0, + kBorderScrollUp, + kBorderScrollDown, + kBorderCloseButton +}; + class Gui { public: Gui(WageEngine *engine); @@ -116,10 +123,11 @@ public: private: void undrawCursor(); void drawDesktop(); - void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType); + void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone, + float scrollPos = 0.0, float scrollSize = 0.0); void renderConsole(Graphics::Surface *g, Common::Rect &r); void drawBox(Graphics::Surface *g, int x, int y, int w, int h); - void fillRect(Graphics::Surface *g, int x, int y, int w, int h); + void fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color = kColorBlack); void loadFonts(); void flowText(Common::String &str); const Graphics::Font *getConsoleFont(); diff --git a/engines/wage/menu.cpp b/engines/wage/menu.cpp index 48f16421b5..12ef8c2219 100644 --- a/engines/wage/menu.cpp +++ b/engines/wage/menu.cpp @@ -206,10 +206,10 @@ void Menu::createCommandsMenu(MenuItem *menu) { char shortcut = 0; const char *shortPtr = strrchr(item.c_str(), '/'); if (shortPtr != NULL) { - if (strlen(shortPtr) == 2) { + if (strlen(shortPtr) >= 2) { shortcut = shortPtr[1]; - item.deleteLastChar(); - item.deleteLastChar(); + item.deleteChar(shortPtr - item.c_str()); + item.deleteChar(shortPtr - item.c_str()); } else { error("Unexpected shortcut: '%s', item '%s' in menu '%s'", shortPtr, item.c_str(), string.c_str()); } diff --git a/engines/wage/module.mk b/engines/wage/module.mk index 548e440c28..21316bbf83 100644 --- a/engines/wage/module.mk +++ b/engines/wage/module.mk @@ -2,6 +2,7 @@ MODULE := engines/wage MODULE_OBJS := \ combat.o \ + debugger.o \ design.o \ detection.o \ dialog.o \ diff --git a/engines/wage/script.cpp b/engines/wage/script.cpp index bd99fa1d86..294c08ed82 100644 --- a/engines/wage/script.cpp +++ b/engines/wage/script.cpp @@ -1124,7 +1124,7 @@ void Script::convertToText() { if (c < 0x80) { if (c < 0x20) - error("Unknown code 0x%02x at %d", c, _data->pos()); + error("convertToText: Unknown code 0x%02x at %d", c, _data->pos()); do { scr->line += c; diff --git a/engines/wage/script.h b/engines/wage/script.h index 325733add7..de9476228c 100644 --- a/engines/wage/script.h +++ b/engines/wage/script.h @@ -150,8 +150,10 @@ private: void assign(byte operandType, int uservar, uint16 value); - Common::Array<ScriptText *> _scriptText; void convertToText(); + +public: + Common::Array<ScriptText *> _scriptText; }; } // End of namespace Wage diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp index b708cff134..e0299c8da2 100644 --- a/engines/wage/wage.cpp +++ b/engines/wage/wage.cpp @@ -102,12 +102,14 @@ WageEngine::~WageEngine() { } Common::Error WageEngine::run() { + debug("WageEngine::init"); + initGraphics(512, 342, true); // Create debugger console. It requires GFX to be initialized _console = new Console(this); - debug("WageEngine::init"); + _debugger = new Debugger(this); // Your main event loop should be (invoked from) here. _resManager = new Common::MacResManager(); @@ -130,6 +132,8 @@ Common::Error WageEngine::run() { _shouldQuit = false; while (!_shouldQuit) { + _debugger->onFrame(); + processEvents(); _gui->draw(); @@ -180,6 +184,11 @@ void WageEngine::processEvents() { break; default: + if (event.kbd.ascii == '~') { + _debugger->attach(); + break; + } + if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) { if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) { _gui->processMenuShortCut(event.kbd.flags, event.kbd.ascii); diff --git a/engines/wage/wage.h b/engines/wage/wage.h index 6905fdc530..8ca306aea3 100644 --- a/engines/wage/wage.h +++ b/engines/wage/wage.h @@ -50,12 +50,13 @@ #include "engines/engine.h" #include "common/debug.h" -#include "gui/debugger.h" #include "common/endian.h" #include "common/rect.h" #include "common/macresman.h" #include "common/random.h" +#include "wage/debugger.h" + struct ADGameDescription; namespace Wage { @@ -181,6 +182,8 @@ public: public: Common::RandomSource *_rnd; + Debugger *_debugger; + Gui *_gui; World *_world; @@ -212,6 +215,8 @@ public: void redrawScene(); void saveGame(); + virtual GUI::Debugger *getDebugger() { return _debugger; } + private: Console *_console; diff --git a/graphics/font.cpp b/graphics/font.cpp index dba48249bc..97662dc15d 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -21,6 +21,7 @@ */ #include "graphics/font.h" +#include "graphics/managed_surface.h" #include "common/array.h" #include "common/util.h" @@ -264,6 +265,14 @@ int Font::getStringWidth(const Common::U32String &str) const { return getStringWidthImpl(*this, str); } +void Font::drawChar(ManagedSurface *dst, uint32 chr, int x, int y, uint32 color) const { + drawChar(&dst->_innerSurface, chr, x, y, color); + + Common::Rect charBox = getBoundingBox(chr); + charBox.translate(x, y); + dst->addDirtyRect(charBox); +} + void Font::drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { Common::String renderStr = useEllipsis ? handleEllipsis(str, w) : str; drawStringImpl(*this, dst, renderStr, x, y, w, color, align, deltax); @@ -273,6 +282,20 @@ void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y, drawStringImpl(*this, dst, str, x, y, w, color, align, 0); } +void Font::drawString(ManagedSurface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { + drawString(&dst->_innerSurface, str, x, y, w, color, align, deltax, useEllipsis); + if (w != 0) { + dst->addDirtyRect(getBoundingBox(str, x, y, w, align, deltax, useEllipsis)); + } +} + +void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align) const { + drawString(&dst->_innerSurface, str, x, y, w, color, align); + if (w != 0) { + dst->addDirtyRect(getBoundingBox(str, x, y, w, align)); + } +} + int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const { return wordWrapTextImpl(*this, str, maxWidth, lines); } diff --git a/graphics/font.h b/graphics/font.h index 35f6792d7f..0478608708 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -34,6 +34,7 @@ template<class T> class Array; namespace Graphics { struct Surface; +class ManagedSurface; /** Text alignment modes */ enum TextAlign { @@ -141,10 +142,13 @@ public: * @param color The color of the character. */ virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const = 0; + void drawChar(ManagedSurface *dst, uint32 chr, int x, int y, uint32 color) const; // TODO: Add doxygen comments to this void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; void drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft) const; + void drawString(ManagedSurface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; + void drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft) const; /** * Compute and return the width the string str has when rendered using this font. diff --git a/graphics/managed_surface.cpp b/graphics/managed_surface.cpp new file mode 100644 index 0000000000..273b15de55 --- /dev/null +++ b/graphics/managed_surface.cpp @@ -0,0 +1,260 @@ +/* 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 "graphics/managed_surface.h" +#include "common/algorithm.h" +#include "common/textconsole.h" + +namespace Graphics { + +const int SCALE_THRESHOLD = 0x100; + +ManagedSurface::ManagedSurface() : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { +} + +ManagedSurface::ManagedSurface(ManagedSurface &surf) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { + *this = surf; +} + +ManagedSurface::ManagedSurface(int width, int height) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { + create(width, height); +} + +ManagedSurface::ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { + create(width, height, pixelFormat); +} + +ManagedSurface::ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds) : + w(_innerSurface.w), h(_innerSurface.h), pitch(_innerSurface.pitch), format(_innerSurface.format), + _disposeAfterUse(DisposeAfterUse::NO), _owner(nullptr) { + create(surf, bounds); +} + +ManagedSurface::~ManagedSurface() { + free(); +} + +ManagedSurface &ManagedSurface::operator=(ManagedSurface &surf) { + // Free any current surface + free(); + + if (surf._disposeAfterUse == DisposeAfterUse::YES) { + // Create a new surface and copy the pixels from the source surface + create(surf.w, surf.h, surf.format); + Common::copy((const byte *)surf.getPixels(), (const byte *)surf.getPixels() + + surf.w * surf.h * surf.format.bytesPerPixel, (byte *)this->getPixels()); + } else { + // Source isn't managed, so simply copy its fields + _owner = surf._owner; + _offsetFromOwner = surf._offsetFromOwner; + void *srcPixels = surf._innerSurface.getPixels(); + _innerSurface.setPixels(srcPixels); + _innerSurface.w = surf.w; + _innerSurface.h = surf.h; + _innerSurface.pitch = surf.pitch; + this->format = surf.format; + } + + return *this; +} + +void ManagedSurface::setPixels(void *newPixels) { + free(); + _innerSurface.setPixels(newPixels); +} + +void ManagedSurface::create(uint16 width, uint16 height) { + create(width, height, PixelFormat::createFormatCLUT8()); +} + +void ManagedSurface::create(uint16 width, uint16 height, const PixelFormat &pixelFormat) { + free(); + _innerSurface.create(width, height, pixelFormat); + + _disposeAfterUse = DisposeAfterUse::YES; + markAllDirty(); +} + +void ManagedSurface::create(ManagedSurface &surf, const Common::Rect &bounds) { + free(); + + _offsetFromOwner = Common::Point(bounds.left, bounds.top); + _innerSurface.setPixels(surf.getBasePtr(bounds.left, bounds.top)); + _innerSurface.pitch = surf.pitch; + _innerSurface.format = surf.format; + _innerSurface.w = bounds.width(); + _innerSurface.h = bounds.height(); + _owner = &surf; + _disposeAfterUse = DisposeAfterUse::NO; +} + +void ManagedSurface::free() { + if (_disposeAfterUse == DisposeAfterUse::YES) + _innerSurface.free(); + + _disposeAfterUse = DisposeAfterUse::NO; + _owner = nullptr; + _offsetFromOwner = Common::Point(0, 0); +} + +bool ManagedSurface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) { + if (destBounds.left >= this->w || destBounds.top >= this->h || + destBounds.right <= 0 || destBounds.bottom <= 0) + return false; + + // Clip the bounds if necessary to fit on-screen + if (destBounds.right > this->w) { + srcBounds.right -= destBounds.right - this->w; + destBounds.right = this->w; + } + + if (destBounds.bottom > this->h) { + srcBounds.bottom -= destBounds.bottom - this->h; + destBounds.bottom = this->h; + } + + if (destBounds.top < 0) { + srcBounds.top += -destBounds.top; + destBounds.top = 0; + } + + if (destBounds.left < 0) { + srcBounds.left += -destBounds.left; + destBounds.left = 0; + } + + return true; +} + +void ManagedSurface::blitFrom(const Surface &src) { + blitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Point(0, 0)); +} + +void ManagedSurface::blitFrom(const Surface &src, const Common::Point &destPos) { + blitFrom(src, Common::Rect(0, 0, src.w, src.h), destPos); +} + +void ManagedSurface::blitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Point &destPos) { + Common::Rect srcBounds = srcRect; + Common::Rect destBounds(destPos.x, destPos.y, destPos.x + srcRect.width(), + destPos.y + srcRect.height()); + assert(src.format.bytesPerPixel == format.bytesPerPixel); + + if (!srcRect.isValidRect() || !clip(srcBounds, destBounds)) + return; + + for (int y = 0; y < srcBounds.height(); ++y) { + const byte *srcP = (const byte *)src.getBasePtr(srcBounds.left, srcBounds.top + y); + byte *destP = (byte *)getBasePtr(destBounds.left, destBounds.top + y); + Common::copy(srcP, srcP + srcBounds.width() * format.bytesPerPixel, destP); + } + + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); +} + +void ManagedSurface::transBlitFrom(const Surface &src, uint transColor, bool flipped, uint overrideColor) { + transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(0, 0, this->w, this->h), + transColor, false, overrideColor); +} + +void ManagedSurface::transBlitFrom(const Surface &src, const Common::Point &destPos, + uint transColor, bool flipped, uint overrideColor) { + transBlitFrom(src, Common::Rect(0, 0, src.w, src.h), Common::Rect(destPos.x, destPos.y, + destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor); +} + +void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Point &destPos, uint transColor, bool flipped, uint overrideColor) { + transBlitFrom(src, srcRect, Common::Rect(destPos.x, destPos.y, + destPos.x + src.w, destPos.y + src.h), transColor, false, overrideColor); +} + +template<typename T> +void transBlit(const Surface &src, const Common::Rect &srcRect, Surface *dest, const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { + int scaleX = SCALE_THRESHOLD * srcRect.width() / destRect.width(); + int scaleY = SCALE_THRESHOLD * srcRect.height() / destRect.height(); + + // Loop through drawing output lines + for (int destY = destRect.top, scaleYCtr = 0; destY < destRect.bottom; ++destY, scaleYCtr += scaleY) { + if (destY < 0 || destY >= dest->h) + continue; + const T *srcLine = (const T *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD); + T *destLine = (T *)dest->getBasePtr(destRect.left, destY); + + // Loop through drawing the pixels of the row + for (int destX = destRect.left, xCtr = 0, scaleXCtr = 0; destX < destRect.right; ++destX, ++xCtr, scaleXCtr += scaleX) { + if (destX < 0 || destX >= dest->w) + continue; + + T srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD]; + if (srcVal != transColor) { + destLine[xCtr] = overrideColor ? overrideColor : srcVal; + } + } + } +} + +void ManagedSurface::transBlitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Rect &destRect, uint transColor, bool flipped, uint overrideColor) { + if (src.w == 0 || src.h == 0 || destRect.width() == 0 || destRect.height() == 0) + return; + + if (format.bytesPerPixel == 1) + transBlit<byte>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); + else if (format.bytesPerPixel == 2) + transBlit<uint16>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); + else if (format.bytesPerPixel == 4) + transBlit<uint32>(src, srcRect, &_innerSurface, destRect, transColor, flipped, overrideColor); + else + error("Surface::transBlitFrom: bytesPerPixel must be 1, 2, or 4"); + + // Mark the affected area + addDirtyRect(destRect); +} + +void ManagedSurface::markAllDirty() { + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); +} + +void ManagedSurface::addDirtyRect(const Common::Rect &r) { + if (_owner) { + Common::Rect bounds = r; + bounds.clip(Common::Rect(0, 0, this->w, this->h)); + bounds.translate(_offsetFromOwner.x, _offsetFromOwner.y); + _owner->addDirtyRect(bounds); + } +} + +void ManagedSurface::clear(uint color) { + fillRect(getBounds(), color); +} + +} // End of namespace Graphics diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h new file mode 100644 index 0000000000..8cbd463266 --- /dev/null +++ b/graphics/managed_surface.h @@ -0,0 +1,376 @@ +/* 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 GRAPHICS_MANAGED_SURFACE_H +#define GRAPHICS_MANAGED_SURFACE_H + +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "common/rect.h" +#include "common/types.h" + +namespace Graphics { + +class Font; + +/** + * A derived graphics surface, which handles automatically managing the allocated + * surface data block, as well as introducing several new blitting methods + */ +class ManagedSurface { + friend class Font; +private: + /** + * The Graphics::Surface that the managed surface encapsulates + */ + Surface _innerSurface; + + /** + * If set, the inner surface will be freed when the surface is recreated, + * as well as when the surface is destroyed + */ + DisposeAfterUse::Flag _disposeAfterUse; + + /** + * Stores the owning surface if this If this managed surface represents + * a sub-section of another + */ + ManagedSurface *_owner; + + /** + * For sub-section areas of an owning parent managed surface, this represents + * the offset from the parent's top-left corner this sub-surface starts at + */ + Common::Point _offsetFromOwner; +protected: + /** + * Clips the given source bounds so the passed destBounds will be entirely on-screen + */ + bool clip(Common::Rect &srcBounds, Common::Rect &destBounds); + + /** + * Base method that descendent classes can override for recording affected + * dirty areas of the surface + */ + virtual void addDirtyRect(const Common::Rect &r); +public: + uint16 &w; + uint16 &h; + uint16 &pitch; + PixelFormat &format; +public: + /** + * Create the managed surface + */ + ManagedSurface(); + + /** + * Create a managed surface from another one. + * If the source surface is maintaining it's own surface data, then + * this surface will create it's own surface of the same size and copy + * the contents from the source surface + */ + ManagedSurface(ManagedSurface &surf); + + /** + * Create the managed surface + */ + ManagedSurface(int width, int height); + + /** + * Create the managed surface + */ + ManagedSurface(int width, int height, const Graphics::PixelFormat &pixelFormat); + + /** + * Create the managed surface + */ + ManagedSurface(ManagedSurface &surf, const Common::Rect &bounds); + + /** + * Destroy the managed surface + */ + virtual ~ManagedSurface(); + + /** + * Implements automatic conversion to a Graphics::Surface by + * simply returning the inner surface. This must be const, + * because we don't want changes being done directly to it, + * since it would bypass dirty rect handling + */ + operator const Surface &() const { return _innerSurface; } + const Surface &rawSurface() const { return _innerSurface; } + + /** + * Reassign one managed surface to another one + * Note that if the source has a managed surface, it will be duplicated + */ + ManagedSurface &operator=(ManagedSurface &surf); + + /** + * Returns true if the surface has not yet been allocated + */ + bool empty() const { return w == 0 || h == 0 || _innerSurface.getPixels() == nullptr; } + + /** + * Returns true if the surface is managing its own pixels + */ + DisposeAfterUse::Flag disposeAfterUse() const { return _disposeAfterUse; } + + /** + * Return a pointer to the pixel at the specified point. + * + * @param x The x coordinate of the pixel. + * @param y The y coordinate of the pixel. + * @return Pointer to the pixel. + */ + inline const void *getBasePtr(int x, int y) const { + return _innerSurface.getBasePtr(x, y); + } + + /** + * Return a pointer to the pixel at the specified point. + * + * @param x The x coordinate of the pixel. + * @param y The y coordinate of the pixel. + * @return Pointer to the pixel. + */ + inline void *getBasePtr(int x, int y) { + return _innerSurface.getBasePtr(x, y); + } + + /** + * Get a reference to the pixel data + */ + inline void *getPixels() { return _innerSurface.getPixels(); } + inline const void *getPixels() const { return _innerSurface.getPixels(); } + + /** + * Sets the pixel data. + */ + virtual void setPixels(void *newPixels); + + /** + * Allocate memory for the pixel data of the surface. + */ + virtual void create(uint16 width, uint16 height); + + /** + * Allocate memory for the pixel data of the surface. + */ + virtual void create(uint16 width, uint16 height, const PixelFormat &pixelFormat); + + /** + * Sets up the surface as a sub-section of another passed parent surface. This surface + * will not own the pixels, and any dirty rect notifications will automatically be + * passed to the original parent surface. + * @remarks Note that this differs from Graphics::Surface::getSubArea, in that that + * method only adds a single initial dirty rect for the whole area, and then none further + */ + virtual void create(ManagedSurface &surf, const Common::Rect &bounds); + + /** + * Release the memory used by the pixels memory of this surface. This is the + * counterpart to create(). + */ + virtual void free(); + + /** + * Clears any pending dirty rects that have been generated for the surface + */ + virtual void clearDirtyRects() {} + + /** + * When the managed surface is a sub-section of a parent surface, returns the + * the offset in the parent surface that the surface starts at + */ + const Common::Point getOffsetFromOwner() const { return _offsetFromOwner; } + + /** + * Return a rect giving the bounds of the surface + */ + const Common::Rect getBounds() const { + return Common::Rect(0, 0, this->w, this->h); + } + + /** + * Copies another surface into this one + */ + void blitFrom(const Surface &src); + + /** + * Copies another surface into this one at a given destination position + */ + void blitFrom(const Surface &src, const Common::Point &destPos); + + /** + * Copies another surface into this one at a given destination position + */ + void blitFrom(const Surface &src, const Common::Rect &srcRect, + const Common::Point &destPos); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param destPos Destination position to draw the surface + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, const Common::Point &destPos, + uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param srcRect Sub-section of source surface to draw + * @param destPos Destination position to draw the surface + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Point &destPos, + uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Copies another surface into this one ignoring pixels of a designated transparent color + * @param src Source surface + * @param srcRect Sub-section of source surface to draw + * @param destRect Destination area to draw the surface in. This can be sized differently + * then srcRect, allowing for arbitrary scaling of the image + * @param transColor Transparency color to ignore copying + * @param flipped Specifies whether to horizontally flip the image + * @param overrideColor Optional color to use instead of non-transparent pixels from + * the source surface + */ + void transBlitFrom(const Surface &src, const Common::Rect &srcRect, const Common::Rect &destRect, + uint transColor = 0, bool flipped = false, uint overrideColor = 0); + + /** + * Clear the entire surface + */ + void clear(uint color = 0); + + /** + * Mark the entire surface as dirty + */ + void markAllDirty(); + + /** + * Copies a bitmap to the Surface internal buffer. The pixel format + * of buffer must match the pixel format of the Surface. + */ + void copyRectToSurface(const void *buffer, int srcPitch, int destX, int destY, int width, int height) { + _innerSurface.copyRectToSurface(buffer, srcPitch, destX, destY, width, height); + } + + /** + * Copies a bitmap to the Surface internal buffer. The pixel format + * of buffer must match the pixel format of the Surface. + */ + void copyRectToSurface(const Graphics::Surface &srcSurface, int destX, int destY, const Common::Rect subRect) { + _innerSurface.copyRectToSurface(srcSurface, destX, destY, subRect); + } + + /** + * Copy the data from another Surface, reinitializing the + * surface to match the dimensions of the passed surface + */ + void copyFrom(const ManagedSurface &surf) { + clearDirtyRects(); + _innerSurface.copyFrom(surf._innerSurface); + } + + /** + * Draw a line. + */ + void drawLine(int x0, int y0, int x1, int y1, uint32 color) { + _innerSurface.drawLine(x0, y0, x1, y1, color); + addDirtyRect(Common::Rect(x0, y0, x1, y1)); + } + + /** + * Draw a thick line. + */ + void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) { + _innerSurface.drawThickLine(x0, y0, x1, y1, penX, penY, color); + addDirtyRect(Common::Rect(x0, y0, x1 + penX, y1 + penY)); + } + + /** + * Draw a horizontal line. + */ + void hLine(int x, int y, int x2, uint32 color) { + _innerSurface.hLine(x, y, x2, color); + addDirtyRect(Common::Rect(x, y, x2 + 1, y + 1)); + } + + /** + * Draw a vertical line. + */ + void vLine(int x, int y, int y2, uint32 color) { + _innerSurface.vLine(x, y, y2, color); + addDirtyRect(Common::Rect(x, y, x + 1, y2 + 1)); + } + + /** + * Fill a rect with a given color. + */ + void fillRect(Common::Rect r, uint32 color) { + _innerSurface.fillRect(r, color); + addDirtyRect(r); + } + + /** + * Draw a frame around a specified rect. + */ + void frameRect(const Common::Rect &r, uint32 color) { + _innerSurface.frameRect(r, color); + addDirtyRect(r); + } + + /** + * Returns a sub-area of the screen, but only adds a single initial dirty rect + * for the retrieved area. + */ + Surface getSubArea(const Common::Rect &area) { + addDirtyRect(area); + return _innerSurface.getSubArea(area); + } +}; + +} // End of namespace Graphics + + +#endif diff --git a/graphics/module.mk b/graphics/module.mk index b6919cf1ab..90f6a3199c 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -12,10 +12,12 @@ MODULE_OBJS := \ fonts/ttf.o \ fonts/winfont.o \ maccursor.o \ + managed_surface.o \ pixelformat.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ + screen.o \ sjis.o \ surface.o \ transform_struct.o \ diff --git a/graphics/screen.cpp b/graphics/screen.cpp new file mode 100644 index 0000000000..ccf651f536 --- /dev/null +++ b/graphics/screen.cpp @@ -0,0 +1,129 @@ +/* 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 "common/system.h" +#include "common/algorithm.h" +#include "graphics/screen.h" +#include "graphics/palette.h" + +namespace Graphics { + +Screen::Screen(): ManagedSurface() { + create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat()); +} + +Screen::Screen(int width, int height): ManagedSurface() { + create(width, height); +} + +Screen::Screen(int width, int height, PixelFormat pixelFormat): ManagedSurface() { + create(width, height, pixelFormat); +} + +void Screen::update() { + // Merge the dirty rects + mergeDirtyRects(); + + // Loop through copying dirty areas to the physical screen + Common::List<Common::Rect>::iterator i; + for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) { + const Common::Rect &r = *i; + const byte *srcP = (const byte *)getBasePtr(r.left, r.top); + g_system->copyRectToScreen(srcP, pitch, r.left, r.top, + r.width(), r.height()); + } + + // Signal the physical screen to update + g_system->updateScreen(); + _dirtyRects.clear(); +} + + +void Screen::addDirtyRect(const Common::Rect &r) { + Common::Rect bounds = r; + bounds.clip(getBounds()); + bounds.translate(getOffsetFromOwner().x, getOffsetFromOwner().y); + + if (bounds.width() > 0 && bounds.height() > 0) + _dirtyRects.push_back(bounds); +} + +void Screen::makeAllDirty() { + addDirtyRect(Common::Rect(0, 0, this->w, this->h)); +} + +void Screen::mergeDirtyRects() { + Common::List<Common::Rect>::iterator rOuter, rInner; + + // Process the dirty rect list to find any rects to merge + for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { + rInner = rOuter; + while (++rInner != _dirtyRects.end()) { + + if ((*rOuter).intersects(*rInner)) { + // These two rectangles overlap, so merge them + unionRectangle(*rOuter, *rOuter, *rInner); + + // remove the inner rect from the list + _dirtyRects.erase(rInner); + + // move back to beginning of list + rInner = rOuter; + } + } + } +} + +bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) { + destRect = src1; + destRect.extend(src2); + + return !destRect.isEmpty(); +} + +void Screen::getPalette(byte palette[PALETTE_SIZE]) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->grabPalette(palette, 0, PALETTE_COUNT); +} + +void Screen::getPalette(byte *palette, uint start, uint num) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->grabPalette(palette, start, num); +} + +void Screen::setPalette(const byte palette[PALETTE_SIZE]) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->setPalette(palette, 0, PALETTE_COUNT); +} + +void Screen::setPalette(const byte *palette, uint start, uint num) { + assert(format.bytesPerPixel == 1); + g_system->getPaletteManager()->setPalette(palette, start, num); +} + +void Screen::clearPalette() { + byte palette[PALETTE_SIZE]; + Common::fill(&palette[0], &palette[PALETTE_SIZE], 0); + setPalette(palette); +} + +} // End of namespace Graphics diff --git a/graphics/screen.h b/graphics/screen.h new file mode 100644 index 0000000000..29816120f1 --- /dev/null +++ b/graphics/screen.h @@ -0,0 +1,118 @@ +/* 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 GRAPHICS_SCREEN_H +#define GRAPHICS_SCREEN_H + +#include "graphics/managed_surface.h" +#include "graphics/pixelformat.h" +#include "common/list.h" +#include "common/rect.h" + +namespace Graphics { + +#define PALETTE_COUNT 256 +#define PALETTE_SIZE (256 * 3) + +/** + * Implements a specialised surface that represents the screen. + * It keeps track of any areas of itself that are updated by drawing + * calls, and provides an update that method that blits the affected + * areas to the physical screen + */ +class Screen : virtual public ManagedSurface { +private: + /** + * List of affected areas of the screen + */ + Common::List<Common::Rect> _dirtyRects; +private: + /** + * Merges together overlapping dirty areas of the screen + */ + void mergeDirtyRects(); + + /** + * Returns the union of two dirty area rectangles + */ + bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2); +protected: + /** + * Adds a rectangle to the list of modified areas of the screen during the + * current frame + */ + virtual void addDirtyRect(const Common::Rect &r); +public: + Screen(); + Screen(int width, int height); + Screen(int width, int height, PixelFormat pixelFormat); + + /** + * Returns true if there are any pending screen updates (dirty areas) + */ + bool isDirty() const { return !_dirtyRects.empty(); } + + /** + * Marks the whole screen as dirty. This forces the next call to update + * to copy the entire screen contents + */ + void makeAllDirty(); + + /** + * Clear the current dirty rects list + */ + virtual void clearDirtyRects() { _dirtyRects.clear(); } + + /** + * Updates the screen by copying any affected areas to the system + */ + virtual void update(); + + /** + * Return the currently active palette + */ + void getPalette(byte palette[PALETTE_SIZE]); + + /** + * Return a portion of the currently active palette + */ + void getPalette(byte *palette, uint start, uint num); + + /** + * Set the palette + */ + void setPalette(const byte palette[PALETTE_SIZE]); + + /** + * Set a subsection of the palette + */ + void setPalette(const byte *palette, uint start, uint num); + + /** + * Clears the current palette, setting all entries to black + */ + void clearPalette(); +}; + +} // End of namespace Graphics + +#endif diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 1b95a11e03..c2deb8c61e 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -164,7 +164,7 @@ struct DrawDataInfo { DrawData id; ///< The actual ID of the DrawData item. const char *name; ///< The name of the DrawData item as it appears in the Theme Description files bool buffer; ///< Sets whether this item is buffered on the backbuffer or drawn directly to the screen. - DrawData parent; ///< Parent DrawData item, for items that overlay. E.g. kButtonIdle -> kButtonHover + DrawData parent; ///< Parent DrawData item, for items that overlay. E.g. kDDButtonIdle -> kDDButtonHover }; /** diff --git a/gui/themes/translations.dat b/gui/themes/translations.dat Binary files differindex 2378bc4e13..b0da793cdf 100644 --- a/gui/themes/translations.dat +++ b/gui/themes/translations.dat diff --git a/po/zh-Latn_CN.po b/po/zh-Latn_CN.po new file mode 100644 index 0000000000..12105429a7 --- /dev/null +++ b/po/zh-Latn_CN.po @@ -0,0 +1,3730 @@ +# LANGUAGE translation for ScummVM. +# Copyright (C) YEAR ScummVM Team +# This file is distributed under the same license as the ScummVM package. +# Chenbo Li <lichenbo1949@gmail.com>, 2016. +# +msgid "" +msgstr "" +"Project-Id-Version: ScummVM 1.9.0git\n" +"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" +"POT-Creation-Date: 2016-02-20 21:22+0000\n" +"PO-Revision-Date: 2016-03-15 04:09-0700\n" +"Last-Translator: Chenbo Li <lichenbo1949@gmail.com>\n" +"Language-Team: Chenbo Li <lichenbo1949@gmail.com>\n" +"Language: Chinese Pinyin (Mandarin)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.7\n" + +#: gui/about.cpp:94 +#, c-format +msgid "(built on %s)" +msgstr "(Bianyiyu %s)" + +#: gui/about.cpp:101 +msgid "Features compiled in:" +msgstr "Gongneng BianyiYu" + +#: gui/about.cpp:110 +msgid "Available engines:" +msgstr "KeyongDeYinQing" + +#: gui/browser.cpp:68 gui/browser_osx.mm:104 +msgid "Show hidden files" +msgstr "Xianshi Yincang Wenjian" + +#: gui/browser.cpp:68 +msgid "Show files marked with the hidden attribute" +msgstr "Xianshi Suoyou Yincang Shuxing Wenjian" + +#: gui/browser.cpp:72 +msgid "Go up" +msgstr "ShangYiJi" + +#: gui/browser.cpp:72 gui/browser.cpp:74 +msgid "Go to previous directory level" +msgstr "Fanhui Zhiqian Mulu" + +#: gui/browser.cpp:74 +msgctxt "lowres" +msgid "Go up" +msgstr "ShangYiJi" + +#: gui/browser.cpp:75 gui/chooser.cpp:46 gui/editrecorddialog.cpp:67 +#: gui/filebrowser-dialog.cpp:64 gui/fluidsynth-dialog.cpp:152 +#: gui/KeysDialog.cpp:43 gui/launcher.cpp:351 gui/massadd.cpp:95 +#: gui/options.cpp:1237 gui/predictivedialog.cpp:73 gui/recorderdialog.cpp:70 +#: gui/recorderdialog.cpp:156 gui/saveload-dialog.cpp:216 +#: gui/saveload-dialog.cpp:276 gui/saveload-dialog.cpp:547 +#: gui/saveload-dialog.cpp:931 gui/themebrowser.cpp:55 engines/engine.cpp:546 +#: backends/events/default/default-events.cpp:196 +#: backends/events/default/default-events.cpp:218 +#: backends/platform/wii/options.cpp:48 engines/drascula/saveload.cpp:49 +#: engines/parallaction/saveload.cpp:274 engines/scumm/dialogs.cpp:191 +#: engines/sword1/control.cpp:865 +msgid "Cancel" +msgstr "Quxiao" + +#: gui/browser.cpp:76 gui/browser_osx.mm:103 gui/chooser.cpp:47 +#: gui/filebrowser-dialog.cpp:65 gui/themebrowser.cpp:56 +msgid "Choose" +msgstr "Xuanze" + +#: gui/editrecorddialog.cpp:58 +msgid "Author:" +msgstr "Zuozhe:" + +#: gui/editrecorddialog.cpp:59 gui/launcher.cpp:204 +msgid "Name:" +msgstr "Xingming:" + +#: gui/editrecorddialog.cpp:60 +msgid "Notes:" +msgstr "Beizhu:" + +#: gui/editrecorddialog.cpp:68 gui/predictivedialog.cpp:74 +msgid "Ok" +msgstr "Queding" + +#: gui/filebrowser-dialog.cpp:49 +msgid "Choose file for loading" +msgstr "Xuanze YaoJiazai de Wenjian" + +#: gui/filebrowser-dialog.cpp:49 +msgid "Enter filename for saving" +msgstr "Shuru Baocun de Wenjianming" + +#: gui/filebrowser-dialog.cpp:132 +msgid "Do you really want to overwrite the file?" +msgstr "Nin Shifou Queding Fugai Ciwenjian" + +#: gui/filebrowser-dialog.cpp:132 gui/fluidsynth-dialog.cpp:217 +#: gui/launcher.cpp:795 gui/launcher.cpp:943 gui/launcher.cpp:1002 +#: backends/events/symbiansdl/symbiansdl-events.cpp:186 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +#: engines/kyra/saveload_eob.cpp:557 engines/kyra/saveload_eob.cpp:590 +msgid "Yes" +msgstr "Shi" + +#: gui/filebrowser-dialog.cpp:132 gui/fluidsynth-dialog.cpp:217 +#: gui/launcher.cpp:795 gui/launcher.cpp:943 gui/launcher.cpp:1002 +#: backends/events/symbiansdl/symbiansdl-events.cpp:186 +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +#: backends/platform/wince/CELauncherDialog.cpp:83 +#: engines/kyra/saveload_eob.cpp:557 engines/kyra/saveload_eob.cpp:590 +msgid "No" +msgstr "Fou" + +#: gui/fluidsynth-dialog.cpp:68 +msgid "Reverb" +msgstr "Hunxiang" + +#: gui/fluidsynth-dialog.cpp:70 gui/fluidsynth-dialog.cpp:102 +msgid "Active" +msgstr "Jihuo" + +#: gui/fluidsynth-dialog.cpp:72 +msgid "Room:" +msgstr "Fangjian:" + +#: gui/fluidsynth-dialog.cpp:79 +msgid "Damp:" +msgstr "Shiqi:" + +#: gui/fluidsynth-dialog.cpp:86 +msgid "Width:" +msgstr "Kuandu:" + +#: gui/fluidsynth-dialog.cpp:93 gui/fluidsynth-dialog.cpp:111 +msgid "Level:" +msgstr "Jibie:" + +#: gui/fluidsynth-dialog.cpp:100 +msgid "Chorus" +msgstr "Hechang:" + +#: gui/fluidsynth-dialog.cpp:104 +msgid "N:" +msgstr "N:" + +#: gui/fluidsynth-dialog.cpp:118 +msgid "Speed:" +msgstr "Sudu:" + +#: gui/fluidsynth-dialog.cpp:125 +msgid "Depth:" +msgstr "Shendu:" + +#: gui/fluidsynth-dialog.cpp:132 +msgid "Type:" +msgstr "Leixing:" + +#: gui/fluidsynth-dialog.cpp:135 +msgid "Sine" +msgstr "Zhengxian" + +#: gui/fluidsynth-dialog.cpp:136 +msgid "Triangle" +msgstr "Sanjiaoxing" + +#: gui/fluidsynth-dialog.cpp:138 gui/options.cpp:1168 +msgid "Misc" +msgstr "Zaxiang" + +#: gui/fluidsynth-dialog.cpp:140 +msgid "Interpolation:" +msgstr "Chazhi:" + +#: gui/fluidsynth-dialog.cpp:143 +msgid "None (fastest)" +msgstr "Wu (Zuikuai)" + +#: gui/fluidsynth-dialog.cpp:144 +msgid "Linear" +msgstr "Xianxing" + +#: gui/fluidsynth-dialog.cpp:145 +msgid "Fourth-order" +msgstr "DiSiXu" + +#: gui/fluidsynth-dialog.cpp:146 +msgid "Seventh-order" +msgstr "DiQiXu" + +#: gui/fluidsynth-dialog.cpp:150 +msgid "Reset" +msgstr "Chongzhi" + +#: gui/fluidsynth-dialog.cpp:150 +msgid "Reset all FluidSynth settings to their default values." +msgstr "Chongzhi Suoyou de FluidSynth Shezhi" + +#: gui/fluidsynth-dialog.cpp:153 gui/KeysDialog.cpp:42 gui/launcher.cpp:352 +#: gui/launcher.cpp:1050 gui/launcher.cpp:1054 gui/massadd.cpp:92 +#: gui/options.cpp:1238 gui/saveload-dialog.cpp:932 engines/engine.cpp:465 +#: engines/engine.cpp:476 backends/platform/wii/options.cpp:47 +#: backends/platform/wince/CELauncherDialog.cpp:54 +#: engines/agos/animation.cpp:558 engines/drascula/saveload.cpp:49 +#: engines/groovie/script.cpp:408 engines/parallaction/saveload.cpp:274 +#: engines/scumm/dialogs.cpp:193 engines/scumm/scumm.cpp:1834 +#: engines/scumm/players/player_v3m.cpp:130 +#: engines/scumm/players/player_v5m.cpp:108 engines/sky/compact.cpp:131 +#: engines/sky/compact.cpp:141 engines/sword1/animation.cpp:524 +#: engines/sword1/animation.cpp:545 engines/sword1/animation.cpp:561 +#: engines/sword1/animation.cpp:569 engines/sword1/control.cpp:865 +#: engines/sword1/logic.cpp:1633 engines/sword2/animation.cpp:425 +#: engines/sword2/animation.cpp:445 engines/sword2/animation.cpp:461 +#: engines/sword2/animation.cpp:471 +msgid "OK" +msgstr "Queding" + +#: gui/fluidsynth-dialog.cpp:217 +msgid "" +"Do you really want to reset all FluidSynth settings to their default values?" +msgstr "Ni Shifou Yao Chongzhi Suoyou de FluidSynth Shezhi?" + +#: gui/gui-manager.cpp:119 backends/keymapper/remap-dialog.cpp:53 +#: engines/scumm/help.cpp:126 engines/scumm/help.cpp:141 +#: engines/scumm/help.cpp:166 engines/scumm/help.cpp:192 +#: engines/scumm/help.cpp:210 +msgid "Close" +msgstr "Guanbi" + +#: gui/gui-manager.cpp:122 +msgid "Mouse click" +msgstr "Shubiao Danji" + +#: gui/gui-manager.cpp:126 base/main.cpp:322 +msgid "Display keyboard" +msgstr "Xianshi Jianpan" + +#: gui/gui-manager.cpp:130 base/main.cpp:326 +msgid "Remap keys" +msgstr "Yingshe Jianwei" + +#: gui/gui-manager.cpp:133 base/main.cpp:329 engines/scumm/help.cpp:87 +msgid "Toggle fullscreen" +msgstr "Quanping Qiehuan" + +#: gui/KeysDialog.cpp:41 +msgid "Map" +msgstr "Yingshe" + +#: gui/KeysDialog.cpp:49 +msgid "Select an action and click 'Map'" +msgstr "Xuanze Yige Xingwei bing Danji ‘Yingshe’" + +#: gui/KeysDialog.cpp:80 gui/KeysDialog.cpp:102 gui/KeysDialog.cpp:141 +#, c-format +msgid "Associated key : %s" +msgstr "Guanlian Anjian : %s" + +#: gui/KeysDialog.cpp:82 gui/KeysDialog.cpp:104 gui/KeysDialog.cpp:143 +#, c-format +msgid "Associated key : none" +msgstr "Guanlian Anjian : Wu" + +#: gui/KeysDialog.cpp:90 +msgid "Please select an action" +msgstr "Qing Xuanze Xingwei" + +#: gui/KeysDialog.cpp:106 +msgid "Press the key to associate" +msgstr "Anxia Anjian lai Guanlian" + +#: gui/KeysDialog.cpp:145 gui/KeysDialog.h:36 +msgid "Choose an action to map" +msgstr "Xuanze yao Yingshe de Xingwei" + +#: gui/launcher.cpp:193 +msgid "Game" +msgstr "Youxi" + +#: gui/launcher.cpp:197 +msgid "ID:" +msgstr "ID:" + +#: gui/launcher.cpp:197 gui/launcher.cpp:199 gui/launcher.cpp:200 +msgid "" +"Short game identifier used for referring to saved games and running the game " +"from the command line" +msgstr "" +"Yige Jianduan de Biaoshifu lai Baocun Youxi huo Cong Minglinghang zhong " +"Yunxing" + +#: gui/launcher.cpp:199 +msgctxt "lowres" +msgid "ID:" +msgstr "ID:" + +#: gui/launcher.cpp:204 gui/launcher.cpp:206 gui/launcher.cpp:207 +msgid "Full title of the game" +msgstr "Youxi Quanming" + +#: gui/launcher.cpp:206 +msgctxt "lowres" +msgid "Name:" +msgstr "Mingcheng:" + +#: gui/launcher.cpp:210 +msgid "Language:" +msgstr "Yuyan:" + +#: gui/launcher.cpp:210 gui/launcher.cpp:211 +msgid "" +"Language of the game. This will not turn your Spanish game version into " +"English" +msgstr "" +"Youxi de Yuyan. CiXiang buhui jiang Yige XibanyaYu Banben Zhuancheng Yingwen" + +#: gui/launcher.cpp:212 gui/launcher.cpp:226 gui/options.cpp:87 +#: gui/options.cpp:735 gui/options.cpp:748 gui/options.cpp:1208 +#: audio/null.cpp:41 +msgid "<default>" +msgstr "<Moren>" + +#: gui/launcher.cpp:222 +msgid "Platform:" +msgstr "Pingtai:" + +#: gui/launcher.cpp:222 gui/launcher.cpp:224 gui/launcher.cpp:225 +msgid "Platform the game was originally designed for" +msgstr "Youxi Chushi Yunxing de Pingtai:" + +#: gui/launcher.cpp:224 +msgctxt "lowres" +msgid "Platform:" +msgstr "Pingtai:" + +#: gui/launcher.cpp:237 +msgid "Engine" +msgstr "Yinqing" + +#: gui/launcher.cpp:245 gui/options.cpp:1071 gui/options.cpp:1088 +msgid "Graphics" +msgstr "Tuxiang" + +#: gui/launcher.cpp:245 gui/options.cpp:1071 gui/options.cpp:1088 +msgid "GFX" +msgstr "GFX" + +#: gui/launcher.cpp:248 +msgid "Override global graphic settings" +msgstr "Fugai Quanju Tuxiang Shezhi" + +#: gui/launcher.cpp:250 +msgctxt "lowres" +msgid "Override global graphic settings" +msgstr "Fugai Quanju Tuxiang Shezhi" + +#: gui/launcher.cpp:257 gui/options.cpp:1094 +msgid "Audio" +msgstr "Yinpin" + +#: gui/launcher.cpp:260 +msgid "Override global audio settings" +msgstr "Fugai Quanju Yinpin Shezhi" + +#: gui/launcher.cpp:262 +msgctxt "lowres" +msgid "Override global audio settings" +msgstr "Fugai QUanju Yinpin Shezhi" + +#: gui/launcher.cpp:271 gui/options.cpp:1099 +msgid "Volume" +msgstr "Yinliang" + +#: gui/launcher.cpp:273 gui/options.cpp:1101 +msgctxt "lowres" +msgid "Volume" +msgstr "YinLiang" + +#: gui/launcher.cpp:276 +msgid "Override global volume settings" +msgstr "Fugai Quanju YinLiang Shezhi" + +#: gui/launcher.cpp:278 +msgctxt "lowres" +msgid "Override global volume settings" +msgstr "Fugai Quanju YinLiang Shezhi" + +#: gui/launcher.cpp:286 gui/options.cpp:1109 +msgid "MIDI" +msgstr "MIDI" + +#: gui/launcher.cpp:289 +msgid "Override global MIDI settings" +msgstr "Fugai Quanju MIDI Shezhi" + +#: gui/launcher.cpp:291 +msgctxt "lowres" +msgid "Override global MIDI settings" +msgstr "Fugai Quanju MIDI Shezhi" + +#: gui/launcher.cpp:300 gui/options.cpp:1115 +msgid "MT-32" +msgstr "MT-32" + +#: gui/launcher.cpp:303 +msgid "Override global MT-32 settings" +msgstr "Fugai Quanju MT-32 Shezhi" + +#: gui/launcher.cpp:305 +msgctxt "lowres" +msgid "Override global MT-32 settings" +msgstr "Fugai Quanju MT-32 Shezhi" + +#: gui/launcher.cpp:314 gui/options.cpp:1122 +msgid "Paths" +msgstr "Lujing" + +#: gui/launcher.cpp:316 gui/options.cpp:1124 +msgctxt "lowres" +msgid "Paths" +msgstr "Lujing" + +#: gui/launcher.cpp:323 +msgid "Game Path:" +msgstr "Youxi Lujing:" + +#: gui/launcher.cpp:325 +msgctxt "lowres" +msgid "Game Path:" +msgstr "Youxi Lujing:" + +#: gui/launcher.cpp:330 gui/options.cpp:1148 +msgid "Extra Path:" +msgstr "Qita Lujing:" + +#: gui/launcher.cpp:330 gui/launcher.cpp:332 gui/launcher.cpp:333 +msgid "Specifies path to additional data used by the game" +msgstr "Zhiding Youxi Suoyong de Shuju de Cunfang Lujing" + +#: gui/launcher.cpp:332 gui/options.cpp:1150 +msgctxt "lowres" +msgid "Extra Path:" +msgstr "Qita Lujing:" + +#: gui/launcher.cpp:339 gui/options.cpp:1132 +msgid "Save Path:" +msgstr "Baocun Lujing:" + +#: gui/launcher.cpp:339 gui/launcher.cpp:341 gui/launcher.cpp:342 +#: gui/options.cpp:1132 gui/options.cpp:1134 gui/options.cpp:1135 +msgid "Specifies where your saved games are put" +msgstr "Zhiding Nin Jiang Youxi Baocun Zai le Nali" + +#: gui/launcher.cpp:341 gui/options.cpp:1134 +msgctxt "lowres" +msgid "Save Path:" +msgstr "Baocun Lujing:" + +#: gui/launcher.cpp:360 gui/launcher.cpp:459 gui/launcher.cpp:517 +#: gui/launcher.cpp:571 gui/options.cpp:1143 gui/options.cpp:1151 +#: gui/options.cpp:1160 gui/options.cpp:1275 gui/options.cpp:1281 +#: gui/options.cpp:1289 gui/options.cpp:1319 gui/options.cpp:1325 +#: gui/options.cpp:1332 gui/options.cpp:1425 gui/options.cpp:1428 +#: gui/options.cpp:1440 +msgctxt "path" +msgid "None" +msgstr "Wu" + +#: gui/launcher.cpp:365 gui/launcher.cpp:465 gui/launcher.cpp:575 +#: gui/options.cpp:1269 gui/options.cpp:1313 gui/options.cpp:1431 +#: backends/platform/wii/options.cpp:56 +msgid "Default" +msgstr "Moren" + +#: gui/launcher.cpp:510 gui/options.cpp:1434 +msgid "Select SoundFont" +msgstr "Xuanze SoundFont" + +#: gui/launcher.cpp:529 gui/launcher.cpp:682 +msgid "Select directory with game data" +msgstr "Xuanze Youxi Shuju Mulu" + +#: gui/launcher.cpp:547 +msgid "Select additional game directory" +msgstr "Xuanze Qita Youxi Mulu" + +#: gui/launcher.cpp:559 gui/options.cpp:1377 +msgid "Select directory for saved games" +msgstr "Xuanze Youxi Baocun Mulu" + +#: gui/launcher.cpp:586 +msgid "This game ID is already taken. Please choose another one." +msgstr "Ci Youxi ID Yi Bei Zhanyong. Qing Xuanze Qita Mingcheng" + +#: gui/launcher.cpp:626 engines/dialogs.cpp:111 +msgid "~Q~uit" +msgstr "~Q~Tuichu" + +#: gui/launcher.cpp:626 backends/platform/sdl/macosx/appmenu_osx.mm:106 +msgid "Quit ScummVM" +msgstr "Tuichu ScummVM" + +#: gui/launcher.cpp:627 +msgid "A~b~out..." +msgstr "~b~Guanyu" + +#: gui/launcher.cpp:627 backends/platform/sdl/macosx/appmenu_osx.mm:80 +msgid "About ScummVM" +msgstr "Guanyu ScummVM" + +#: gui/launcher.cpp:628 +msgid "~O~ptions..." +msgstr "~O~Xuanxiang" + +#: gui/launcher.cpp:628 +msgid "Change global ScummVM options" +msgstr "Genggai ScummVM Quanju Shezhi" + +#: gui/launcher.cpp:630 +msgid "~S~tart" +msgstr "~S~Kaishi" + +#: gui/launcher.cpp:630 +msgid "Start selected game" +msgstr "Kaishi Xuanze de Youxi" + +#: gui/launcher.cpp:633 +msgid "~L~oad..." +msgstr "~L~Jiazai" + +#: gui/launcher.cpp:633 +msgid "Load saved game for selected game" +msgstr "Jiazai Xuanze Baocun de Youxi" + +#: gui/launcher.cpp:638 +msgid "~A~dd Game..." +msgstr "~A~Tianjia Youxi ..." + +#: gui/launcher.cpp:638 gui/launcher.cpp:645 +msgid "Hold Shift for Mass Add" +msgstr "Anzhu Shift Lai Piliang Tianjia" + +#: gui/launcher.cpp:640 +msgid "~E~dit Game..." +msgstr "~E~Bianji Youxi ..." + +#: gui/launcher.cpp:640 gui/launcher.cpp:647 +msgid "Change game options" +msgstr "Genggai Youxi Xuanxiang" + +#: gui/launcher.cpp:642 +msgid "~R~emove Game" +msgstr "~R~Yichu Youxi" + +#: gui/launcher.cpp:642 gui/launcher.cpp:649 +msgid "Remove game from the list. The game data files stay intact" +msgstr "Cong Liebiao zhong YIchu Youxi. Baoliu Youxi Shuju Wenjian" + +#: gui/launcher.cpp:645 +msgctxt "lowres" +msgid "~A~dd Game..." +msgstr "~A~Tianjia Youxi ..." + +#: gui/launcher.cpp:647 +msgctxt "lowres" +msgid "~E~dit Game..." +msgstr "~E~Bianji Youxi ..." + +#: gui/launcher.cpp:649 +msgctxt "lowres" +msgid "~R~emove Game" +msgstr "~R~Yichu Youxi ..." + +#: gui/launcher.cpp:657 +msgid "Search in game list" +msgstr "Zai Youxi Liebiao zhong Sousuo" + +#: gui/launcher.cpp:661 gui/launcher.cpp:1224 +msgid "Search:" +msgstr "Sousuo:" + +#: gui/launcher.cpp:685 engines/dialogs.cpp:115 engines/cruise/menu.cpp:214 +#: engines/mohawk/riven.cpp:718 engines/pegasus/pegasus.cpp:353 +#: engines/tsage/scenes.cpp:600 +msgid "Load game:" +msgstr "Jiazai Youxi:" + +#: gui/launcher.cpp:685 engines/dialogs.cpp:115 +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +#: engines/cruise/menu.cpp:214 engines/mohawk/riven.cpp:718 +#: engines/parallaction/saveload.cpp:197 engines/pegasus/pegasus.cpp:353 +#: engines/scumm/dialogs.cpp:189 engines/tsage/scenes.cpp:600 +msgid "Load" +msgstr "Jiazai" + +#: gui/launcher.cpp:794 +msgid "" +"Do you really want to run the mass game detector? This could potentially add " +"a huge number of games." +msgstr "" +"Nin Queding yao Yunxing Youxi Piliang Jiance Ma? Zhe You Keneng Hui Zengjia " +"Daliang Youxi." + +#: gui/launcher.cpp:843 +msgid "ScummVM couldn't open the specified directory!" +msgstr "ScummVM Wufa Dakai Zhiding Mulu!" + +#: gui/launcher.cpp:855 +msgid "ScummVM could not find any game in the specified directory!" +msgstr "ScummVM zai Zhiding Mulu Zhong Wufa Zhaodao Renhe Youxi!" + +#: gui/launcher.cpp:869 +msgid "Pick the game:" +msgstr "Xuanze Youxi:" + +#: gui/launcher.cpp:943 +msgid "Do you really want to remove this game configuration?" +msgstr "Nin Zhende Xiangyao Yichu Zhege Youxi Peizhi?" + +#: gui/launcher.cpp:1001 +msgid "Do you want to load saved game?" +msgstr "Nin Yao Zairu Baocun de Youxi ma?" + +#: gui/launcher.cpp:1050 +msgid "This game does not support loading games from the launcher." +msgstr "Ci Youxi Bu Zhichi cong Jiazaiqi Zhong Jiazai Youxi." + +#: gui/launcher.cpp:1054 +msgid "ScummVM could not find any engine capable of running the selected game!" +msgstr "ScummVM Wufa Zhaodao Keyi Yunxing Ci Youxi de Yinqing!" + +#: gui/launcher.cpp:1161 +msgid "Mass Add..." +msgstr "PiLiang Zengjia ..." + +#: gui/launcher.cpp:1163 +msgid "Record..." +msgstr "Luxiang ..." + +#: gui/massadd.cpp:79 gui/massadd.cpp:82 +msgid "... progress ..." +msgstr "... Jindu ..." + +#: gui/massadd.cpp:259 +msgid "Scan complete!" +msgstr "Saomiao Wancheng!" + +#: gui/massadd.cpp:262 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games." +msgstr "Faxian le %d ge Xinyouxi, Hulue %d ge YiTianjia de Youxi" + +#: gui/massadd.cpp:266 +#, c-format +msgid "Scanned %d directories ..." +msgstr "YiSaomiao %d ge Mulu ..." + +#: gui/massadd.cpp:269 +#, c-format +msgid "Discovered %d new games, ignored %d previously added games ..." +msgstr "Faxian le %d ge Xinyouxi, Hulue %d ge YiTianjia de Youxi ..." + +#: gui/onscreendialog.cpp:101 gui/onscreendialog.cpp:103 +msgid "Stop" +msgstr "Tingzhi" + +#: gui/onscreendialog.cpp:106 +msgid "Edit record description" +msgstr "Bianji Luxiang Shuoming" + +#: gui/onscreendialog.cpp:108 +msgid "Switch to Game" +msgstr "Qiehuan zhi Youxi" + +#: gui/onscreendialog.cpp:110 +msgid "Fast replay" +msgstr "Kuaisu Huitui" + +#: gui/options.cpp:85 +msgid "Never" +msgstr "Yongbu" + +#: gui/options.cpp:85 +msgid "every 5 mins" +msgstr "Mei 5 Fenzhong" + +#: gui/options.cpp:85 +msgid "every 10 mins" +msgstr "Mei 10 Fenzhong" + +#: gui/options.cpp:85 +msgid "every 15 mins" +msgstr "Mei 15 Fenzhong" + +#: gui/options.cpp:85 +msgid "every 30 mins" +msgstr "Mei 30 Fenzhong" + +#: gui/options.cpp:87 +msgid "8 kHz" +msgstr "8 kHz" + +#: gui/options.cpp:87 +msgid "11 kHz" +msgstr "11 kHz" + +#: gui/options.cpp:87 +msgid "22 kHz" +msgstr "22 kHz" + +#: gui/options.cpp:87 +msgid "44 kHz" +msgstr "44 kHz" + +#: gui/options.cpp:87 +msgid "48 kHz" +msgstr "48 kHz" + +#: gui/options.cpp:255 gui/options.cpp:479 gui/options.cpp:580 +#: gui/options.cpp:649 gui/options.cpp:857 +msgctxt "soundfont" +msgid "None" +msgstr "Wu" + +#: gui/options.cpp:389 +msgid "Failed to apply some of the graphic options changes:" +msgstr "Tuxing Xuanxiang Genggai Shibai:" + +#: gui/options.cpp:401 +msgid "the video mode could not be changed." +msgstr "Shipin Moshi Wufa Genggai." + +#: gui/options.cpp:407 +msgid "the fullscreen setting could not be changed" +msgstr "Quanping Shezhi Wufa Genggai" + +#: gui/options.cpp:413 +msgid "the aspect ratio setting could not be changed" +msgstr "Bili Xuanxiang Wufa Genggai" + +#: gui/options.cpp:732 +msgid "Graphics mode:" +msgstr "Tuxing Moshi:" + +#: gui/options.cpp:746 +msgid "Render mode:" +msgstr "Xuanran Moshi:" + +#: gui/options.cpp:746 gui/options.cpp:747 +msgid "Special dithering modes supported by some games" +msgstr "Youxi Zhichi Teshu de Doudong Moshi" + +#: gui/options.cpp:758 +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2316 +msgid "Fullscreen mode" +msgstr "Quanping Moshi" + +#: gui/options.cpp:761 +msgid "Aspect ratio correction" +msgstr "Bili Jiaozheng" + +#: gui/options.cpp:761 +msgid "Correct aspect ratio for 320x200 games" +msgstr "320x200 Youxi Bili Jiaozheng" + +#: gui/options.cpp:769 +msgid "Preferred Device:" +msgstr "Youxian Shebei:" + +#: gui/options.cpp:769 +msgid "Music Device:" +msgstr "Yinyue Shebei:" + +#: gui/options.cpp:769 gui/options.cpp:771 +msgid "Specifies preferred sound device or sound card emulator" +msgstr "Zhiding Youxian Shengyin Shebei huo Shengka Moniqi" + +#: gui/options.cpp:769 gui/options.cpp:771 gui/options.cpp:772 +msgid "Specifies output sound device or sound card emulator" +msgstr "Zhiding Shuchu Shengyin Shebei huo Shengka Moniqi" + +#: gui/options.cpp:771 +msgctxt "lowres" +msgid "Preferred Dev.:" +msgstr "Youxian Shebei:" + +#: gui/options.cpp:771 +msgctxt "lowres" +msgid "Music Device:" +msgstr "Yinyue Shebei:" + +#: gui/options.cpp:798 +msgid "AdLib emulator:" +msgstr "AdLib Moniqi:" + +#: gui/options.cpp:798 gui/options.cpp:799 +msgid "AdLib is used for music in many games" +msgstr "AdLib bei Henduo Youxi Yonglai Bofang Yinyue" + +#: gui/options.cpp:809 +msgid "Output rate:" +msgstr "Shuchu Malv:" + +#: gui/options.cpp:809 gui/options.cpp:810 +msgid "" +"Higher value specifies better sound quality but may be not supported by your " +"soundcard" +msgstr "" +"Genggao de Shuxing Hui Tisheng Yinyue Zhiliang dan Youkeneng Nin de Shengka " +"Buzhichi" + +#: gui/options.cpp:820 +msgid "GM Device:" +msgstr "GM Shebei:" + +#: gui/options.cpp:820 +msgid "Specifies default sound device for General MIDI output" +msgstr "Zhiding Tongyong MIDI Shuchu Moren Shengyin Shebei" + +#: gui/options.cpp:831 +msgid "Don't use General MIDI music" +msgstr "Buyao Shiyong Tongyong MIDI Yinyue" + +#: gui/options.cpp:842 gui/options.cpp:908 +msgid "Use first available device" +msgstr "Shiyong Diyige keyong de Shebei" + +#: gui/options.cpp:854 +msgid "SoundFont:" +msgstr "SoundFont:" + +#: gui/options.cpp:854 gui/options.cpp:856 gui/options.cpp:857 +msgid "SoundFont is supported by some audio cards, FluidSynth and Timidity" +msgstr "Yixie Shengka Zhichi SoundFont, Biru FluidSynth He Timidity" + +#: gui/options.cpp:856 +msgctxt "lowres" +msgid "SoundFont:" +msgstr "SoundFont:" + +#: gui/options.cpp:862 +msgid "Mixed AdLib/MIDI mode" +msgstr "Hunhe AdLib/MIDI Moshi" + +#: gui/options.cpp:862 +msgid "Use both MIDI and AdLib sound generation" +msgstr "TongShi Shiyong MIDI He AdLib Shengyin Shengcheng" + +#: gui/options.cpp:865 +msgid "MIDI gain:" +msgstr "MIDI gain:" + +#: gui/options.cpp:872 +msgid "FluidSynth Settings" +msgstr "FluidSynth Xuanxiang" + +#: gui/options.cpp:879 +msgid "MT-32 Device:" +msgstr "MT-32 Shebei:" + +#: gui/options.cpp:879 +msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output" +msgstr "" +"QIng Zhiding Yongyu Roland MT-32/LAPC1/CM32I/CM64 Shuchu de Moren Shengyin " +"Shebei" + +#: gui/options.cpp:884 +msgid "True Roland MT-32 (disable GM emulation)" +msgstr "Zhen Roland MT-32 (Jinyong GM Moni)" + +#: gui/options.cpp:884 gui/options.cpp:886 +msgid "" +"Check if you want to use your real hardware Roland-compatible sound device " +"connected to your computer" +msgstr "" +"Jiancha Shifou Nin Xiang Shiyong Lianjie Dao Jisuanji de Zhenshi de Yingjian " +"Roland Jianrong Shengyin Shebei" + +#: gui/options.cpp:886 +msgctxt "lowres" +msgid "True Roland MT-32 (no GM emulation)" +msgstr "Zhen Roland MT-32 Shebei (Wu GM Moni)" + +#: gui/options.cpp:889 +msgid "Roland GS Device (enable MT-32 mappings)" +msgstr "Roland GS Shebei (Qiyong MT-32 Yingshe)" + +#: gui/options.cpp:889 +msgid "" +"Check if you want to enable patch mappings to emulate an MT-32 on a Roland " +"GS device" +msgstr "" +"Jiancha Shifou Nin Xiang Qiyong patch Yingshe Lai Zai Roland GS Shebei " +"Shangmian Moni MT-32" + +#: gui/options.cpp:898 +msgid "Don't use Roland MT-32 music" +msgstr "Buyao Shiyong Roland MT-32 Yinyue" + +#: gui/options.cpp:925 +msgid "Text and Speech:" +msgstr "Wenzi he Yuyin:" + +#: gui/options.cpp:929 gui/options.cpp:939 +msgid "Speech" +msgstr "Yuyin" + +#: gui/options.cpp:930 gui/options.cpp:940 +msgid "Subtitles" +msgstr "Zimu" + +#: gui/options.cpp:931 +msgid "Both" +msgstr "Liangzhe" + +#: gui/options.cpp:933 +msgid "Subtitle speed:" +msgstr "Zimu Sudu:" + +#: gui/options.cpp:935 +msgctxt "lowres" +msgid "Text and Speech:" +msgstr "Wenben he Yuyin:" + +#: gui/options.cpp:939 +msgid "Spch" +msgstr "Zimu" + +#: gui/options.cpp:940 +msgid "Subs" +msgstr "Yuyin" + +#: gui/options.cpp:941 +msgctxt "lowres" +msgid "Both" +msgstr "Dou" + +#: gui/options.cpp:941 +msgid "Show subtitles and play speech" +msgstr "Xianshi Zimu Bing Bofang Yuyin" + +#: gui/options.cpp:943 +msgctxt "lowres" +msgid "Subtitle speed:" +msgstr "Zimu Sudu" + +#: gui/options.cpp:959 +msgid "Music volume:" +msgstr "Yinyue Yinliang:" + +#: gui/options.cpp:961 +msgctxt "lowres" +msgid "Music volume:" +msgstr "Yinyue Yinliang:" + +#: gui/options.cpp:968 +msgid "Mute All" +msgstr "Quanbu Jinyin" + +#: gui/options.cpp:971 +msgid "SFX volume:" +msgstr "Yinxiao Yinliang:" + +#: gui/options.cpp:971 gui/options.cpp:973 gui/options.cpp:974 +msgid "Special sound effects volume" +msgstr "Texiao Yinliang" + +#: gui/options.cpp:973 +msgctxt "lowres" +msgid "SFX volume:" +msgstr "Yinxiao Yinliang:" + +#: gui/options.cpp:981 +msgid "Speech volume:" +msgstr "Yuyin Yinliang:" + +#: gui/options.cpp:983 +msgctxt "lowres" +msgid "Speech volume:" +msgstr "Yuyin Yinliang:" + +#: gui/options.cpp:1140 +msgid "Theme Path:" +msgstr "Zhuti Lujing:" + +#: gui/options.cpp:1142 +msgctxt "lowres" +msgid "Theme Path:" +msgstr "Zhuti Lujing:" + +#: gui/options.cpp:1148 gui/options.cpp:1150 gui/options.cpp:1151 +msgid "Specifies path to additional data used by all games or ScummVM" +msgstr "Zhiding Suoyou Youxi huo ScummVM de Shuju Lujing" + +#: gui/options.cpp:1157 +msgid "Plugins Path:" +msgstr "Chajian Lujing:" + +#: gui/options.cpp:1159 +msgctxt "lowres" +msgid "Plugins Path:" +msgstr "Chajian Lujing:" + +#: gui/options.cpp:1170 +msgctxt "lowres" +msgid "Misc" +msgstr "Zaxiang" + +#: gui/options.cpp:1172 +msgid "Theme:" +msgstr "Zhuti:" + +#: gui/options.cpp:1176 +msgid "GUI Renderer:" +msgstr "Jiemian Xuanran:" + +#: gui/options.cpp:1188 +msgid "Autosave:" +msgstr "Zidong Baocun:" + +#: gui/options.cpp:1190 +msgctxt "lowres" +msgid "Autosave:" +msgstr "Zidong Baocun:" + +#: gui/options.cpp:1198 +msgid "Keys" +msgstr "Guanjianzi" + +#: gui/options.cpp:1205 +msgid "GUI Language:" +msgstr "Jiemian Yuyan:" + +#: gui/options.cpp:1205 +msgid "Language of ScummVM GUI" +msgstr "ScummVM Jiemian Yuyan" + +#: gui/options.cpp:1364 +msgid "You have to restart ScummVM before your changes will take effect." +msgstr "Nin Xuyao Chongqi ScummVM Lai Shi Genggai Shengxiao" + +#: gui/options.cpp:1384 +msgid "The chosen directory cannot be written to. Please select another one." +msgstr "Zhiding de Mulu Buneng Xieru. Qing Xuanze Qita de Mulu." + +#: gui/options.cpp:1393 +msgid "Select directory for GUI themes" +msgstr "Xuanze Jiemian Zhuti de Mulu" + +#: gui/options.cpp:1403 +msgid "Select directory for extra files" +msgstr "Xuanze QIta Wenjian Mulu" + +#: gui/options.cpp:1414 +msgid "Select directory for plugins" +msgstr "Xuanze Chajian Mulu" + +#: gui/options.cpp:1467 +msgid "" +"The theme you selected does not support your current language. If you want " +"to use this theme you need to switch to another language first." +msgstr "" +"Nin Xuanze de Zhuti Bu Zhichi Xianzai de Yuyan. Qing Xian Qiehuan Dao Qita " +"Yuyan." + +#. I18N: You must leave "#" as is, only word 'next' is translatable +#: gui/predictivedialog.cpp:86 +msgid "# next" +msgstr "# Xia Yi Ge" + +#: gui/predictivedialog.cpp:87 +msgid "add" +msgstr "Zengjia" + +#: gui/predictivedialog.cpp:92 gui/predictivedialog.cpp:164 +msgid "Delete char" +msgstr "Shanchu Zifu" + +#: gui/predictivedialog.cpp:97 gui/predictivedialog.cpp:168 +msgid "<" +msgstr "<" + +#. I18N: Pre means 'Predictive', leave '*' as is +#: gui/predictivedialog.cpp:99 gui/predictivedialog.cpp:572 +msgid "* Pre" +msgstr "* Guanci" + +#. I18N: 'Num' means Numbers +#: gui/predictivedialog.cpp:575 +msgid "* Num" +msgstr "* Shuzi" + +#. I18N: 'Abc' means Latin alphabet input +#: gui/predictivedialog.cpp:578 +msgid "* Abc" +msgstr "* Zimu" + +#: gui/recorderdialog.cpp:64 +msgid "Recorder or Playback Gameplay" +msgstr "Youxi Luxiang Huo Huifang" + +#: gui/recorderdialog.cpp:69 gui/recorderdialog.cpp:156 +#: gui/saveload-dialog.cpp:220 gui/saveload-dialog.cpp:276 +msgid "Delete" +msgstr "Shanchu" + +#: gui/recorderdialog.cpp:71 +msgid "Record" +msgstr "Luxiang" + +#: gui/recorderdialog.cpp:72 +msgid "Playback" +msgstr "Huifang" + +#: gui/recorderdialog.cpp:74 +msgid "Edit" +msgstr "Binaji" + +#: gui/recorderdialog.cpp:86 gui/recorderdialog.cpp:243 +#: gui/recorderdialog.cpp:253 +msgid "Author: " +msgstr "Zuozhe:" + +#: gui/recorderdialog.cpp:87 gui/recorderdialog.cpp:244 +#: gui/recorderdialog.cpp:254 +msgid "Notes: " +msgstr "Zhushi:" + +#: gui/recorderdialog.cpp:155 +msgid "Do you really want to delete this record?" +msgstr "Nin Zhende Xinagyao Shanchu Zhege Luxiang ma?" + +#: gui/recorderdialog.cpp:174 +msgid "Unknown Author" +msgstr "Weizhi Zuozhe" + +#: gui/saveload-dialog.cpp:167 +msgid "List view" +msgstr "Liebiao Shitu" + +#: gui/saveload-dialog.cpp:168 +msgid "Grid view" +msgstr "Wangge Shitu" + +#: gui/saveload-dialog.cpp:211 gui/saveload-dialog.cpp:360 +msgid "No date saved" +msgstr "Riqi Wei Baocun" + +#: gui/saveload-dialog.cpp:212 gui/saveload-dialog.cpp:361 +msgid "No time saved" +msgstr "Shijian Wei Baocun" + +#: gui/saveload-dialog.cpp:213 gui/saveload-dialog.cpp:362 +msgid "No playtime saved" +msgstr "Bofang Shijian Wei Baocun" + +#: gui/saveload-dialog.cpp:275 +msgid "Do you really want to delete this saved game?" +msgstr "Nin Zhende Yao Shanchu Zhege Baocun Youxi ma?" + +#: gui/saveload-dialog.cpp:385 gui/saveload-dialog.cpp:884 +msgid "Date: " +msgstr "Riqi: " + +#: gui/saveload-dialog.cpp:389 gui/saveload-dialog.cpp:890 +msgid "Time: " +msgstr "Shijian: " + +#: gui/saveload-dialog.cpp:395 gui/saveload-dialog.cpp:898 +msgid "Playtime: " +msgstr "Bofang Shijian:" + +#: gui/saveload-dialog.cpp:408 gui/saveload-dialog.cpp:496 +msgid "Untitled savestate" +msgstr "Wuming Baocun Zhuangtai" + +#: gui/saveload-dialog.cpp:548 +msgid "Next" +msgstr "Xia Yi Ge" + +#: gui/saveload-dialog.cpp:551 +msgid "Prev" +msgstr "Shang Yi Ge" + +#: gui/saveload-dialog.cpp:748 +msgid "New Save" +msgstr "Xinjian Cundang" + +#: gui/saveload-dialog.cpp:748 +msgid "Create a new save game" +msgstr "Chuangjian Yige Xin Cundang" + +#: gui/saveload-dialog.cpp:877 +msgid "Name: " +msgstr "Mingcheng:" + +#: gui/saveload-dialog.cpp:949 +#, c-format +msgid "Enter a description for slot %d:" +msgstr "Shuru dui %d Dangwei de Miaoshu" + +#: gui/themebrowser.cpp:45 +msgid "Select a Theme" +msgstr "Xuanze Zhuti" + +#: gui/ThemeEngine.cpp:347 +msgid "Disabled GFX" +msgstr "Jinyong GFX" + +#: gui/ThemeEngine.cpp:347 +msgctxt "lowres" +msgid "Disabled GFX" +msgstr "Jinyong GFX" + +#: gui/ThemeEngine.cpp:348 +msgid "Standard Renderer" +msgstr "Biaozhun Xuanranqi" + +#: gui/ThemeEngine.cpp:348 engines/scumm/dialogs.cpp:663 +msgid "Standard" +msgstr "Biaozhun" + +#: gui/ThemeEngine.cpp:350 +msgid "Antialiased Renderer" +msgstr "Fanjuchi Xuanranqi" + +#: gui/ThemeEngine.cpp:350 +msgid "Antialiased" +msgstr "Fanjuchi" + +#: gui/widget.cpp:323 gui/widget.cpp:325 gui/widget.cpp:331 gui/widget.cpp:333 +msgid "Clear value" +msgstr "Qingchu Zhi" + +#: base/main.cpp:237 +#, c-format +msgid "Engine does not support debug level '%s'" +msgstr "Yinqing Buzhichi Tiaoshi Jibie ‘%s’" + +#: base/main.cpp:309 +msgid "Menu" +msgstr "Caidan" + +#: base/main.cpp:312 backends/platform/symbian/src/SymbianActions.cpp:45 +#: backends/platform/wince/CEActionsPocket.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:46 +msgid "Skip" +msgstr "Tiaoguo" + +#: base/main.cpp:315 backends/platform/symbian/src/SymbianActions.cpp:50 +#: backends/platform/wince/CEActionsPocket.cpp:42 +msgid "Pause" +msgstr "Zanting" + +#: base/main.cpp:318 +msgid "Skip line" +msgstr "Tiaoguo Cihang" + +#: base/main.cpp:510 +msgid "Error running game:" +msgstr "Youxi Yunxing Cuowu:" + +#: base/main.cpp:557 +msgid "Could not find any engine capable of running the selected game" +msgstr "Wufa Zhaodao Shihe Yunxing Youxi de Yinqing" + +#: common/error.cpp:38 +msgid "No error" +msgstr "Wu Cuowu" + +#: common/error.cpp:40 +msgid "Game data not found" +msgstr "Youxi Shuju Weizhaodao" + +#: common/error.cpp:42 +msgid "Game id not supported" +msgstr "Youxi id Bu Zhichi" + +#: common/error.cpp:44 +msgid "Unsupported color mode" +msgstr "Buzhichi Secai Moshi" + +#: common/error.cpp:47 +msgid "Read permission denied" +msgstr "Wu Duqu Quanxian" + +#: common/error.cpp:49 +msgid "Write permission denied" +msgstr "Wu XIeru Quanxian" + +#: common/error.cpp:52 +msgid "Path does not exist" +msgstr "Lujing Bu Cunzai" + +#: common/error.cpp:54 +msgid "Path not a directory" +msgstr "Lujing Bushi Mulu" + +#: common/error.cpp:56 +msgid "Path not a file" +msgstr "Lujing Bushi Wenjian" + +#: common/error.cpp:59 +msgid "Cannot create file" +msgstr "Wufa Chuangjian Wenjian" + +#: common/error.cpp:61 +msgid "Reading data failed" +msgstr "Duqu Shuju Shibai" + +#: common/error.cpp:63 +msgid "Writing data failed" +msgstr "Xieru Shuju Shibai" + +#: common/error.cpp:66 +msgid "Could not find suitable engine plugin" +msgstr "Wufa Zhaodao Heshi de Yinqing Chajian" + +#: common/error.cpp:68 +msgid "Engine plugin does not support save states" +msgstr "Yingqing Chajian Buzhichi Baocun Zhuangtai" + +#: common/error.cpp:71 +msgid "User canceled" +msgstr "Yonghu Quxiao" + +#: common/error.cpp:75 +msgid "Unknown error" +msgstr "Weizhi Cuowu" + +#. I18N: Hercules is graphics card name +#: common/rendermode.cpp:35 +msgid "Hercules Green" +msgstr "Hercules Green" + +#: common/rendermode.cpp:36 +msgid "Hercules Amber" +msgstr "Hercules Amber" + +#: common/rendermode.cpp:42 +msgid "PC-9821 (256 Colors)" +msgstr "PC-9821 (256 Se)" + +#: common/rendermode.cpp:43 +msgid "PC-9801 (16 Colors)" +msgstr "PC-9801 (16 Se)" + +#: common/rendermode.cpp:73 +msgctxt "lowres" +msgid "Hercules Green" +msgstr "Hercules Green" + +#: common/rendermode.cpp:74 +msgctxt "lowres" +msgid "Hercules Amber" +msgstr "Hercules Amber" + +#: engines/advancedDetector.cpp:317 +#, c-format +msgid "The game in '%s' seems to be unknown." +msgstr "'%s' Zhong de Youxi Weizhi." + +#: engines/advancedDetector.cpp:318 +msgid "Please, report the following data to the ScummVM team along with name" +msgstr "Qing JIang Xialie Shuju Yiji Youxi Baogao Gei ScummVM Tuandui" + +#: engines/advancedDetector.cpp:320 +msgid "of the game you tried to add and its version/language/etc.:" +msgstr "BingQie Fushang Shitu Tianjia de Youximing Yiji Banben/Yuyan Deng" + +#: engines/dialogs.cpp:85 +msgid "~R~esume" +msgstr "~R~Jixu" + +#: engines/dialogs.cpp:87 +msgid "~L~oad" +msgstr "~L~Zairu" + +#: engines/dialogs.cpp:91 +msgid "~S~ave" +msgstr "~S~Baocun" + +#: engines/dialogs.cpp:95 +msgid "~O~ptions" +msgstr "~O~Xuanxiang" + +#: engines/dialogs.cpp:100 +msgid "~H~elp" +msgstr "~H~Bangzhu" + +#: engines/dialogs.cpp:102 +msgid "~A~bout" +msgstr "~A~Guanyu" + +#: engines/dialogs.cpp:105 engines/dialogs.cpp:181 +msgid "~R~eturn to Launcher" +msgstr "~R~Fanhui Qidongqi" + +#: engines/dialogs.cpp:107 engines/dialogs.cpp:183 +msgctxt "lowres" +msgid "~R~eturn to Launcher" +msgstr "~R~Fanhui Qidongqi" + +#: engines/dialogs.cpp:116 engines/agi/saveload.cpp:764 +#: engines/avalanche/parser.cpp:1899 engines/cge/events.cpp:74 +#: engines/cge2/events.cpp:67 engines/cruise/menu.cpp:212 +#: engines/drascula/saveload.cpp:336 engines/dreamweb/saveload.cpp:261 +#: engines/hugo/file.cpp:298 engines/neverhood/menumodule.cpp:877 +#: engines/pegasus/pegasus.cpp:377 engines/sci/engine/kfile.cpp:768 +#: engines/sherlock/scalpel/scalpel.cpp:1249 +#: engines/sherlock/tattoo/widget_files.cpp:75 engines/toltecs/menu.cpp:281 +#: engines/toon/toon.cpp:3338 engines/tsage/scenes.cpp:598 +msgid "Save game:" +msgstr "Baocun Youxi:" + +#: engines/dialogs.cpp:116 backends/platform/symbian/src/SymbianActions.cpp:44 +#: backends/platform/wince/CEActionsPocket.cpp:43 +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:45 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +#: engines/agi/saveload.cpp:764 engines/avalanche/parser.cpp:1899 +#: engines/cge/events.cpp:74 engines/cge2/events.cpp:67 +#: engines/cruise/menu.cpp:212 engines/drascula/saveload.cpp:336 +#: engines/dreamweb/saveload.cpp:261 engines/hugo/file.cpp:298 +#: engines/neverhood/menumodule.cpp:877 engines/parallaction/saveload.cpp:212 +#: engines/pegasus/pegasus.cpp:377 engines/sci/engine/kfile.cpp:768 +#: engines/scumm/dialogs.cpp:188 engines/sherlock/scalpel/scalpel.cpp:1249 +#: engines/sherlock/tattoo/widget_files.cpp:75 engines/toltecs/menu.cpp:281 +#: engines/toon/toon.cpp:3338 engines/tsage/scenes.cpp:598 +msgid "Save" +msgstr "Baocun" + +#: engines/dialogs.cpp:145 +msgid "" +"Sorry, this engine does not currently provide in-game help. Please consult " +"the README for basic information, and for instructions on how to obtain " +"further assistance." +msgstr "" +"Duibuqi, Ci Yinqing Buzhichi Youxi Nei Bangzhu. Qing Chayue README Lai Huoqu " +"Jiben Xinxi Yiji Ruhe Huode Gengduo Bangzhu." + +#: engines/dialogs.cpp:234 engines/pegasus/pegasus.cpp:393 +#, c-format +msgid "" +"Gamestate save failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Cundang Baocun Shibai (%s)! Qing Chayue README Huode Jiben Xinxi, Yiji " +"Gengduo Xinxi" + +#: engines/dialogs.cpp:307 engines/mohawk/dialogs.cpp:109 +#: engines/mohawk/dialogs.cpp:170 engines/tsage/dialogs.cpp:106 +msgid "~O~K" +msgstr "~O~Queding" + +#: engines/dialogs.cpp:308 engines/mohawk/dialogs.cpp:110 +#: engines/mohawk/dialogs.cpp:171 engines/tsage/dialogs.cpp:107 +msgid "~C~ancel" +msgstr "~C~Quxiao" + +#: engines/dialogs.cpp:311 +msgid "~K~eys" +msgstr "~K~Guanjianzi" + +#: engines/engine.cpp:339 +msgid "Could not initialize color format." +msgstr "Wufa Chushihua Secai Geshi." + +#: engines/engine.cpp:347 +msgid "Could not switch to video mode: '" +msgstr "Wufa Qiehuandao Shipin Moshi: '" + +#: engines/engine.cpp:356 +msgid "Could not apply aspect ratio setting." +msgstr "Wufa Shezhi Bili Xuanxiang" + +#: engines/engine.cpp:361 +msgid "Could not apply fullscreen setting." +msgstr "Wufa Shezhi Quanping Xuanxiang" + +#: engines/engine.cpp:461 +msgid "" +"You appear to be playing this game directly\n" +"from the CD. This is known to cause problems,\n" +"and it is therefore recommended that you copy\n" +"the data files to your hard disk instead.\n" +"See the README file for details." +msgstr "" +"Sihu Ni Zhengzai Cong CD Zhong Yunxing\n" +"Youxi. Zhe Keneng Hui Yinfa Wenti.\n" +"Women Tuijian Nin Ba Shuju Wenjian\n" +"Kaobei Dao Yingpan Zhong Lai Yunxing.\n" +"Chakan README Huode Gengduo Xinxi." + +#: engines/engine.cpp:472 +msgid "" +"This game has audio tracks in its disk. These\n" +"tracks need to be ripped from the disk using\n" +"an appropriate CD audio extracting tool in\n" +"order to listen to the game's music.\n" +"See the README file for details." +msgstr "" +"Ci Youxi Zai Guangpan Zhong Baohan Yinyue Wenjian. \n" +"Zhexie Yingui Xuyao Yong Xiangying de CD Yinpin\n" +"Jieya Gongju Kaobei Dao Cipan Zhong Cai Neng \n" +"Bofang.\n" +"Juti Xinxi Qing Chakan README." + +#: engines/engine.cpp:530 +#, c-format +msgid "" +"Gamestate load failed (%s)! Please consult the README for basic information, " +"and for instructions on how to obtain further assistance." +msgstr "" +"Cundang Zairu Shibai (%s)! Qing Chayue README Huode Bangzhu XInxi Yiji " +"Gengduo Bangzhu." + +#: engines/engine.cpp:543 +msgid "" +"WARNING: The game you are about to start is not yet fully supported by " +"ScummVM. As such, it is likely to be unstable, and any saves you make might " +"not work in future versions of ScummVM." +msgstr "" +"Jinggao: Nin Yao Yunxing de Youxi Bingwei Wanquan Bei ScummVM Zhichi. Youxi " +"Yunxing Youkeneng Buwending, Renhe Cundang Youkeneng zai Yihou de ScummVM " +"Banben Bu Keyong." + +#: engines/engine.cpp:546 +msgid "Start anyway" +msgstr "Qiangzhi Qidong" + +#: audio/adlib.cpp:2291 +msgid "AdLib Emulator" +msgstr "AdLib Moniqi" + +#: audio/fmopl.cpp:62 +msgid "MAME OPL emulator" +msgstr "MAME OPL Moniqi" + +#: audio/fmopl.cpp:64 +msgid "DOSBox OPL emulator" +msgstr "DosBox OPL Moniqi" + +#: audio/fmopl.cpp:67 +msgid "ALSA Direct FM" +msgstr "ALSA Direct FM" + +#: audio/mididrv.cpp:209 +#, c-format +msgid "" +"The selected audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Weizhaodao Xuanding de Yinpin Shebei '%s' (Liru, Youkeneng Guandiao Huozhe " +"Weilianjie)." + +#: audio/mididrv.cpp:209 audio/mididrv.cpp:221 audio/mididrv.cpp:257 +#: audio/mididrv.cpp:272 +msgid "Attempting to fall back to the next available device..." +msgstr "ZhengChangshi Xiayige Keyong Shebei..." + +#: audio/mididrv.cpp:221 +#, c-format +msgid "" +"The selected audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Xuanding de Yinpin Shebei '%s' Wufa Shiyong. Chakan Rizhi Wenjian Huoqu " +"Gengduo Xinxi." + +#: audio/mididrv.cpp:257 +#, c-format +msgid "" +"The preferred audio device '%s' was not found (e.g. might be turned off or " +"disconnected)." +msgstr "" +"Youxian Yinpin Shebei '%s' WeiZhaodao (Liru, Youkeneng Guanbi Huo " +"Weilianjie)." + +#: audio/mididrv.cpp:272 +#, c-format +msgid "" +"The preferred audio device '%s' cannot be used. See log file for more " +"information." +msgstr "" +"Youxian Yinpin Shebei '%s' Wufa Shiyong. Chakan Rizhi Wenjian Huode Gengduo " +"Xinxi." + +#: audio/mods/paula.cpp:196 +msgid "Amiga Audio Emulator" +msgstr "Amiga Yinpin Moniqi" + +#: audio/null.h:44 +msgid "No music" +msgstr "Wu Yinyue" + +#: audio/softsynth/appleiigs.cpp:33 +msgid "Apple II GS Emulator (NOT IMPLEMENTED)" +msgstr "Apple II GS Moniqi (Wei Shixian)" + +#: audio/softsynth/cms.cpp:350 +msgid "Creative Music System Emulator" +msgstr "Creative Music System Moniqi" + +#: audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp:33 +msgid "FM-Towns Audio" +msgstr "FM-Towns Yinpin" + +#: audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp:58 +msgid "PC-98 Audio" +msgstr "PC-98 Yinpin" + +#: audio/softsynth/mt32.cpp:200 +msgid "Initializing MT-32 Emulator" +msgstr "Chushihua MT-32 Moniqi" + +#: audio/softsynth/mt32.cpp:426 +msgid "MT-32 Emulator" +msgstr "MT-32 Moniqi" + +#: audio/softsynth/pcspk.cpp:139 +msgid "PC Speaker Emulator" +msgstr "PC Yangshengqi Moniqi" + +#: audio/softsynth/pcspk.cpp:158 +msgid "IBM PCjr Emulator" +msgstr "IBM PCjr Moniqi" + +#: audio/softsynth/sid.cpp:1430 +msgid "C64 Audio Emulator" +msgstr "C64 Yinpin Moniqi" + +#: backends/events/default/default-events.cpp:196 +msgid "Do you really want to return to the Launcher?" +msgstr "Nin Zhende Xinagyao Fanhui Qidongqi Ma?" + +#: backends/events/default/default-events.cpp:196 +msgid "Launcher" +msgstr "Qidongqi" + +#: backends/events/default/default-events.cpp:218 +msgid "Do you really want to quit?" +msgstr "Nin Zhende Yao Tuichu Ma?" + +#: backends/events/default/default-events.cpp:218 +#: backends/platform/symbian/src/SymbianActions.cpp:52 +#: backends/platform/wince/CEActionsPocket.cpp:44 +#: backends/platform/wince/CEActionsSmartphone.cpp:52 +#: engines/scumm/dialogs.cpp:192 engines/scumm/help.cpp:83 +#: engines/scumm/help.cpp:85 +msgid "Quit" +msgstr "Tuichu" + +#: backends/events/gph/gph-events.cpp:385 +#: backends/events/gph/gph-events.cpp:428 +#: backends/events/openpandora/op-events.cpp:168 +msgid "Touchscreen 'Tap Mode' - Left Click" +msgstr "Chuping 'Chumo Moshi' - Zuojian Danji" + +#: backends/events/gph/gph-events.cpp:387 +#: backends/events/gph/gph-events.cpp:430 +#: backends/events/openpandora/op-events.cpp:170 +msgid "Touchscreen 'Tap Mode' - Right Click" +msgstr "Chuping 'Chumo Moshi' - Youjian Danji" + +#: backends/events/gph/gph-events.cpp:389 +#: backends/events/gph/gph-events.cpp:432 +#: backends/events/openpandora/op-events.cpp:172 +msgid "Touchscreen 'Tap Mode' - Hover (No Click)" +msgstr "Chuping 'Chumo Moshi' - Xuanting (Bu Danji)" + +#: backends/events/gph/gph-events.cpp:409 +msgid "Maximum Volume" +msgstr "Zuida YInliang" + +#: backends/events/gph/gph-events.cpp:411 +msgid "Increasing Volume" +msgstr "Zengda Yinliang" + +#: backends/events/gph/gph-events.cpp:417 +msgid "Minimal Volume" +msgstr "Zuixiao Yinliang" + +#: backends/events/gph/gph-events.cpp:419 +msgid "Decreasing Volume" +msgstr "Jianshao Yinliang" + +#: backends/events/maemosdl/maemosdl-events.cpp:180 +msgid "Clicking Enabled" +msgstr "Qidong Dianji" + +#: backends/events/maemosdl/maemosdl-events.cpp:180 +msgid "Clicking Disabled" +msgstr "Jinyong Dianji" + +#: backends/events/openpandora/op-events.cpp:174 +msgid "Touchscreen 'Tap Mode' - Hover (DPad Clicks)" +msgstr "Chuping 'Chumo Moshi' - Xuanting (Shoubing Dianji)" + +#: backends/events/symbiansdl/symbiansdl-events.cpp:186 +msgid "Do you want to quit ?" +msgstr "Nin Zhende Yao Tuichu Ma?" + +#. I18N: Trackpad mode toggle status. +#: backends/events/webossdl/webossdl-events.cpp:308 +msgid "Trackpad mode is now" +msgstr "Muqian Wei Chumoban Moshi" + +#. I18N: Trackpad mode on or off. +#. I18N: Auto-drag on or off. +#: backends/events/webossdl/webossdl-events.cpp:311 +#: backends/events/webossdl/webossdl-events.cpp:338 +msgid "ON" +msgstr "Kai" + +#: backends/events/webossdl/webossdl-events.cpp:311 +#: backends/events/webossdl/webossdl-events.cpp:338 +msgid "OFF" +msgstr "Guan" + +#: backends/events/webossdl/webossdl-events.cpp:315 +msgid "Swipe two fingers to the right to toggle." +msgstr "XiangYou Shiyong Liang Gen Shouzhi Huadong Qiehuan" + +#. I18N: Auto-drag toggle status. +#: backends/events/webossdl/webossdl-events.cpp:335 +msgid "Auto-drag mode is now" +msgstr "Muqian Wei Zidong Tuozhuai Moshi" + +#: backends/events/webossdl/webossdl-events.cpp:342 +msgid "Swipe three fingers to the right to toggle." +msgstr "Xiangyou Huadong San Gen Shouzhi Qiehuan" + +#: backends/graphics/opengl/opengl-graphics.cpp:119 +msgid "OpenGL" +msgstr "OpenGL" + +#: backends/graphics/opengl/opengl-graphics.cpp:120 +msgid "OpenGL (No filtering)" +msgstr "OpenGL" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:47 +#: backends/graphics/wincesdl/wincesdl-graphics.cpp:88 +#: backends/graphics/wincesdl/wincesdl-graphics.cpp:95 +msgid "Normal (no scaling)" +msgstr "Putong" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:66 +msgctxt "lowres" +msgid "Normal (no scaling)" +msgstr "Putong" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2215 +msgid "Enabled aspect ratio correction" +msgstr "Qiyong Bili Jiaozheng" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2221 +msgid "Disabled aspect ratio correction" +msgstr "Jinyong Bili Jiaozheng" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2276 +msgid "Active graphics filter:" +msgstr "Huodong de Tuxing Guolvqi:" + +#: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2318 +msgid "Windowed mode" +msgstr "Chuangkou Moshi" + +#: backends/keymapper/remap-dialog.cpp:48 +msgid "Keymap:" +msgstr "Jianpan Yingshe:" + +#: backends/keymapper/remap-dialog.cpp:67 +msgid " (Effective)" +msgstr " (Shengxiao)" + +#: backends/keymapper/remap-dialog.cpp:107 +msgid " (Active)" +msgstr " (Huodong)" + +#: backends/keymapper/remap-dialog.cpp:107 +msgid " (Blocked)" +msgstr " (Zuzhi)" + +#: backends/keymapper/remap-dialog.cpp:120 +msgid " (Global)" +msgstr " (Quanju)" + +#: backends/keymapper/remap-dialog.cpp:128 +msgid " (Game)" +msgstr " (Youxi)" + +#: backends/midi/windows.cpp:165 +msgid "Windows MIDI" +msgstr "Windows MIDI" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:56 +#: engines/scumm/dialogs.cpp:291 +msgid "~C~lose" +msgstr "~C~Guanbi" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:57 +msgid "ScummVM Main Menu" +msgstr "ScummVM Zhucaidan" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:63 +msgid "~L~eft handed mode" +msgstr "~L~Zuoshou Moshi" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:64 +msgid "~I~ndy fight controls" +msgstr "~I~Indy fight Kongzhi" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:65 +msgid "Show mouse cursor" +msgstr "Xianshi Shubiao Zhizhen" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:66 +msgid "Snap to edges" +msgstr "TieFu Yu Bianjie" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:68 +msgid "Touch X Offset" +msgstr "Chumo X Pianyi" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:75 +msgid "Touch Y Offset" +msgstr "Chumo Y Pianyi" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:87 +msgid "Use laptop trackpad-style cursor control" +msgstr "Shiyong Bijiben Diannao Chumoban Shi Zhizhen Kongzhi" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:88 +msgid "Tap for left click, double tap right click" +msgstr "Chumo Yici Wei Zuojian, Chumo LIangci Wei Youjian" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:90 +msgid "Sensitivity" +msgstr "Mingandu" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:99 +msgid "Initial top screen scale:" +msgstr "Chushi Shangping Daxiao" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:105 +msgid "Main screen scaling:" +msgstr "Zhu Pingmu Daxiao" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:107 +msgid "Hardware scale (fast, but low quality)" +msgstr "yingjian Suofang (Kuaisu DiXiao)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:108 +msgid "Software scale (good quality, but slower)" +msgstr "Ruanjian Suofang (Gaoxiao Mansu)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:109 +msgid "Unscaled (you must scroll left and right)" +msgstr "Wei Suofang (Xuyao ZuoYou Yidong)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:111 +msgid "Brightness:" +msgstr "Liangdu:" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:121 +msgid "High quality audio (slower) (reboot)" +msgstr "Gaozhiliang Yinpin (Man) (Chongqi)" + +#: backends/platform/ds/arm9/source/dsoptions.cpp:122 +msgid "Disable power off" +msgstr "Jinyong Guanji" + +#: backends/platform/ios7/ios7_osys_events.cpp:309 +#: backends/platform/ios7/ios7_osys_events.cpp:519 +#: backends/platform/iphone/osys_events.cpp:300 +msgid "Mouse-click-and-drag mode enabled." +msgstr "QIdong Shubiao Dianji-tuozhuai Moshi" + +#: backends/platform/ios7/ios7_osys_events.cpp:311 +#: backends/platform/ios7/ios7_osys_events.cpp:521 +#: backends/platform/iphone/osys_events.cpp:302 +msgid "Mouse-click-and-drag mode disabled." +msgstr "Jinyong Shubiao Dianji-Tuozhuai Moshi." + +#: backends/platform/ios7/ios7_osys_events.cpp:322 +#: backends/platform/ios7/ios7_osys_events.cpp:540 +#: backends/platform/iphone/osys_events.cpp:313 +msgid "Touchpad mode enabled." +msgstr "Qiyong Chumoban Moshi" + +#: backends/platform/ios7/ios7_osys_events.cpp:324 +#: backends/platform/ios7/ios7_osys_events.cpp:542 +#: backends/platform/iphone/osys_events.cpp:315 +msgid "Touchpad mode disabled." +msgstr "Jinyong Chumoban Moshi" + +#: backends/platform/maemo/maemo.cpp:208 +msgid "Click Mode" +msgstr "Danji Moshi" + +#: backends/platform/maemo/maemo.cpp:214 +#: backends/platform/symbian/src/SymbianActions.cpp:42 +#: backends/platform/tizen/form.cpp:275 +#: backends/platform/wince/CEActionsPocket.cpp:60 +#: backends/platform/wince/CEActionsSmartphone.cpp:43 +msgid "Left Click" +msgstr "Zuojian Danji" + +#: backends/platform/maemo/maemo.cpp:217 +msgid "Middle Click" +msgstr "Zhongjian Danji" + +#: backends/platform/maemo/maemo.cpp:220 +#: backends/platform/symbian/src/SymbianActions.cpp:43 +#: backends/platform/tizen/form.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:44 +msgid "Right Click" +msgstr "Youjian Danji" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:88 +msgid "Hide ScummVM" +msgstr "Yincang ScummVM" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:93 +msgid "Hide Others" +msgstr "Yincang QIta" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:98 +msgid "Show All" +msgstr "Xianshi Quanbu" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:120 +#: backends/platform/sdl/macosx/appmenu_osx.mm:131 +msgid "Window" +msgstr "Chuangkou" + +#: backends/platform/sdl/macosx/appmenu_osx.mm:125 +msgid "Minimize" +msgstr "Zuixiaohua" + +#: backends/platform/symbian/src/SymbianActions.cpp:38 +#: backends/platform/wince/CEActionsSmartphone.cpp:39 +msgid "Up" +msgstr "Shang" + +#: backends/platform/symbian/src/SymbianActions.cpp:39 +#: backends/platform/wince/CEActionsSmartphone.cpp:40 +msgid "Down" +msgstr "Xia" + +#: backends/platform/symbian/src/SymbianActions.cpp:40 +#: backends/platform/wince/CEActionsSmartphone.cpp:41 +msgid "Left" +msgstr "Zuo" + +#: backends/platform/symbian/src/SymbianActions.cpp:41 +#: backends/platform/wince/CEActionsSmartphone.cpp:42 +msgid "Right" +msgstr "You" + +#: backends/platform/symbian/src/SymbianActions.cpp:46 +#: backends/platform/wince/CEActionsSmartphone.cpp:47 +msgid "Zone" +msgstr "Quyu" + +#: backends/platform/symbian/src/SymbianActions.cpp:47 +#: backends/platform/wince/CEActionsPocket.cpp:54 +#: backends/platform/wince/CEActionsSmartphone.cpp:48 +msgid "Multi Function" +msgstr "Duo Gongneng" + +#: backends/platform/symbian/src/SymbianActions.cpp:48 +msgid "Swap character" +msgstr "Qiehuan Juese" + +#: backends/platform/symbian/src/SymbianActions.cpp:49 +msgid "Skip text" +msgstr "Tiaoguo Wenben" + +#: backends/platform/symbian/src/SymbianActions.cpp:51 +msgid "Fast mode" +msgstr "Kuaisu Moshi" + +#: backends/platform/symbian/src/SymbianActions.cpp:53 +msgid "Debugger" +msgstr "Tiaoshi Qi" + +#: backends/platform/symbian/src/SymbianActions.cpp:54 +msgid "Global menu" +msgstr "Quanju Caidan" + +#: backends/platform/symbian/src/SymbianActions.cpp:55 +msgid "Virtual keyboard" +msgstr "Xuni JIanpan" + +#: backends/platform/symbian/src/SymbianActions.cpp:56 +msgid "Key mapper" +msgstr "Jianpan yingshe" + +#: backends/platform/tizen/form.cpp:263 +msgid "Right Click Once" +msgstr "Youjian Danji" + +#: backends/platform/tizen/form.cpp:271 +msgid "Move Only" +msgstr "Jin Yidong" + +#: backends/platform/tizen/form.cpp:294 +msgid "Escape Key" +msgstr "Esc Jian" + +#: backends/platform/tizen/form.cpp:299 +msgid "Game Menu" +msgstr "Youxi Caidan" + +#: backends/platform/tizen/form.cpp:304 +msgid "Show Keypad" +msgstr "Xianshi Jianpan" + +#: backends/platform/tizen/form.cpp:309 +msgid "Control Mouse" +msgstr "Kongzhi Shubiao" + +#: backends/platform/tizen/fs.cpp:259 +msgid "[ Data ]" +msgstr "[ Shuju ]" + +#: backends/platform/tizen/fs.cpp:263 +msgid "[ Resources ]" +msgstr "[ Ziyuan]" + +#: backends/platform/tizen/fs.cpp:267 +msgid "[ SDCard ]" +msgstr "[ SD Ka ]" + +#: backends/platform/tizen/fs.cpp:271 +msgid "[ Media ]" +msgstr "[ Meiti ]" + +#: backends/platform/tizen/fs.cpp:275 +msgid "[ Shared ]" +msgstr "[ gongxiang ]" + +#: backends/platform/wii/options.cpp:51 +msgid "Video" +msgstr "Shipin" + +#: backends/platform/wii/options.cpp:54 +msgid "Current video mode:" +msgstr "Muqian Shipin Moshi:" + +#: backends/platform/wii/options.cpp:56 +msgid "Double-strike" +msgstr "Shuang Ji" + +#: backends/platform/wii/options.cpp:60 +msgid "Horizontal underscan:" +msgstr "Shuiping Saomiao" + +#: backends/platform/wii/options.cpp:66 +msgid "Vertical underscan:" +msgstr "Chuizhi Saomiao" + +#: backends/platform/wii/options.cpp:71 +msgid "Input" +msgstr "Shuru" + +#: backends/platform/wii/options.cpp:74 +msgid "GC Pad sensitivity:" +msgstr "GC Pan Mingandu" + +#: backends/platform/wii/options.cpp:80 +msgid "GC Pad acceleration:" +msgstr "GC Pad Jiasu" + +#: backends/platform/wii/options.cpp:86 +msgid "DVD" +msgstr "DVD" + +#: backends/platform/wii/options.cpp:89 backends/platform/wii/options.cpp:101 +msgid "Status:" +msgstr "Zhuangtai:" + +#: backends/platform/wii/options.cpp:90 backends/platform/wii/options.cpp:102 +msgid "Unknown" +msgstr "Weizhi" + +#: backends/platform/wii/options.cpp:93 +msgid "Mount DVD" +msgstr "Jiazai DVD" + +#: backends/platform/wii/options.cpp:94 +msgid "Unmount DVD" +msgstr "Xiezai DVD" + +#: backends/platform/wii/options.cpp:98 +msgid "SMB" +msgstr "SMB" + +#: backends/platform/wii/options.cpp:106 +msgid "Server:" +msgstr "Fuwuqi:" + +#: backends/platform/wii/options.cpp:110 +msgid "Share:" +msgstr "Gongxiang:" + +#: backends/platform/wii/options.cpp:114 +msgid "Username:" +msgstr "Yonghuming:" + +#: backends/platform/wii/options.cpp:118 +msgid "Password:" +msgstr "Mima:" + +#: backends/platform/wii/options.cpp:121 +msgid "Init network" +msgstr "Chushihua Wangluo" + +#: backends/platform/wii/options.cpp:123 +msgid "Mount SMB" +msgstr "Jiazai SMB" + +#: backends/platform/wii/options.cpp:124 +msgid "Unmount SMB" +msgstr "Xiezai SMB" + +#: backends/platform/wii/options.cpp:143 +msgid "DVD Mounted successfully" +msgstr "DVD Jiazai Chenggong" + +#: backends/platform/wii/options.cpp:146 +msgid "Error while mounting the DVD" +msgstr "Jiazai DVD Chucuo" + +#: backends/platform/wii/options.cpp:148 +msgid "DVD not mounted" +msgstr "DVD Wei Jiazai" + +#: backends/platform/wii/options.cpp:161 +msgid "Network up, share mounted" +msgstr "Wangluo Lianjie, Gongxiang Jiazai" + +#: backends/platform/wii/options.cpp:163 +msgid "Network up" +msgstr "Wangluo Lianjie" + +#: backends/platform/wii/options.cpp:166 +msgid ", error while mounting the share" +msgstr ", Jiazai Gongxiang Chucuo" + +#: backends/platform/wii/options.cpp:168 +msgid ", share not mounted" +msgstr ", Wei jiazai Gongxiang" + +#: backends/platform/wii/options.cpp:174 +msgid "Network down" +msgstr "Wangluo Duanxian" + +#: backends/platform/wii/options.cpp:178 +msgid "Initializing network" +msgstr "Chushihua Wnagluo" + +#: backends/platform/wii/options.cpp:182 +msgid "Timeout while initializing network" +msgstr "Chushihua Wnagluo Chaoshi" + +#: backends/platform/wii/options.cpp:186 +#, c-format +msgid "Network not initialized (%d)" +msgstr "Wangluo Wei Chushihua (%d)" + +#: backends/platform/wince/CEActionsPocket.cpp:46 +msgid "Hide Toolbar" +msgstr "YIncang Gongjulan" + +#: backends/platform/wince/CEActionsPocket.cpp:47 +msgid "Show Keyboard" +msgstr "Xianshi JIanpan" + +#: backends/platform/wince/CEActionsPocket.cpp:48 +msgid "Sound on/off" +msgstr "Shengyin Kai/Guan" + +#: backends/platform/wince/CEActionsPocket.cpp:49 +msgid "Right click" +msgstr "Youjian Danji" + +#: backends/platform/wince/CEActionsPocket.cpp:50 +msgid "Show/Hide Cursor" +msgstr "Xianshi/Yincang Zhizhen" + +#: backends/platform/wince/CEActionsPocket.cpp:51 +msgid "Free look" +msgstr "Ziyou Chakan" + +#: backends/platform/wince/CEActionsPocket.cpp:52 +msgid "Zoom up" +msgstr "Fangda" + +#: backends/platform/wince/CEActionsPocket.cpp:53 +msgid "Zoom down" +msgstr "Suoxiao" + +#: backends/platform/wince/CEActionsPocket.cpp:55 +#: backends/platform/wince/CEActionsSmartphone.cpp:49 +msgid "Bind Keys" +msgstr "Bangding Jianwei" + +#: backends/platform/wince/CEActionsPocket.cpp:56 +msgid "Cursor Up" +msgstr "Zhizhen Shang" + +#: backends/platform/wince/CEActionsPocket.cpp:57 +msgid "Cursor Down" +msgstr "Zhizhen Xia" + +#: backends/platform/wince/CEActionsPocket.cpp:58 +msgid "Cursor Left" +msgstr "Zhizhen Zuo" + +#: backends/platform/wince/CEActionsPocket.cpp:59 +msgid "Cursor Right" +msgstr "Zhizhen You" + +#: backends/platform/wince/CEActionsPocket.cpp:267 +#: backends/platform/wince/CEActionsSmartphone.cpp:231 +msgid "Do you want to load or save the game?" +msgstr "Nin Xinagyao Zairu Huo Baocun Youxi Ma?" + +#: backends/platform/wince/CEActionsPocket.cpp:326 +#: backends/platform/wince/CEActionsSmartphone.cpp:287 +msgid " Are you sure you want to quit ? " +msgstr "Nin Queding Tuichu ma ?" + +#: backends/platform/wince/CEActionsSmartphone.cpp:50 +msgid "Keyboard" +msgstr "Jianpan" + +#: backends/platform/wince/CEActionsSmartphone.cpp:51 +msgid "Rotate" +msgstr "Xuanzhuan" + +#: backends/platform/wince/CELauncherDialog.cpp:56 +msgid "Using SDL driver " +msgstr "Shiyong SDL Qudong" + +#: backends/platform/wince/CELauncherDialog.cpp:60 +msgid "Display " +msgstr "Xianshi" + +#: backends/platform/wince/CELauncherDialog.cpp:83 +msgid "Do you want to perform an automatic scan ?" +msgstr "Nin Xiwang Zidong Saomiao ma?" + +#: backends/platform/wince/wince-sdl.cpp:516 +msgid "Map right click action" +msgstr "Yingshe Youjian Dianji Xingwei" + +#: backends/platform/wince/wince-sdl.cpp:520 +msgid "You must map a key to the 'Right Click' action to play this game" +msgstr "Nin Bixu Yingshe Yige Jian Dao 'Youjian Danji' Lai kaishi Youxi" + +#: backends/platform/wince/wince-sdl.cpp:529 +msgid "Map hide toolbar action" +msgstr "yingshe YIncang Gongjulan Xingwei" + +#: backends/platform/wince/wince-sdl.cpp:533 +msgid "You must map a key to the 'Hide toolbar' action to play this game" +msgstr "Nin Bixu Yingshe Yigejian Dao 'Yincang Gongjulan' Lai Kaishi Youxi" + +#: backends/platform/wince/wince-sdl.cpp:542 +msgid "Map Zoom Up action (optional)" +msgstr "Yingshe Fnagda Xingwei (Kexuan)" + +#: backends/platform/wince/wince-sdl.cpp:545 +msgid "Map Zoom Down action (optional)" +msgstr "Yingshe Suoxiao Xingwei (Kexuan)" + +#: backends/platform/wince/wince-sdl.cpp:553 +msgid "" +"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory" +msgstr "" +"Buyao Wnagji Yingshe YIge Jian Dao 'YIncang Gongjulan' Lai Chakan Suoyou " +"xiang" + +#: backends/updates/macosx/macosx-updates.mm:67 +msgid "Check for Updates..." +msgstr "Jiancha Gengxin..." + +#: engines/agi/detection.cpp:147 engines/cine/detection.cpp:70 +#: engines/drascula/detection.cpp:302 engines/dreamweb/detection.cpp:47 +#: engines/neverhood/detection.cpp:160 engines/sci/detection.cpp:404 +#: engines/toltecs/detection.cpp:200 engines/zvision/detection_tables.h:51 +msgid "Use original save/load screens" +msgstr "Shiyong Yuanshi Baocun/Zairu Pingmu" + +#: engines/agi/detection.cpp:148 engines/cine/detection.cpp:71 +#: engines/drascula/detection.cpp:303 engines/dreamweb/detection.cpp:48 +#: engines/neverhood/detection.cpp:161 engines/sci/detection.cpp:405 +#: engines/toltecs/detection.cpp:201 +msgid "Use the original save/load screens, instead of the ScummVM ones" +msgstr "Shiyong Yuanshi Baocun/Zairu Pingmu, Bu Shiyong ScummVM de" + +#: engines/agi/detection.cpp:157 +msgid "Use an alternative palette" +msgstr "Shiyong Qita Mianban" + +#: engines/agi/detection.cpp:158 +msgid "" +"Use an alternative palette, common for all Amiga games. This was the old " +"behavior" +msgstr "Shiyong QIta Mianban, Yiban Yonghu suoyou de Amiga Youxi. " + +#: engines/agi/detection.cpp:167 +msgid "Mouse support" +msgstr "Shubiao Zhichi" + +#: engines/agi/detection.cpp:168 +msgid "" +"Enables mouse support. Allows to use mouse for movement and in game menus." +msgstr "" +"Qiyong shubiao zhichi. Yunxu Shiyong Shubiao jinxing Yidong He Youxi Nei " +"Caidan" + +#: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 +#: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 +#: engines/drascula/saveload.cpp:349 engines/dreamweb/saveload.cpp:169 +#: engines/hugo/file.cpp:400 engines/neverhood/menumodule.cpp:890 +#: engines/sci/engine/kfile.cpp:867 engines/sherlock/scalpel/scalpel.cpp:1262 +#: engines/sherlock/tattoo/widget_files.cpp:94 engines/toltecs/menu.cpp:256 +#: engines/toon/toon.cpp:3430 +msgid "Restore game:" +msgstr "Huifu Youxi:" + +#: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 +#: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 +#: engines/drascula/saveload.cpp:349 engines/dreamweb/saveload.cpp:169 +#: engines/hugo/file.cpp:400 engines/neverhood/menumodule.cpp:890 +#: engines/sci/engine/kfile.cpp:867 engines/sherlock/scalpel/scalpel.cpp:1262 +#: engines/sherlock/tattoo/widget_files.cpp:94 engines/toltecs/menu.cpp:256 +#: engines/toon/toon.cpp:3430 +msgid "Restore" +msgstr "Huifu" + +#: engines/agos/saveload.cpp:160 engines/scumm/scumm.cpp:2377 +#, c-format +msgid "" +"Failed to load game state from file:\n" +"\n" +"%s" +msgstr "" +"Jiazai Youxi Cundang Shibai:\n" +"\n" +"%s" + +#: engines/agos/saveload.cpp:195 engines/scumm/scumm.cpp:2370 +#, c-format +msgid "" +"Failed to save game state to file:\n" +"\n" +"%s" +msgstr "" +"Baocun Youxi Cundang Shibai:\n" +"\n" +"%s" + +#: engines/agos/saveload.cpp:203 engines/scumm/scumm.cpp:2388 +#, c-format +msgid "" +"Successfully saved game state in file:\n" +"\n" +"%s" +msgstr "" +"Chenggong Baocun Youxi Cundang:\n" +"\n" +"%s" + +#: engines/agos/animation.cpp:557 +#, c-format +msgid "Cutscene file '%s' not found!" +msgstr "Changjing Qiehuan Wenjian '%s' Wei Zhaodao!" + +#: engines/cge/detection.cpp:105 engines/cge2/detection.cpp:101 +msgid "Color Blind Mode" +msgstr "Semang Moshi" + +#: engines/cge/detection.cpp:106 engines/cge2/detection.cpp:102 +msgid "Enable Color Blind Mode by default" +msgstr "Moren Qiyong Semang Moshi" + +#: engines/drascula/saveload.cpp:47 +msgid "" +"ScummVM found that you have old savefiles for Drascula that should be " +"converted.\n" +"The old save game format is no longer supported, so you will not be able to " +"load your games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked again the next " +"time you start the game.\n" +msgstr "" +"ScummVM Faxian Nin You Jiu de Drascula de Cundang Wenjian Xuyao Bei " +"Zhuanhuan.\n" +"Jiude cundang Wenjian Buzai Zhichi, Suoyi Nin Buneng Guo zai Zhuanhuan " +"Zhiqian Duqu.\n" +"Dianji Shi Lai Xianzai Zhuanhuan, Fouze Xiaci Qidong Youxi Shi Nin Huibei " +"Zaici Xunwen\n" +" \n" + +#: engines/dreamweb/detection.cpp:57 +msgid "Use bright palette mode" +msgstr "Shiyong Liang Tiaoseban Moshi" + +#: engines/dreamweb/detection.cpp:58 +msgid "Display graphics using the game's bright palette" +msgstr "Shiyong youxi de Liang Tiaoseban Lai Xianshi Tuxiang" + +#: engines/gob/inter_playtoons.cpp:256 engines/gob/inter_v2.cpp:1470 +#: engines/gob/inter_geisha.cpp:232 engines/tinsel/saveload.cpp:532 +msgid "Failed to load game state from file." +msgstr "Wufa Cong Cundang Wenjian Zhong Duqu" + +#: engines/gob/inter_v2.cpp:1540 engines/gob/inter_geisha.cpp:263 +#: engines/tinsel/saveload.cpp:545 +msgid "Failed to save game state to file." +msgstr "Wufa Baocun Cundang " + +#: engines/gob/inter_v5.cpp:107 +msgid "Failed to delete file." +msgstr "Wufa Shanchu Wenjian" + +#: engines/groovie/detection.cpp:312 +msgid "Fast movie speed" +msgstr "Kuaisu Yingpian" + +#: engines/groovie/detection.cpp:313 +msgid "Play movies at an increased speed" +msgstr "Yong Gengkuai de Sudu Bofang Yingpian" + +#: engines/groovie/script.cpp:408 +msgid "Failed to save game" +msgstr "Wufa baocun Youxi" + +#: engines/hopkins/detection.cpp:76 engines/hopkins/detection.cpp:86 +msgid "Gore Mode" +msgstr "Gore Moshi" + +#: engines/hopkins/detection.cpp:77 engines/hopkins/detection.cpp:87 +msgid "Enable Gore Mode when available" +msgstr "Dang Kexing Shi Qiyong Gore Moshi" + +#. I18N: Studio audience adds an applause and cheering sounds whenever +#. Malcolm makes a joke. +#: engines/kyra/detection.cpp:62 +msgid "Studio audience" +msgstr "Luyinpeng Guanzhong" + +#: engines/kyra/detection.cpp:63 +msgid "Enable studio audience" +msgstr "Qiyong Luyinpeng Guanzhong" + +#. I18N: This option allows the user to skip text and cutscenes. +#: engines/kyra/detection.cpp:73 +msgid "Skip support" +msgstr "Zhichi Tiaoguo" + +#: engines/kyra/detection.cpp:74 +msgid "Allow text and cutscenes to be skipped" +msgstr "Zhichi Wenzi he Changjing Bei Tiaoguo" + +#. I18N: Helium mode makes people sound like they've inhaled Helium. +#: engines/kyra/detection.cpp:84 +msgid "Helium mode" +msgstr "Haiqi Moshi" + +#: engines/kyra/detection.cpp:85 +msgid "Enable helium mode" +msgstr "Shiyong Haiqi Moshi" + +#. I18N: When enabled, this option makes scrolling smoother when +#. changing from one screen to another. +#: engines/kyra/detection.cpp:99 +msgid "Smooth scrolling" +msgstr "Pinghua Gundong" + +#: engines/kyra/detection.cpp:100 +msgid "Enable smooth scrolling when walking" +msgstr "Qiyong Zoulu Shi de pinghua Gundong" + +#. I18N: When enabled, this option changes the cursor when it floats to the +#. edge of the screen to a directional arrow. The player can then click to +#. walk towards that direction. +#: engines/kyra/detection.cpp:112 +msgid "Floating cursors" +msgstr "Xuanfu Guangbiao" + +#: engines/kyra/detection.cpp:113 +msgid "Enable floating cursors" +msgstr "Qiyong Xuanfu Guangbiao" + +#. I18N: HP stands for Hit Points +#: engines/kyra/detection.cpp:127 +msgid "HP bar graphs" +msgstr "HP Tiao Tu" + +#: engines/kyra/detection.cpp:128 +msgid "Enable hit point bar graphs" +msgstr "Qiyong Jida Dian Tiao Tu" + +#: engines/kyra/lol.cpp:478 +msgid "Attack 1" +msgstr "Gongji 1" + +#: engines/kyra/lol.cpp:479 +msgid "Attack 2" +msgstr "Gongji 2" + +#: engines/kyra/lol.cpp:480 +msgid "Attack 3" +msgstr "Gongji 3" + +#: engines/kyra/lol.cpp:481 +msgid "Move Forward" +msgstr "Xiangqian Yidong" + +#: engines/kyra/lol.cpp:482 +msgid "Move Back" +msgstr "Xinaghou Yidong" + +#: engines/kyra/lol.cpp:483 +msgid "Slide Left" +msgstr "Xiangzuo Huadong" + +#: engines/kyra/lol.cpp:484 +msgid "Slide Right" +msgstr "Xiangyou Huadong" + +#: engines/kyra/lol.cpp:485 engines/pegasus/pegasus.cpp:2509 +msgid "Turn Left" +msgstr "Zuozhuan" + +#: engines/kyra/lol.cpp:486 engines/pegasus/pegasus.cpp:2510 +msgid "Turn Right" +msgstr "Youzhuan" + +#: engines/kyra/lol.cpp:487 +msgid "Rest" +msgstr "Xiuxi" + +#: engines/kyra/lol.cpp:488 +msgid "Options" +msgstr "Xuanxiang" + +#: engines/kyra/lol.cpp:489 +msgid "Choose Spell" +msgstr "Xuanze Pinxie" + +#: engines/kyra/sound_midi.cpp:477 +msgid "" +"You appear to be using a General MIDI device,\n" +"but your game only supports Roland MT32 MIDI.\n" +"We try to map the Roland MT32 instruments to\n" +"General MIDI ones. It is still possible that\n" +"some tracks sound incorrect." +msgstr "" +"Nin Sihu zhengzai shiyong yige Tongyong MIDI Shebei\n" +"Danshi Nin de Youxi jinzhichi Roland MT32 MIDI.\n" +"Women zhengzai changshi JIang Roland MT32 Yingshe\n" +"wei Tongyong MIDI. Youxie YIngui Rengran Youkeneng\n" +"Bu zheng que." + +#: engines/kyra/saveload_eob.cpp:557 +#, c-format +msgid "" +"The following original save game file has been found in your game path:\n" +"\n" +"%s %s\n" +"\n" +"Do you wish to use this save game file with ScummVM?\n" +"\n" +msgstr "" +"Xialie Yuanshi Cundang Wenjian Zai Nin de Youxi Lujing Zhong:\n" +"\n" +"%s %s\n" +"\n" +"Nin Xiwang Shiyong Zhege ScummVM Cundang Wenjian ma?\n" +"\n" + +#: engines/kyra/saveload_eob.cpp:590 +#, c-format +msgid "" +"A save game file was found in the specified slot %d. Overwrite?\n" +"\n" +msgstr "" +"Zai Cundang %d Zhong Yifaxian Baocun de Youxi WEnjian. Fugai Ma?\n" +"\n" + +#: engines/kyra/saveload_eob.cpp:623 +#, c-format +msgid "" +"%d original save game files have been successfully imported into\n" +"ScummVM. If you want to manually import original save game files later you " +"will\n" +"need to open the ScummVM debug console and use the command " +"'import_savefile'.\n" +"\n" +msgstr "" +"%d Ge yuanshi Cundang Youxi Wenjian Chenggong Daoru Daole\n" +"ScummVM. Ruguo Nin Xinag Zhihou Rengong Daoru Cundang Wenjian, Nin Xuyao\n" +"Dagai ScummVM Tiaoshi Kongzhitai, Bingqie shiyong mingling " +"'import_savefile'.\n" +"\n" + +#. I18N: Option for fast scene switching +#: engines/mohawk/dialogs.cpp:92 engines/mohawk/dialogs.cpp:167 +msgid "~Z~ip Mode Activated" +msgstr "~Z~Yasuo Moshi Qidong" + +#: engines/mohawk/dialogs.cpp:93 +msgid "~T~ransitions Enabled" +msgstr "~T~Qiyong Zhuanyi" + +#. I18N: Drop book page +#: engines/mohawk/dialogs.cpp:95 +msgid "~D~rop Page" +msgstr "~D~shanchu Yemian" + +#: engines/mohawk/dialogs.cpp:99 +msgid "~S~how Map" +msgstr "~S~Xianshi Ditu" + +#: engines/mohawk/dialogs.cpp:105 +msgid "~M~ain Menu" +msgstr "~M~Zhucaidan" + +#: engines/mohawk/dialogs.cpp:168 +msgid "~W~ater Effect Enabled" +msgstr "~W~Qiyong Shuimian Xiaoguo" + +#: engines/neverhood/detection.cpp:167 +msgid "Skip the Hall of Records storyboard scenes" +msgstr "Tiaoguo Hall of Records Jishiban Changjing" + +#: engines/neverhood/detection.cpp:168 +msgid "Allows the player to skip past the Hall of Records storyboard scenes" +msgstr "Yunxu Wanjia Tiaoguo Hall of Records Jishiban Changjing" + +#: engines/neverhood/detection.cpp:174 +msgid "Scale the making of videos to full screen" +msgstr "Fangda Shipin Zhizuo Dao Quanping" + +#: engines/neverhood/detection.cpp:175 +msgid "Scale the making of videos, so that they use the whole screen" +msgstr "Suofang Shipin Zhizuo, Quanping Ke Yong" + +#: engines/parallaction/saveload.cpp:133 +#, c-format +msgid "" +"Can't save game in slot %i\n" +"\n" +msgstr "" +"Wufa Baocun Youxi Dao Kongwei %i\n" +"\n" + +#: engines/parallaction/saveload.cpp:197 +msgid "Load file" +msgstr "Zairu Wenjian" + +#: engines/parallaction/saveload.cpp:204 +msgid "Loading game..." +msgstr "Zairu youxi..." + +#: engines/parallaction/saveload.cpp:212 +msgid "Save file" +msgstr "Baocun Wenjian" + +#: engines/parallaction/saveload.cpp:219 +msgid "Saving game..." +msgstr "Baocun Youxi..." + +#: engines/parallaction/saveload.cpp:272 +msgid "" +"ScummVM found that you have old savefiles for Nippon Safes that should be " +"renamed.\n" +"The old names are no longer supported, so you will not be able to load your " +"games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked next time.\n" +msgstr "" +"ScummVM Faxian Youyixie Nippon Safes de Cundang Wenjian xuyao Gaiming.\n" +"Yuanlai de wenjianming Buzai Bei Zhichi, Ruguo Bujing Zhuanhuan Ninjing " +"Buneng Zairu Tamen.\n" +"Dianji Queding Lai Xianzai Zhuanhuan, Fouze Nin Hui zai Xiaci Bei Xunwen\n" + +#: engines/parallaction/saveload.cpp:319 +msgid "ScummVM successfully converted all your savefiles." +msgstr "ScummVM Chenggong Zhuanhuan le Nin de Suoyou Cundang Wenjian." + +#: engines/parallaction/saveload.cpp:321 +msgid "" +"ScummVM printed some warnings in your console window and can't guarantee all " +"your files have been converted.\n" +"\n" +"Please report to the team." +msgstr "" +"ScummVM zai Kongzhitai Zhong Youyixie Jinggai, Bingqie Women Buneng Baozheng " +"Suoyou de wenjian doubei zhuanhuan\n" +".\n" +"\n" +"QIng Jiang Qingkuang Baogao Gei Tuandui." + +#: engines/pegasus/pegasus.cpp:714 +msgid "Invalid save file name" +msgstr "Wuxiao Baocun Wenjianming" + +#: engines/pegasus/pegasus.cpp:2507 +msgid "Up/Zoom In/Move Forward/Open Doors" +msgstr "Xiangshang/fangda/Qianjin/Kaimen" + +#: engines/pegasus/pegasus.cpp:2508 +msgid "Down/Zoom Out" +msgstr "Xiangxia/Suoxiao" + +#: engines/pegasus/pegasus.cpp:2511 +msgid "Display/Hide Inventory Tray" +msgstr "Xianshi/Yincang Wupinlan" + +#: engines/pegasus/pegasus.cpp:2512 +msgid "Display/Hide Biochip Tray" +msgstr "Xianshi/Yincang Biochip Lan" + +#: engines/pegasus/pegasus.cpp:2513 +msgid "Action/Select" +msgstr "Dongzuo/Xuanze" + +#: engines/pegasus/pegasus.cpp:2514 +msgid "Toggle Center Data Display" +msgstr "Qiehuan Shuju Zhongxin Xianshi" + +#: engines/pegasus/pegasus.cpp:2515 +msgid "Display/Hide Info Screen" +msgstr "Xianshi/Yincang Xinxi Pingmu" + +#: engines/pegasus/pegasus.cpp:2516 +msgid "Display/Hide Pause Menu" +msgstr "Xianshi/Yincang Zanting Caidan" + +#: engines/queen/detection.cpp:56 +msgid "Alternative intro" +msgstr "QIta Jieshao" + +#: engines/queen/detection.cpp:57 +msgid "Use an alternative game intro (CD version only)" +msgstr "Shiyong Qita Youxi Jieshao (Jin CD Ban)" + +#: engines/sci/detection.cpp:374 +msgid "Skip EGA dithering pass (full color backgrounds)" +msgstr "Tiaoguo EGA Doudong (quancai Beijing)" + +#: engines/sci/detection.cpp:375 +msgid "Skip dithering pass in EGA games, graphics are shown with full colors" +msgstr "tiaoguo EGA Youxi Zhong de Doudong, Tuxiang Yi Quancai Xianshi" + +#: engines/sci/detection.cpp:384 +msgid "Enable high resolution graphics" +msgstr "QIyong Gaofenbianlv Tu" + +#: engines/sci/detection.cpp:385 +msgid "Enable high resolution graphics/content" +msgstr "Qiyong Gaofenbianlv Tubian/Neirong" + +#: engines/sci/detection.cpp:394 +msgid "Prefer digital sound effects" +msgstr "Youxianshiyong Shuzi Yinxiao" + +#: engines/sci/detection.cpp:395 +msgid "Prefer digital sound effects instead of synthesized ones" +msgstr "Youxian SHiyong shuzi YInxiao, er fei Hecheng" + +#: engines/sci/detection.cpp:414 +msgid "Use IMF/Yamaha FB-01 for MIDI output" +msgstr "Shiyong IMF/yamaha Fb-01 Huo MIDI shuchu" + +#: engines/sci/detection.cpp:415 +msgid "" +"Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " +"output" +msgstr "" +"Shiyong IBM Music Feature Ka Huozhe Yamaha FB-01 FM hecheng Mokuai zuowei " +"MIDI shuchu" + +#: engines/sci/detection.cpp:425 +msgid "Use CD audio" +msgstr "Shiyong CD YInpin" + +#: engines/sci/detection.cpp:426 +msgid "Use CD audio instead of in-game audio, if available" +msgstr "Shiyong CD Yinpin erfei Youxinei Yinpin (ruguo keyong)" + +#: engines/sci/detection.cpp:436 +msgid "Use Windows cursors" +msgstr "Shiyong WIndows Guangbiao" + +#: engines/sci/detection.cpp:437 +msgid "" +"Use the Windows cursors (smaller and monochrome) instead of the DOS ones" +msgstr "Shiyong Windows Guangbiao (gengxiao Danse) erfei DOS Guangbiao" + +#: engines/sci/detection.cpp:447 +msgid "Use silver cursors" +msgstr "Shiyong Yinse Guangbiao" + +#: engines/sci/detection.cpp:448 +msgid "" +"Use the alternate set of silver cursors, instead of the normal golden ones" +msgstr "Shiyong Qita Yinse Guangbiao" + +#: engines/scumm/dialogs.cpp:176 +#, c-format +msgid "Insert Disk %c and Press Button to Continue." +msgstr "Charu Guangpan %c Bing An Anniu YI jixu" + +#: engines/scumm/dialogs.cpp:177 +#, c-format +msgid "Unable to Find %s, (%c%d) Press Button." +msgstr "Wufa zhaodao %s, (%c%d) Qing an anniu." + +#: engines/scumm/dialogs.cpp:178 +#, c-format +msgid "Error reading disk %c, (%c%d) Press Button." +msgstr "Duqu Guangpan %c Cuowu, (%c%d) Qing An Anniu." + +#: engines/scumm/dialogs.cpp:179 +msgid "Game Paused. Press SPACE to Continue." +msgstr "Youxi Zanting. An Kongge Yi jixu." + +#. I18N: You may specify 'Yes' symbol at the end of the line, like this: +#. "Moechten Sie wirklich neu starten? (J/N)J" +#. Will react to J as 'Yes' +#: engines/scumm/dialogs.cpp:183 +msgid "Are you sure you want to restart? (Y/N)Y" +msgstr "NinQueding Yao Chongqi ma? (Y/N)Y" + +#. I18N: you may specify 'Yes' symbol at the end of the line. See previous comment +#: engines/scumm/dialogs.cpp:185 +msgid "Are you sure you want to quit? (Y/N)Y" +msgstr "NinQueding Yao Tuichu Ma? (Y/N)Y" + +#: engines/scumm/dialogs.cpp:190 +msgid "Play" +msgstr "Kaishi" + +#: engines/scumm/dialogs.cpp:194 +msgid "Insert save/load game disk" +msgstr "Charu Cundang/Duqu youxi Guangpan" + +#: engines/scumm/dialogs.cpp:195 +msgid "You must enter a name" +msgstr "Nin bixu Shuru Yige Mingcheng" + +#: engines/scumm/dialogs.cpp:196 +msgid "The game was NOT saved (disk full?)" +msgstr "Youxi Meiyou Baocun (Cipan Kongjian YIman?)" + +#: engines/scumm/dialogs.cpp:197 +msgid "The game was NOT loaded" +msgstr "Youxi Meiyou Jiazai" + +#: engines/scumm/dialogs.cpp:198 +#, c-format +msgid "Saving '%s'" +msgstr "Baocun '%s'" + +#: engines/scumm/dialogs.cpp:199 +#, c-format +msgid "Loading '%s'" +msgstr "Zairu '%s'" + +#: engines/scumm/dialogs.cpp:200 +msgid "Name your SAVE game" +msgstr "Wei baocun Youxi Qiming" + +#: engines/scumm/dialogs.cpp:201 +msgid "Select a game to LOAD" +msgstr "Qing Xuanze Yige Youxi Jiazai" + +#: engines/scumm/dialogs.cpp:202 +msgid "Game title)" +msgstr "Youxi Biaoti)" + +#. I18N: Previous page button +#: engines/scumm/dialogs.cpp:288 +msgid "~P~revious" +msgstr "~P~Shangyige" + +#. I18N: Next page button +#: engines/scumm/dialogs.cpp:290 +msgid "~N~ext" +msgstr "~N~Xiayige" + +#: engines/scumm/dialogs.cpp:602 +msgid "Speech Only" +msgstr "Jin Yuyin" + +#: engines/scumm/dialogs.cpp:603 +msgid "Speech and Subtitles" +msgstr "Yuyin he Zimu" + +#: engines/scumm/dialogs.cpp:604 +msgid "Subtitles Only" +msgstr "Jin Zimu" + +#: engines/scumm/dialogs.cpp:612 +msgctxt "lowres" +msgid "Speech & Subs" +msgstr "Yuyin He Zimu" + +#: engines/scumm/dialogs.cpp:658 +msgid "Select a Proficiency Level." +msgstr "Qing Xuanze Shulian Dengji" + +#: engines/scumm/dialogs.cpp:660 +msgid "Refer to your Loom(TM) manual for help." +msgstr "Qingchayue Loom(TM) Shouce Huoqu Bangzhu." + +#: engines/scumm/dialogs.cpp:664 +msgid "Practice" +msgstr "Lianxi" + +#: engines/scumm/dialogs.cpp:665 +msgid "Expert" +msgstr "Zhuanjia" + +#: engines/scumm/help.cpp:74 +msgid "Common keyboard commands:" +msgstr "Changyong Jianpan Mingling:" + +#: engines/scumm/help.cpp:75 +msgid "Save / Load dialog" +msgstr "Baocun/Zairu Duihua" + +#: engines/scumm/help.cpp:77 +msgid "Skip line of text" +msgstr "Tiaoguo cihang wenben" + +#: engines/scumm/help.cpp:78 +msgid "Esc" +msgstr "Esc" + +#: engines/scumm/help.cpp:78 +msgid "Skip cutscene" +msgstr "Tiaoguo Guochang" + +#: engines/scumm/help.cpp:79 +msgid "Space" +msgstr "Kongge" + +#: engines/scumm/help.cpp:79 +msgid "Pause game" +msgstr "Zanting Youxi" + +#: engines/scumm/help.cpp:80 engines/scumm/help.cpp:85 +#: engines/scumm/help.cpp:96 engines/scumm/help.cpp:97 +#: engines/scumm/help.cpp:98 engines/scumm/help.cpp:99 +#: engines/scumm/help.cpp:100 engines/scumm/help.cpp:101 +#: engines/scumm/help.cpp:102 engines/scumm/help.cpp:103 +msgid "Ctrl" +msgstr "Ctrl" + +#: engines/scumm/help.cpp:80 +msgid "Load game state 1-10" +msgstr "Zairu Youxi Cundang 1-10" + +#: engines/scumm/help.cpp:81 engines/scumm/help.cpp:85 +#: engines/scumm/help.cpp:87 engines/scumm/help.cpp:101 +#: engines/scumm/help.cpp:102 engines/scumm/help.cpp:103 +msgid "Alt" +msgstr "Alt" + +#: engines/scumm/help.cpp:81 +msgid "Save game state 1-10" +msgstr "Baocun Youxi Cundang 1-10" + +#: engines/scumm/help.cpp:87 engines/scumm/help.cpp:90 +msgid "Enter" +msgstr "Huiche" + +#: engines/scumm/help.cpp:88 +msgid "Music volume up / down" +msgstr "Yinyue YInliang Gao/Di" + +#: engines/scumm/help.cpp:89 +msgid "Text speed slower / faster" +msgstr "Wenben Sudu Man/Kuai" + +#: engines/scumm/help.cpp:90 +msgid "Simulate left mouse button" +msgstr "Moni Shubiao Zuojian" + +#: engines/scumm/help.cpp:91 +msgid "Tab" +msgstr "Tab" + +#: engines/scumm/help.cpp:91 +msgid "Simulate right mouse button" +msgstr "Moni Shubiao Youjian" + +#: engines/scumm/help.cpp:94 +msgid "Special keyboard commands:" +msgstr "Jianpan Teshu MIngling:" + +#: engines/scumm/help.cpp:95 +msgid "Show / Hide console" +msgstr "Xianshi/YIncang Kongzhitai" + +#: engines/scumm/help.cpp:96 +msgid "Start the debugger" +msgstr "Yunxing Tiaoshiqi" + +#: engines/scumm/help.cpp:97 +msgid "Show memory consumption" +msgstr "Xianshi Neicun xioahao" + +#: engines/scumm/help.cpp:98 +msgid "Run in fast mode (*)" +msgstr "zai Kuaisu Moshi Zhong YUnxing (*)" + +#: engines/scumm/help.cpp:99 +msgid "Run in really fast mode (*)" +msgstr "Zai Chaokuai Moshi XIa yunxing(*)" + +#: engines/scumm/help.cpp:100 +msgid "Toggle mouse capture" +msgstr "Qiehuan Shubiao buzhuo" + +#: engines/scumm/help.cpp:101 +msgid "Switch between graphics filters" +msgstr "QIehuan Tuxiang Guolvqi" + +#: engines/scumm/help.cpp:102 +msgid "Increase / Decrease scale factor" +msgstr "Zengjia / Jianshao suofang Yinzi" + +#: engines/scumm/help.cpp:103 +msgid "Toggle aspect-ratio correction" +msgstr "Qiehuan bili Jiaozheng" + +#: engines/scumm/help.cpp:108 +msgid "* Note that using ctrl-f and" +msgstr "* Zhuyi Ctrl-f He" + +#: engines/scumm/help.cpp:109 +msgid " ctrl-g are not recommended" +msgstr " Ctrl-g BIngbu zhichi" + +#: engines/scumm/help.cpp:110 +msgid " since they may cause crashes" +msgstr " Yinwei Keneng zaocheng cuowu" + +#: engines/scumm/help.cpp:111 +msgid " or incorrect game behavior." +msgstr " Huo Buzhengque de Youxi xingwei" + +#: engines/scumm/help.cpp:115 +msgid "Spinning drafts on the keyboard:" +msgstr "Jianpanshang xuanzhuan Zaogao:" + +#: engines/scumm/help.cpp:117 +msgid "Main game controls:" +msgstr "Youxi zhuyao Kongzhi:" + +#: engines/scumm/help.cpp:122 engines/scumm/help.cpp:137 +#: engines/scumm/help.cpp:162 +msgid "Push" +msgstr "Tui" + +#: engines/scumm/help.cpp:123 engines/scumm/help.cpp:138 +#: engines/scumm/help.cpp:163 +msgid "Pull" +msgstr "La" + +#: engines/scumm/help.cpp:124 engines/scumm/help.cpp:139 +#: engines/scumm/help.cpp:164 engines/scumm/help.cpp:198 +#: engines/scumm/help.cpp:208 +msgid "Give" +msgstr "gei" + +#: engines/scumm/help.cpp:125 engines/scumm/help.cpp:140 +#: engines/scumm/help.cpp:165 engines/scumm/help.cpp:191 +#: engines/scumm/help.cpp:209 +msgid "Open" +msgstr "Dakai" + +#: engines/scumm/help.cpp:127 +msgid "Go to" +msgstr "Qudao" + +#: engines/scumm/help.cpp:128 +msgid "Get" +msgstr "Dedao" + +#: engines/scumm/help.cpp:129 engines/scumm/help.cpp:153 +#: engines/scumm/help.cpp:171 engines/scumm/help.cpp:199 +#: engines/scumm/help.cpp:214 engines/scumm/help.cpp:225 +#: engines/scumm/help.cpp:251 +msgid "Use" +msgstr "Shiyong" + +#: engines/scumm/help.cpp:130 engines/scumm/help.cpp:142 +msgid "Read" +msgstr "du" + +#: engines/scumm/help.cpp:131 engines/scumm/help.cpp:148 +msgid "New kid" +msgstr "Xin kid" + +#: engines/scumm/help.cpp:132 engines/scumm/help.cpp:154 +#: engines/scumm/help.cpp:172 +msgid "Turn on" +msgstr "DaKai" + +#: engines/scumm/help.cpp:133 engines/scumm/help.cpp:155 +#: engines/scumm/help.cpp:173 +msgid "Turn off" +msgstr "Guanbi" + +#: engines/scumm/help.cpp:143 engines/scumm/help.cpp:168 +#: engines/scumm/help.cpp:195 +msgid "Walk to" +msgstr "Zouxiang" + +#: engines/scumm/help.cpp:144 engines/scumm/help.cpp:169 +#: engines/scumm/help.cpp:196 engines/scumm/help.cpp:211 +#: engines/scumm/help.cpp:228 +msgid "Pick up" +msgstr "Jianqi" + +#: engines/scumm/help.cpp:145 engines/scumm/help.cpp:170 +msgid "What is" +msgstr "Shenme" + +#: engines/scumm/help.cpp:147 +msgid "Unlock" +msgstr "Jiesuo" + +#: engines/scumm/help.cpp:150 +msgid "Put on" +msgstr "Chuanshang" + +#: engines/scumm/help.cpp:151 +msgid "Take off" +msgstr "Tuoxia" + +#: engines/scumm/help.cpp:157 +msgid "Fix" +msgstr "Xiufu" + +#: engines/scumm/help.cpp:159 +msgid "Switch" +msgstr "Qiehuan" + +#: engines/scumm/help.cpp:167 engines/scumm/help.cpp:229 +msgid "Look" +msgstr "Kan" + +#: engines/scumm/help.cpp:174 engines/scumm/help.cpp:224 +msgid "Talk" +msgstr "Shuohua" + +#: engines/scumm/help.cpp:175 +msgid "Travel" +msgstr "LvXing" + +#: engines/scumm/help.cpp:176 +msgid "To Henry / To Indy" +msgstr "Gei Henry/ Gei Indy" + +#. I18N: These are different musical notes +#: engines/scumm/help.cpp:180 +msgid "play C minor on distaff" +msgstr "Yanzou C Xiaodiao" + +#: engines/scumm/help.cpp:181 +msgid "play D on distaff" +msgstr "Yanzou D" + +#: engines/scumm/help.cpp:182 +msgid "play E on distaff" +msgstr "Yanzou E" + +#: engines/scumm/help.cpp:183 +msgid "play F on distaff" +msgstr "Yanozu F" + +#: engines/scumm/help.cpp:184 +msgid "play G on distaff" +msgstr "Yanzou G" + +#: engines/scumm/help.cpp:185 +msgid "play A on distaff" +msgstr "Yanzou A" + +#: engines/scumm/help.cpp:186 +msgid "play B on distaff" +msgstr "Yanzou B" + +#: engines/scumm/help.cpp:187 +msgid "play C major on distaff" +msgstr "Yanzou C Dadiao" + +#: engines/scumm/help.cpp:193 engines/scumm/help.cpp:215 +msgid "puSh" +msgstr "Tui" + +#: engines/scumm/help.cpp:194 engines/scumm/help.cpp:216 +msgid "pull (Yank)" +msgstr "La" + +#: engines/scumm/help.cpp:197 engines/scumm/help.cpp:213 +#: engines/scumm/help.cpp:249 +msgid "Talk to" +msgstr "Shuohua" + +#: engines/scumm/help.cpp:200 engines/scumm/help.cpp:212 +msgid "Look at" +msgstr "Kanxiang" + +#: engines/scumm/help.cpp:201 +msgid "turn oN" +msgstr "Dakai" + +#: engines/scumm/help.cpp:202 +msgid "turn oFf" +msgstr "Guanbi" + +#: engines/scumm/help.cpp:218 +msgid "KeyUp" +msgstr "TaiqiAnjian" + +#: engines/scumm/help.cpp:218 +msgid "Highlight prev dialogue" +msgstr "Gaoliang Zhiqian Duihua" + +#: engines/scumm/help.cpp:219 +msgid "KeyDown" +msgstr "AnxiaAnjian" + +#: engines/scumm/help.cpp:219 +msgid "Highlight next dialogue" +msgstr "Gaoliang Zhihou Duihua" + +#: engines/scumm/help.cpp:223 +msgid "Walk" +msgstr "Zou" + +#: engines/scumm/help.cpp:226 engines/scumm/help.cpp:235 +#: engines/scumm/help.cpp:242 engines/scumm/help.cpp:250 +msgid "Inventory" +msgstr "Wupin" + +#: engines/scumm/help.cpp:227 +msgid "Object" +msgstr "Dongxi" + +#: engines/scumm/help.cpp:230 +msgid "Black and White / Color" +msgstr "Heibai / Caise" + +#: engines/scumm/help.cpp:233 +msgid "Eyes" +msgstr "yanjing" + +#: engines/scumm/help.cpp:234 +msgid "Tongue" +msgstr "Shetou" + +#: engines/scumm/help.cpp:236 +msgid "Punch" +msgstr "Quantou" + +#: engines/scumm/help.cpp:237 +msgid "Kick" +msgstr "Ti" + +#: engines/scumm/help.cpp:240 engines/scumm/help.cpp:248 +msgid "Examine" +msgstr "Jiancha" + +#: engines/scumm/help.cpp:241 +msgid "Regular cursor" +msgstr "Putong Guangbiao" + +#. I18N: Comm is a communication device +#: engines/scumm/help.cpp:244 +msgid "Comm" +msgstr "Tongxin" + +#: engines/scumm/help.cpp:247 +msgid "Save / Load / Options" +msgstr "Baocun / Zairu / Xuanxiang" + +#: engines/scumm/help.cpp:256 +msgid "Other game controls:" +msgstr "QIta youxi Kongzhi:" + +#: engines/scumm/help.cpp:258 engines/scumm/help.cpp:268 +msgid "Inventory:" +msgstr "Wupin:" + +#: engines/scumm/help.cpp:259 engines/scumm/help.cpp:275 +msgid "Scroll list up" +msgstr "LIebiao Shanghua" + +#: engines/scumm/help.cpp:260 engines/scumm/help.cpp:276 +msgid "Scroll list down" +msgstr "LIebiao Xiahua" + +#: engines/scumm/help.cpp:261 engines/scumm/help.cpp:269 +msgid "Upper left item" +msgstr "Zuoshang Wupin" + +#: engines/scumm/help.cpp:262 engines/scumm/help.cpp:271 +msgid "Lower left item" +msgstr "Zuoxia Wupin" + +#: engines/scumm/help.cpp:263 engines/scumm/help.cpp:272 +msgid "Upper right item" +msgstr "Youshang Wupin" + +#: engines/scumm/help.cpp:264 engines/scumm/help.cpp:274 +msgid "Lower right item" +msgstr "Youxia Wupin" + +#: engines/scumm/help.cpp:270 +msgid "Middle left item" +msgstr "Zhongzuo Wupin" + +#: engines/scumm/help.cpp:273 +msgid "Middle right item" +msgstr "Zhongyou Wupin" + +#: engines/scumm/help.cpp:280 engines/scumm/help.cpp:285 +msgid "Switching characters:" +msgstr "Qiehuan Juese" + +#: engines/scumm/help.cpp:282 +msgid "Second kid" +msgstr "Dierge Kid" + +#: engines/scumm/help.cpp:283 +msgid "Third kid" +msgstr "Disange Kid" + +#: engines/scumm/help.cpp:292 +msgid "Toggle Inventory/IQ Points display" +msgstr "Qiehuan Wupin/IQ Dian Xianshi" + +#: engines/scumm/help.cpp:293 +msgid "Toggle Keyboard/Mouse Fighting (*)" +msgstr "Qiehuan JInapan/Shubiao Zhandou (*)" + +#: engines/scumm/help.cpp:295 +msgid "* Keyboard Fighting is always on," +msgstr "* Jianpan Zhandou zongshi Kaiqi," + +#: engines/scumm/help.cpp:296 +msgid " so despite the in-game message this" +msgstr " Suoyi Hulue youxi nei xinxi" + +#: engines/scumm/help.cpp:297 +msgid " actually toggles Mouse Fighting Off/On" +msgstr " Zhe shiji shang shi Zai qiehuan Shubiao Zhandou de Kaiguan" + +#: engines/scumm/help.cpp:304 +msgid "Fighting controls (numpad):" +msgstr "Zhandou Kongzhi (zhuzijianpan):" + +#: engines/scumm/help.cpp:305 engines/scumm/help.cpp:306 +#: engines/scumm/help.cpp:307 +msgid "Step back" +msgstr "Shangyibu" + +#: engines/scumm/help.cpp:308 +msgid "Block high" +msgstr "Zudang Gaochu" + +#: engines/scumm/help.cpp:309 +msgid "Block middle" +msgstr "Zudang Zhongjian" + +#: engines/scumm/help.cpp:310 +msgid "Block low" +msgstr "Zudang Dichu" + +#: engines/scumm/help.cpp:311 +msgid "Punch high" +msgstr "Quanda Gaochu" + +#: engines/scumm/help.cpp:312 +msgid "Punch middle" +msgstr "Quanda Zhongjian" + +#: engines/scumm/help.cpp:313 +msgid "Punch low" +msgstr "Quanda Dichu" + +#: engines/scumm/help.cpp:315 +msgid "Sucker punch" +msgstr "Sucker Punch" + +#: engines/scumm/help.cpp:318 +msgid "These are for Indy on left." +msgstr "Weile zai Zuobian de Indy" + +#: engines/scumm/help.cpp:319 +msgid "When Indy is on the right," +msgstr "Dnag Indy Zai Youbian Shi," + +#: engines/scumm/help.cpp:320 +msgid "7, 4, and 1 are switched with" +msgstr "7, 4 He 1 Fenbie keyi Yong" + +#: engines/scumm/help.cpp:321 +msgid "9, 6, and 3, respectively." +msgstr "9, 6, 3 Laiqiehuan." + +#: engines/scumm/help.cpp:328 +msgid "Biplane controls (numpad):" +msgstr "Shuangmian Kongzhi (shuzijianpan):" + +#: engines/scumm/help.cpp:329 +msgid "Fly to upper left" +msgstr "Feiwang Zuoshang" + +#: engines/scumm/help.cpp:330 +msgid "Fly to left" +msgstr "Feiwang Zuobian" + +#: engines/scumm/help.cpp:331 +msgid "Fly to lower left" +msgstr "Feiwang Zuoxia" + +#: engines/scumm/help.cpp:332 +msgid "Fly upwards" +msgstr "Xinagshang Fei" + +#: engines/scumm/help.cpp:333 +msgid "Fly straight" +msgstr "Zhifei" + +#: engines/scumm/help.cpp:334 +msgid "Fly down" +msgstr "Xiangxia Fei" + +#: engines/scumm/help.cpp:335 +msgid "Fly to upper right" +msgstr "Feiwang YouShang" + +#: engines/scumm/help.cpp:336 +msgid "Fly to right" +msgstr "Feiwang Youbian" + +#: engines/scumm/help.cpp:337 +msgid "Fly to lower right" +msgstr "Feiwang Youxia" + +#: engines/scumm/input.cpp:580 +msgid "Snap scroll on" +msgstr "Snap hundong kai" + +#: engines/scumm/input.cpp:582 +msgid "Snap scroll off" +msgstr "Snap Gundong Guan" + +#: engines/scumm/input.cpp:595 +msgid "Music volume: " +msgstr "Yinyue Yinliang:" + +#: engines/scumm/input.cpp:612 +msgid "Subtitle speed: " +msgstr "Zimu Sudu:" + +#: engines/scumm/scumm.cpp:1832 +#, c-format +msgid "" +"Native MIDI support requires the Roland Upgrade from LucasArts,\n" +"but %s is missing. Using AdLib instead." +msgstr "" +"Bendi MIDI Zhichi Xuyao Cong LucasArts Shengji Zhi Roland,\n" +"Dnahsi %s meiyou zhaodao. Shiyong AdLib." + +#: engines/scumm/scumm.cpp:2644 +msgid "" +"Usually, Maniac Mansion would start now. But for that to work, the game " +"files for Maniac Mansion have to be in the 'Maniac' directory inside the " +"Tentacle game directory, and the game has to be added to ScummVM." +msgstr "" +"Tongchang, Maniac mansion Huizai XIanzai kaishi. Raner, Manian mansion De " +"youxi Wenjian Xuyao zai 'maniac'Mulu Nei , weiyu Tentacle Youxi Mulu, Ci " +"Youxi Xuyao Bei tianjia dao ScummVM Zhong." + +#: engines/scumm/players/player_v3m.cpp:129 +msgid "" +"Could not find the 'Loom' Macintosh executable to read the\n" +"instruments from. Music will be disabled." +msgstr "" +"Wufa Zhaodao 'Loom' Macintosh Chengxu lai\n" +" Duru Yinyue. Yinyue Huibei Jinyong." + +#: engines/scumm/players/player_v5m.cpp:107 +msgid "" +"Could not find the 'Monkey Island' Macintosh executable to read the\n" +"instruments from. Music will be disabled." +msgstr "" +"Wufa Zhaodao 'monkey Island' Macintosh Chengxu lai Duru YInyue. yinyue " +"Huibei Jinyong." + +#: engines/sherlock/detection.cpp:71 +msgid "Use original savegame dialog" +msgstr "Shiyong Yuanshi Youxi baocun Duihuakuang" + +#: engines/sherlock/detection.cpp:72 +msgid "" +"Files button in-game shows original savegame dialog rather than the ScummVM " +"menu" +msgstr "" +"YouxiNei wenjian Anniu shiyong Yuanshi Youxi Duihuakuang Erfei ScummVM Caidan" + +#: engines/sherlock/detection.cpp:81 +msgid "Pixellated scene transitions" +msgstr "Xiangsuhua Changjing Qiehuan" + +#: engines/sherlock/detection.cpp:82 +msgid "When changing scenes, a randomized pixel transition is done" +msgstr "Dang Qiehuan Changjingshi, Shiyong Suiji Xiangsu zhuanhuan" + +#: engines/sherlock/detection.cpp:91 +msgid "Don't show hotspots when moving mouse" +msgstr "Dnag Yidong shubiao Shi Buyao Xianshi Redian" + +#: engines/sherlock/detection.cpp:92 +msgid "" +"Only show hotspot names after you actually click on a hotspot or action " +"button" +msgstr "JInzai Dianji Redian Huo Dongzuo Anjianhou Xianshi Redianming" + +#: engines/sherlock/detection.cpp:101 +msgid "Show character portraits" +msgstr "Xianshi Juese Huaxiang" + +#: engines/sherlock/detection.cpp:102 +msgid "Show portraits for the characters when conversing" +msgstr "Jiaotan Shi Xianshi Juese Huaxiang" + +#: engines/sherlock/detection.cpp:111 +msgid "Slide dialogs into view" +msgstr "Huadon Duigua dao SHituzhong" + +#: engines/sherlock/detection.cpp:112 +msgid "Slide UI dialogs into view, rather than simply showing them immediately" +msgstr "Jiang UI Duihuakuang Huaru Shitu, Erfei Turan Chuxian" + +#: engines/sherlock/detection.cpp:121 +msgid "Transparent windows" +msgstr "Touming Chuangkou" + +#: engines/sherlock/detection.cpp:122 +msgid "Show windows with a partially transparent background" +msgstr "Xianshi Diayou Bantouming Beijing de Chuangkou" + +#: engines/sky/compact.cpp:130 +msgid "" +"Unable to find \"sky.cpt\" file!\n" +"Please download it from www.scummvm.org" +msgstr "" +"Wufa Zhaodao \"sky.cpt\" Wenjian\n" +"Qing Cong www.cummvm.org Xiazai" + +#: engines/sky/compact.cpp:141 +msgid "" +"The \"sky.cpt\" file has an incorrect size.\n" +"Please (re)download it from www.scummvm.org" +msgstr "" +"Wenjian \"sky.cpt\" Chicun Cuowu.\n" +"Qing Cong www.scummvm.org Chongxin Xiazai" + +#: engines/sky/detection.cpp:44 +msgid "Floppy intro" +msgstr "Ruanpan Jieshao" + +#: engines/sky/detection.cpp:45 +msgid "Use the floppy version's intro (CD version only)" +msgstr "Shiyong Ruanpan Banben JIeshao (jin CD banben)" + +#: engines/sword1/animation.cpp:524 +#, c-format +msgid "PSX stream cutscene '%s' cannot be played in paletted mode" +msgstr "PSX liu Changjing '%s' Wufa zai Tiaosiban Moshi xia Bofang" + +#: engines/sword1/animation.cpp:545 engines/sword2/animation.cpp:445 +msgid "DXA cutscenes found but ScummVM has been built without zlib" +msgstr "Zhaodao DXA Guochang Danshi ScummVM Meiyou yu Zlib bianyi" + +#: engines/sword1/animation.cpp:561 engines/sword2/animation.cpp:461 +msgid "" +"MPEG-2 cutscenes found but ScummVM has been built without MPEG-2 support" +msgstr "MPEG-2 Guocheng Zhaodao Dnashi ScummVM Meiyou He MPEG-2 Zhichi Bianyi" + +#: engines/sword1/animation.cpp:568 engines/sword2/animation.cpp:470 +#, c-format +msgid "Cutscene '%s' not found" +msgstr "Guochang '%s' Weizhaodao" + +#: engines/sword1/control.cpp:863 +msgid "" +"ScummVM found that you have old savefiles for Broken Sword 1 that should be " +"converted.\n" +"The old save game format is no longer supported, so you will not be able to " +"load your games if you don't convert them.\n" +"\n" +"Press OK to convert them now, otherwise you will be asked again the next " +"time you start the game.\n" +msgstr "" +"ScummVM Zhaodaole Zhiqian De Broken Sword 1 Cundang, qie YInggai Bei " +"zhuanhuan.\n" +"Zhiqian de cundang Geshi BUzai Zhichi, Nin Bixu Xianzhuanhuan Caineng Duqu " +"Cundang\n" +"Dianji Queding Lia zhuanhuancundang, Fouze NIn Huizai Xiaci Kaishi Youxi Shi " +"bei Zaici Tishi\n" + +#: engines/sword1/control.cpp:1232 +#, c-format +msgid "" +"Target new save game already exists!\n" +"Would you like to keep the old save game (%s) or the new one (%s)?\n" +msgstr "" +"Mubiao Xin Cundang Yijing Cunzai!\n" +"Niyao Baocun jiude Cundang (%s) Haishi Xinde(%s)?\n" + +#: engines/sword1/control.cpp:1235 +msgid "Keep the old one" +msgstr "Baoliu Jiude" + +#: engines/sword1/control.cpp:1235 +msgid "Keep the new one" +msgstr "Baoliu Xinde" + +#: engines/sword1/logic.cpp:1633 +msgid "This is the end of the Broken Sword 1 Demo" +msgstr "Zheshi Broken Sword 1 Demo de Jiewei" + +#: engines/sword2/animation.cpp:425 +msgid "" +"PSX cutscenes found but ScummVM has been built without RGB color support" +msgstr "PSX Guochang zhaodao Danshi ScmummVM Meiyou Zhichi RGB Secai Bianyi" + +#: engines/sword2/sword2.cpp:79 +msgid "Show object labels" +msgstr "Xianshi Wuti Biaoqian" + +#: engines/sword2/sword2.cpp:80 +msgid "Show labels for objects on mouse hover" +msgstr "Dang Shubiao Yishang Shi Xianshi Wuti Biaoqian" + +#: engines/teenagent/resources.cpp:95 +msgid "" +"You're missing the 'teenagent.dat' file. Get it from the ScummVM website" +msgstr "" +"Zhaobudao 'teenagent.dat' Wenjian. Cong ScummVM wangzhan Shangmian Dedao" + +#: engines/teenagent/resources.cpp:116 +msgid "" +"The teenagent.dat file is compressed and zlib hasn't been included in this " +"executable. Please decompress it" +msgstr "" +"teenagent.dat Wenjian Yibeiyasuo Bingqie zlib Bingmeiyou Zai chengxu Zhong. " +"Qing jieya." + +#: engines/wintermute/detection.cpp:58 +msgid "Show FPS-counter" +msgstr "Xianshi FPS Jishuqi" + +#: engines/wintermute/detection.cpp:59 +msgid "Show the current number of frames per second in the upper left corner" +msgstr "Zai Zuoshangjiao Xianshi Xianzai Meimiaozhong Zhenshu" + +#: engines/zvision/detection_tables.h:52 +msgid "Use the original save/load screens instead of the ScummVM interface" +msgstr "Shiyong Yuanshi baocun/zairu Pingmu Erfei ScummVM jiemian" + +#: engines/zvision/detection_tables.h:61 +msgid "Double FPS" +msgstr "Shuangbei FPS" + +#: engines/zvision/detection_tables.h:62 +msgid "Increase framerate from 30 to 60 FPS" +msgstr "Zengjia zhenlv cong 30 dao 60 FPS" + +#: engines/zvision/detection_tables.h:71 +msgid "Enable Venus" +msgstr "Qiyong Venus" + +#: engines/zvision/detection_tables.h:72 +msgid "Enable the Venus help system" +msgstr "Qiyong Venus Bangzhu Xitong" + +#: engines/zvision/detection_tables.h:81 +msgid "Disable animation while turning" +msgstr "zhuanxinag shi Jinyong Donghua" + +#: engines/zvision/detection_tables.h:82 +msgid "Disable animation while turning in panorama mode" +msgstr "Quanjing Moshi xia Zhuanxiang shi Jinyong Donghua" + +#: engines/zvision/detection_tables.h:91 +msgid "Use high resolution MPEG video" +msgstr "Shiyong Gaofenbianlv MPEG shipin" + +#: engines/zvision/detection_tables.h:92 +msgid "Use MPEG video from the DVD version, instead of lower resolution AVI" +msgstr "Cong DVD Banben Zhong shiyong MPEG shipin, erfei Difenbianlv AVI" @@ -223,6 +223,8 @@ ifneq ($(BACKEND), iphone) ifneq ($(BACKEND), ios7) # Static libaries, used for the scummvm-static and iphone targets OSX_STATIC_LIBS := `$(SDLCONFIG) --static-libs` +# With sdl2-config we don't always get the OpenGL framework +OSX_STATIC_LIBS += -framework OpenGL endif endif |