aboutsummaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
authorWillem Jan Palenstijn2016-07-21 13:30:47 +0200
committerWillem Jan Palenstijn2016-07-21 13:30:47 +0200
commit6f001d831623a46f643379554d20e94463d8c2f1 (patch)
tree901caa296592814b48a98ac2e3d381331c5a7821 /backends
parent75fdd1504de98c7c6937344877685bfef6514344 (diff)
parent5f301b24002fffb3e8e05061a92ae2e0ee3a92ec (diff)
downloadscummvm-rg350-6f001d831623a46f643379554d20e94463d8c2f1.tar.gz
scummvm-rg350-6f001d831623a46f643379554d20e94463d8c2f1.tar.bz2
scummvm-rg350-6f001d831623a46f643379554d20e94463d8c2f1.zip
Merge branch 'master' into titanic
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.h)27
-rw-r--r--backends/graphics/opengl/pipelines/fixed.cpp70
-rw-r--r--backends/graphics/opengl/pipelines/fixed.h (renamed from backends/graphics/opengl/extensions.cpp)32
-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.cpp332
-rw-r--r--backends/graphics/opengl/shader.h288
-rw-r--r--backends/graphics/opengl/texture.cpp546
-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/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/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/wince/portdefs.h3
-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
64 files changed, 5867 insertions, 582 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.h b/backends/graphics/opengl/pipelines/clut8.h
index 87452429e2..16724e4652 100644
--- a/backends/graphics/opengl/extensions.h
+++ b/backends/graphics/opengl/pipelines/clut8.h
@@ -20,21 +20,26 @@
*
*/
-#ifndef BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
-#define BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
+
+#include "backends/graphics/opengl/pipelines/shader.h"
namespace OpenGL {
-/**
- * Checks for availability of extensions we want to use and initializes them
- * when available.
- */
-void initializeGLExtensions();
+#if !USE_FORCED_GLES
+class CLUT8LookUpPipeline : public ShaderPipeline {
+public:
+ CLUT8LookUpPipeline();
-/**
- * Whether non power of two textures are supported
- */
-extern bool g_extNPOTSupported;
+ void setPaletteTexture(const GLTexture *paletteTexture) { _paletteTexture = paletteTexture; }
+
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates);
+
+private:
+ const GLTexture *_paletteTexture;
+};
+#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
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/extensions.cpp b/backends/graphics/opengl/pipelines/fixed.h
index 4482ef82b5..6bfe140c19 100644
--- a/backends/graphics/opengl/extensions.cpp
+++ b/backends/graphics/opengl/pipelines/fixed.h
@@ -20,29 +20,27 @@
*
*/
-#include "backends/graphics/opengl/extensions.h"
-#include "backends/graphics/opengl/opengl-sys.h"
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
-#include "common/tokenizer.h"
+#include "backends/graphics/opengl/pipelines/pipeline.h"
namespace OpenGL {
-bool g_extNPOTSupported = false;
+#if !USE_FORCED_GLES2
+class FixedPipeline : public Pipeline {
+public:
+ virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
-void initializeGLExtensions() {
- const char *extString = (const char *)glGetString(GL_EXTENSIONS);
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates);
- // Initialize default state.
- g_extNPOTSupported = false;
+ virtual void setProjectionMatrix(const GLfloat *projectionMatrix);
- Common::StringTokenizer tokenizer(extString, " ");
- while (!tokenizer.empty()) {
- Common::String token = tokenizer.nextToken();
-
- if (token == "GL_ARB_texture_non_power_of_two") {
- g_extNPOTSupported = true;
- }
- }
-}
+protected:
+ virtual void activateInternal();
+};
+#endif // !USE_FORCED_GLES2
} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/pipelines/pipeline.cpp b/backends/graphics/opengl/pipelines/pipeline.cpp
new file mode 100644
index 0000000000..6a59cd28e7
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/pipeline.cpp
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+#include "backends/graphics/opengl/framebuffer.h"
+
+namespace OpenGL {
+
+Pipeline::Pipeline()
+ : _activeFramebuffer(nullptr), _isActive(false) {
+}
+
+void Pipeline::activate() {
+ _isActive = true;
+
+ if (_activeFramebuffer) {
+ _activeFramebuffer->activate();
+ }
+
+ activateInternal();
+}
+
+void Pipeline::deactivate() {
+ deactivateInternal();
+
+ if (_activeFramebuffer) {
+ _activeFramebuffer->deactivate();
+ }
+
+ _isActive = false;
+}
+
+Framebuffer *Pipeline::setFramebuffer(Framebuffer *framebuffer) {
+ Framebuffer *oldFramebuffer = _activeFramebuffer;
+ if (_isActive && oldFramebuffer) {
+ oldFramebuffer->deactivate();
+ }
+
+ _activeFramebuffer = framebuffer;
+ if (_isActive && _activeFramebuffer) {
+ _activeFramebuffer->activate();
+ }
+
+ return oldFramebuffer;
+}
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/pipelines/pipeline.h b/backends/graphics/opengl/pipelines/pipeline.h
new file mode 100644
index 0000000000..9f32d33b95
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/pipeline.h
@@ -0,0 +1,126 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
+
+#include "backends/graphics/opengl/opengl-sys.h"
+#include "backends/graphics/opengl/texture.h"
+
+namespace OpenGL {
+
+class Framebuffer;
+
+/**
+ * Interface for OpenGL pipeline functionality.
+ *
+ * This encapsulates differences in various rendering pipelines used for
+ * OpenGL, OpenGL ES 1, and OpenGL ES 2.
+ */
+class Pipeline {
+public:
+ Pipeline();
+ virtual ~Pipeline() {}
+
+ /**
+ * Activate the pipeline.
+ *
+ * This sets the OpenGL state to make use of drawing with the given
+ * OpenGL pipeline.
+ */
+ void activate();
+
+ /**
+ * Deactivate the pipeline.
+ */
+ void deactivate();
+
+ /**
+ * Set framebuffer to render to.
+ *
+ * Client is responsible for any memory management related to framebuffer.
+ *
+ * @param framebuffer Framebuffer to activate.
+ * @return Formerly active framebuffer.
+ */
+ Framebuffer *setFramebuffer(Framebuffer *framebuffer);
+
+ /**
+ * Set modulation color.
+ *
+ * @param r Red component in [0,1].
+ * @param g Green component in [0,1].
+ * @param b Blue component in [0,1].
+ * @param a Alpha component in [0,1].
+ */
+ virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) = 0;
+
+ /**
+ * Draw a texture rectangle to the currently active framebuffer.
+ *
+ * @param texture Texture to use for drawing.
+ * @param coordinates x1, y1, x2, y2 coordinates where to draw the texture.
+ */
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates) = 0;
+
+ void drawTexture(const GLTexture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
+ const GLfloat coordinates[4*2] = {
+ x, y,
+ x + w, y,
+ x, y + h,
+ x + w, y + h
+ };
+ drawTexture(texture, coordinates);
+ }
+
+ /**
+ * Set the projection matrix.
+ *
+ * This is intended to be only ever be used by Framebuffer subclasses.
+ */
+ virtual void setProjectionMatrix(const GLfloat *projectionMatrix) = 0;
+
+protected:
+ /**
+ * Activate the pipeline.
+ *
+ * This sets the OpenGL state to make use of drawing with the given
+ * OpenGL pipeline.
+ */
+ virtual void activateInternal() = 0;
+
+ /**
+ * Deactivate the pipeline.
+ */
+ virtual void deactivateInternal() {}
+
+ bool isActive() const { return _isActive; }
+
+ Framebuffer *_activeFramebuffer;
+
+private:
+ bool _isActive;
+};
+
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/pipelines/shader.cpp b/backends/graphics/opengl/pipelines/shader.cpp
new file mode 100644
index 0000000000..8e38458f73
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/shader.cpp
@@ -0,0 +1,94 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/pipelines/shader.h"
+#include "backends/graphics/opengl/shader.h"
+#include "backends/graphics/opengl/framebuffer.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES
+ShaderPipeline::ShaderPipeline(Shader *shader)
+ : _activeShader(shader), _colorAttributes() {
+ _vertexAttribLocation = shader->getAttributeLocation("position");
+ _texCoordAttribLocation = shader->getAttributeLocation("texCoordIn");
+ _colorAttribLocation = shader->getAttributeLocation("blendColorIn");
+
+ assert(_vertexAttribLocation != -1);
+ assert(_texCoordAttribLocation != -1);
+ assert(_colorAttribLocation != -1);
+
+ // One of the attributes needs to be passed through location 0, otherwise
+ // we get no output for GL contexts due to GL compatibility reasons. Let's
+ // check whether this ever happens. If this ever gets hit, we need to
+ // enable location 0 and pass some dummy values through it to fix output.
+ assert( _vertexAttribLocation == 0
+ || _texCoordAttribLocation == 0
+ || _colorAttribLocation == 0);
+}
+
+void ShaderPipeline::activateInternal() {
+ GL_CALL(glEnableVertexAttribArray(_vertexAttribLocation));
+ GL_CALL(glEnableVertexAttribArray(_texCoordAttribLocation));
+ GL_CALL(glEnableVertexAttribArray(_colorAttribLocation));
+
+ if (g_context.multitextureSupported) {
+ GL_CALL(glActiveTexture(GL_TEXTURE0));
+ }
+
+ _activeShader->activate();
+}
+
+void ShaderPipeline::deactivateInternal() {
+ GL_CALL(glDisableVertexAttribArray(_vertexAttribLocation));
+ GL_CALL(glDisableVertexAttribArray(_texCoordAttribLocation));
+ GL_CALL(glDisableVertexAttribArray(_colorAttribLocation));
+
+ _activeShader->deactivate();
+}
+
+void ShaderPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ GLfloat *dst = _colorAttributes;
+ for (uint i = 0; i < 4; ++i) {
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+ *dst++ = a;
+ }
+
+ GL_CALL(glVertexAttribPointer(_colorAttribLocation, 4, GL_FLOAT, GL_FALSE, 0, _colorAttributes));
+}
+
+void ShaderPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) {
+ texture.bind();
+
+ GL_CALL(glVertexAttribPointer(_texCoordAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, texture.getTexCoords()));
+ GL_CALL(glVertexAttribPointer(_vertexAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, coordinates));
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
+}
+
+void ShaderPipeline::setProjectionMatrix(const GLfloat *projectionMatrix) {
+ _activeShader->setUniform("projection", new ShaderUniformMatrix44(projectionMatrix));
+}
+#endif // !USE_FORCED_GLES
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/pipelines/shader.h b/backends/graphics/opengl/pipelines/shader.h
new file mode 100644
index 0000000000..6159607099
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/shader.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
+
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES
+class Shader;
+
+class ShaderPipeline : public Pipeline {
+public:
+ ShaderPipeline(Shader *shader);
+
+ virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates);
+
+ virtual void setProjectionMatrix(const GLfloat *projectionMatrix);
+
+protected:
+ virtual void activateInternal();
+ virtual void deactivateInternal();
+
+ GLint _vertexAttribLocation;
+ GLint _texCoordAttribLocation;
+ GLint _colorAttribLocation;
+
+ GLfloat _colorAttributes[4*4];
+
+ Shader *const _activeShader;
+};
+#endif // !USE_FORCED_GLES
+
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/shader.cpp b/backends/graphics/opengl/shader.cpp
new file mode 100644
index 0000000000..27981f25dc
--- /dev/null
+++ b/backends/graphics/opengl/shader.cpp
@@ -0,0 +1,332 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/shader.h"
+
+#if !USE_FORCED_GLES
+
+#include "common/textconsole.h"
+#include "common/util.h"
+
+namespace Common {
+DECLARE_SINGLETON(OpenGL::ShaderManager);
+}
+
+namespace OpenGL {
+
+namespace {
+
+#pragma mark - Builtin Shader Sources -
+
+const char *const g_defaultVertexShader =
+ "attribute vec4 position;\n"
+ "attribute vec2 texCoordIn;\n"
+ "attribute vec4 blendColorIn;\n"
+ "\n"
+ "uniform mat4 projection;\n"
+ "\n"
+ "varying vec2 texCoord;\n"
+ "varying vec4 blendColor;\n"
+ "\n"
+ "void main(void) {\n"
+ "\ttexCoord = texCoordIn;\n"
+ "\tblendColor = blendColorIn;\n"
+ "\tgl_Position = projection * position;\n"
+ "}\n";
+
+const char *const g_defaultFragmentShader =
+ "varying vec2 texCoord;\n"
+ "varying vec4 blendColor;\n"
+ "\n"
+ "uniform sampler2D texture;\n"
+ "\n"
+ "void main(void) {\n"
+ "\tgl_FragColor = blendColor * texture2D(texture, texCoord);\n"
+ "}\n";
+
+const char *const g_lookUpFragmentShader =
+ "varying vec2 texCoord;\n"
+ "varying vec4 blendColor;\n"
+ "\n"
+ "uniform sampler2D texture;\n"
+ "uniform sampler2D palette;\n"
+ "\n"
+ "const float adjustFactor = 255.0 / 256.0 + 1.0 / (2.0 * 256.0);"
+ "\n"
+ "void main(void) {\n"
+ "\tvec4 index = texture2D(texture, texCoord);\n"
+ "\tgl_FragColor = blendColor * texture2D(palette, vec2(index.a * adjustFactor, 0.0));\n"
+ "}\n";
+
+
+// Taken from: https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_03#OpenGL_ES_2_portability
+const char *const g_precisionDefines =
+ "#ifdef GL_ES\n"
+ "\t#if defined(GL_FRAGMENT_PRECISION_HIGH) && GL_FRAGMENT_PRECISION_HIGH == 1\n"
+ "\t\tprecision highp float;\n"
+ "\t#else\n"
+ "\t\tprecision mediump float;\n"
+ "\t#endif\n"
+ "#else\n"
+ "\t#define highp\n"
+ "\t#define mediump\n"
+ "\t#define lowp\n"
+ "#endif\n";
+
+} // End of anonymous namespace
+
+#pragma mark - Uniform Values -
+
+void ShaderUniformInteger::set(GLint location) const {
+ GL_CALL(glUniform1i(location, _value));
+}
+
+void ShaderUniformFloat::set(GLint location) const {
+ GL_CALL(glUniform1f(location, _value));
+}
+
+void ShaderUniformMatrix44::set(GLint location) const {
+ GL_CALL(glUniformMatrix4fv(location, 1, GL_FALSE, _matrix));
+}
+
+#pragma mark - Shader Implementation -
+
+Shader::Shader(const Common::String &vertex, const Common::String &fragment)
+ : _vertex(vertex), _fragment(fragment), _isActive(false), _program(0), _uniforms() {
+ recreate();
+}
+
+Shader::~Shader() {
+ // According to extension specification glDeleteObjectARB silently ignores
+ // 0. However, with nVidia drivers this can cause GL_INVALID_VALUE, thus
+ // we do not call it with 0 as parameter to avoid warnings.
+ if (_program) {
+ GL_CALL_SAFE(glDeleteProgram, (_program));
+ }
+}
+
+void Shader::destroy() {
+ // According to extension specification glDeleteObjectARB silently ignores
+ // 0. However, with nVidia drivers this can cause GL_INVALID_VALUE, thus
+ // we do not call it with 0 as parameter to avoid warnings.
+ if (_program) {
+ GL_CALL(glDeleteProgram(_program));
+ _program = 0;
+ }
+}
+
+bool Shader::recreate() {
+ // Make sure any old programs are destroyed properly.
+ destroy();
+
+ GLshader vertexShader = compileShader(_vertex.c_str(), GL_VERTEX_SHADER);
+ if (!vertexShader) {
+ return false;
+ }
+
+ GLshader fragmentShader = compileShader(_fragment.c_str(), GL_FRAGMENT_SHADER);
+ if (!fragmentShader) {
+ GL_CALL(glDeleteShader(vertexShader));
+ return false;
+ }
+
+ GL_ASSIGN(_program, glCreateProgram());
+ if (!_program) {
+ GL_CALL(glDeleteShader(vertexShader));
+ GL_CALL(glDeleteShader(fragmentShader));
+ return false;
+ }
+
+ GL_CALL(glAttachShader(_program, vertexShader));
+ GL_CALL(glAttachShader(_program, fragmentShader));
+
+ GL_CALL(glLinkProgram(_program));
+
+ GL_CALL(glDetachShader(_program, fragmentShader));
+ GL_CALL(glDeleteShader(fragmentShader));
+
+ GL_CALL(glDetachShader(_program, vertexShader));
+ GL_CALL(glDeleteShader(vertexShader));
+
+ GLint result;
+ GL_CALL(glGetProgramiv(_program, GL_LINK_STATUS, &result));
+ if (result == GL_FALSE) {
+ GLint logSize;
+ GL_CALL(glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &logSize));
+
+ GLchar *log = new GLchar[logSize];
+ GL_CALL(glGetProgramInfoLog(_program, logSize, nullptr, log));
+ warning("Could not link shader: \"%s\"", log);
+ delete[] log;
+
+ destroy();
+ return false;
+ }
+
+ // Set program object in case shader is active during recreation.
+ if (_isActive) {
+ GL_CALL(glUseProgram(_program));
+ }
+
+ for (UniformMap::iterator i = _uniforms.begin(), end = _uniforms.end(); i != end; ++i) {
+ i->_value.location = getUniformLocation(i->_key.c_str());
+ i->_value.altered = true;
+ if (_isActive) {
+ i->_value.set();
+ }
+ }
+
+ return true;
+}
+
+void Shader::activate() {
+ // Activate program.
+ GL_CALL(glUseProgram(_program));
+
+ // Reset changed uniform values.
+ for (UniformMap::iterator i = _uniforms.begin(), end = _uniforms.end(); i != end; ++i) {
+ i->_value.set();
+ }
+
+ _isActive = true;
+}
+
+void Shader::deactivate() {
+ _isActive = false;
+}
+
+GLint Shader::getAttributeLocation(const char *name) const {
+ GLint result = -1;
+ GL_ASSIGN(result, glGetAttribLocation(_program, name));
+ return result;
+}
+
+GLint Shader::getUniformLocation(const char *name) const {
+ GLint result = -1;
+ GL_ASSIGN(result, glGetUniformLocation(_program, name));
+ return result;
+}
+
+bool Shader::setUniform(const Common::String &name, ShaderUniformValue *value) {
+ UniformMap::iterator uniformIter = _uniforms.find(name);
+ Uniform *uniform;
+
+ if (uniformIter == _uniforms.end()) {
+ const GLint location = getUniformLocation(name.c_str());
+ if (location == -1) {
+ delete value;
+ return false;
+ }
+
+ uniform = &_uniforms[name];
+ uniform->location = location;
+ } else {
+ uniform = &uniformIter->_value;
+ }
+
+ uniform->value = Common::SharedPtr<ShaderUniformValue>(value);
+ uniform->altered = true;
+ if (_isActive) {
+ uniform->set();
+ }
+
+ return true;
+}
+
+GLshader Shader::compileShader(const char *source, GLenum shaderType) {
+ GLshader handle;
+ GL_ASSIGN(handle, glCreateShader(shaderType));
+ if (!handle) {
+ return 0;
+ }
+
+ const char *const sources[2] = {
+ g_precisionDefines,
+ source
+ };
+
+ GL_CALL(glShaderSource(handle, ARRAYSIZE(sources), sources, nullptr));
+ GL_CALL(glCompileShader(handle));
+
+ GLint result;
+ GL_CALL(glGetShaderiv(handle, GL_COMPILE_STATUS, &result));
+ if (result == GL_FALSE) {
+ GLint logSize;
+ GL_CALL(glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &logSize));
+
+ GLchar *log = new GLchar[logSize];
+ GL_CALL(glGetShaderInfoLog(handle, logSize, nullptr, log));
+ warning("Could not compile shader \"%s\": \"%s\"", source, log);
+ delete[] log;
+
+ GL_CALL(glDeleteShader(handle));
+ return 0;
+ }
+
+ return handle;
+}
+
+ShaderManager::ShaderManager() : _initializeShaders(true) {
+}
+
+ShaderManager::~ShaderManager() {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ delete _builtIn[i];
+ }
+}
+
+void ShaderManager::notifyDestroy() {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ _builtIn[i]->destroy();
+ }
+}
+
+void ShaderManager::notifyCreate() {
+ if (_initializeShaders) {
+ _initializeShaders = false;
+
+ _builtIn[kDefault] = new Shader(g_defaultVertexShader, g_defaultFragmentShader);
+ _builtIn[kCLUT8LookUp] = new Shader(g_defaultVertexShader, g_lookUpFragmentShader);
+ _builtIn[kCLUT8LookUp]->setUniform1I("palette", 1);
+
+ for (uint i = 0; i < kMaxUsages; ++i) {
+ _builtIn[i]->setUniform1I("texture", 0);
+ }
+ } else {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ _builtIn[i]->recreate();
+ }
+ }
+}
+
+Shader *ShaderManager::query(ShaderUsage shader) const {
+ if (shader == kMaxUsages) {
+ warning("OpenGL: ShaderManager::query used with kMaxUsages");
+ return nullptr;
+ }
+
+ return _builtIn[shader];
+}
+
+} // End of namespace OpenGL
+
+#endif // !USE_FORCED_GLES
diff --git a/backends/graphics/opengl/shader.h b/backends/graphics/opengl/shader.h
new file mode 100644
index 0000000000..ec1e516d14
--- /dev/null
+++ b/backends/graphics/opengl/shader.h
@@ -0,0 +1,288 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_SHADER_H
+#define BACKENDS_GRAPHICS_OPENGL_SHADER_H
+
+#include "backends/graphics/opengl/opengl-sys.h"
+
+#if !USE_FORCED_GLES
+
+#include "common/singleton.h"
+#include "common/hash-str.h"
+#include "common/ptr.h"
+
+namespace OpenGL {
+
+/**
+ * A generic uniform value interface for a shader program.
+ */
+class ShaderUniformValue {
+public:
+ virtual ~ShaderUniformValue() {}
+
+ /**
+ * Setup the the value to the given location.
+ *
+ * @param location Location of the uniform.
+ */
+ virtual void set(GLint location) const = 0;
+};
+
+/**
+ * Integer value for a shader uniform.
+ */
+class ShaderUniformInteger : public ShaderUniformValue {
+public:
+ ShaderUniformInteger(GLint value) : _value(value) {}
+
+ virtual void set(GLint location) const override;
+
+private:
+ const GLint _value;
+};
+
+/**
+ * Float value for a shader uniform.
+ */
+class ShaderUniformFloat : public ShaderUniformValue {
+public:
+ ShaderUniformFloat(GLfloat value) : _value(value) {}
+
+ virtual void set(GLint location) const override;
+
+private:
+ const GLfloat _value;
+};
+
+/**
+ * 4x4 Matrix value for a shader uniform.
+ */
+class ShaderUniformMatrix44 : public ShaderUniformValue {
+public:
+ ShaderUniformMatrix44(const GLfloat *mat44) {
+ memcpy(_matrix, mat44, sizeof(_matrix));
+ }
+
+ virtual void set(GLint location) const override;
+
+private:
+ GLfloat _matrix[4*4];
+};
+
+class Shader {
+public:
+ Shader(const Common::String &vertex, const Common::String &fragment);
+ ~Shader();
+
+ /**
+ * Destroy the shader program.
+ *
+ * This keeps the vertex and fragment shader sources around and thus
+ * allows for recreating the shader on context recreation. It also keeps
+ * the uniform state around.
+ */
+ void destroy();
+
+ /**
+ * Recreate shader program.
+ *
+ * @return true on success, false on failure.
+ */
+ bool recreate();
+
+ /**
+ * Make shader active.
+ */
+ void activate();
+
+ /**
+ * Make shader inactive.
+ */
+ void deactivate();
+
+ /**
+ * Return location for attribute with given name.
+ *
+ * @param name Name of the attribute to look up in the shader.
+ * @return The loctaion of -1 if attribute was not found.
+ */
+ GLint getAttributeLocation(const char *name) const;
+ GLint getAttributeLocation(const Common::String &name) const {
+ return getAttributeLocation(name.c_str());
+ }
+
+ /**
+ * Return location for uniform with given name.
+ *
+ * @param name Name of the uniform to look up in the shader.
+ * @return The location or -1 if uniform was not found.
+ */
+ GLint getUniformLocation(const char *name) const;
+ GLint getUniformLocation(const Common::String &name) const {
+ return getUniformLocation(name.c_str());
+ }
+
+ /**
+ * Bind value to uniform.
+ *
+ * @param name The name of the uniform to be set.
+ * @param value The value to be set.
+ * @return 'false' on error (i.e. uniform unknown or otherwise),
+ * 'true' otherwise.
+ */
+ bool setUniform(const Common::String &name, ShaderUniformValue *value);
+
+ /**
+ * Bind integer value to uniform.
+ *
+ * @param name The name of the uniform to be set.
+ * @param value The value to be set.
+ * @return 'false' on error (i.e. uniform unknown or otherwise),
+ * 'true' otherwise.
+ */
+ bool setUniform1I(const Common::String &name, GLint value) {
+ return setUniform(name, new ShaderUniformInteger(value));
+ }
+protected:
+ /**
+ * Vertex shader sources.
+ */
+ const Common::String _vertex;
+
+ /**
+ * Fragment shader sources.
+ */
+ const Common::String _fragment;
+
+ /**
+ * Whether the shader is active or not.
+ */
+ bool _isActive;
+
+ /**
+ * Shader program handle.
+ */
+ GLprogram _program;
+
+ /**
+ * A uniform descriptor.
+ *
+ * This stores the state of a shader uniform. The state is made up of the
+ * uniform location, whether the state was altered since last set, and the
+ * value of the uniform.
+ */
+ struct Uniform {
+ Uniform() : location(-1), altered(false), value() {}
+ Uniform(GLint loc, ShaderUniformValue *val)
+ : location(loc), altered(true), value(val) {}
+
+ /**
+ * Write uniform value into currently active shader.
+ */
+ void set() {
+ if (altered && value) {
+ value->set(location);
+ altered = false;
+ }
+ }
+
+ /**
+ * The location of the uniform or -1 in case it does not exist.
+ */
+ GLint location;
+
+ /**
+ * Whether the uniform state was aletered since last 'set'.
+ */
+ bool altered;
+
+ /**
+ * The value of the uniform.
+ */
+ Common::SharedPtr<ShaderUniformValue> value;
+ };
+
+ typedef Common::HashMap<Common::String, Uniform> UniformMap;
+
+ /**
+ * Map from uniform name to associated uniform description.
+ */
+ UniformMap _uniforms;
+
+ /**
+ * Compile a vertex or fragment shader.
+ *
+ * @param source Sources to the shader.
+ * @param shaderType Type of shader to compile (GL_FRAGMENT_SHADER_ARB or
+ * GL_VERTEX_SHADER_ARB)
+ * @return The shader object or 0 on failure.
+ */
+ static GLshader compileShader(const char *source, GLenum shaderType);
+};
+
+class ShaderManager : public Common::Singleton<ShaderManager> {
+public:
+ enum ShaderUsage {
+ /** Default shader implementing the GL fixed-function pipeline. */
+ kDefault = 0,
+
+ /** CLUT8 look up shader. */
+ kCLUT8LookUp,
+
+ /** Number of built-in shaders. Should not be used for query. */
+ kMaxUsages
+ };
+
+ /**
+ * Notify shader manager about context destruction.
+ */
+ void notifyDestroy();
+
+ /**
+ * Notify shader manager about context creation.
+ */
+ void notifyCreate();
+
+ /**
+ * Query a built-in shader.
+ */
+ Shader *query(ShaderUsage shader) const;
+
+private:
+ friend class Common::Singleton<SingletonBaseType>;
+ ShaderManager();
+ ~ShaderManager();
+
+ bool _initializeShaders;
+
+ Shader *_builtIn[kMaxUsages];
+};
+
+} // End of namespace OpenGL
+
+/** Shortcut for accessing the font manager. */
+#define ShaderMan (OpenGL::ShaderManager::instance())
+
+#endif // !USE_FORCED_GLES
+
+#endif
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
index 7b0b22d630..8b94549971 100644
--- a/backends/graphics/opengl/texture.cpp
+++ b/backends/graphics/opengl/texture.cpp
@@ -21,8 +21,10 @@
*/
#include "backends/graphics/opengl/texture.h"
-#include "backends/graphics/opengl/extensions.h"
-#include "backends/graphics/opengl/debug.h"
+#include "backends/graphics/opengl/shader.h"
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+#include "backends/graphics/opengl/pipelines/clut8.h"
+#include "backends/graphics/opengl/framebuffer.h"
#include "common/rect.h"
#include "common/textconsole.h"
@@ -41,94 +43,140 @@ static GLuint nextHigher2(GLuint v) {
return ++v;
}
-GLint Texture::_maxTextureSize = 0;
-void Texture::queryTextureInformation() {
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize);
- debug(5, "OpenGL maximum texture size: %d", _maxTextureSize);
+GLTexture::GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType)
+ : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType),
+ _width(0), _height(0), _logicalWidth(0), _logicalHeight(0),
+ _texCoords(), _glFilter(GL_NEAREST),
+ _glTexture(0) {
+ create();
}
-Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
- : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), _format(format), _glFilter(GL_NEAREST),
- _glTexture(0), _textureData(), _userPixelData(), _allDirty(false) {
- recreateInternalTexture();
+GLTexture::~GLTexture() {
+ GL_CALL_SAFE(glDeleteTextures, (1, &_glTexture));
}
-Texture::~Texture() {
- releaseInternalTexture();
- _textureData.free();
+void GLTexture::enableLinearFiltering(bool enable) {
+ if (enable) {
+ _glFilter = GL_LINEAR;
+ } else {
+ _glFilter = GL_NEAREST;
+ }
+
+ bind();
+
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
}
-void Texture::releaseInternalTexture() {
- GLCALL(glDeleteTextures(1, &_glTexture));
+void GLTexture::destroy() {
+ GL_CALL(glDeleteTextures(1, &_glTexture));
_glTexture = 0;
}
-void Texture::recreateInternalTexture() {
+void GLTexture::create() {
// Release old texture name in case it exists.
- releaseInternalTexture();
+ destroy();
// Get a new texture name.
- GLCALL(glGenTextures(1, &_glTexture));
+ GL_CALL(glGenTextures(1, &_glTexture));
// Set up all texture parameters.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
- GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
-
- // In case there is an actual texture setup we reinitialize it.
- if (_textureData.getPixels()) {
+ bind();
+ GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+ // If a size is specified, allocate memory for it.
+ if (_width != 0 && _height != 0) {
// Allocate storage for OpenGL texture.
- GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
- _textureData.h, 0, _glFormat, _glType, NULL));
-
- // Mark dirts such that it will be completely refreshed the next time.
- flagDirty();
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, _height,
+ 0, _glFormat, _glType, NULL));
}
}
-void Texture::enableLinearFiltering(bool enable) {
- if (enable) {
- _glFilter = GL_LINEAR;
+void GLTexture::bind() const {
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+}
+
+void GLTexture::setSize(uint width, uint height) {
+ const uint oldWidth = _width;
+ const uint oldHeight = _height;
+
+ if (!g_context.NPOTSupported) {
+ _width = nextHigher2(width);
+ _height = nextHigher2(height);
} else {
- _glFilter = GL_NEAREST;
+ _width = width;
+ _height = height;
}
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+ _logicalWidth = width;
+ _logicalHeight = height;
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
-}
+ // If a size is specified, allocate memory for it.
+ if (width != 0 && height != 0) {
+ const GLfloat texWidth = (GLfloat)width / _width;
+ const GLfloat texHeight = (GLfloat)height / _height;
-void Texture::allocate(uint width, uint height) {
- uint texWidth = width, texHeight = height;
- if (!g_extNPOTSupported) {
- texWidth = nextHigher2(texWidth);
- texHeight = nextHigher2(texHeight);
- }
+ _texCoords[0] = 0;
+ _texCoords[1] = 0;
- // In case the needed texture dimension changed we will reinitialize the
- // texture.
- if (texWidth != _textureData.w || texHeight != _textureData.h) {
- // Create a buffer for the texture data.
- _textureData.create(texWidth, texHeight, _format);
+ _texCoords[2] = texWidth;
+ _texCoords[3] = 0;
- // Set the texture.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+ _texCoords[4] = 0;
+ _texCoords[5] = texHeight;
- // Allocate storage for OpenGL texture.
- GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
- _textureData.h, 0, _glFormat, _glType, NULL));
+ _texCoords[6] = texWidth;
+ _texCoords[7] = texHeight;
+
+ // Allocate storage for OpenGL texture if necessary.
+ if (oldWidth != _width || oldHeight != _height) {
+ bind();
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width,
+ _height, 0, _glFormat, _glType, NULL));
+ }
}
+}
- // Create a sub-buffer for raw access.
- _userPixelData = _textureData.getSubArea(Common::Rect(width, height));
+void GLTexture::updateArea(const Common::Rect &area, const Graphics::Surface &src) {
+ // Set the texture on the active texture unit.
+ bind();
+
+ // Update the actual texture.
+ // Although we have the area of the texture buffer we want to update we
+ // cannot take advantage of the left/right boundries here because it is
+ // not possible to specify a pitch to glTexSubImage2D. To be precise, with
+ // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
+ // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
+ // with the following options:
+ //
+ // 1) (As we do right now) Simply always update the whole texture lines of
+ // rect changed. This is simplest to implement. In case performance is
+ // really an issue we can think of switching to another method.
+ //
+ // 2) Copy the dirty rect to a temporary buffer and upload that by using
+ // glTexSubImage2D. This is what the Android backend does. It is more
+ // complicated though.
+ //
+ // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
+ // graphics manager did but it is much slower! Thus, we do not use it.
+ GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, area.top, src.w, area.height(),
+ _glFormat, _glType, src.getBasePtr(0, area.top)));
+}
+
+//
+// Surface
+//
+
+Surface::Surface()
+ : _allDirty(false), _dirtyArea() {
}
-void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
+void Surface::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
Graphics::Surface *dstSurf = getSurface();
assert(x + w <= dstSurf->w);
assert(y + h <= dstSurf->h);
@@ -159,50 +207,74 @@ void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcP
}
}
-void Texture::fill(uint32 color) {
+void Surface::fill(uint32 color) {
Graphics::Surface *dst = getSurface();
dst->fillRect(Common::Rect(dst->w, dst->h), color);
flagDirty();
}
-void Texture::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
- // Only do any processing when the Texture is initialized.
- if (!_textureData.getPixels()) {
- return;
+Common::Rect Surface::getDirtyArea() const {
+ if (_allDirty) {
+ return Common::Rect(getWidth(), getHeight());
+ } else {
+ return _dirtyArea;
+ }
+}
+
+//
+// Surface implementations
+//
+
+Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
+ : Surface(), _format(format), _glTexture(glIntFormat, glFormat, glType),
+ _textureData(), _userPixelData() {
+}
+
+Texture::~Texture() {
+ _textureData.free();
+}
+
+void Texture::destroy() {
+ _glTexture.destroy();
+}
+
+void Texture::recreate() {
+ _glTexture.create();
+
+ // In case image date exists assure it will be completely refreshed next
+ // time.
+ if (_textureData.getPixels()) {
+ flagDirty();
+ }
+}
+
+void Texture::enableLinearFiltering(bool enable) {
+ _glTexture.enableLinearFiltering(enable);
+}
+
+void Texture::allocate(uint width, uint height) {
+ // Assure the texture can contain our user data.
+ _glTexture.setSize(width, height);
+
+ // In case the needed texture dimension changed we will reinitialize the
+ // texture data buffer.
+ if (_glTexture.getWidth() != _textureData.w || _glTexture.getHeight() != _textureData.h) {
+ // Create a buffer for the texture data.
+ _textureData.create(_glTexture.getWidth(), _glTexture.getHeight(), _format);
}
- // First update any potentional changes.
- updateTexture();
-
- // Set the texture.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
-
- // Calculate the texture rect that will be drawn.
- const GLfloat texWidth = (GLfloat)_userPixelData.w / _textureData.w;
- const GLfloat texHeight = (GLfloat)_userPixelData.h / _textureData.h;
- const GLfloat texcoords[4*2] = {
- 0, 0,
- texWidth, 0,
- 0, texHeight,
- texWidth, texHeight
- };
- GLCALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords));
-
- // Calculate the screen rect where the texture will be drawn.
- const GLfloat vertices[4*2] = {
- x, y,
- x + w, y,
- x, y + h,
- x + w, y + h
- };
- GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices));
-
- // Draw the texture to the screen buffer.
- GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
-}
-
-void Texture::updateTexture() {
+ // Create a sub-buffer for raw access.
+ _userPixelData = _textureData.getSubArea(Common::Rect(width, height));
+
+ // The whole texture is dirty after we changed the size. This fixes
+ // multiple texture size changes without any actual update in between.
+ // Without this we might try to write a too big texture into the GL
+ // texture.
+ flagDirty();
+}
+
+void Texture::updateGLTexture() {
if (!isDirty()) {
return;
}
@@ -211,7 +283,7 @@ void Texture::updateTexture() {
// In case we use linear filtering we might need to duplicate the last
// pixel row/column to avoid glitches with filtering.
- if (_glFilter == GL_LINEAR) {
+ if (_glTexture.isLinearFilteringEnabled()) {
if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) {
uint height = dirtyArea.height();
@@ -238,42 +310,12 @@ void Texture::updateTexture() {
}
}
- // Set the texture.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
-
- // Update the actual texture.
- // Although we keep track of the dirty part of the texture buffer we
- // cannot take advantage of the left/right boundries here because it is
- // not possible to specify a pitch to glTexSubImage2D. To be precise, with
- // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
- // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
- // with the following options:
- //
- // 1) (As we do right now) Simply always update the whole texture lines of
- // rect changed. This is simplest to implement. In case performance is
- // really an issue we can think of switching to another method.
- //
- // 2) Copy the dirty rect to a temporary buffer and upload that by using
- // glTexSubImage2D. This is what the Android backend does. It is more
- // complicated though.
- //
- // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
- // graphics manager did but it is much slower! Thus, we do not use it.
- GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(),
- _glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top)));
+ _glTexture.updateArea(dirtyArea, _textureData);
// We should have handled everything, thus not dirty anymore.
clearDirty();
}
-Common::Rect Texture::getDirtyArea() const {
- if (_allDirty) {
- return Common::Rect(_userPixelData.w, _userPixelData.h);
- } else {
- return _dirtyArea;
- }
-}
-
TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
: Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) {
memset(_palette, 0, sizeof(byte) * format.bytesPerPixel);
@@ -301,6 +343,25 @@ Graphics::PixelFormat TextureCLUT8::getFormat() const {
return Graphics::PixelFormat::createFormatCLUT8();
}
+void TextureCLUT8::setColorKey(uint colorKey) {
+ // We remove all alpha bits from the palette entry of the color key.
+ // This makes sure its properly handled as color key.
+ const uint32 aMask = (0xFF >> _format.aLoss) << _format.aShift;
+
+ if (_format.bytesPerPixel == 2) {
+ uint16 *palette = (uint16 *)_palette + colorKey;
+ *palette &= ~aMask;
+ } else if (_format.bytesPerPixel == 4) {
+ uint32 *palette = (uint32 *)_palette + colorKey;
+ *palette &= ~aMask;
+ } else {
+ warning("TextureCLUT8::setColorKey: Unsupported pixel depth %d", _format.bytesPerPixel);
+ }
+
+ // A palette changes means we need to refresh the whole surface.
+ flagDirty();
+}
+
namespace {
template<typename ColorType>
inline void convertPalette(ColorType *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) {
@@ -312,14 +373,12 @@ inline void convertPalette(ColorType *dst, const byte *src, uint colors, const G
} // End of anonymous namespace
void TextureCLUT8::setPalette(uint start, uint colors, const byte *palData) {
- const Graphics::PixelFormat &hardwareFormat = getHardwareFormat();
-
- if (hardwareFormat.bytesPerPixel == 2) {
- convertPalette<uint16>((uint16 *)_palette + start, palData, colors, hardwareFormat);
- } else if (hardwareFormat.bytesPerPixel == 4) {
- convertPalette<uint32>((uint32 *)_palette + start, palData, colors, hardwareFormat);
+ if (_format.bytesPerPixel == 2) {
+ convertPalette<uint16>((uint16 *)_palette + start, palData, colors, _format);
+ } else if (_format.bytesPerPixel == 4) {
+ convertPalette<uint32>((uint32 *)_palette + start, palData, colors, _format);
} else {
- warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", hardwareFormat.bytesPerPixel);
+ warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", _format.bytesPerPixel);
}
// A palette changes means we need to refresh the whole surface.
@@ -343,7 +402,7 @@ inline void doPaletteLookUp(PixelType *dst, const byte *src, uint width, uint he
}
} // End of anonymous namespace
-void TextureCLUT8::updateTexture() {
+void TextureCLUT8::updateGLTexture() {
if (!isDirty()) {
return;
}
@@ -368,7 +427,222 @@ void TextureCLUT8::updateTexture() {
}
// Do generic handling of updating the texture.
- Texture::updateTexture();
+ Texture::updateGLTexture();
+}
+
+#if !USE_FORCED_GL
+TextureRGB555::TextureRGB555()
+ : Texture(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)),
+ _rgb555Data() {
+}
+
+TextureRGB555::~TextureRGB555() {
+ _rgb555Data.free();
+}
+
+void TextureRGB555::allocate(uint width, uint height) {
+ Texture::allocate(width, height);
+
+ // We only need to reinitialize our RGB555 surface when the output size
+ // changed.
+ if (width == _rgb555Data.w && height == _rgb555Data.h) {
+ return;
+ }
+
+ _rgb555Data.create(width, height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
+}
+
+Graphics::PixelFormat TextureRGB555::getFormat() const {
+ return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+}
+
+void TextureRGB555::updateTexture() {
+ if (!isDirty()) {
+ return;
+ }
+
+ // Convert color space.
+ Graphics::Surface *outSurf = Texture::getSurface();
+
+ const Common::Rect dirtyArea = getDirtyArea();
+
+ uint16 *dst = (uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
+ const uint dstAdd = outSurf->pitch - 2 * dirtyArea.width();
+
+ const uint16 *src = (const uint16 *)_rgb555Data.getBasePtr(dirtyArea.left, dirtyArea.top);
+ const uint srcAdd = _rgb555Data.pitch - 2 * dirtyArea.width();
+
+ for (int height = dirtyArea.height(); height > 0; --height) {
+ for (int width = dirtyArea.width(); width > 0; --width) {
+ const uint16 color = *src++;
+
+ *dst++ = ((color & 0x7C00) << 1) // R
+ | (((color & 0x03E0) << 1) | ((color & 0x0200) >> 4)) // G
+ | (color & 0x001F); // B
+ }
+
+ src = (const uint16 *)((const byte *)src + srcAdd);
+ dst = (uint16 *)((byte *)dst + dstAdd);
+ }
+
+ // Do generic handling of updating the texture.
+ Texture::updateGLTexture();
+}
+#endif // !USE_FORCED_GL
+
+#if !USE_FORCED_GLES
+
+// _clut8Texture needs 8 bits internal precision, otherwise graphics glitches
+// can occur. GL_ALPHA does not have any internal precision requirements.
+// However, in practice (according to fuzzie) it's 8bit. If we run into
+// problems, we need to switch to GL_R8 and GL_RED, but that is only supported
+// for ARB_texture_rg and GLES3+ (EXT_rexture_rg does not support GL_R8).
+TextureCLUT8GPU::TextureCLUT8GPU()
+ : _clut8Texture(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE),
+ _paletteTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE),
+ _target(new TextureTarget()), _clut8Pipeline(new CLUT8LookUpPipeline()),
+ _clut8Vertices(), _clut8Data(), _userPixelData(), _palette(),
+ _paletteDirty(false) {
+ // Allocate space for 256 colors.
+ _paletteTexture.setSize(256, 1);
+
+ // Setup pipeline.
+ _clut8Pipeline->setFramebuffer(_target);
+ _clut8Pipeline->setPaletteTexture(&_paletteTexture);
+}
+
+TextureCLUT8GPU::~TextureCLUT8GPU() {
+ delete _clut8Pipeline;
+ delete _target;
+ _clut8Data.free();
+}
+
+void TextureCLUT8GPU::destroy() {
+ _clut8Texture.destroy();
+ _paletteTexture.destroy();
+ _target->destroy();
+}
+
+void TextureCLUT8GPU::recreate() {
+ _clut8Texture.create();
+ _paletteTexture.create();
+ _target->create();
+
+ // In case image date exists assure it will be completely refreshed next
+ // time.
+ if (_clut8Data.getPixels()) {
+ flagDirty();
+ _paletteDirty = true;
+ }
+}
+
+void TextureCLUT8GPU::enableLinearFiltering(bool enable) {
+ _target->getTexture()->enableLinearFiltering(enable);
+}
+
+void TextureCLUT8GPU::allocate(uint width, uint height) {
+ // Assure the texture can contain our user data.
+ _clut8Texture.setSize(width, height);
+ _target->setSize(width, height);
+
+ // In case the needed texture dimension changed we will reinitialize the
+ // texture data buffer.
+ if (_clut8Texture.getWidth() != _clut8Data.w || _clut8Texture.getHeight() != _clut8Data.h) {
+ // Create a buffer for the texture data.
+ _clut8Data.create(_clut8Texture.getWidth(), _clut8Texture.getHeight(), Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ // Create a sub-buffer for raw access.
+ _userPixelData = _clut8Data.getSubArea(Common::Rect(width, height));
+
+ // Setup structures for internal rendering to _glTexture.
+ _clut8Vertices[0] = 0;
+ _clut8Vertices[1] = 0;
+
+ _clut8Vertices[2] = width;
+ _clut8Vertices[3] = 0;
+
+ _clut8Vertices[4] = 0;
+ _clut8Vertices[5] = height;
+
+ _clut8Vertices[6] = width;
+ _clut8Vertices[7] = height;
+
+ // The whole texture is dirty after we changed the size. This fixes
+ // multiple texture size changes without any actual update in between.
+ // Without this we might try to write a too big texture into the GL
+ // texture.
+ flagDirty();
+}
+
+Graphics::PixelFormat TextureCLUT8GPU::getFormat() const {
+ return Graphics::PixelFormat::createFormatCLUT8();
+}
+
+void TextureCLUT8GPU::setColorKey(uint colorKey) {
+ _palette[colorKey * 4 + 3] = 0x00;
+
+ _paletteDirty = true;
+}
+
+void TextureCLUT8GPU::setPalette(uint start, uint colors, const byte *palData) {
+ byte *dst = _palette + start * 4;
+
+ while (colors-- > 0) {
+ memcpy(dst, palData, 3);
+ dst[3] = 0xFF;
+
+ dst += 4;
+ palData += 3;
+ }
+
+ _paletteDirty = true;
+}
+
+const GLTexture &TextureCLUT8GPU::getGLTexture() const {
+ return *_target->getTexture();
+}
+
+void TextureCLUT8GPU::updateGLTexture() {
+ const bool needLookUp = Surface::isDirty() || _paletteDirty;
+
+ // Update CLUT8 texture if necessary.
+ if (Surface::isDirty()) {
+ _clut8Texture.updateArea(getDirtyArea(), _clut8Data);
+ clearDirty();
+ }
+
+ // Update palette if necessary.
+ if (_paletteDirty) {
+ Graphics::Surface palSurface;
+ palSurface.init(256, 1, 256, _palette,
+#ifdef SCUMM_LITTLE_ENDIAN
+ Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24) // ABGR8888
+#else
+ Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0) // RGBA8888
+#endif
+ );
+
+ _paletteTexture.updateArea(Common::Rect(256, 1), palSurface);
+ _paletteDirty = false;
+ }
+
+ // In case any data changed, do color look up and store result in _target.
+ if (needLookUp) {
+ lookUpColors();
+ }
+}
+
+void TextureCLUT8GPU::lookUpColors() {
+ // Setup pipeline to do color look up.
+ Pipeline *oldPipeline = g_context.setPipeline(_clut8Pipeline);
+
+ // Do color look up.
+ g_context.getActivePipeline()->drawTexture(_clut8Texture, _clut8Vertices);
+
+ // Restore old state.
+ g_context.setPipeline(oldPipeline);
}
+#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h
index ad70833544..3be09cb9f9 100644
--- a/backends/graphics/opengl/texture.h
+++ b/backends/graphics/opengl/texture.h
@@ -32,115 +32,267 @@
namespace OpenGL {
+class Shader;
+
/**
- * An OpenGL texture wrapper. It automatically takes care of all OpenGL
- * texture handling issues and also provides access to the texture data.
+ * A simple GL texture object abstraction.
+ *
+ * This is used for low-level GL texture handling.
*/
-class Texture {
+class GLTexture {
public:
/**
- * Create a new texture with the specific internal format.
+ * Constrcut a new GL texture object.
*
* @param glIntFormat The internal format to use.
* @param glFormat The input format.
* @param glType The input type.
- * @param format The format used for the texture input.
*/
- Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
- virtual ~Texture();
+ GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType);
+ ~GLTexture();
+
+ /**
+ * Enable or disable linear texture filtering.
+ *
+ * @param enable true to enable and false to disable.
+ */
+ void enableLinearFiltering(bool enable);
+
+ /**
+ * Test whether linear filtering is enabled.
+ */
+ bool isLinearFilteringEnabled() const { return (_glFilter == GL_LINEAR); }
/**
* Destroy the OpenGL texture name.
*/
- void releaseInternalTexture();
+ void destroy();
+
+ /**
+ * Create the OpenGL texture name.
+ */
+ void create();
+
+ /**
+ * Bind the texture to the active texture unit.
+ */
+ void bind() const;
+
+ /**
+ * Sets the size of the texture in pixels.
+ *
+ * The internal OpenGL texture might have a different size. To query the
+ * actual size use getWidth()/getHeight().
+ *
+ * @param width The desired logical width.
+ * @param height The desired logical height.
+ */
+ void setSize(uint width, uint height);
+
+ /**
+ * Copy image data to the texture.
+ *
+ * @param area The area to update.
+ * @param src Surface for the whole texture containing the pixel data
+ * to upload. Only the area described by area will be
+ * uploaded.
+ */
+ void updateArea(const Common::Rect &area, const Graphics::Surface &src);
/**
- * Create the OpenGL texture name and flag the whole texture as dirty.
+ * Query the GL texture's width.
*/
- void recreateInternalTexture();
+ uint getWidth() const { return _width; }
+
+ /**
+ * Query the GL texture's height.
+ */
+ uint getHeight() const { return _height; }
+
+ /**
+ * Query the logical texture's width.
+ */
+ uint getLogicalWidth() const { return _logicalWidth; }
+
+ /**
+ * Query the logical texture's height.
+ */
+ uint getLogicalHeight() const { return _logicalHeight; }
+
+ /**
+ * Obtain texture coordinates for rectangular drawing.
+ */
+ const GLfloat *getTexCoords() const { return _texCoords; }
+
+ /**
+ * Obtain texture name.
+ *
+ * Beware that the texture name changes whenever create is used.
+ * destroy will invalidate the texture name.
+ */
+ GLuint getGLTexture() const { return _glTexture; }
+private:
+ const GLenum _glIntFormat;
+ const GLenum _glFormat;
+ const GLenum _glType;
+
+ uint _width, _height;
+ uint _logicalWidth, _logicalHeight;
+ GLfloat _texCoords[4*2];
+
+ GLint _glFilter;
+
+ GLuint _glTexture;
+};
+
+/**
+ * Interface for OpenGL implementations of a 2D surface.
+ */
+class Surface {
+public:
+ Surface();
+ virtual ~Surface() {}
+
+ /**
+ * Destroy OpenGL description of surface.
+ */
+ virtual void destroy() = 0;
+
+ /**
+ * Recreate OpenGL description of surface.
+ */
+ virtual void recreate() = 0;
/**
* Enable or disable linear texture filtering.
*
* @param enable true to enable and false to disable.
*/
- void enableLinearFiltering(bool enable);
+ virtual void enableLinearFiltering(bool enable) = 0;
/**
- * Allocate texture space for the desired dimensions. This wraps any
- * handling of requirements for POT textures.
+ * Allocate storage for surface.
*
* @param width The desired logical width.
* @param height The desired logical height.
*/
- virtual void allocate(uint width, uint height);
+ virtual void allocate(uint width, uint height) = 0;
+ /**
+ * Copy image data to the surface.
+ *
+ * The format of the input data needs to match the format returned by
+ * getFormat.
+ *
+ * @param x X coordinate of upper left corner to copy data to.
+ * @param y Y coordinate of upper left corner to copy data to.
+ * @param w Width of the image data to copy.
+ * @param h Height of the image data to copy.
+ * @param src Pointer to image data.
+ * @param srcPitch The number of bytes in a row of the image data.
+ */
void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch);
+ /**
+ * Fill the surface with a fixed color.
+ *
+ * @param color Color value in format returned by getFormat.
+ */
void fill(uint32 color);
- void draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h);
-
void flagDirty() { _allDirty = true; }
- bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
-
- uint getWidth() const { return _userPixelData.w; }
- uint getHeight() const { return _userPixelData.h; }
+ virtual bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
- /**
- * @return The hardware format of the texture data.
- */
- const Graphics::PixelFormat &getHardwareFormat() const { return _format; }
+ virtual uint getWidth() const = 0;
+ virtual uint getHeight() const = 0;
/**
* @return The logical format of the texture data.
*/
- virtual Graphics::PixelFormat getFormat() const { return _format; }
+ virtual Graphics::PixelFormat getFormat() const = 0;
- virtual Graphics::Surface *getSurface() { return &_userPixelData; }
- virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+ virtual Graphics::Surface *getSurface() = 0;
+ virtual const Graphics::Surface *getSurface() const = 0;
/**
- * @return Whether the texture data is using a palette.
+ * @return Whether the surface is having a palette.
*/
virtual bool hasPalette() const { return false; }
+ /**
+ * Set color key for paletted textures.
+ *
+ * This needs to be called after any palette update affecting the color
+ * key. Calling this multiple times will result in multiple color indices
+ * to be treated as color keys.
+ */
+ virtual void setColorKey(uint colorKey) {}
virtual void setPalette(uint start, uint colors, const byte *palData) {}
- virtual void *getPalette() { return 0; }
- virtual const void *getPalette() const { return 0; }
-
/**
- * Query texture related OpenGL information from the context. This only
- * queries the maximum texture size for now.
+ * Update underlying OpenGL texture to reflect current state.
*/
- static void queryTextureInformation();
+ virtual void updateGLTexture() = 0;
/**
- * @return Return the maximum texture dimensions supported.
+ * Obtain underlying OpenGL texture.
*/
- static GLint getMaximumTextureSize() { return _maxTextureSize; }
+ virtual const GLTexture &getGLTexture() const = 0;
protected:
- virtual void updateTexture();
+ void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
Common::Rect getDirtyArea() const;
private:
- const GLenum _glIntFormat;
- const GLenum _glFormat;
- const GLenum _glType;
+ bool _allDirty;
+ Common::Rect _dirtyArea;
+};
+
+/**
+ * An OpenGL texture wrapper. It automatically takes care of all OpenGL
+ * texture handling issues and also provides access to the texture data.
+ */
+class Texture : public Surface {
+public:
+ /**
+ * Create a new texture with the specific internal format.
+ *
+ * @param glIntFormat The internal format to use.
+ * @param glFormat The input format.
+ * @param glType The input type.
+ * @param format The format used for the texture input.
+ */
+ Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
+ virtual ~Texture();
+
+ virtual void destroy();
+
+ virtual void recreate();
+
+ virtual void enableLinearFiltering(bool enable);
+
+ virtual void allocate(uint width, uint height);
+
+ virtual uint getWidth() const { return _userPixelData.w; }
+ virtual uint getHeight() const { return _userPixelData.h; }
+
+ /**
+ * @return The logical format of the texture data.
+ */
+ virtual Graphics::PixelFormat getFormat() const { return _format; }
+
+ virtual Graphics::Surface *getSurface() { return &_userPixelData; }
+ virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+
+ virtual void updateGLTexture();
+ virtual const GLTexture &getGLTexture() const { return _glTexture; }
+protected:
const Graphics::PixelFormat _format;
- GLint _glFilter;
- GLuint _glTexture;
+private:
+ GLTexture _glTexture;
Graphics::Surface _textureData;
Graphics::Surface _userPixelData;
-
- bool _allDirty;
- Common::Rect _dirtyArea;
- void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
-
- static GLint _maxTextureSize;
};
class TextureCLUT8 : public Texture {
@@ -154,21 +306,95 @@ public:
virtual bool hasPalette() const { return true; }
+ virtual void setColorKey(uint colorKey);
virtual void setPalette(uint start, uint colors, const byte *palData);
- virtual void *getPalette() { return _palette; }
- virtual const void *getPalette() const { return _palette; }
-
virtual Graphics::Surface *getSurface() { return &_clut8Data; }
virtual const Graphics::Surface *getSurface() const { return &_clut8Data; }
-protected:
+ virtual void updateGLTexture();
+private:
+ Graphics::Surface _clut8Data;
+ byte *_palette;
+};
+
+#if !USE_FORCED_GL
+class TextureRGB555 : public Texture {
+public:
+ TextureRGB555();
+ virtual ~TextureRGB555();
+
+ virtual void allocate(uint width, uint height);
+
+ virtual Graphics::PixelFormat getFormat() const;
+
+ virtual Graphics::Surface *getSurface() { return &_rgb555Data; }
+ virtual const Graphics::Surface *getSurface() const { return &_rgb555Data; }
+
virtual void updateTexture();
+private:
+ Graphics::Surface _rgb555Data;
+};
+#endif // !USE_FORCED_GL
+
+#if !USE_FORCED_GLES
+class TextureTarget;
+class CLUT8LookUpPipeline;
+
+class TextureCLUT8GPU : public Surface {
+public:
+ TextureCLUT8GPU();
+ virtual ~TextureCLUT8GPU();
+
+ virtual void destroy();
+
+ virtual void recreate();
+ virtual void enableLinearFiltering(bool enable);
+
+ virtual void allocate(uint width, uint height);
+
+ virtual bool isDirty() const { return _paletteDirty || Surface::isDirty(); }
+
+ virtual uint getWidth() const { return _userPixelData.w; }
+ virtual uint getHeight() const { return _userPixelData.h; }
+
+ virtual Graphics::PixelFormat getFormat() const;
+
+ virtual bool hasPalette() const { return true; }
+
+ virtual void setColorKey(uint colorKey);
+ virtual void setPalette(uint start, uint colors, const byte *palData);
+
+ virtual Graphics::Surface *getSurface() { return &_userPixelData; }
+ virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+
+ virtual void updateGLTexture();
+ virtual const GLTexture &getGLTexture() const;
+
+ static bool isSupportedByContext() {
+ return g_context.shadersSupported
+ && g_context.multitextureSupported
+ && g_context.framebufferObjectSupported;
+ }
private:
+ void lookUpColors();
+
+ GLTexture _clut8Texture;
+ GLTexture _paletteTexture;
+
+ TextureTarget *_target;
+ CLUT8LookUpPipeline *_clut8Pipeline;
+
+ GLfloat _clut8Vertices[4*2];
+
Graphics::Surface _clut8Data;
- byte *_palette;
+ Graphics::Surface _userPixelData;
+
+ byte _palette[4 * 256];
+ bool _paletteDirty;
};
+#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index 0d140ee4d7..7ea1860d93 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -45,6 +45,100 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ // Setup proper SDL OpenGL context creation.
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ OpenGL::ContextType glContextType;
+
+ // Context version 1.4 is choosen arbitrarily based on what most shader
+ // extensions were written against.
+#define DEFAULT_GL_MAJOR 1
+#define DEFAULT_GL_MINOR 4
+
+#define DEFAULT_GLES_MAJOR 1
+#define DEFAULT_GLES_MINOR 1
+
+#define DEFAULT_GLES2_MAJOR 2
+#define DEFAULT_GLES2_MINOR 0
+
+#if USE_FORCED_GL
+ glContextType = OpenGL::kContextGL;
+ _glContextProfileMask = 0;
+ _glContextMajor = DEFAULT_GL_MAJOR;
+ _glContextMinor = DEFAULT_GL_MINOR;
+#elif USE_FORCED_GLES
+ glContextType = OpenGL::kContextGLES;
+ _glContextProfileMask = SDL_GL_CONTEXT_PROFILE_ES;
+ _glContextMajor = DEFAULT_GLES_MAJOR;
+ _glContextMinor = DEFAULT_GLES_MINOR;
+#elif USE_FORCED_GLES2
+ glContextType = OpenGL::kContextGLES2;
+ _glContextProfileMask = SDL_GL_CONTEXT_PROFILE_ES;
+ _glContextMajor = DEFAULT_GLES2_MAJOR;
+ _glContextMinor = DEFAULT_GLES2_MINOR;
+#else
+ bool noDefaults = false;
+
+ // Obtain the default GL(ES) context SDL2 tries to setup.
+ //
+ // Please note this might not actually be SDL2's defaults when multiple
+ // instances of this object have been created. But that is no issue
+ // because then we already set up what we want to use.
+ //
+ // In case no defaults are given we prefer OpenGL over OpenGL ES.
+ if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask) != 0) {
+ _glContextProfileMask = 0;
+ noDefaults = true;
+ }
+
+ if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor) != 0) {
+ noDefaults = true;
+ }
+
+ if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor) != 0) {
+ noDefaults = true;
+ }
+
+ if (noDefaults) {
+ if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) {
+ _glContextMajor = DEFAULT_GLES_MAJOR;
+ _glContextMinor = DEFAULT_GLES_MINOR;
+ } else {
+ _glContextProfileMask = 0;
+ _glContextMajor = DEFAULT_GL_MAJOR;
+ _glContextMinor = DEFAULT_GL_MINOR;
+ }
+ }
+
+ if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) {
+ if (_glContextMajor >= 2) {
+ glContextType = OpenGL::kContextGLES2;
+ } else {
+ glContextType = OpenGL::kContextGLES;
+ }
+ } else if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_CORE) {
+ glContextType = OpenGL::kContextGL;
+
+ // Core profile does not allow legacy functionality, which we use.
+ // Thus we request a standard OpenGL context.
+ _glContextProfileMask = 0;
+ _glContextMajor = DEFAULT_GL_MAJOR;
+ _glContextMinor = DEFAULT_GL_MINOR;
+ } else {
+ glContextType = OpenGL::kContextGL;
+ }
+#undef DEFAULT_GL_MAJOR
+#undef DEFAULT_GL_MINOR
+#undef DEFAULT_GLES_MAJOR
+#undef DEFAULT_GLES_MINOR
+#undef DEFAULT_GLES2_MAJOR
+#undef DEFAULT_GLES2_MINOR
+#endif
+
+ setContextType(glContextType);
+#else
+ setContextType(OpenGL::kContextGL);
+#endif
+
// Retrieve a list of working fullscreen modes
#if SDL_VERSION_ATLEAST(2, 0, 0)
const int numModes = SDL_GetNumDisplayModes(0);
@@ -100,6 +194,10 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt
}
OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() {
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ notifyContextDestroy();
+ SDL_GL_DeleteContext(_glContext);
+#endif
}
void OpenGLSdlGraphicsManager::activateManager() {
@@ -210,20 +308,26 @@ Common::List<Graphics::PixelFormat> OpenGLSdlGraphicsManager::getSupportedFormat
// RGBA4444
formats.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0));
-#ifndef USE_GLES
+#if !USE_FORCED_GLES && !USE_FORCED_GLES2
+#if !USE_FORCED_GL
+ if (!isGLESContext()) {
+#endif
#ifdef SCUMM_LITTLE_ENDIAN
- // RGBA8888
- formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+ // RGBA8888
+ formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
#else
- // ABGR8888
- formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+ // ABGR8888
+ formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+#endif
+#if !USE_FORCED_GL
+ }
+#endif
#endif
- // ARGB8888, this should not be here, but Sword25 requires it. :-/
- formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
// RGB555, this is used by SCUMM HE 16 bit games.
+ // This is not natively supported by OpenGL ES implementations, we convert
+ // the pixel format internally.
formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
-#endif
formats.push_back(Graphics::PixelFormat::createFormatCLUT8());
@@ -305,6 +409,10 @@ void OpenGLSdlGraphicsManager::refreshScreen() {
#endif
}
+void *OpenGLSdlGraphicsManager::getProcAddress(const char *name) const {
+ return SDL_GL_GetProcAddress(name);
+}
+
bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
// In case we request a fullscreen mode we will use the mode the user
// has chosen last time or the biggest mode available.
@@ -378,6 +486,11 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
flags |= SDL_WINDOW_RESIZABLE;
}
+ // Request a OpenGL (ES) context we can use.
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, _glContextProfileMask);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, _glContextMajor);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, _glContextMinor);
+
if (!_window->createWindow(width, height, flags)) {
// We treat fullscreen requests as a "hint" for now. This means in
// case it is not available we simply ignore it.
@@ -390,13 +503,6 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
}
}
-#ifdef USE_GLES
- // SDL2 will create a GLES2 context by default, so this is needed for GLES1-profile
- // functions to work.
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
-#endif
_glContext = SDL_GL_CreateContext(_window->getSDLWindow());
if (!_glContext) {
return false;
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.h b/backends/graphics/openglsdl/openglsdl-graphics.h
index 1552593575..51edcb4363 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.h
+++ b/backends/graphics/openglsdl/openglsdl-graphics.h
@@ -67,10 +67,13 @@ protected:
virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format);
virtual void refreshScreen();
+
+ virtual void *getProcAddress(const char *name) const;
private:
bool setupMode(uint width, uint height);
#if SDL_VERSION_ATLEAST(2, 0, 0)
+ int _glContextProfileMask, _glContextMajor, _glContextMinor;
SDL_GLContext _glContext;
#else
uint32 _lastVideoModeLoad;
diff --git a/backends/module.mk b/backends/module.mk
index 3d412c031a..2e100d215d 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -3,6 +3,7 @@ MODULE := backends
MODULE_OBJS := \
base-backend.o \
modular-backend.o \
+ audiocd/audiocd-stream.o \
audiocd/default/default-audiocd.o \
events/default/default-events.o \
fs/abstract-fs.o \
@@ -52,10 +53,16 @@ endif
# OpenGL specific source files.
ifdef USE_OPENGL
MODULE_OBJS += \
+ graphics/opengl/context.o \
graphics/opengl/debug.o \
- graphics/opengl/extensions.o \
+ graphics/opengl/framebuffer.o \
graphics/opengl/opengl-graphics.o \
- graphics/opengl/texture.o
+ graphics/opengl/shader.o \
+ graphics/opengl/texture.o \
+ graphics/opengl/pipelines/clut8.o \
+ graphics/opengl/pipelines/fixed.o \
+ graphics/opengl/pipelines/pipeline.o \
+ graphics/opengl/pipelines/shader.o
endif
# SDL specific source files.
@@ -97,6 +104,7 @@ endif
ifdef MACOSX
MODULE_OBJS += \
+ audiocd/macosx/macosx-audiocd.o \
midi/coreaudio.o \
midi/coremidi.o \
updates/macosx/macosx-updates.o \
@@ -105,6 +113,7 @@ endif
ifdef WIN32
MODULE_OBJS += \
+ audiocd/win32/win32-audiocd.o \
fs/windows/windows-fs.o \
fs/windows/windows-fs-factory.o \
midi/windows.o \
@@ -128,6 +137,11 @@ MODULE_OBJS += \
events/ps3sdl/ps3sdl-events.o
endif
+ifdef USE_LINUXCD
+MODULE_OBJS += \
+ audiocd/linux/linux-audiocd.o
+endif
+
ifeq ($(BACKEND),tizen)
MODULE_OBJS += \
timer/tizen/timer.o
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index d8ab549c3a..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/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/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/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