aboutsummaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/audiocd/audiocd-stream.cpp199
-rw-r--r--backends/audiocd/audiocd-stream.h108
-rw-r--r--backends/audiocd/audiocd.h77
-rw-r--r--backends/audiocd/default/default-audiocd.cpp93
-rw-r--r--backends/audiocd/default/default-audiocd.h45
-rw-r--r--backends/audiocd/linux/linux-audiocd.cpp471
-rw-r--r--backends/audiocd/linux/linux-audiocd.h62
-rw-r--r--backends/audiocd/macosx/macosx-audiocd.cpp306
-rw-r--r--backends/audiocd/macosx/macosx-audiocd.h61
-rw-r--r--backends/audiocd/sdl/sdl-audiocd.cpp63
-rw-r--r--backends/audiocd/sdl/sdl-audiocd.h11
-rw-r--r--backends/audiocd/win32/msvc/ntddcdrm.h362
-rw-r--r--backends/audiocd/win32/win32-audiocd.cpp388
-rw-r--r--backends/audiocd/win32/win32-audiocd.h60
-rw-r--r--backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp8
-rw-r--r--backends/graphics/opengl/context.cpp182
-rw-r--r--backends/graphics/opengl/debug.cpp2
-rw-r--r--backends/graphics/opengl/debug.h4
-rw-r--r--backends/graphics/opengl/framebuffer.cpp259
-rw-r--r--backends/graphics/opengl/framebuffer.h175
-rw-r--r--backends/graphics/opengl/opengl-defs.h262
-rw-r--r--backends/graphics/opengl/opengl-func.h153
-rw-r--r--backends/graphics/opengl/opengl-graphics.cpp240
-rw-r--r--backends/graphics/opengl/opengl-graphics.h70
-rw-r--r--backends/graphics/opengl/opengl-sys.h154
-rw-r--r--backends/graphics/opengl/pipelines/clut8.cpp46
-rw-r--r--backends/graphics/opengl/pipelines/clut8.h (renamed from backends/graphics/opengl/extensions.cpp)32
-rw-r--r--backends/graphics/opengl/pipelines/fixed.cpp70
-rw-r--r--backends/graphics/opengl/pipelines/fixed.h46
-rw-r--r--backends/graphics/opengl/pipelines/pipeline.cpp66
-rw-r--r--backends/graphics/opengl/pipelines/pipeline.h126
-rw-r--r--backends/graphics/opengl/pipelines/shader.cpp94
-rw-r--r--backends/graphics/opengl/pipelines/shader.h59
-rw-r--r--backends/graphics/opengl/shader.cpp335
-rw-r--r--backends/graphics/opengl/shader.h288
-rw-r--r--backends/graphics/opengl/texture.cpp547
-rw-r--r--backends/graphics/opengl/texture.h332
-rw-r--r--backends/graphics/openglsdl/openglsdl-graphics.cpp136
-rw-r--r--backends/graphics/openglsdl/openglsdl-graphics.h3
-rw-r--r--backends/module.mk18
-rw-r--r--backends/platform/3ds/3ds.mk64
-rw-r--r--backends/platform/3ds/README185
-rw-r--r--backends/platform/3ds/app/banner.pngbin0 -> 19241 bytes
-rw-r--r--backends/platform/3ds/app/banner.wavbin0 -> 2212 bytes
-rw-r--r--backends/platform/3ds/app/icon.pngbin0 -> 3800 bytes
-rw-r--r--backends/platform/3ds/app/scummvm.rsf219
-rw-r--r--backends/platform/3ds/config.cpp87
-rw-r--r--backends/platform/3ds/config.h (renamed from backends/graphics/opengl/extensions.h)32
-rw-r--r--backends/platform/3ds/gui.cpp46
-rw-r--r--backends/platform/3ds/gui.h41
-rw-r--r--backends/platform/3ds/main.cpp54
-rw-r--r--backends/platform/3ds/module.mk18
-rw-r--r--backends/platform/3ds/options-dialog.cpp98
-rw-r--r--backends/platform/3ds/options-dialog.h70
-rw-r--r--backends/platform/3ds/osystem-audio.cpp110
-rw-r--r--backends/platform/3ds/osystem-events.cpp302
-rw-r--r--backends/platform/3ds/osystem-graphics.cpp517
-rw-r--r--backends/platform/3ds/osystem.cpp193
-rw-r--r--backends/platform/3ds/osystem.h221
-rw-r--r--backends/platform/3ds/shader.v.pica59
-rw-r--r--backends/platform/3ds/sprite.cpp144
-rw-r--r--backends/platform/3ds/sprite.h71
-rw-r--r--backends/platform/dc/dc.h21
-rw-r--r--backends/platform/dc/dcmain.cpp68
-rw-r--r--backends/platform/dingux/README.GCW09
-rwxr-xr-xbackends/platform/dingux/build.gcw0.sh2
-rw-r--r--backends/platform/dingux/dingux.mk7
-rw-r--r--backends/platform/ds/arm9/source/osystem_ds.cpp2
-rw-r--r--backends/platform/ds/arm9/source/osystem_ds.h3
-rw-r--r--backends/platform/sdl/amigaos/amigaos-main.cpp35
-rw-r--r--backends/platform/sdl/macosx/macosx.cpp7
-rw-r--r--backends/platform/sdl/macosx/macosx.h5
-rw-r--r--backends/platform/sdl/posix/posix.cpp13
-rw-r--r--backends/platform/sdl/posix/posix.h2
-rw-r--r--backends/platform/sdl/sdl.cpp19
-rw-r--r--backends/platform/sdl/sdl.h5
-rw-r--r--backends/platform/sdl/win32/win32.cpp5
-rw-r--r--backends/platform/sdl/win32/win32.h4
-rw-r--r--backends/platform/symbian/src/portdefs.h3
-rw-r--r--backends/platform/tizen/graphics.cpp5
-rw-r--r--backends/platform/tizen/graphics.h2
-rw-r--r--backends/platform/tizen/system.cpp49
-rw-r--r--backends/platform/wince/portdefs.h3
-rw-r--r--backends/saves/default/default-saves.cpp155
-rw-r--r--backends/saves/default/default-saves.h25
-rw-r--r--backends/taskbar/macosx/dockplugin/dockplugin.m125
-rw-r--r--backends/taskbar/macosx/macosx-taskbar.h1
-rw-r--r--backends/taskbar/macosx/macosx-taskbar.mm67
-rw-r--r--backends/updates/macosx/macosx-updates.h6
-rw-r--r--backends/updates/macosx/macosx-updates.mm75
90 files changed, 8603 insertions, 664 deletions
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/dinguxsdl/dinguxsdl-graphics.cpp b/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp
index 0b9cc0c7e8..7a248f1859 100644
--- a/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp
+++ b/backends/graphics/dinguxsdl/dinguxsdl-graphics.cpp
@@ -35,6 +35,10 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{0, 0, 0}
};
+#ifndef USE_SCALERS
+#define DownscaleAllByHalf 0
+#endif
+
DINGUXSdlGraphicsManager::DINGUXSdlGraphicsManager(SdlEventSource *boss, SdlWindow *window)
: SurfaceSdlGraphicsManager(boss, window) {
}
@@ -61,9 +65,11 @@ bool DINGUXSdlGraphicsManager::setGraphicsMode(int mode) {
case GFX_NORMAL:
newScaleFactor = 1;
break;
+#ifdef USE_SCALERS
case GFX_HALF:
newScaleFactor = 1;
break;
+#endif
default:
warning("unknown gfx mode %d", mode);
return false;
@@ -89,9 +95,11 @@ void DINGUXSdlGraphicsManager::setGraphicsModeIntern() {
case GFX_NORMAL:
newScalerProc = Normal1x;
break;
+#ifdef USE_SCALERS
case GFX_HALF:
newScalerProc = DownscaleAllByHalf;
break;
+#endif
default:
error("Unknown gfx mode %d", _videoMode.mode);
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..a2dabb7c22
--- /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();
+
+ GL_CALL(glVertexAttribPointer(_colorAttribLocation, 4, GL_FLOAT, GL_FALSE, 0, _colorAttributes));
+}
+
+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;
+ }
+}
+
+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..0b4c677d70
--- /dev/null
+++ b/backends/graphics/opengl/shader.cpp
@@ -0,0 +1,335 @@
+/* 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) {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ _builtIn[i] = nullptr;
+ }
+}
+
+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..33598b5488 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,223 @@ 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);
+ _clut8Pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
+}
+
+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/3ds/3ds.mk b/backends/platform/3ds/3ds.mk
new file mode 100644
index 0000000000..7ab58995f6
--- /dev/null
+++ b/backends/platform/3ds/3ds.mk
@@ -0,0 +1,64 @@
+TARGET := scummvm
+
+APP_TITLE := ScummVM
+APP_DESCRIPTION := Point-and-click adventure game engines
+APP_AUTHOR := ScummVM Team
+APP_ICON := backends/platform/3ds/app/icon.png
+
+APP_RSF := backends/platform/3ds/app/scummvm.rsf
+APP_BANNER_IMAGE:= backends/platform/3ds/app/banner.png
+APP_BANNER_AUDIO:= backends/platform/3ds/app/banner.wav
+
+ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
+CXXFLAGS += -std=gnu++11
+ASFLAGS += -mfloat-abi=hard
+LDFLAGS += -specs=3dsx.specs $(ARCH) -L$(DEVKITPRO)/libctru/lib -L$(DEVKITPRO)/portlibs/3ds/lib
+
+.PHONY: clean_3ds
+
+clean: clean_3ds
+
+clean_3ds:
+ $(RM) $(TARGET).3dsx
+ $(RM) $(TARGET).cia
+
+$(TARGET).smdh: $(APP_ICON)
+ @bannertool makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i $(APP_ICON) -o $@
+ @echo built ... $(notdir $@)
+
+$(TARGET).3dsx: $(EXECUTABLE) $(TARGET).smdh
+ @3dsxtool $< $@ --smdh=$(TARGET).smdh
+ @echo built ... $(notdir $@)
+
+$(TARGET).bnr: $(APP_BANNER_IMAGE) $(APP_BANNER_AUDIO)
+ @bannertool makebanner -o $@ -i $(APP_BANNER_IMAGE) -a $(APP_BANNER_AUDIO)
+ @echo built ... $(notdir $@)
+
+$(TARGET).cia: $(EXECUTABLE) $(APP_RSF) $(TARGET).smdh $(TARGET).bnr
+ @makerom -f cia -target t -exefslogo -o $@ -elf $(EXECUTABLE) -rsf $(APP_RSF) -banner $(TARGET).bnr -icon $(TARGET).smdh
+ @echo built ... $(notdir $@)
+
+#---------------------------------------------------------------------------------
+# rules for assembling GPU shaders
+#---------------------------------------------------------------------------------
+define shader-as
+ $(eval FILEPATH := $(patsubst %.shbin.o,%.shbin,$@))
+ $(eval FILE := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
+ picasso -o $(FILEPATH) $1
+ bin2s $(FILEPATH) | $(AS) -o $@
+ echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(FILEPATH) | tr . _)`.h
+ echo "extern const u8" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(FILEPATH) | tr . _)`.h
+ echo "extern const u32" `(echo $(FILE) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(FILEPATH) | tr . _)`.h
+endef
+
+%.shbin.o : %.v.pica %.g.pica
+ @echo $(notdir $^)
+ @$(call shader-as,$^)
+
+%.shbin.o : %.v.pica
+ @echo $(notdir $<)
+ @$(call shader-as,$<)
+
+%.shbin.o : %.shlist
+ @echo $(notdir $<)
+ @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
diff --git a/backends/platform/3ds/README b/backends/platform/3ds/README
new file mode 100644
index 0000000000..516e694f64
--- /dev/null
+++ b/backends/platform/3ds/README
@@ -0,0 +1,185 @@
+ScummVM 3DS README
+------------------------------------------------------------------------
+
+Table of Contents:
+------------------
+1.0) Installation
+ * 1.1 3DSX installation
+ * 1.2 CIA installation
+2.0) Controls
+ * 2.1 Default key mappings
+ * 2.2 Hover mode
+ * 2.3 Drag mode
+3.0) Supported Games
+4.0) Compiling
+ * 4.1 Prerequisites
+ * * 4.1.1 Compiling third-party libraries
+ * 4.2 Compiling ScummVM
+ * 4.3 Warning for 3DSX build
+
+
+
+1.0) Installation
+-----------------
+There are two possible formats to be used: 3DSX and CIA (recommended).
+The 3DSX format is exclusively used by the Homebrew Launcher and its derivatives.
+The CIA format can be installed directly to the 3DS home menu and can be launched
+using any CFW (Custom Firmware) of your choice.
+
+Installing the Homebrew Launcher or any CFW is beyond the scope of this README.
+Look elsewhere to see how to install those if you do not already have them set up.
+
+
+1.1) 3DSX installation
+----------------
+The CIA format is recommended for stability and maximum game support. If that is
+not an option, you will need one of a collection of 3DS titles installed on your
+system in order to properly launch ScummVM as a 3DSX. This is because the
+Homebrew Launcher hijacks other processes to run 3DSX homebrew, and ScummVM is a
+particularly large homebrew that can't be launched with the resources provided
+by standard system applications.
+
+You will need one of the following (installed or physically in cart slot):
+
+- Youtube
+- Monster Hunter 4 Ultimate Special Demo
+- Monster Hunter 4 Ultimate
+- Monster Hunter 4G
+- Super Smash Bros. for Nintendo 3DS Demo
+- Super Smash Bros. for Nintendo 3DS Special Demo
+- Super Smash Bros. for Nintendo 3DS
+
+Once you have one of the above, you need to merely extract all ScummVM 3DS files
+to the root of your SD card so that all files reside in the /3ds/scummvm/ directory.
+
+
+1.2) CIA installation
+---------------------
+The CIA format requires a DSP binary dump saved on your SD card as /3ds/dspfirm.cdc
+for proper audio support. You can search online to find software to dump this.
+Not having this file will cause many problems with games that need audio, sometimes
+even crashing, so this is NOT considered optional.
+
+Using any CIA installation software (search elsewhere for that), you need to install
+the scummvm.cia file. Then, just like what is done with the 3DSX installation, you
+need to extract all ScummVM 3DS files (scummvm.cia excluded) to the root of your SD
+card so that all files reside in the /3ds/scummvm/ directory.
+
+
+
+2.0) Controls
+-------------
+
+2.1) Default key mappings
+-------------------------
+The D-Pad and A/B/X/Y buttons have mirrored usage. So they do the same things
+depending on if you're right or left-handed.
+
+| Buttons | Function |
+|------------|--------------------------------|
+| A / D-left | Left-click |
+| X / D-up | Right-click |
+| B / D-down | ESC (skips cutscenes and such) |
+| L | Use virtual keyboard |
+| R | Toggle hover/drag modes |
+| Start | Open game menu |
+| Select | Open 3DS config menu |
+| Circle Pad | Move the cursor |
+
+
+2.2) Hover mode
+---------------
+When you use the touchscreen, you are simulating the mere moving of the mouse. You
+can click only with taps, meaning it is impossible to drag stuff or hold down a
+mouse button without using buttons mapped to right/left-click.
+
+
+2.3) Drag mode
+--------------
+Every time you touch and release the touchscreen, you are simulating the click and
+release of the mouse buttons. At the moment, this is only a left-click.
+
+
+
+3.0) Supported Games
+--------------------
+The full game engine compatibility list can be found here:
+http://scummvm.org/compatibility/
+
+While all the above games should run on the 3DS (report if they do not), there are
+many games which are unplayable due to the lack of CPU speed on the 3DS. So if
+you play any games that run really slow, this is not considered a bug, but rather
+a hardware limitation. Though possible GPU optimizations are always in the works.
+The New 3DS console has much better performance, but there are still many newer and
+high-resolution games that cannot be played. A list of these unplayable games and
+game engines will eventually be listed here.
+
+
+
+4.0) Compiling
+--------------
+
+4.1) Prerequisites
+------------------
+ - devkitARM (presumably with libctru, picasso and such)
+ - citro3d
+ - Optional: You should compile third-party libraries for the 3ds (commonly referred
+ to as portlibs in the devkitPRO community). Some games requires these to operate
+ properly.
+
+
+4.1.1) Compiling third-party libraries
+--------------------------------------
+Most libraries used can be compiled with same commands and configuration flags.
+
+It is assumed that you have these environment variables defined:
+ - DEVKITPRO Your root devkitPro directory
+ - DEVKITARM Your root devkitARM directory (probably same as $DEVKITPRO/devkitARM)
+ - CTRULIB Your root libctru directory (probably same as $DEVKITPRO/libctru)
+
+In the source directory of the library:
+ - $ export PORTLIBS=$DEVKITPRO/portlibs/armv6k
+ - $ export PATH=$DEVKITARM/bin:$PATH
+ - $ export PKG_CONFIG_PATH=$PORTLIBS/lib/pkgconfig
+ - $ export CFLAGS="-g -march=armv6k -mtune=mpcore -mfloat-abi=hard -O2
+ -mword-relocations -ffunction-sections -fdata-sections"
+ - $ export CPPFLAGS="-I$PORTLIBS/include -I$CTRULIB/include"
+ - $ export LDFLAGS="-L$PORTLIBS/lib"
+ - $ mkdir -p $PORTLIBS
+ - $ ./configure --prefix=$PORTLIBS --host=arm-none-eabi --disable-shared
+ --enable-static
+ - $ make
+ - $ make install
+
+Useful libraries (and special config flags needed):
+ - zlib
+ - libpng
+ - libjpeg
+ - freetype2 --without-bzip2 --without-harfbuzz
+ - libmad
+ - tremor
+ - flac --disable-cpplibs --without-flac
+ - faad
+
+
+4.2) Compiling ScummVM
+----------------------
+ - $ ./configure --host=3ds
+ - $ make
+
+Additionally compile to specific formats to be used on the 3ds:
+ - $ make scummvm.3dsx
+ - $ make scummvm.cia
+
+
+4.3) Warning for 3DSX build
+---------------------------
+The above configuration command will include all game engines by default and will
+likely be too massive to run using the 3DSX format. Until dynamic modules are figured
+out, you should configure engines like this for 3DSX builds:
+
+ - $ ./configure --host=3ds --disable-all-engines--enable-engine=scumm-7-8,myst,riven,
+ sword1,sword2,sword25,sci,lure,sky,agi,agos
+
+Choose whatever engines you want, but if the ELF's .text section exceeds ~10MB, it
+won't be playable unless it's a CIA.
diff --git a/backends/platform/3ds/app/banner.png b/backends/platform/3ds/app/banner.png
new file mode 100644
index 0000000000..a3b02150ec
--- /dev/null
+++ b/backends/platform/3ds/app/banner.png
Binary files differ
diff --git a/backends/platform/3ds/app/banner.wav b/backends/platform/3ds/app/banner.wav
new file mode 100644
index 0000000000..e0b684b62f
--- /dev/null
+++ b/backends/platform/3ds/app/banner.wav
Binary files differ
diff --git a/backends/platform/3ds/app/icon.png b/backends/platform/3ds/app/icon.png
new file mode 100644
index 0000000000..07022fbac1
--- /dev/null
+++ b/backends/platform/3ds/app/icon.png
Binary files differ
diff --git a/backends/platform/3ds/app/scummvm.rsf b/backends/platform/3ds/app/scummvm.rsf
new file mode 100644
index 0000000000..a4518949bb
--- /dev/null
+++ b/backends/platform/3ds/app/scummvm.rsf
@@ -0,0 +1,219 @@
+BasicInfo:
+ Title : ScummVM
+ ProductCode : ScummVM
+ Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
+
+TitleInfo:
+ Category : Application
+ UniqueId : 0xFF321
+
+Option:
+ UseOnSD : true # true if App is to be installed to SD
+ FreeProductCode : true # Removes limitations on ProductCode
+ MediaFootPadding : false # If true CCI files are created with padding
+ EnableCrypt : false # Enables encryption for NCCH and CIA
+ EnableCompress : false # Compresses where applicable (currently only exefs:/.code)
+
+AccessControlInfo:
+ CoreVersion : 2
+
+ # Exheader Format Version
+ DescVersion : 2
+
+ # Minimum Required Kernel Version (below is for 4.5.0)
+ ReleaseKernelMajor : "02"
+ ReleaseKernelMinor : "33"
+
+ # ExtData
+ UseExtSaveData : false # enables ExtData
+ #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
+
+ # FS:USER Archive Access Permissions
+ # Uncomment as required
+ FileSystemAccess:
+ #- CategorySystemApplication
+ #- CategoryHardwareCheck
+ #- CategoryFileSystemTool
+ #- Debug
+ #- TwlCardBackup
+ #- TwlNandData
+ #- Boss
+ - DirectSdmc
+ #- Core
+ #- CtrNandRo
+ #- CtrNandRw
+ #- CtrNandRoWrite
+ #- CategorySystemSettings
+ #- CardBoard
+ #- ExportImportIvs
+ #- DirectSdmcWrite
+ #- SwitchCleanup
+ #- SaveDataMove
+ #- Shop
+ #- Shell
+ #- CategoryHomeMenu
+
+ # Process Settings
+ MemoryType : Application # Application/System/Base
+ SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB
+ IdealProcessor : 0
+ AffinityMask : 1
+ Priority : 16
+ MaxCpu : 0 # Let system decide
+ HandleTableSize : 0x200
+ DisableDebug : false
+ EnableForceDebug : false
+ CanWriteSharedPage : true
+ CanUsePrivilegedPriority : false
+ CanUseNonAlphabetAndNumber : true
+ PermitMainFunctionArgument : true
+ CanShareDeviceMemory : true
+ RunnableOnSleep : false
+ SpecialMemoryArrange : true
+
+ # New3DS Exclusive Process Settings
+ SystemModeExt : 124MB # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
+ CpuSpeed : 804MHz # 268MHz(Default)/804MHz
+ EnableL2Cache : true # false(default)/true
+ CanAccessCore2 : true
+
+ # Virtual Address Mappings
+ IORegisterMapping:
+ - 1ff00000-1ff7ffff # DSP memory
+ MemoryMapping:
+ - 1f000000-1f5fffff:r # VRAM
+
+ # Accessible SVCs, <Name>:<ID>
+ SystemCallAccess:
+ ArbitrateAddress: 34
+ Break: 60
+ CancelTimer: 28
+ ClearEvent: 25
+ ClearTimer: 29
+ CloseHandle: 35
+ ConnectToPort: 45
+ ControlMemory: 1
+ CreateAddressArbiter: 33
+ CreateEvent: 23
+ CreateMemoryBlock: 30
+ CreateMutex: 19
+ CreateSemaphore: 21
+ CreateThread: 8
+ CreateTimer: 26
+ DuplicateHandle: 39
+ ExitProcess: 3
+ ExitThread: 9
+ GetCurrentProcessorNumber: 17
+ GetHandleInfo: 41
+ GetProcessId: 53
+ GetProcessIdOfThread: 54
+ GetProcessIdealProcessor: 6
+ GetProcessInfo: 43
+ GetResourceLimit: 56
+ GetResourceLimitCurrentValues: 58
+ GetResourceLimitLimitValues: 57
+ GetSystemInfo: 42
+ GetSystemTick: 40
+ GetThreadContext: 59
+ GetThreadId: 55
+ GetThreadIdealProcessor: 15
+ GetThreadInfo: 44
+ GetThreadPriority: 11
+ MapMemoryBlock: 31
+ OutputDebugString: 61
+ QueryMemory: 2
+ ReleaseMutex: 20
+ ReleaseSemaphore: 22
+ SendSyncRequest1: 46
+ SendSyncRequest2: 47
+ SendSyncRequest3: 48
+ SendSyncRequest4: 49
+ SendSyncRequest: 50
+ SetThreadPriority: 12
+ SetTimer: 27
+ SignalEvent: 24
+ SleepThread: 10
+ UnmapMemoryBlock: 32
+ WaitSynchronization1: 36
+ WaitSynchronizationN: 37
+ Backdoor: 123
+
+ # Service List
+ # Maximum 34 services (32 if firmware is prior to 9.3.0)
+ ServiceAccessControl:
+ - cfg:u
+ - fs:USER
+ - gsp::Gpu
+ - hid:USER
+ - ndm:u
+ - pxi:dev
+ - APT:U
+ - ac:u
+ - act:u
+ - am:net
+ - boss:U
+ - cam:u
+ - cecd:u
+ - dsp::DSP
+ - frd:u
+ - http:C
+ - ir:USER
+ - ir:u
+ - ir:rst
+ - ldr:ro
+ - mic:u
+ - news:u
+ - nim:aoc
+ - nwm::UDS
+ - ptm:u
+ - qtm:u
+ - soc:U
+ - ssl:C
+ - y2r:u
+ - gsp::Lcd
+
+
+SystemControlInfo:
+ SaveDataSize: 0K
+ RemasterVersion: 0
+ StackSize: 0x40000
+
+ # Modules that run services listed above should be included below
+ # Maximum 48 dependencies
+ # If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves)
+ # So act, nfc and qtm are commented for 4.x support. Uncomment if you need these.
+ # <module name>:<module titleid>
+ Dependency:
+ ac: 0x0004013000002402
+ #act: 0x0004013000003802
+ am: 0x0004013000001502
+ boss: 0x0004013000003402
+ camera: 0x0004013000001602
+ cecd: 0x0004013000002602
+ cfg: 0x0004013000001702
+ codec: 0x0004013000001802
+ csnd: 0x0004013000002702
+ dlp: 0x0004013000002802
+ dsp: 0x0004013000001a02
+ friends: 0x0004013000003202
+ gpio: 0x0004013000001b02
+ gsp: 0x0004013000001c02
+ hid: 0x0004013000001d02
+ http: 0x0004013000002902
+ i2c: 0x0004013000001e02
+ ir: 0x0004013000003302
+ mcu: 0x0004013000001f02
+ mic: 0x0004013000002002
+ ndm: 0x0004013000002b02
+ news: 0x0004013000003502
+ #nfc: 0x0004013000004002
+ nim: 0x0004013000002c02
+ nwm: 0x0004013000002d02
+ pdn: 0x0004013000002102
+ ps: 0x0004013000003102
+ ptm: 0x0004013000002202
+ #qtm: 0x0004013020004202
+ ro: 0x0004013000003702
+ socket: 0x0004013000002e02
+ spi: 0x0004013000002302
+ ssl: 0x0004013000002f02
diff --git a/backends/platform/3ds/config.cpp b/backends/platform/3ds/config.cpp
new file mode 100644
index 0000000000..117b979d9f
--- /dev/null
+++ b/backends/platform/3ds/config.cpp
@@ -0,0 +1,87 @@
+/* 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 "config.h"
+#include "osystem.h"
+#include "options-dialog.h"
+#include "common/config-manager.h"
+#include <3ds.h>
+
+namespace _3DS {
+
+Config config;
+static Common::String prefix = "3ds_";
+
+static bool confGetBool(Common::String key, bool defaultVal) {
+ if (ConfMan.hasKey(prefix + key))
+ return ConfMan.getBool(prefix + key);
+ return defaultVal;
+}
+
+static void confSetBool(Common::String key, bool val) {
+ ConfMan.setBool(prefix + key, val);
+}
+
+static int confGetInt(Common::String key, int defaultVal) {
+ if (ConfMan.hasKey(prefix + key))
+ return ConfMan.getInt(prefix + key);
+ return defaultVal;
+}
+
+static void confSetInt(Common::String key, int val) {
+ ConfMan.setInt(prefix + key, val);
+}
+
+void loadConfig() {
+ config.showCursor = confGetBool("showcursor", true);
+ config.snapToBorder = confGetBool("snaptoborder", true);
+ config.stretchToFit = confGetBool("stretchtofit", false);
+ config.sensitivity = confGetInt("sensitivity", -5);
+ config.screen = confGetInt("screen", kScreenBoth);
+
+ // Turn off the backlight of any screen not used
+ if (R_SUCCEEDED(gspLcdInit())) {
+ if (config.screen == kScreenTop) {
+ GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_TOP);
+ GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM);
+ } else if (config.screen == kScreenBottom) {
+ GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTTOM);
+ GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP);
+ } else
+ GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
+ gspLcdExit();
+ }
+
+ OSystem_3DS *osys = (OSystem_3DS *)g_system;
+ osys->updateConfig();
+}
+
+void saveConfig() {
+ confSetBool("showcursor", config.showCursor);
+ confSetBool("snaptoborder", config.snapToBorder);
+ confSetBool("stretchtofit", config.stretchToFit);
+ confSetInt("sensitivity", config.sensitivity);
+ confSetInt("screen", config.screen);
+ ConfMan.flushToDisk();
+}
+
+} // namespace _3DS
diff --git a/backends/graphics/opengl/extensions.h b/backends/platform/3ds/config.h
index 87452429e2..c8b75736ad 100644
--- a/backends/graphics/opengl/extensions.h
+++ b/backends/platform/3ds/config.h
@@ -20,22 +20,26 @@
*
*/
-#ifndef BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
-#define BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
+#ifndef CONFIG_3DS_H
+#define CONFIG_3DS_H
-namespace OpenGL {
+#include "common/str.h"
-/**
- * Checks for availability of extensions we want to use and initializes them
- * when available.
- */
-void initializeGLExtensions();
+namespace _3DS {
+
+struct Config {
+ bool showCursor;
+ bool snapToBorder;
+ bool stretchToFit;
+ int sensitivity;
+ int screen;
+};
-/**
- * Whether non power of two textures are supported
- */
-extern bool g_extNPOTSupported;
+extern Config config;
+
+void loadConfig();
+void saveConfig();
-} // End of namespace OpenGL
+} // namespace _3DS
-#endif
+#endif // CONFIG_3DS_H
diff --git a/backends/platform/3ds/gui.cpp b/backends/platform/3ds/gui.cpp
new file mode 100644
index 0000000000..0883d5a102
--- /dev/null
+++ b/backends/platform/3ds/gui.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/platform/3ds/gui.h"
+#include "common/system.h"
+
+StatusMessageDialog* StatusMessageDialog::_opened = 0;
+
+StatusMessageDialog::StatusMessageDialog(const Common::String &message, uint32 duration)
+ : MessageDialog(message, 0, 0) {
+ _timer = g_system->getMillis() + duration;
+ if (_opened)
+ _opened->close();
+ _opened = this;
+}
+
+void StatusMessageDialog::handleTickle() {
+ MessageDialog::handleTickle();
+ if (g_system->getMillis() > _timer)
+ close();
+}
+
+void StatusMessageDialog::close() {
+ GUI::Dialog::close();
+ if (_opened)
+ _opened = 0;
+}
diff --git a/backends/platform/3ds/gui.h b/backends/platform/3ds/gui.h
new file mode 100644
index 0000000000..66c6547139
--- /dev/null
+++ b/backends/platform/3ds/gui.h
@@ -0,0 +1,41 @@
+/* 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 GUI_3DS_H
+#define GUI_3DS_H
+
+#include "gui/message.h"
+
+class StatusMessageDialog : public GUI::MessageDialog {
+public:
+ StatusMessageDialog(const Common::String &message, uint32 duration);
+
+ void handleTickle();
+
+protected:
+ virtual void close();
+
+ uint32 _timer;
+ static StatusMessageDialog* _opened;
+};
+
+#endif // GUI_3DS_H
diff --git a/backends/platform/3ds/main.cpp b/backends/platform/3ds/main.cpp
new file mode 100644
index 0000000000..6cc2c5cf5d
--- /dev/null
+++ b/backends/platform/3ds/main.cpp
@@ -0,0 +1,54 @@
+/* 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 "osystem.h"
+#include <3ds.h>
+
+int main(int argc, char *argv[]) {
+ // Initialize basic libctru stuff
+ gfxInitDefault();
+ cfguInit();
+ osSetSpeedupEnable(true);
+// consoleInit(GFX_TOP, NULL);
+
+ g_system = new _3DS::OSystem_3DS();
+ assert(g_system);
+
+ // Invoke the actual ScummVM main entry point
+// if (argc > 2)
+// res = scummvm_main(argc-2, &argv[2]);
+// else
+// res = scummvm_main(argc, argv);
+ scummvm_main(0, nullptr);
+
+ delete dynamic_cast<_3DS::OSystem_3DS*>(g_system);
+
+ // Turn on both screen backlights before exiting.
+ if (R_SUCCEEDED(gspLcdInit())) {
+ GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
+ gspLcdExit();
+ }
+
+ cfguExit();
+ gfxExit();
+ return 0;
+}
diff --git a/backends/platform/3ds/module.mk b/backends/platform/3ds/module.mk
new file mode 100644
index 0000000000..3eb15aef81
--- /dev/null
+++ b/backends/platform/3ds/module.mk
@@ -0,0 +1,18 @@
+MODULE := backends/platform/3ds
+
+MODULE_OBJS := \
+ main.o \
+ shader.shbin.o \
+ sprite.o \
+ gui.o \
+ config.o \
+ options-dialog.o \
+ osystem.o \
+ osystem-graphics.o \
+ osystem-audio.o \
+ osystem-events.o
+
+# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
+MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
+OBJS := $(MODULE_OBJS) $(OBJS)
+MODULE_DIRS += $(sort $(dir $(MODULE_OBJS)))
diff --git a/backends/platform/3ds/options-dialog.cpp b/backends/platform/3ds/options-dialog.cpp
new file mode 100644
index 0000000000..0f8bfd0c66
--- /dev/null
+++ b/backends/platform/3ds/options-dialog.cpp
@@ -0,0 +1,98 @@
+/* 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 "options-dialog.h"
+#include "config.h"
+#include "gui/dialog.h"
+#include "gui/gui-manager.h"
+#include "gui/widgets/list.h"
+#include "gui/widgets/tab.h"
+#include "osystem.h"
+#include "engines/scumm/scumm.h"
+#include "gui/widgets/popup.h"
+
+#include "common/translation.h"
+
+namespace _3DS {
+
+bool optionMenuOpened = false;
+
+OptionsDialog::OptionsDialog() : GUI::Dialog(20, 20, 280, 200) {
+
+ optionMenuOpened = true;
+
+ new GUI::ButtonWidget(this, 120, 180, 72, 16, _("~C~lose"), 0, GUI::kCloseCmd);
+ new GUI::ButtonWidget(this, 200, 180, 72, 16, _("~S~ave"), 0, GUI::kOKCmd);
+
+ _showCursorCheckbox = new GUI::CheckboxWidget(this, 5, 5, 130, 20, _("Show mouse cursor"), 0, 0, 'T');
+ _showCursorCheckbox->setState(config.showCursor);
+
+ _snapToBorderCheckbox = new GUI::CheckboxWidget(this, 5, 22, 130, 20, _("Snap to edges"), 0, 0, 'T');
+ _snapToBorderCheckbox->setState(config.snapToBorder);
+
+ _stretchToFitCheckbox = new GUI::CheckboxWidget(this, 140, 5, 130, 20, _("Stretch to fit"), 0, 0, 'T');
+ _stretchToFitCheckbox->setState(config.stretchToFit);
+
+ new GUI::StaticTextWidget(this, 0, 60, 110, 15, _("Use Screen:"), Graphics::kTextAlignRight);
+ _screenRadioGroup = new GUI::RadiobuttonGroup(this, kScreenRadioGroup);
+ _screenTopRadioWidget = new GUI::RadiobuttonWidget(this, 120, 50, 60, 20, _screenRadioGroup, kScreenTop, _("Top"));
+ _screenBottomRadioWidget = new GUI::RadiobuttonWidget(this, 190, 50, 80, 20, _screenRadioGroup, kScreenBottom, _("Bottom"));
+ _screenBothRadioWidget = new GUI::RadiobuttonWidget(this, 155, 70, 80, 20, _screenRadioGroup, kScreenBoth, _("Both"));
+ _screenRadioGroup->setValue(config.screen);
+
+ new GUI::StaticTextWidget(this, 0, 100, 110, 15, _("C-Pad Sensitivity:"), Graphics::kTextAlignRight);
+ _sensitivity = new GUI::SliderWidget(this, 115, 100, 160, 15, "TODO: Add tooltip", 1);
+ _sensitivity->setMinValue(-15);
+ _sensitivity->setMaxValue(30);
+ _sensitivity->setValue(config.sensitivity);
+ _sensitivity->setFlags(GUI::WIDGET_CLEARBG);
+}
+
+OptionsDialog::~OptionsDialog() {
+ optionMenuOpened = false;
+}
+
+void OptionsDialog::updateConfigManager() {
+ config.showCursor = _showCursorCheckbox->getState();
+ config.snapToBorder = _snapToBorderCheckbox->getState();
+ config.stretchToFit = _stretchToFitCheckbox->getState();
+ config.sensitivity = _sensitivity->getValue();
+ config.screen = _screenRadioGroup->getValue();
+ saveConfig();
+ loadConfig();
+}
+
+void OptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ switch(cmd) {
+ case GUI::kOKCmd:
+ updateConfigManager();
+ // Fall through
+ case GUI::kCloseCmd:
+ close();
+ break;
+ default:
+ Dialog::handleCommand(sender, cmd, data);
+ break;
+ }
+}
+
+} // namespace _3DS
diff --git a/backends/platform/3ds/options-dialog.h b/backends/platform/3ds/options-dialog.h
new file mode 100644
index 0000000000..6673b88e7b
--- /dev/null
+++ b/backends/platform/3ds/options-dialog.h
@@ -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.
+ *
+ */
+
+#ifndef OPTIONS_DIALOG_3DS_H
+#define OPTIONS_DIALOG_3DS_H
+
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "gui/object.h"
+#include "gui/widget.h"
+#include "gui/dialog.h"
+#include "gui/widgets/tab.h"
+#include "scumm/dialogs.h"
+
+namespace _3DS {
+
+enum {
+ kSave = 0x10000000,
+ kScreenRadioGroup,
+ kScreenTop,
+ kScreenBottom,
+ kScreenBoth,
+};
+
+extern bool optionMenuOpened;
+
+class OptionsDialog : public GUI::Dialog {
+
+public:
+ OptionsDialog();
+ ~OptionsDialog();
+
+protected:
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+ void updateConfigManager();
+
+ GUI::SliderWidget *_sensitivity;
+ GUI::CheckboxWidget *_showCursorCheckbox;
+ GUI::CheckboxWidget *_snapToBorderCheckbox;
+ GUI::CheckboxWidget *_stretchToFitCheckbox;
+
+ GUI::RadiobuttonGroup *_screenRadioGroup;
+ GUI::RadiobuttonWidget *_screenTopRadioWidget;
+ GUI::RadiobuttonWidget *_screenBottomRadioWidget;
+ GUI::RadiobuttonWidget *_screenBothRadioWidget;
+};
+
+} // namespace _3DS
+
+#endif // OPTIONS_DIALOG_3DS_H
diff --git a/backends/platform/3ds/osystem-audio.cpp b/backends/platform/3ds/osystem-audio.cpp
new file mode 100644
index 0000000000..17e419c36d
--- /dev/null
+++ b/backends/platform/3ds/osystem-audio.cpp
@@ -0,0 +1,110 @@
+/* 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 "osystem.h"
+#include "audio/mixer.h"
+
+namespace _3DS {
+
+static bool hasAudio = false;
+
+static void audioThreadFunc(void *arg) {
+ Audio::MixerImpl *mixer = (Audio::MixerImpl *)arg;
+ OSystem_3DS *osys = (OSystem_3DS *)g_system;
+
+ int i;
+ const int channel = 0;
+ int bufferIndex = 0;
+ const int bufferCount = 3;
+ const int bufferSize = 80000; // Can't be too small, based on delayMillis duration
+ const int sampleRate = mixer->getOutputRate();
+ int sampleLen = 0;
+ uint32 lastTime = osys->getMillis(true);
+ uint32 time = lastTime;
+ ndspWaveBuf buffers[bufferCount];
+
+ for (i = 0; i < bufferCount; ++i) {
+ memset(&buffers[i], 0, sizeof(ndspWaveBuf));
+ buffers[i].data_vaddr = linearAlloc(bufferSize);
+ buffers[i].looping = false;
+ buffers[i].status = NDSP_WBUF_FREE;
+ }
+
+ ndspChnReset(channel);
+ ndspChnSetInterp(channel, NDSP_INTERP_LINEAR);
+ ndspChnSetRate(channel, sampleRate);
+ ndspChnSetFormat(channel, NDSP_FORMAT_STEREO_PCM16);
+
+ while (!osys->exiting) {
+ osys->delayMillis(100); // Note: Increasing the delay requires a bigger buffer
+
+ time = osys->getMillis(true);
+ sampleLen = (time - lastTime) * 22 * 4; // sampleRate / 1000 * channelCount * sizeof(int16);
+ lastTime = time;
+
+ if (!osys->sleeping && sampleLen > 0) {
+ bufferIndex++;
+ bufferIndex %= bufferCount;
+ ndspWaveBuf *buf = &buffers[bufferIndex];
+
+ buf->nsamples = mixer->mixCallback(buf->data_adpcm, sampleLen);
+ if (buf->nsamples > 0) {
+ DSP_FlushDataCache(buf->data_vaddr, bufferSize);
+ ndspChnWaveBufAdd(channel, buf);
+ }
+ }
+ }
+
+ for (i = 0; i < bufferCount; ++i)
+ linearFree(buffers[i].data_pcm8);
+}
+
+void OSystem_3DS::initAudio() {
+ _mixer = new Audio::MixerImpl(this, 22050);
+
+ hasAudio = R_SUCCEEDED(ndspInit());
+ _mixer->setReady(false);
+
+ if (hasAudio) {
+ s32 prio = 0;
+ svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
+ audioThread = threadCreate(&audioThreadFunc, _mixer, 32 * 1048, prio - 1, -2, false);
+ }
+}
+
+void OSystem_3DS::destroyAudio() {
+ if (hasAudio) {
+ threadJoin(audioThread, U64_MAX);
+ threadFree(audioThread);
+ ndspExit();
+ }
+
+ delete _mixer;
+ _mixer = 0;
+}
+
+Audio::Mixer *OSystem_3DS::getMixer() {
+ assert(_mixer);
+ return _mixer;
+}
+
+} // namespace _3DS
diff --git a/backends/platform/3ds/osystem-events.cpp b/backends/platform/3ds/osystem-events.cpp
new file mode 100644
index 0000000000..ae8a9b8b2b
--- /dev/null
+++ b/backends/platform/3ds/osystem-events.cpp
@@ -0,0 +1,302 @@
+/* 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/timer/default/default-timer.h"
+#include "engines/engine.h"
+#include "gui.h"
+#include "options-dialog.h"
+#include "config.h"
+#include "osystem.h"
+
+namespace _3DS {
+
+static Common::Mutex *eventMutex;
+static InputMode inputMode = MODE_DRAG;
+static aptHookCookie cookie;
+static bool optionMenuOpening = false;
+static Common::String messageOSD;
+static bool showMessageOSD = false;
+
+static void pushEventQueue(Common::Queue<Common::Event> *queue, Common::Event &event) {
+ Common::StackLock lock(*eventMutex);
+ queue->push(event);
+}
+
+static void eventThreadFunc(void *arg) {
+ OSystem_3DS *osys = (OSystem_3DS *)g_system;
+ auto eventQueue = (Common::Queue<Common::Event> *)arg;
+
+ uint32 touchStartTime = osys->getMillis();
+ touchPosition lastTouch = {0, 0};
+ bool isRightClick = false;
+ float cursorX = 0;
+ float cursorY = 0;
+ float cursorDeltaX = 0;
+ float cursorDeltaY = 0;
+ int circleDeadzone = 20;
+ int borderSnapZone = 6;
+ Common::Event event;
+
+ while (!osys->exiting) {
+ do {
+ osys->delayMillis(10);
+ } while (osys->sleeping && !osys->exiting);
+
+ hidScanInput();
+ touchPosition touch;
+ circlePosition circle;
+ u32 held = hidKeysHeld();
+ u32 keysPressed = hidKeysDown();
+ u32 keysReleased = hidKeysUp();
+
+ // C-Pad used to control the cursor
+ hidCircleRead(&circle);
+ if (circle.dx < circleDeadzone && circle.dx > -circleDeadzone)
+ circle.dx = 0;
+ if (circle.dy < circleDeadzone && circle.dy > -circleDeadzone)
+ circle.dy = 0;
+ cursorDeltaX = (0.0002f + config.sensitivity / 100000.f) * circle.dx * abs(circle.dx);
+ cursorDeltaY = (0.0002f + config.sensitivity / 100000.f) * circle.dy * abs(circle.dy);
+
+ // Touch screen events
+ if (held & KEY_TOUCH) {
+ hidTouchRead(&touch);
+ if (config.snapToBorder) {
+ if (touch.px < borderSnapZone)
+ touch.px = 0;
+ if (touch.px > 319 - borderSnapZone)
+ touch.px = 319;
+ if (touch.py < borderSnapZone)
+ touch.py = 0;
+ if (touch.py > 239 - borderSnapZone)
+ touch.py = 239;
+ }
+ cursorX = touch.px;
+ cursorY = touch.py;
+ osys->transformPoint(touch);
+
+ osys->warpMouse(touch.px, touch.py);
+ event.mouse.x = touch.px;
+ event.mouse.y = touch.py;
+
+ if (keysPressed & KEY_TOUCH) {
+ touchStartTime = osys->getMillis();
+ isRightClick = (held & KEY_X || held & KEY_DUP);
+ if (inputMode == MODE_DRAG) {
+ event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN;
+ pushEventQueue(eventQueue, event);
+ }
+ } else if (touch.px != lastTouch.px || touch.py != lastTouch.py) {
+ event.type = Common::EVENT_MOUSEMOVE;
+ pushEventQueue(eventQueue, event);
+ }
+
+ lastTouch = touch;
+ } else if (keysReleased & KEY_TOUCH) {
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ if (inputMode == MODE_DRAG) {
+ event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ } else if (osys->getMillis() - touchStartTime < 200) {
+ // Process click in MODE_HOVER
+ event.type = Common::EVENT_MOUSEMOVE;
+ pushEventQueue(eventQueue, event);
+ event.type = isRightClick ? Common::EVENT_RBUTTONDOWN : Common::EVENT_LBUTTONDOWN;
+ pushEventQueue(eventQueue, event);
+ event.type = isRightClick ? Common::EVENT_RBUTTONUP : Common::EVENT_LBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ } else if (cursorDeltaX != 0 || cursorDeltaY != 0) {
+ cursorX += cursorDeltaX;
+ cursorY -= cursorDeltaY;
+ if (cursorX < 0) cursorX = 0;
+ if (cursorY < 0) cursorY = 0;
+ if (cursorX > 320) cursorX = 320;
+ if (cursorY > 240) cursorY = 240;
+ lastTouch.px = cursorX;
+ lastTouch.py = cursorY;
+ osys->transformPoint(lastTouch);
+ osys->warpMouse(lastTouch.px, lastTouch.py);
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ event.type = Common::EVENT_MOUSEMOVE;
+ pushEventQueue(eventQueue, event);
+ }
+
+ // Button events
+ if (keysPressed & KEY_R) {
+ if (inputMode == MODE_DRAG) {
+ inputMode = MODE_HOVER;
+ osys->displayMessageOnOSD("Hover Mode");
+ } else {
+ inputMode = MODE_DRAG;
+ osys->displayMessageOnOSD("Drag Mode");
+ }
+ }
+ if (keysPressed & KEY_A || keysPressed & KEY_DLEFT || keysReleased & KEY_A || keysReleased & KEY_DLEFT) {
+ // SIMULATE LEFT CLICK
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ if (keysPressed & KEY_A || keysPressed & KEY_DLEFT)
+ event.type = Common::EVENT_LBUTTONDOWN;
+ else
+ event.type = Common::EVENT_LBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_X || keysPressed & KEY_DUP || keysReleased & KEY_X || keysReleased & KEY_DUP) {
+ // SIMULATE RIGHT CLICK
+ event.mouse.x = lastTouch.px;
+ event.mouse.y = lastTouch.py;
+ if (keysPressed & KEY_X || keysPressed & KEY_DUP)
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else
+ event.type = Common::EVENT_RBUTTONUP;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_L) {
+ event.type = Common::EVENT_VIRTUAL_KEYBOARD;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_START) {
+ event.type = Common::EVENT_MAINMENU;
+ pushEventQueue(eventQueue, event);
+ }
+ if (keysPressed & KEY_SELECT) {
+ if (!optionMenuOpened)
+ optionMenuOpening = true;
+ }
+ if (keysPressed & KEY_B || keysReleased & KEY_B || keysPressed & KEY_DDOWN || keysReleased & KEY_DDOWN) {
+ if (keysPressed & KEY_B || keysPressed & KEY_DDOWN)
+ event.type = Common::EVENT_KEYDOWN;
+ else
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = Common::ASCII_ESCAPE;
+ event.kbd.flags = 0;
+ pushEventQueue(eventQueue, event);
+ }
+
+ // TODO: EVENT_PREDICTIVE_DIALOG
+ // EVENT_SCREEN_CHANGED
+ }
+}
+
+static void aptHookFunc(APT_HookType hookType, void *param) {
+ OSystem_3DS *osys = (OSystem_3DS *)g_system;
+
+ switch (hookType) {
+ case APTHOOK_ONSUSPEND:
+ case APTHOOK_ONSLEEP:
+ if (g_engine)
+ g_engine->pauseEngine(true);
+ osys->sleeping = true;
+ if (R_SUCCEEDED(gspLcdInit())) {
+ GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
+ gspLcdExit();
+ }
+ break;
+ case APTHOOK_ONRESTORE:
+ case APTHOOK_ONWAKEUP:
+ if (g_engine)
+ g_engine->pauseEngine(false);
+ osys->sleeping = false;
+ loadConfig();
+ break;
+ default: {
+ Common::StackLock lock(*eventMutex);
+ Common::Event event;
+ event.type = Common::EVENT_QUIT;
+ g_system->getEventManager()->pushEvent(event);
+ }
+ }
+}
+
+static void timerThreadFunc(void *arg) {
+ OSystem_3DS *osys = (OSystem_3DS *)arg;
+ DefaultTimerManager *tm = (DefaultTimerManager *)osys->getTimerManager();
+ while (!osys->exiting) {
+ g_system->delayMillis(10);
+ tm->handler();
+ }
+}
+
+void OSystem_3DS::initEvents() {
+ eventMutex = new Common::Mutex();
+ s32 prio = 0;
+ svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
+ _timerThread = threadCreate(&timerThreadFunc, this, 32 * 1024, prio - 1, -2, false);
+ _eventThread = threadCreate(&eventThreadFunc, &_eventQueue, 32 * 1024, prio - 1, -2, false);
+
+ aptHook(&cookie, aptHookFunc, this);
+}
+
+void OSystem_3DS::destroyEvents() {
+ threadJoin(_timerThread, U64_MAX);
+ threadFree(_timerThread);
+
+ threadJoin(_eventThread, U64_MAX);
+ threadFree(_eventThread);
+ delete eventMutex;
+}
+
+void OSystem_3DS::transformPoint(touchPosition &point) {
+ if (!_overlayVisible) {
+ point.px = static_cast<float>(point.px) / _gameBottomTexture.getScaleX() - _gameBottomX;
+ point.py = static_cast<float>(point.py) / _gameBottomTexture.getScaleY() - _gameBottomY;
+ }
+}
+
+void OSystem_3DS::displayMessageOnOSD(const char *msg) {
+ messageOSD = msg;
+ showMessageOSD = true;
+}
+
+bool OSystem_3DS::pollEvent(Common::Event &event) {
+ if (showMessageOSD) {
+ showMessageOSD = false;
+ StatusMessageDialog dialog(messageOSD, 800);
+ dialog.runModal();
+ }
+
+ aptMainLoop(); // Call apt hook when necessary
+
+ if (optionMenuOpening) {
+ optionMenuOpening = false;
+ OptionsDialog dialog;
+ if (g_engine)
+ g_engine->pauseEngine(true);
+ dialog.runModal();
+ if (g_engine)
+ g_engine->pauseEngine(false);
+ }
+
+ Common::StackLock lock(*eventMutex);
+
+ if (_eventQueue.empty())
+ return false;
+
+ event = _eventQueue.pop();
+ return true;
+}
+
+} // namespace _3DS
diff --git a/backends/platform/3ds/osystem-graphics.cpp b/backends/platform/3ds/osystem-graphics.cpp
new file mode 100644
index 0000000000..0cfd70c9cd
--- /dev/null
+++ b/backends/platform/3ds/osystem-graphics.cpp
@@ -0,0 +1,517 @@
+/* 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/platform/3ds/osystem.h"
+#include "backends/platform/3ds/shader_shbin.h"
+#include "common/rect.h"
+#include "options-dialog.h"
+#include "config.h"
+
+// Used to transfer the final rendered display to the framebuffer
+#define DISPLAY_TRANSFER_FLAGS \
+ (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | \
+ GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | \
+ GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \
+ GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+
+namespace _3DS {
+
+void OSystem_3DS::initGraphics() {
+ _pfGame = Graphics::PixelFormat::createFormatCLUT8();
+ _pfGameTexture = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+
+ C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
+
+ // Initialize the render targets
+ _renderTargetTop =
+ C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
+ C3D_RenderTargetSetClear(_renderTargetTop, C3D_CLEAR_ALL, 0x0000000, 0);
+ C3D_RenderTargetSetOutput(_renderTargetTop, GFX_TOP, GFX_LEFT,
+ DISPLAY_TRANSFER_FLAGS);
+
+ _renderTargetBottom =
+ C3D_RenderTargetCreate(240, 320, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8);
+ C3D_RenderTargetSetClear(_renderTargetBottom, C3D_CLEAR_ALL, 0x00000000, 0);
+ C3D_RenderTargetSetOutput(_renderTargetBottom, GFX_BOTTOM, GFX_LEFT,
+ DISPLAY_TRANSFER_FLAGS);
+
+ // Load and bind simple default shader (shader.v.pica)
+ _dvlb = DVLB_ParseFile((u32*)shader_shbin, shader_shbin_size);
+ shaderProgramInit(&_program);
+ shaderProgramSetVsh(&_program, &_dvlb->DVLE[0]);
+ C3D_BindProgram(&_program);
+
+ _projectionLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "projection");
+ _modelviewLocation = shaderInstanceGetUniformLocation(_program.vertexShader, "modelView");
+
+ C3D_AttrInfo *attrInfo = C3D_GetAttrInfo();
+ AttrInfo_Init(attrInfo);
+ AttrInfo_AddLoader(attrInfo, 0, GPU_FLOAT, 3); // v0=position
+ AttrInfo_AddLoader(attrInfo, 1, GPU_FLOAT, 2); // v1=texcoord
+
+ Mtx_OrthoTilt(&_projectionTop, 0.0, 400.0, 240.0, 0.0, 0.0, 1.0);
+ Mtx_OrthoTilt(&_projectionBottom, 0.0, 320.0, 240.0, 0.0, 0.0, 1.0);
+
+ C3D_TexEnv *env = C3D_GetTexEnv(0);
+ C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, 0, 0);
+ C3D_TexEnvOp(env, C3D_Both, 0, 0, 0);
+ C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE);
+
+ C3D_DepthTest(false, GPU_GEQUAL, GPU_WRITE_ALL);
+ C3D_CullFace(GPU_CULL_NONE);
+}
+
+void OSystem_3DS::destroyGraphics() {
+ _gameScreen.free();
+ _gameTopTexture.free();
+ _gameBottomTexture.free();
+ _overlay.free();
+
+ shaderProgramFree(&_program);
+ DVLB_Free(_dvlb);
+
+ C3D_RenderTargetDelete(_renderTargetTop);
+ C3D_RenderTargetDelete(_renderTargetBottom);
+
+ C3D_Fini();
+}
+
+bool OSystem_3DS::hasFeature(OSystem::Feature f) {
+ return (f == OSystem::kFeatureCursorPalette ||
+ f == OSystem::kFeatureOverlaySupportsAlpha);
+}
+
+void OSystem_3DS::setFeatureState(OSystem::Feature f, bool enable) {
+ switch (f) {
+ case OSystem::kFeatureCursorPalette:
+ _cursorPaletteEnabled = enable;
+ flushCursor();
+ break;
+ default:
+ break;
+ }
+}
+
+bool OSystem_3DS::getFeatureState(OSystem::Feature f) {
+ switch (f) {
+ case OSystem::kFeatureCursorPalette:
+ return _cursorPaletteEnabled;
+ default:
+ return false;
+ }
+}
+
+const OSystem::GraphicsMode *
+OSystem_3DS::getSupportedGraphicsModes() const {
+ return s_graphicsModes;
+}
+
+int OSystem_3DS::getDefaultGraphicsMode() const {
+ return GFX_LINEAR;
+}
+
+bool OSystem_3DS::setGraphicsMode(int mode) {
+ return true;
+}
+
+void OSystem_3DS::resetGraphicsScale() {
+ debug("resetGraphicsScale");
+}
+
+int OSystem_3DS::getGraphicsMode() const {
+ return GFX_LINEAR;
+}
+void OSystem_3DS::initSize(uint width, uint height,
+ const Graphics::PixelFormat *format) {
+ debug("3ds initsize w:%d h:%d", width, height);
+ _gameWidth = width;
+ _gameHeight = height;
+ _gameTopTexture.create(width, height, _pfGameTexture);
+ _overlay.create(getOverlayWidth(), getOverlayHeight(), _pfGameTexture);
+
+ if (format) {
+ debug("pixelformat: %d %d %d %d %d", format->bytesPerPixel, format->rBits(), format->gBits(), format->bBits(), format->aBits());;
+ _pfGame = *format;
+ }
+
+ _gameScreen.create(width, height, _pfGame);
+
+ _focusDirty = true;
+ _focusRect = Common::Rect(_gameWidth, _gameHeight);
+
+ updateSize();
+}
+
+void OSystem_3DS::updateSize() {
+ if (config.stretchToFit) {
+ _gameTopX = _gameTopY = _gameBottomX = _gameBottomY = 0;
+ _gameTopTexture.setScale(400.f / _gameWidth, 240.f / _gameHeight);
+ _gameBottomTexture.setScale(320.f / _gameWidth, 240.f / _gameHeight);
+ } else {
+ float ratio = static_cast<float>(_gameWidth) / _gameHeight;
+
+ if (ratio > 400.f / 240.f) {
+ float r = 400.f / _gameWidth;
+ _gameTopTexture.setScale(r, r);
+ _gameTopX = 0;
+ _gameTopY = (240.f - r * _gameHeight) / 2.f;
+ } else {
+ float r = 240.f / _gameHeight;
+ _gameTopTexture.setScale(r, r);
+ _gameTopY = 0;
+ _gameTopX = (400.f - r * _gameWidth) / 2.f;
+ }
+ if (ratio > 320.f / 240.f) {
+ float r = 320.f / _gameWidth;
+ _gameBottomTexture.setScale(r, r);
+ _gameBottomX = 0;
+ _gameBottomY = (240.f - r * _gameHeight) / 2.f;
+ } else {
+ float r = 240.f / _gameHeight;
+ _gameBottomTexture.setScale(r, r);
+ _gameBottomY = 0;
+ _gameBottomX = (320.f - r * _gameWidth) / 2.f;
+ }
+ }
+ _gameTopTexture.setPosition(_gameTopX, _gameTopY);
+ _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY);
+ if (_overlayVisible)
+ _cursorTexture.setScale(1.f, 1.f);
+ else if (config.screen == kScreenTop)
+ _cursorTexture.setScale(_gameTopTexture.getScaleX(), _gameTopTexture.getScaleY());
+ else
+ _cursorTexture.setScale(_gameBottomTexture.getScaleX(), _gameBottomTexture.getScaleY());
+}
+
+Common::List<Graphics::PixelFormat> OSystem_3DS::getSupportedFormats() const {
+ Common::List<Graphics::PixelFormat> list;
+ list.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); // GPU_RGBA8
+ list.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); // GPU_RGB565
+// list.push_back(Graphics::PixelFormat(3, 0, 0, 0, 8, 0, 8, 16, 0)); // GPU_RGB8
+ list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); // RGB555 (needed for FMTOWNS?)
+ list.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); // GPU_RGBA5551
+ list.push_back(Graphics::PixelFormat::createFormatCLUT8());
+ return list;
+}
+
+void OSystem_3DS::beginGFXTransaction() {
+ //
+}
+OSystem::TransactionError OSystem_3DS::endGFXTransaction() {
+ return OSystem::kTransactionSuccess;
+}
+
+void OSystem_3DS::setPalette(const byte *colors, uint start, uint num) {
+ assert(start + num <= 256);
+ memcpy(_palette + 3 * start, colors, 3 * num);
+
+ // Manually update all color that were changed
+ if (_gameScreen.format.bytesPerPixel == 1) {
+ flushGameScreen();
+ }
+}
+void OSystem_3DS::grabPalette(byte *colors, uint start, uint num) {
+ assert(start + num <= 256);
+ memcpy(colors, _palette + 3 * start, 3 * num);
+}
+
+void OSystem_3DS::copyRectToScreen(const void *buf, int pitch, int x,
+ int y, int w, int h) {
+ Common::Rect rect(x, y, x+w, y+h);
+ _gameScreen.copyRectToSurface(buf, pitch, x, y, w, h);
+ Graphics::Surface subSurface = _gameScreen.getSubArea(rect);
+
+ Graphics::Surface *convertedSubSurface = subSurface.convertTo(_pfGameTexture, _palette);
+ _gameTopTexture.copyRectToSurface(*convertedSubSurface, x, y, Common::Rect(w, h));
+
+ convertedSubSurface->free();
+ delete convertedSubSurface;
+ _gameTopTexture.markDirty();
+}
+
+void OSystem_3DS::flushGameScreen() {
+ Graphics::Surface *converted = _gameScreen.convertTo(_pfGameTexture, _palette);
+ _gameTopTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h));
+ _gameTopTexture.markDirty();
+ converted->free();
+ delete converted;
+}
+
+Graphics::Surface *OSystem_3DS::lockScreen() {
+ return &_gameScreen;
+}
+void OSystem_3DS::unlockScreen() {
+ flushGameScreen();
+}
+
+void OSystem_3DS::updateScreen() {
+
+ if (sleeping || exiting)
+ return;
+
+// updateFocus();
+
+ C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
+ // Render top screen
+ C3D_FrameDrawOn(_renderTargetTop);
+ if (config.screen == kScreenTop || config.screen == kScreenBoth) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionTop);
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameTopTexture.getMatrix());
+ _gameTopTexture.render();
+ _gameTopTexture.render();
+ if (_overlayVisible && config.screen == kScreenTop) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix());
+ _overlay.render();
+ }
+ if (_cursorVisible && config.showCursor && config.screen == kScreenTop) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix());
+ _cursorTexture.render();
+ }
+ }
+
+ // Render bottom screen
+ C3D_FrameDrawOn(_renderTargetBottom);
+ if (config.screen == kScreenBottom || config.screen == kScreenBoth) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _projectionLocation, &_projectionBottom);
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _gameBottomTexture.getMatrix());
+ _gameTopTexture.render();
+ _gameTopTexture.render();
+ if (_overlayVisible) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _overlay.getMatrix());
+ _overlay.render();
+ }
+ if (_cursorVisible && config.showCursor) {
+ C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, _modelviewLocation, _cursorTexture.getMatrix());
+ _cursorTexture.render();
+ }
+ }
+ C3D_FrameEnd(0);
+}
+
+void OSystem_3DS::setShakePos(int shakeOffset) {
+ // TODO: implement this in overlay, top screen, and mouse too
+ _screenShakeOffset = shakeOffset;
+ _gameTopTexture.setPosition(_gameTopX, _gameTopY + _gameTopTexture.getScaleY() * shakeOffset);
+ _gameBottomTexture.setPosition(_gameBottomX, _gameBottomY + _gameBottomTexture.getScaleY() * shakeOffset);
+}
+
+void OSystem_3DS::setFocusRectangle(const Common::Rect &rect) {
+ debug("setfocus: %d %d %d %d", rect.left, rect.top, rect.width(), rect.height());
+ _focusRect = rect;
+ _focusDirty = true;
+ _focusClearTime = 0;
+}
+
+void OSystem_3DS::clearFocusRectangle() {
+ _focusClearTime = getMillis();
+}
+
+void OSystem_3DS::updateFocus() {
+
+ if (_focusClearTime && getMillis() - _focusClearTime > 5000) {
+ _focusClearTime = 0;
+ _focusDirty = true;
+ _focusRect = Common::Rect(_gameWidth, _gameHeight);
+ }
+
+ if (_focusDirty) {
+ float duration = 1.f / 20.f; // Focus animation in frame duration
+ float w = 400.f;
+ float h = 240.f;
+ float ratio = _focusRect.width() / _focusRect.height();
+ if (ratio > w/h) {
+ _focusTargetScaleX = w / _focusRect.width();
+ float newHeight = (float)_focusRect.width() / w/h;
+ _focusTargetScaleY = h / newHeight;
+ _focusTargetPosX = _focusTargetScaleX * _focusRect.left;
+ _focusTargetPosY = _focusTargetScaleY * ((float)_focusRect.top - (newHeight - _focusRect.height())/2.f);
+ } else {
+ _focusTargetScaleY = h / _focusRect.height();
+ float newWidth = (float)_focusRect.height() * w/h;
+ _focusTargetScaleX = w / newWidth;
+ _focusTargetPosY = _focusTargetScaleY * _focusRect.top;
+ _focusTargetPosX = _focusTargetScaleX * ((float)_focusRect.left - (newWidth - _focusRect.width())/2.f);
+ }
+ if (_focusTargetPosX < 0 && _focusTargetScaleY != 240.f / _gameHeight)
+ _focusTargetPosX = 0;
+ if (_focusTargetPosY < 0 && _focusTargetScaleX != 400.f / _gameWidth)
+ _focusTargetPosY = 0;
+ _focusStepPosX = duration * (_focusTargetPosX - _focusPosX);
+ _focusStepPosY = duration * (_focusTargetPosY - _focusPosY);
+ _focusStepScaleX = duration * (_focusTargetScaleX - _focusScaleX);
+ _focusStepScaleY = duration * (_focusTargetScaleY - _focusScaleY);
+ }
+
+ if (_focusDirty || _focusPosX != _focusTargetPosX || _focusPosY != _focusTargetPosY ||
+ _focusScaleX != _focusTargetScaleX || _focusScaleY != _focusTargetScaleY) {
+ _focusDirty = false;
+
+ if ((_focusStepPosX > 0 && _focusPosX > _focusTargetPosX) || (_focusStepPosX < 0 && _focusPosX < _focusTargetPosX))
+ _focusPosX = _focusTargetPosX;
+ else if (_focusPosX != _focusTargetPosX)
+ _focusPosX += _focusStepPosX;
+
+ if ((_focusStepPosY > 0 && _focusPosY > _focusTargetPosY) || (_focusStepPosY < 0 && _focusPosY < _focusTargetPosY))
+ _focusPosY = _focusTargetPosY;
+ else if (_focusPosY != _focusTargetPosY)
+ _focusPosY += _focusStepPosY;
+
+ if ((_focusStepScaleX > 0 && _focusScaleX > _focusTargetScaleX) || (_focusStepScaleX < 0 && _focusScaleX < _focusTargetScaleX))
+ _focusScaleX = _focusTargetScaleX;
+ else if (_focusScaleX != _focusTargetScaleX)
+ _focusScaleX += _focusStepScaleX;
+
+ if ((_focusStepScaleY > 0 && _focusScaleY > _focusTargetScaleY) || (_focusStepScaleY < 0 && _focusScaleY < _focusTargetScaleY))
+ _focusScaleY = _focusTargetScaleY;
+ else if (_focusScaleY != _focusTargetScaleY)
+ _focusScaleY += _focusStepScaleY;
+
+ Mtx_Identity(&_focusMatrix);
+ Mtx_Translate(&_focusMatrix, -_focusPosX, -_focusPosY, 0);
+ Mtx_Scale(&_focusMatrix, _focusScaleX, _focusScaleY, 1.f);
+ }
+}
+
+void OSystem_3DS::showOverlay() {
+ _overlayVisible = true;
+ updateSize();
+ updateScreen();
+}
+
+void OSystem_3DS::hideOverlay() {
+ _overlayVisible = false;
+ updateSize();
+ updateScreen();
+}
+
+Graphics::PixelFormat OSystem_3DS::getOverlayFormat() const {
+ return _pfGameTexture;
+}
+
+void OSystem_3DS::clearOverlay() {
+ _overlay.clear();
+}
+
+void OSystem_3DS::grabOverlay(void *buf, int pitch) {
+ for (int y = 0; y < getOverlayHeight(); ++y) {
+ memcpy(buf, _overlay.getBasePtr(0, y), pitch);
+ }
+}
+
+void OSystem_3DS::copyRectToOverlay(const void *buf, int pitch, int x,
+ int y, int w, int h) {
+ _overlay.copyRectToSurface(buf, pitch, x, y, w, h);
+ _overlay.markDirty();
+}
+
+int16 OSystem_3DS::getOverlayHeight() {
+ return 240;
+}
+
+int16 OSystem_3DS::getOverlayWidth() {
+ return 320;
+}
+
+bool OSystem_3DS::showMouse(bool visible) {
+ _cursorVisible = visible;
+ flushCursor();
+ return !visible;
+}
+
+void OSystem_3DS::warpMouse(int x, int y) {
+ _cursorX = x;
+ _cursorY = y;
+ warning("x:%d y:%d", x, y);
+ // TODO: adjust for _cursorScalable ?
+ int offsetx = 0;
+ int offsety = 0;
+ x -= _cursorHotspotX;
+ y -= _cursorHotspotY;
+ if (!_overlayVisible) {
+ offsetx += config.screen == kScreenTop ? _gameTopX : _gameBottomX;
+ offsety += config.screen == kScreenTop ? _gameTopY : _gameBottomY;
+ }
+ float scalex = config.screen == kScreenTop ? (float)_gameTopTexture.actualWidth / _gameWidth : 1.f;
+ float scaley = config.screen == kScreenTop ? (float)_gameTopTexture.actualHeight / _gameHeight : 1.f;
+ _cursorTexture.setPosition(scalex * x + offsetx,
+ scaley * y + offsety);
+}
+
+void OSystem_3DS::setCursorDelta(float deltaX, float deltaY) {
+ _cursorDeltaX = deltaX;
+ _cursorDeltaY = deltaY;
+}
+
+void OSystem_3DS::setMouseCursor(const void *buf, uint w, uint h,
+ int hotspotX, int hotspotY,
+ uint32 keycolor, bool dontScale,
+ const Graphics::PixelFormat *format) {
+ _cursorScalable = !dontScale;
+ _cursorHotspotX = hotspotX;
+ _cursorHotspotY = hotspotY;
+ _cursorKeyColor = keycolor;
+ _pfCursor = !format ? Graphics::PixelFormat::createFormatCLUT8() : *format;
+
+ if (w != _cursor.w || h != _cursor.h || _cursor.format != _pfCursor) {
+ _cursor.create(w, h, _pfCursor);
+ _cursorTexture.create(w, h, _pfGameTexture);
+ }
+
+ _cursor.copyRectToSurface(buf, w, 0, 0, w, h);
+ flushCursor();
+
+ warpMouse(_cursorX, _cursorY);
+}
+
+void OSystem_3DS::setCursorPalette(const byte *colors, uint start, uint num) {
+ assert(start + num <= 256);
+ memcpy(_cursorPalette + 3 * start, colors, 3 * num);
+ _cursorPaletteEnabled = true;
+ flushCursor();
+}
+
+void OSystem_3DS::flushCursor() {
+ if (_cursor.getPixels()) {
+ Graphics::Surface *converted = _cursor.convertTo(_pfGameTexture, _cursorPaletteEnabled ? _cursorPalette : _palette);
+ _cursorTexture.copyRectToSurface(*converted, 0, 0, Common::Rect(converted->w, converted->h));
+ _cursorTexture.markDirty();
+ converted->free();
+ delete converted;
+
+ if (_pfCursor.bytesPerPixel == 1) {
+ uint* dest = (uint*) _cursorTexture.getPixels();
+ byte* src = (byte*) _cursor.getPixels();
+ for (int y = 0; y < _cursor.h; ++y) {
+ for (int x = 0; x < _cursor.w; ++x) {
+ if (*src++ == _cursorKeyColor)
+ *dest++ = 0;
+ else
+ dest++;
+ }
+ dest += _cursorTexture.w - _cursorTexture.actualWidth;
+ }
+ }
+ }
+}
+
+} // namespace _3DS
diff --git a/backends/platform/3ds/osystem.cpp b/backends/platform/3ds/osystem.cpp
new file mode 100644
index 0000000000..f6278eb16b
--- /dev/null
+++ b/backends/platform/3ds/osystem.cpp
@@ -0,0 +1,193 @@
+/* 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.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_EXCEPTION_printf
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
+
+#include "osystem.h"
+
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "backends/events/default/default-events.h"
+#include "audio/mixer_intern.h"
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/str.h"
+#include "config.h"
+
+#include "backends/fs/posix/posix-fs-factory.h"
+#include "backends/fs/posix/posix-fs.h"
+#include <unistd.h>
+#include <time.h>
+
+namespace _3DS {
+
+OSystem_3DS::OSystem_3DS():
+ _focusDirty(true),
+ _focusRect(Common::Rect(1, 1)),
+ _focusPosX(0),
+ _focusPosY(0),
+ _focusTargetPosX(0),
+ _focusTargetPosY(0),
+ _focusStepPosX(0),
+ _focusStepPosY(0),
+ _focusScaleX(1.f),
+ _focusScaleY(1.f),
+ _focusTargetScaleX(1.f),
+ _focusTargetScaleY(1.f),
+ _focusStepScaleX(0.f),
+ _focusStepScaleY(0.f),
+ _focusClearTime(0),
+ _cursorPaletteEnabled(false),
+ _cursorVisible(false),
+ _cursorScalable(false),
+ _cursorX(0),
+ _cursorY(0),
+ _cursorHotspotX(0),
+ _cursorHotspotY(0),
+ _gameTopX(0),
+ _gameTopY(0),
+ _gameBottomX(0),
+ _gameBottomY(0),
+ _gameWidth(320),
+ _gameHeight(240),
+ _overlayVisible(false),
+ exiting(false),
+ sleeping(false)
+{
+ chdir("sdmc:/");
+ _fsFactory = new POSIXFilesystemFactory();
+ Posix::assureDirectoryExists("/3ds/scummvm/saves/");
+}
+
+OSystem_3DS::~OSystem_3DS() {
+ exiting = true;
+ destroyEvents();
+ destroyAudio();
+ destroyGraphics();
+
+ delete _timerManager;
+ _timerManager = 0;
+}
+
+void OSystem_3DS::quit() {
+ printf("OSystem_3DS::quit()\n");
+}
+
+void OSystem_3DS::initBackend() {
+ loadConfig();
+ ConfMan.registerDefault("fullscreen", true);
+ ConfMan.registerDefault("aspect_ratio", true);
+ if (!ConfMan.hasKey("vkeybd_pack_name"))
+ ConfMan.set("vkeybd_pack_name", "vkeybd_small");
+ if (!ConfMan.hasKey("vkeybdpath"))
+ ConfMan.set("vkeybdpath", "/3ds/scummvm/kb");
+ if (!ConfMan.hasKey("themepath"))
+ ConfMan.set("themepath", "/3ds/scummvm");
+ if (!ConfMan.hasKey("gui_theme"))
+ ConfMan.set("gui_theme", "builtin");
+
+ _timerManager = new DefaultTimerManager();
+ _savefileManager = new DefaultSaveFileManager("/3ds/scummvm/saves/");
+
+ initGraphics();
+ initAudio();
+ initEvents();
+ EventsBaseBackend::initBackend();
+}
+
+void OSystem_3DS::updateConfig() {
+ if (_gameScreen.getPixels()) {
+ updateSize();
+ warpMouse(_cursorX, _cursorY);
+ }
+}
+
+Common::String OSystem_3DS::getDefaultConfigFileName() {
+ return "/3ds/scummvm/scummvm.ini";
+}
+
+uint32 OSystem_3DS::getMillis(bool skipRecord) {
+ return svcGetSystemTick() / TICKS_PER_MSEC;
+}
+
+void OSystem_3DS::delayMillis(uint msecs) {
+ svcSleepThread(msecs * 1000000);
+}
+
+void OSystem_3DS::getTimeAndDate(TimeDate& td) const {
+ time_t curTime = time(0);
+ struct tm t = *localtime(&curTime);
+ td.tm_sec = t.tm_sec;
+ td.tm_min = t.tm_min;
+ td.tm_hour = t.tm_hour;
+ td.tm_mday = t.tm_mday;
+ td.tm_mon = t.tm_mon;
+ td.tm_year = t.tm_year;
+ td.tm_wday = t.tm_wday;
+}
+
+OSystem::MutexRef OSystem_3DS::createMutex() {
+ RecursiveLock *mutex = new RecursiveLock();
+ RecursiveLock_Init(mutex);
+ return (OSystem::MutexRef) mutex;
+}
+void OSystem_3DS::lockMutex(MutexRef mutex) {
+ RecursiveLock_Lock((RecursiveLock*)mutex);
+}
+void OSystem_3DS::unlockMutex(MutexRef mutex) {
+ RecursiveLock_Unlock((RecursiveLock*)mutex);
+}
+void OSystem_3DS::deleteMutex(MutexRef mutex) {
+ delete (RecursiveLock*)mutex;
+}
+
+Common::String OSystem_3DS::getSystemLanguage() const {
+ u8 langcode;
+ CFGU_GetSystemLanguage(&langcode);
+ switch (langcode) {
+ case CFG_LANGUAGE_JP: return "ja_JP";
+ case CFG_LANGUAGE_EN: return "en_US";
+ case CFG_LANGUAGE_FR: return "fr_FR";
+ case CFG_LANGUAGE_DE: return "de_DE";
+ case CFG_LANGUAGE_IT: return "it_IT";
+ case CFG_LANGUAGE_ES: return "es_ES";
+ case CFG_LANGUAGE_ZH: return "zh_CN";
+ case CFG_LANGUAGE_KO: return "ko_KR";
+ case CFG_LANGUAGE_NL: return "nl_NL";
+ case CFG_LANGUAGE_PT: return "pt_BR";
+ case CFG_LANGUAGE_RU: return "ru_RU";
+ case CFG_LANGUAGE_TW: return "zh_HK";
+ default: return "en_US";
+ }
+}
+
+void OSystem_3DS::fatalError() {
+ printf("FatalError!\n");
+}
+
+void OSystem_3DS::logMessage(LogMessageType::Type type, const char *message) {
+ printf("3DS log: %s\n", message);
+}
+
+} // namespace _3DS
diff --git a/backends/platform/3ds/osystem.h b/backends/platform/3ds/osystem.h
new file mode 100644
index 0000000000..478085acba
--- /dev/null
+++ b/backends/platform/3ds/osystem.h
@@ -0,0 +1,221 @@
+/* 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 PLATFORM_3DS_H
+#define PLATFORM_3DS_H
+
+#include <citro3d.h>
+#include "backends/mutex/mutex.h"
+#include "backends/base-backend.h"
+#include "graphics/palette.h"
+#include "base/main.h"
+#include "audio/mixer_intern.h"
+#include "backends/graphics/graphics.h"
+#include "backends/platform/3ds/sprite.h"
+#include "common/rect.h"
+#include "common/queue.h"
+
+#define TICKS_PER_MSEC 268123
+
+namespace _3DS {
+
+enum {
+ GFX_LINEAR = 0,
+ GFX_NEAREST = 1
+};
+
+enum InputMode {
+ MODE_HOVER,
+ MODE_DRAG,
+};
+
+static const OSystem::GraphicsMode s_graphicsModes[] = {
+ {"default", "Default Test", GFX_LINEAR},
+ { 0, 0, 0 }
+};
+
+class OSystem_3DS : public EventsBaseBackend, public PaletteManager {
+public:
+ OSystem_3DS();
+ virtual ~OSystem_3DS();
+
+ volatile bool exiting;
+ volatile bool sleeping;
+
+ virtual void initBackend();
+
+ virtual bool hasFeature(OSystem::Feature f);
+ virtual void setFeatureState(OSystem::Feature f, bool enable);
+ virtual bool getFeatureState(OSystem::Feature f);
+
+ virtual bool pollEvent(Common::Event &event);
+
+ virtual uint32 getMillis(bool skipRecord = false);
+ virtual void delayMillis(uint msecs);
+ virtual void getTimeAndDate(TimeDate &t) const;
+
+ virtual MutexRef createMutex();
+ virtual void lockMutex(MutexRef mutex);
+ virtual void unlockMutex(MutexRef mutex);
+ virtual void deleteMutex(MutexRef mutex);
+
+ virtual void logMessage(LogMessageType::Type type, const char *message);
+
+ virtual Audio::Mixer *getMixer();
+ virtual PaletteManager *getPaletteManager() { return this; }
+ virtual Common::String getSystemLanguage() const;
+ virtual void fatalError();
+ virtual void quit();
+
+ virtual Common::String getDefaultConfigFileName();
+
+ // Graphics
+ virtual const OSystem::GraphicsMode *getSupportedGraphicsModes() const;
+ int getDefaultGraphicsMode() const;
+ bool setGraphicsMode(int mode);
+ void resetGraphicsScale();
+ int getGraphicsMode() const;
+ inline Graphics::PixelFormat getScreenFormat() const { return _pfGame; }
+ virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const;
+ void initSize(uint width, uint height,
+ const Graphics::PixelFormat *format = NULL);
+ virtual int getScreenChangeID() const { return 0; };
+
+ void beginGFXTransaction();
+ OSystem::TransactionError endGFXTransaction();
+ int16 getHeight(){ return _gameHeight; }
+ int16 getWidth(){ return _gameWidth; }
+ void setPalette(const byte *colors, uint start, uint num);
+ void grabPalette(byte *colors, uint start, uint num);
+ void copyRectToScreen(const void *buf, int pitch, int x, int y, int w,
+ int h);
+ Graphics::Surface *lockScreen();
+ void unlockScreen();
+ void updateScreen();
+ void setShakePos(int shakeOffset);
+ void setFocusRectangle(const Common::Rect &rect);
+ void clearFocusRectangle();
+ void showOverlay();
+ void hideOverlay();
+ Graphics::PixelFormat getOverlayFormat() const;
+ void clearOverlay();
+ void grabOverlay(void *buf, int pitch);
+ void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w,
+ int h);
+ virtual int16 getOverlayHeight();
+ virtual int16 getOverlayWidth();
+ virtual void displayMessageOnOSD(const char *msg);
+
+ bool showMouse(bool visible);
+ void warpMouse(int x, int y);
+ void setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
+ int hotspotY, uint32 keycolor, bool dontScale = false,
+ const Graphics::PixelFormat *format = NULL);
+ void setCursorPalette(const byte *colors, uint start, uint num);
+
+ // Transform point from touchscreen coords into gamescreen coords
+ void transformPoint(touchPosition &point);
+
+ void setCursorDelta(float deltaX, float deltaY);
+
+ void updateFocus();
+ void updateConfig();
+ void updateSize();
+
+private:
+ void initGraphics();
+ void destroyGraphics();
+ void initAudio();
+ void destroyAudio();
+ void initEvents();
+ void destroyEvents();
+
+ void flushGameScreen();
+ void flushCursor();
+
+protected:
+ Audio::MixerImpl *_mixer;
+
+private:
+ u16 _gameWidth, _gameHeight;
+ u16 _gameTopX, _gameTopY;
+ u16 _gameBottomX, _gameBottomY;
+
+ // Audio
+ Thread audioThread;
+
+ // Graphics
+ Graphics::PixelFormat _pfGame;
+ Graphics::PixelFormat _pfGameTexture;
+ Graphics::PixelFormat _pfCursor;
+ byte _palette[3 * 256];
+ byte _cursorPalette[3 * 256];
+
+ Graphics::Surface _gameScreen;
+ Sprite _gameTopTexture;
+ Sprite _gameBottomTexture;
+ Sprite _overlay;
+
+ int _screenShakeOffset;
+ bool _overlayVisible;
+
+ DVLB_s *_dvlb;
+ shaderProgram_s _program;
+ int _projectionLocation;
+ int _modelviewLocation;
+ C3D_Mtx _projectionTop;
+ C3D_Mtx _projectionBottom;
+ C3D_RenderTarget* _renderTargetTop;
+ C3D_RenderTarget* _renderTargetBottom;
+
+ // Focus
+ Common::Rect _focusRect;
+ bool _focusDirty;
+ C3D_Mtx _focusMatrix;
+ int _focusPosX, _focusPosY;
+ int _focusTargetPosX, _focusTargetPosY;
+ float _focusStepPosX, _focusStepPosY;
+ float _focusScaleX, _focusScaleY;
+ float _focusTargetScaleX, _focusTargetScaleY;
+ float _focusStepScaleX, _focusStepScaleY;
+ uint32 _focusClearTime;
+
+ // Events
+ Thread _eventThread;
+ Thread _timerThread;
+ Common::Queue<Common::Event> _eventQueue;
+
+ // Cursor
+ Graphics::Surface _cursor;
+ Sprite _cursorTexture;
+ bool _cursorPaletteEnabled;
+ bool _cursorVisible;
+ bool _cursorScalable;
+ float _cursorX, _cursorY;
+ float _cursorDeltaX, _cursorDeltaY;
+ int _cursorHotspotX, _cursorHotspotY;
+ uint32 _cursorKeyColor;
+};
+
+} // namespace _3DS
+
+#endif
diff --git a/backends/platform/3ds/shader.v.pica b/backends/platform/3ds/shader.v.pica
new file mode 100644
index 0000000000..2d18985622
--- /dev/null
+++ b/backends/platform/3ds/shader.v.pica
@@ -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.
+;*
+
+; Uniforms
+.fvec projection[4], modelView[4]
+
+; Constants
+.constf myconst(0.0, 1.0, -1.0, 0.1)
+.alias zeros myconst.xxxx ; Vector full of zeros
+.alias ones myconst.yyyy ; Vector full of ones
+
+; Outputs
+.out outpos position
+.out outtex texcoord0
+
+; Inputs (defined as aliases for convenience)
+.alias inpos v0
+.alias intex v1
+
+.proc main
+ ; Force the w component of inpos to be 1.0
+ mov r0.xyz, inpos
+ mov r0.w, ones
+
+ ; r1 = modelView * inpos
+ dp4 r1.x, modelView[0], r0
+ dp4 r1.y, modelView[1], r0
+ dp4 r1.z, modelView[2], r0
+ dp4 r1.w, modelView[3], r0
+
+ ; outpos = projection * r1
+ dp4 outpos.x, projection[0], r1
+ dp4 outpos.y, projection[1], r1
+ dp4 outpos.z, projection[2], r1
+ dp4 outpos.w, projection[3], r1
+
+ mov outtex, intex
+
+ end
+.end
+
diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp
new file mode 100644
index 0000000000..a0aee385a9
--- /dev/null
+++ b/backends/platform/3ds/sprite.cpp
@@ -0,0 +1,144 @@
+/* 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/platform/3ds/sprite.h"
+#include "common/util.h"
+#include <3ds.h>
+
+static uint nextHigher2(uint v) {
+ if (v == 0)
+ return 1;
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return ++v;
+}
+
+Sprite::Sprite()
+ : dirtyPixels(true)
+ , dirtyMatrix(true)
+ , actualWidth(0)
+ , actualHeight(0)
+ , posX(0)
+ , posY(0)
+ , scaleX(1.f)
+ , scaleY(1.f)
+{
+ Mtx_Identity(&modelview);
+
+ vertices = (vertex *)linearAlloc(sizeof(vertex) * 4);
+}
+
+Sprite::~Sprite() {
+ //
+}
+
+void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) {
+ free();
+
+ actualWidth = width;
+ actualHeight = height;
+ format = f;
+ w = MAX(nextHigher2(width), 64u);
+ h = MAX(nextHigher2(height), 64u);;
+ pitch = w * format.bytesPerPixel;
+ dirtyPixels = true;
+
+ if (width && height) {
+ pixels = linearAlloc(h * pitch);
+ C3D_TexInit(&texture, w, h, GPU_RGBA8);
+ C3D_TexSetFilter(&texture, GPU_LINEAR, GPU_LINEAR);
+ assert(pixels && texture.data);
+ clear();
+ }
+
+ float x = 0.f, y = 0.f;
+ float u = (float)width/w;
+ float v = (float)height/h;
+ vertex tmp[4] = {
+ {{x, y, 0.5f}, {0, 0}},
+ {{x+width, y, 0.5f}, {u, 0}},
+ {{x, y+height, 0.5f}, {0, v}},
+ {{x+width, y+height, 0.5f}, {u, v}},
+ };
+ memcpy(vertices, tmp, sizeof(vertex) * 4);
+}
+
+
+void Sprite::free() {
+ linearFree(vertices);
+ linearFree(pixels);
+ C3D_TexDelete(&texture);
+ pixels = 0;
+ w = h = pitch = 0;
+ actualWidth = actualHeight = 0;
+ format = Graphics::PixelFormat();
+}
+
+void Sprite::convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette) {
+ //
+}
+
+void Sprite::render() {
+ if (dirtyPixels) {
+ dirtyPixels = false;
+ GSPGPU_FlushDataCache(pixels, w * h * format.bytesPerPixel);
+ C3D_SafeDisplayTransfer((u32*)pixels, GX_BUFFER_DIM(w, h), (u32*)texture.data, GX_BUFFER_DIM(w, h), TEXTURE_TRANSFER_FLAGS);
+ gspWaitForPPF();
+ }
+ C3D_TexBind(0, &texture);
+
+ C3D_BufInfo *bufInfo = C3D_GetBufInfo();
+ BufInfo_Init(bufInfo);
+ BufInfo_Add(bufInfo, vertices, sizeof(vertex), 2, 0x10);
+ C3D_DrawArrays(GPU_TRIANGLE_STRIP, 0, 4);
+}
+
+void Sprite::clear(uint32 color) {
+ dirtyPixels = true;
+ memset(pixels, color, w * h * format.bytesPerPixel);
+}
+
+void Sprite::setScale (float x, float y) {
+ scaleX = x;
+ scaleY = y;
+ dirtyMatrix = true;
+}
+
+void Sprite::setPosition(int x, int y) {
+ posX = x;
+ posY = y;
+ dirtyMatrix = true;
+}
+
+C3D_Mtx* Sprite::getMatrix() {
+ if (dirtyMatrix) {
+ dirtyMatrix = false;
+ Mtx_Identity(&modelview);
+ Mtx_Scale(&modelview, scaleX, scaleY, 1.f);
+ Mtx_Translate(&modelview, posX, posY, 0);
+ }
+ return &modelview;
+}
diff --git a/backends/platform/3ds/sprite.h b/backends/platform/3ds/sprite.h
new file mode 100644
index 0000000000..6d88ae4ce1
--- /dev/null
+++ b/backends/platform/3ds/sprite.h
@@ -0,0 +1,71 @@
+/* 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_SPRITE_3DS_H
+#define GRAPHICS_SPRITE_3DS_H
+
+#include <citro3d.h>
+#include "graphics/surface.h"
+
+#define TEXTURE_TRANSFER_FLAGS \
+ (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \
+ GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \
+ GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))
+
+typedef struct {
+ float position[3];
+ float texcoord[2];
+} vertex;
+
+class Sprite : public Graphics::Surface {
+public:
+ Sprite();
+ ~Sprite();
+ void create(uint16 width, uint16 height, const Graphics::PixelFormat &format);
+ void free();
+ void convertToInPlace(const Graphics::PixelFormat &dstFormat, const byte *palette = 0);
+ void render();
+ void clear(uint32 color = 0);
+ void markDirty(){ dirtyPixels = true; }
+
+ void setPosition(int x, int y);
+ void setScale(float x, float y);
+ float getScaleX(){ return scaleX; }
+ float getScaleY(){ return scaleY; }
+ C3D_Mtx* getMatrix();
+
+ uint16 actualWidth;
+ uint16 actualHeight;
+
+private:
+ bool dirtyPixels;
+ bool dirtyMatrix;
+ C3D_Mtx modelview;
+ C3D_Tex texture;
+ vertex* vertices;
+ int posX;
+ int posY;
+ float scaleX;
+ float scaleY;
+};
+
+#endif
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index d8ab549c3a..6cd938ec9c 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
+ bool 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..c84aef9c47 100644
--- a/backends/platform/dc/dcmain.cpp
+++ b/backends/platform/dc/dcmain.cpp
@@ -90,43 +90,53 @@ 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;
+bool DCCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate);
+
+ // If we're playing now return here
+ if (isPlaying()) {
+ return true;
+ }
+
+ // If we should only play emulated tracks stop here.
+ if (onlyEmulate) {
+ return false;
+ }
+
+ 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)
+ numLoops = 15; // infinity
-bool DCCDManager::pollCD()
-{
- extern int getCdState();
- return getCdState() == 3;
+ if (!find_track(track, firstSec, lastSec))
+ return false;
+
+ if (duration)
+ lastSec = firstSec + startFrame + duration;
+
+ firstSec += startFrame;
+ play_cdda_sectors(firstSec, lastSec, numLoops);
+
+ return true;
}
-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/README.GCW0 b/backends/platform/dingux/README.GCW0
index 1875e5323a..1b7e30e266 100644
--- a/backends/platform/dingux/README.GCW0
+++ b/backends/platform/dingux/README.GCW0
@@ -24,3 +24,12 @@ It's pretty simple if you are running Linux on an x86/amd64 machine:
3. Run backends/platform/dingux/build.gcw0.sh script
4. Copy the resulting file scummvm.opk to your device
5. Enjoy
+
+Troubleshooting
+===============
+In case you need to submit a bugreport, you may find the log file at the
+following path:
+
+ /var/tmp/scummvm.log
+
+The log file is being overwritten at every ScummVM run.
diff --git a/backends/platform/dingux/build.gcw0.sh b/backends/platform/dingux/build.gcw0.sh
index c1a4fa29c2..7a31d4fd27 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-mt32emu --disable-hq-scalers && make -j6 gcw-opk && ls -l scummvm.opk
+./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release && make -j6 gcw-opk && ls -l scummvm.opk
diff --git a/backends/platform/dingux/dingux.mk b/backends/platform/dingux/dingux.mk
index 56c26c3be1..dc87e41241 100644
--- a/backends/platform/dingux/dingux.mk
+++ b/backends/platform/dingux/dingux.mk
@@ -59,19 +59,20 @@ endif
echo >> $(gcw0_bundle)/README.man.txt
echo '[General README]' >> $(gcw0_bundle)/README.man.txt
echo >> $(gcw0_bundle)/README.man.txt
- cat README >> $(gcw0_bundle)/README.man.txt
+ cat $(srcdir)/README | sed -e 's/\[/⟦/g' -e 's/\]/⟧/g' -e '/^1\.1)/,$$ s/^[0-9][0-9]*\.[0-9][0-9]*.*/\[&\]/' >> $(gcw0_bundle)/README.man.txt
+
# $(CP) GeneralUser\ GS\ FluidSynth\ v1.44.sf2 $(gcw0_bundle)/
gcw0-opk-unstripped: $(gcw0_bundle)
$(CP) $(PLUGINS) $(gcw0_bundle)/plugins/
$(CP) $(EXECUTABLE) $(gcw0_bundle)/scummvm
- ./dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
+ $(srcdir)/dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
gcw-opk: $(gcw0_bundle)
$(STRIP) $(gcw0_bundle)/plugins/*
$(STRIP) $(gcw0_bundle)/scummvm
- ./dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
+ $(srcdir)/dists/gcw0/opk_make.sh -d $(gcw0_bundle) -o scummvm
GeneralUser_GS_1.44-FluidSynth.zip:
curl -s http://www.scummvm.org/frs/extras/SoundFont/GeneralUser_GS_1.44-FluidSynth.zip -o GeneralUser_GS_1.44-FluidSynth.zip
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/amigaos/amigaos-main.cpp b/backends/platform/sdl/amigaos/amigaos-main.cpp
index 65da6bbf85..7bbf8d1fff 100644
--- a/backends/platform/sdl/amigaos/amigaos-main.cpp
+++ b/backends/platform/sdl/amigaos/amigaos-main.cpp
@@ -24,13 +24,44 @@
#if defined(__amigaos4__)
+#include "backends/fs/amigaos4/amigaos4-fs.h"
#include "backends/platform/sdl/amigaos/amigaos.h"
#include "backends/plugins/sdl/sdl-provider.h"
#include "base/main.h"
int main(int argc, char *argv[]) {
- // Set up a stack cookie to avoid crashes due to too few stack set by users
+ // The following will gather the application name and add the install path
+ // to a variable in AmigaOS4's ENV(ARC) system. It will be placed in AppPaths
+ // so that ScummVM can become AmiUpdate aware
+ const char *const appname = "ScummVM";
+
+ BPTR lock;
+ APTR oldwin;
+
+ // Obtain a lock to the home directory
+ if ((lock = IDOS->GetProgramDir())) {
+ TEXT progpath[2048];
+ TEXT apppath[1024] = "AppPaths";
+
+ if (IDOS->DevNameFromLock(lock,
+ progpath,
+ sizeof(progpath),
+ DN_FULLPATH)) {
+
+ // Stop any "Insert volume..." type requesters
+ oldwin = IDOS->SetProcWindow((APTR)-1);
+
+ // Finally, set the variable to the path the executable was run from
+ IDOS->AddPart( apppath, appname, 1024);
+ IDOS->SetVar( apppath, progpath, -1, GVF_GLOBAL_ONLY|GVF_SAVE_VAR );
+
+ // Turn system requesters back on
+ IDOS->SetProcWindow( oldwin );
+ }
+ }
+
+ // Set up a stack cookie to avoid crashes from a stack set too low
static const char *stack_cookie __attribute__((used)) = "$STACK: 600000";
// Create our OSystem instance
@@ -44,7 +75,7 @@ int main(int argc, char *argv[]) {
PluginManager::instance().addPluginProvider(new SDLPluginProvider());
#endif
- // Invoke the actual ScummVM main entry point:
+ // Invoke the actual ScummVM main entry point
int res = scummvm_main(argc, argv);
// Free OSystem
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.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/symbian/src/portdefs.h b/backends/platform/symbian/src/portdefs.h
index f9da09d3eb..e2465e4767 100644
--- a/backends/platform/symbian/src/portdefs.h
+++ b/backends/platform/symbian/src/portdefs.h
@@ -65,7 +65,8 @@ typedef signed long int int32;
#undef remove
#endif
-#define SMALL_SCREEN_DEVICE
+#define GUI_ONLY_FULLSCREEN
+#define GUI_ENABLE_KEYSDIALOG
#define DISABLE_COMMAND_LINE
#define USE_RGB_COLOR
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/platform/wince/portdefs.h b/backends/platform/wince/portdefs.h
index 3304ee0893..c74fc39169 100644
--- a/backends/platform/wince/portdefs.h
+++ b/backends/platform/wince/portdefs.h
@@ -30,7 +30,8 @@
// Missing string/stdlib/assert declarations for WinCE 2.xx
#if _WIN32_WCE < 300
- #define SMALL_SCREEN_DEVICE
+ #define GUI_ONLY_FULLSCREEN
+ #define GUI_ENABLE_KEYSDIALOG
void *calloc(size_t n, size_t s);
int isalnum(int c);
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/backends/taskbar/macosx/dockplugin/dockplugin.m b/backends/taskbar/macosx/dockplugin/dockplugin.m
new file mode 100644
index 0000000000..9d864ef2a8
--- /dev/null
+++ b/backends/taskbar/macosx/dockplugin/dockplugin.m
@@ -0,0 +1,125 @@
+/* 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 <Cocoa/Cocoa.h>
+
+@interface ScummVMDockTilePlugIn : NSObject <NSDockTilePlugIn> {
+ NSMenu *recentGamesMenu;
+}
+@end
+
+@interface StartGameMenuItem : NSMenuItem {
+ NSString *game;
+}
+- (IBAction) startGame;
+- (NSMenuItem*)initWithGame:(NSString *)gameId description:(NSString*)desc icon:(NSString*)iconFile;
+@end
+
+@implementation ScummVMDockTilePlugIn
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ recentGamesMenu = nil;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [recentGamesMenu release];
+ [super dealloc];
+}
+
+
+- (void)setDockTile:(NSDockTile *)dockTile {
+}
+
+- (NSMenu*)dockMenu {
+ // Get the list or recent games
+ CFPreferencesAppSynchronize(CFSTR("org.scummvm.scummvm"));
+ NSArray *array = CFPreferencesCopyAppValue(CFSTR("recentGames"), CFSTR("org.scummvm.scummvm"));
+ if (array == nil)
+ return nil;
+
+ // Create the menu
+ if (recentGamesMenu == nil)
+ recentGamesMenu = [[NSMenu alloc] init];
+ else
+ [recentGamesMenu removeAllItems];
+
+ NSEnumerator *enumerator = [array objectEnumerator];
+ NSDictionary *recentGame;
+ while (recentGame = [enumerator nextObject]) {
+ NSString *gameId = [recentGame valueForKey:@"game"];
+ NSString *desc = [recentGame valueForKey:@"description"];
+ NSString *iconFile = [recentGame valueForKey:@"icon"];
+
+ StartGameMenuItem *menuItem = [[StartGameMenuItem alloc] initWithGame:gameId description:desc icon:iconFile];
+ [recentGamesMenu addItem:menuItem];
+ [menuItem release];
+ }
+
+ return recentGamesMenu;
+}
+
+@end
+
+@implementation StartGameMenuItem
+
+- (NSMenuItem*)initWithGame:(NSString *)gameId description:(NSString*)desc icon:(NSString*)iconFile {
+ self = [super initWithTitle:(desc == nil ? gameId : desc) action:@selector(startGame) keyEquivalent:@""];
+ [self setTarget:self];
+
+ if (iconFile != nil) {
+ NSImage *image = [[NSImage alloc] initWithContentsOfFile:iconFile];
+ [self setImage:image];
+ [image release];
+ }
+
+ game = gameId;
+ [game retain];
+
+ return self;
+}
+
+- (void)dealloc {
+ [game release];
+ [super dealloc];
+}
+
+- (IBAction) startGame {
+ NSLog(@"Starting Game %@...", game);
+
+ NSString *scummVMPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:@"org.scummvm.scummvm"];
+ if (scummVMPath == nil) {
+ NSLog(@"Cannot find ScummVM.app!");
+ return;
+ }
+ // Start ScummVM.app with the game ID as argument
+ NSURL *url = [NSURL fileURLWithPath:scummVMPath];
+ NSMutableDictionary *args = [[NSMutableDictionary alloc] init];
+ [args setObject:[NSArray arrayWithObject:game] forKey:NSWorkspaceLaunchConfigurationArguments];
+ [[NSWorkspace sharedWorkspace] launchApplicationAtURL:url options:NSWorkspaceLaunchDefault configuration:args error:nil];
+ [args release];
+}
+
+@end
diff --git a/backends/taskbar/macosx/macosx-taskbar.h b/backends/taskbar/macosx/macosx-taskbar.h
index 5d5b9d02cd..55bb97a691 100644
--- a/backends/taskbar/macosx/macosx-taskbar.h
+++ b/backends/taskbar/macosx/macosx-taskbar.h
@@ -37,6 +37,7 @@ public:
virtual void setProgressValue(int completed, int total);
virtual void setProgressState(TaskbarProgressState state);
virtual void setCount(int count);
+ virtual void addRecent(const Common::String &name, const Common::String &description);
virtual void notifyError();
virtual void clearError();
diff --git a/backends/taskbar/macosx/macosx-taskbar.mm b/backends/taskbar/macosx/macosx-taskbar.mm
index ae087dfb85..577320b79d 100644
--- a/backends/taskbar/macosx/macosx-taskbar.mm
+++ b/backends/taskbar/macosx/macosx-taskbar.mm
@@ -29,9 +29,6 @@
// NSDockTile was introduced with Mac OS X 10.5.
// Try provide backward compatibility by avoiding NSDockTile symbols.
-// TODO: Implement recent list, maybe as a custom menu on dock tile when app is not running
-// See Dock Tile plug-in at https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/CreatingaDockTilePlug-in/CreatingaDockTilePlug-in.html
-
#include "backends/taskbar/macosx/macosx-taskbar.h"
#include "common/config-manager.h"
#include "common/file.h"
@@ -39,6 +36,9 @@
#include <AppKit/NSApplication.h>
#include <AppKit/NSImage.h>
#include <Foundation/NSString.h>
+#include <Foundation/NSDictionary.h>
+#include <Foundation/NSArray.h>
+#include <Foundation/NSUserDefaults.h>
#include <AppKit/NSImageView.h>
#include <AppKit/NSColor.h>
#include <AppKit/NSBezierPath.h>
@@ -120,7 +120,7 @@ void MacOSXTaskbarManager::setOverlayIcon(const Common::String &name, const Comm
initOverlayIconView();
CFStringRef imageFile = CFStringCreateWithCString(0, path.c_str(), kCFStringEncodingASCII);
- NSImage* image = [[NSImage alloc] initWithContentsOfFile:(NSString *)imageFile];
+ NSImage *image = [[NSImage alloc] initWithContentsOfFile:(NSString *)imageFile];
[_overlayIconView setImage:image];
[image release];
CFRelease(imageFile);
@@ -234,5 +234,64 @@ return (path); \
return "";
}
+void MacOSXTaskbarManager::addRecent(const Common::String &name, const Common::String &description) {
+ //warning("[MacOSXTaskbarManager::addRecent] Adding recent list entry: %s (%s)", name.c_str(), description.c_str());
+
+ if (_dockTile == nil)
+ return;
+
+ // Store the game, description and icon in user preferences.
+ // The NSDockTilePlugin will retrieve them there to list them in the dock tile menu.
+
+ CFStringRef gameName = CFStringCreateWithCString(0, name.c_str(), kCFStringEncodingASCII);
+ CFStringRef desc = CFStringCreateWithCString(0, description.c_str(), kCFStringEncodingASCII);
+
+ // First build the dictionary for this game.
+ NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
+ [dict setObject:(NSString *)gameName forKey:@"game"];
+ [dict setObject:(NSString *)desc forKey:@"description"];
+
+ // Icon
+ Common::String iconPath = getIconPath(name);
+ if (!iconPath.empty()) {
+ CFStringRef icon = CFStringCreateWithCString(0, iconPath.c_str(), kCFStringEncodingASCII);
+ [dict setObject:(NSString *)icon forKey:@"icon"];
+ CFRelease(icon);
+ }
+
+ // Retrieve the current list of recent items and update it.
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSArray *oldArray = [defaults arrayForKey:@"recentGames"];
+ if (oldArray == nil) {
+ [defaults setObject:[NSArray arrayWithObject:dict] forKey:@"recentGames"];
+ } else {
+ NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:oldArray];
+ // Insert the new game at the start
+ [newArray insertObject:dict atIndex:0];
+ // If the game was already present in the array, remove it
+ for (int i = 1 ; i < [newArray count] ; ++i) {
+ NSDictionary *oldDict = [newArray objectAtIndex:i];
+ if (oldDict == nil)
+ continue;
+ NSString *oldGame = [oldDict valueForKey:@"game"];
+ if (oldGame != nil && [oldGame isEqualToString:(NSString*)gameName]) {
+ [newArray removeObjectAtIndex:i];
+ break;
+ }
+ }
+ // And make sure we limit the size of the array to 5 games
+ if ([newArray count] > 5)
+ [newArray removeLastObject];
+ [defaults setObject:newArray forKey:@"recentGames"];
+ [newArray release];
+ }
+
+ // Finally release the dictionary
+ [dict release];
+ CFRelease(gameName);
+ CFRelease(desc);
+}
+
+
#endif
diff --git a/backends/updates/macosx/macosx-updates.h b/backends/updates/macosx/macosx-updates.h
index fd2d1f46f5..6fb9af7712 100644
--- a/backends/updates/macosx/macosx-updates.h
+++ b/backends/updates/macosx/macosx-updates.h
@@ -39,8 +39,10 @@ public:
virtual void setAutomaticallyChecksForUpdates(UpdateState state);
virtual UpdateState getAutomaticallyChecksForUpdates();
- virtual void setUpdateCheckInterval(UpdateInterval interval);
- virtual UpdateInterval getUpdateCheckInterval();
+ virtual void setUpdateCheckInterval(int interval);
+ virtual int getUpdateCheckInterval();
+
+ virtual bool getLastUpdateCheckTimeAndDate(TimeDate &t);
};
#endif
diff --git a/backends/updates/macosx/macosx-updates.mm b/backends/updates/macosx/macosx-updates.mm
index a94f1c21fd..db9362a459 100644
--- a/backends/updates/macosx/macosx-updates.mm
+++ b/backends/updates/macosx/macosx-updates.mm
@@ -23,14 +23,18 @@
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "common/system.h"
#include "backends/updates/macosx/macosx-updates.h"
#ifdef USE_SPARKLE
#include "common/translation.h"
+#include "common/config-manager.h"
#include <Cocoa/Cocoa.h>
#include <Sparkle/Sparkle.h>
+#include <AvailabilityMacros.h>
+
SUUpdater *sparkleUpdater;
/**
@@ -45,14 +49,22 @@ SUUpdater *sparkleUpdater;
*
*/
MacOSXUpdateManager::MacOSXUpdateManager() {
+ NSBundle* mainBundle = [NSBundle mainBundle];
+
+ NSString *version = [mainBundle objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleVersionKey];
+ if (!version || [version isEqualToString:@""]) {
+ warning("Running not in bundle, skipping Sparkle initialization");
+
+ sparkleUpdater = nullptr;
+ return;
+ }
+
NSMenuItem *menuItem = [[NSApp mainMenu] itemAtIndex:0];
NSMenu *applicationMenu = [menuItem submenu];
// Init Sparkle
sparkleUpdater = [SUUpdater sharedUpdater];
- NSBundle* mainBundle = [NSBundle mainBundle];
-
NSString* feedbackURL = [mainBundle objectForInfoDictionaryKey:@"SUFeedURL"];
// Set appcast URL
@@ -74,11 +86,13 @@ MacOSXUpdateManager::MacOSXUpdateManager() {
// Finally give up our references to the objects
[menuItem release];
- // Enable automatic update checking once a day (alternatively use
- // checkForUpdates() here to check for updates on every startup)
- // TODO: Should be removed when an update settings gui is implemented
- setAutomaticallyChecksForUpdates(kUpdateStateEnabled);
- setUpdateCheckInterval(kUpdateIntervalOneDay);
+ if (!ConfMan.hasKey("updates_check")
+ || ConfMan.getInt("updates_check") == Common::UpdateManager::kUpdateIntervalNotSupported) {
+ setAutomaticallyChecksForUpdates(kUpdateStateDisabled);
+ } else {
+ setAutomaticallyChecksForUpdates(kUpdateStateEnabled);
+ setUpdateCheckInterval(normalizeInterval(ConfMan.getInt("updates_check")));
+ }
}
MacOSXUpdateManager::~MacOSXUpdateManager() {
@@ -86,6 +100,9 @@ MacOSXUpdateManager::~MacOSXUpdateManager() {
}
void MacOSXUpdateManager::checkForUpdates() {
+ if (sparkleUpdater == nullptr)
+ return;
+
[sparkleUpdater checkForUpdatesInBackground];
}
@@ -93,24 +110,38 @@ void MacOSXUpdateManager::setAutomaticallyChecksForUpdates(UpdateManager::Update
if (state == kUpdateStateNotSupported)
return;
+ if (sparkleUpdater == nullptr)
+ return;
+
[sparkleUpdater setAutomaticallyChecksForUpdates:(state == kUpdateStateEnabled ? YES : NO)];
}
Common::UpdateManager::UpdateState MacOSXUpdateManager::getAutomaticallyChecksForUpdates() {
+ if (sparkleUpdater == nullptr)
+ return kUpdateStateDisabled;
+
if ([sparkleUpdater automaticallyChecksForUpdates])
return kUpdateStateEnabled;
else
return kUpdateStateDisabled;
}
-void MacOSXUpdateManager::setUpdateCheckInterval(UpdateInterval interval) {
+void MacOSXUpdateManager::setUpdateCheckInterval(int interval) {
+ if (sparkleUpdater == nullptr)
+ return;
+
if (interval == kUpdateIntervalNotSupported)
return;
+ interval = normalizeInterval(interval);
+
[sparkleUpdater setUpdateCheckInterval:(NSTimeInterval)interval];
}
-Common::UpdateManager::UpdateInterval MacOSXUpdateManager::getUpdateCheckInterval() {
+int MacOSXUpdateManager::getUpdateCheckInterval() {
+ if (sparkleUpdater == nullptr)
+ return kUpdateIntervalOneDay;
+
// This is kind of a hack but necessary, as the value stored by Sparkle
// might have been changed outside of ScummVM (in which case we return the
// default interval of one day)
@@ -128,4 +159,30 @@ Common::UpdateManager::UpdateInterval MacOSXUpdateManager::getUpdateCheckInterva
}
}
+bool MacOSXUpdateManager::getLastUpdateCheckTimeAndDate(TimeDate &t) {
+ if (sparkleUpdater == nullptr)
+ return false;
+
+ NSDate *date = [sparkleUpdater lastUpdateCheckDate];
+#ifdef MAC_OS_X_VERSION_10_10
+ NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+ NSDateComponents *components = [gregorian components:(NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:date];
+#else
+ NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
+ NSDateComponents *components = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:date];
+#endif
+
+ t.tm_wday = [components weekday];
+ t.tm_year = [components year];
+ t.tm_mon = [components month];
+ t.tm_mday = [components day];
+ t.tm_hour = [components hour];
+ t.tm_min = [components minute];
+ t.tm_sec = [components second];
+
+ [gregorian release];
+
+ return true;
+}
+
#endif