aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/graphics/opengl/context.cpp182
-rw-r--r--backends/graphics/opengl/debug.cpp2
-rw-r--r--backends/graphics/opengl/debug.h4
-rw-r--r--backends/graphics/opengl/framebuffer.cpp259
-rw-r--r--backends/graphics/opengl/framebuffer.h175
-rw-r--r--backends/graphics/opengl/opengl-defs.h262
-rw-r--r--backends/graphics/opengl/opengl-func.h153
-rw-r--r--backends/graphics/opengl/opengl-graphics.cpp240
-rw-r--r--backends/graphics/opengl/opengl-graphics.h70
-rw-r--r--backends/graphics/opengl/opengl-sys.h154
-rw-r--r--backends/graphics/opengl/pipelines/clut8.cpp46
-rw-r--r--backends/graphics/opengl/pipelines/clut8.h (renamed from backends/graphics/opengl/extensions.cpp)32
-rw-r--r--backends/graphics/opengl/pipelines/fixed.cpp70
-rw-r--r--backends/graphics/opengl/pipelines/fixed.h46
-rw-r--r--backends/graphics/opengl/pipelines/pipeline.cpp66
-rw-r--r--backends/graphics/opengl/pipelines/pipeline.h126
-rw-r--r--backends/graphics/opengl/pipelines/shader.cpp94
-rw-r--r--backends/graphics/opengl/pipelines/shader.h59
-rw-r--r--backends/graphics/opengl/shader.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.mk10
-rw-r--r--backends/platform/tizen/graphics.cpp5
-rw-r--r--backends/platform/tizen/graphics.h2
-rwxr-xr-xconfigure199
-rw-r--r--devtools/create_project/create_project.cpp16
-rw-r--r--devtools/create_project/create_project.h4
-rw-r--r--devtools/create_project/msbuild.cpp2
-rw-r--r--devtools/create_project/xcode.cpp20
-rw-r--r--engines/adl/adl.cpp1038
-rw-r--r--engines/adl/adl.h247
-rw-r--r--engines/adl/configure.engine3
-rw-r--r--engines/adl/detection.cpp247
-rw-r--r--engines/adl/detection.h (renamed from backends/graphics/opengl/extensions.h)30
-rw-r--r--engines/adl/display.cpp520
-rw-r--r--engines/adl/display.h102
-rw-r--r--engines/adl/hires1.cpp396
-rw-r--r--engines/adl/hires1.h113
-rw-r--r--engines/adl/module.mk18
-rw-r--r--engines/agi/text.cpp6
-rw-r--r--engines/sherlock/tattoo/widget_inventory.cpp2
-rw-r--r--engines/wage/detection_tables.h25
-rw-r--r--engines/wage/gui.cpp64
-rw-r--r--engines/wage/gui.h3
-rw-r--r--engines/wintermute/base/base_persistence_manager.cpp2
-rw-r--r--gui/ThemeEngine.cpp2
49 files changed, 6218 insertions, 535 deletions
diff --git a/backends/graphics/opengl/context.cpp b/backends/graphics/opengl/context.cpp
new file mode 100644
index 0000000000..a7f640d37e
--- /dev/null
+++ b/backends/graphics/opengl/context.cpp
@@ -0,0 +1,182 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/opengl-sys.h"
+#include "backends/graphics/opengl/opengl-graphics.h"
+#include "backends/graphics/opengl/shader.h"
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+#include "backends/graphics/opengl/framebuffer.h"
+
+#include "common/tokenizer.h"
+#include "common/debug.h"
+
+namespace OpenGL {
+
+void Context::reset() {
+ maxTextureSize = 0;
+
+ NPOTSupported = false;
+ shadersSupported = false;
+ multitextureSupported = false;
+ framebufferObjectSupported = false;
+
+#define GL_FUNC_DEF(ret, name, param) name = nullptr;
+#include "backends/graphics/opengl/opengl-func.h"
+#undef GL_FUNC_DEF
+
+ activePipeline = nullptr;
+}
+
+Pipeline *Context::setPipeline(Pipeline *pipeline) {
+ Pipeline *oldPipeline = activePipeline;
+ if (oldPipeline) {
+ oldPipeline->deactivate();
+ }
+
+ activePipeline = pipeline;
+ if (activePipeline) {
+ activePipeline->activate();
+ }
+
+ return oldPipeline;
+}
+
+Context g_context;
+
+void OpenGLGraphicsManager::setContextType(ContextType type) {
+#if USE_FORCED_GL
+ type = kContextGL;
+#elif USE_FORCED_GLES
+ type = kContextGLES;
+#elif USE_FORCED_GLES2
+ type = kContextGLES2;
+#endif
+
+ g_context.type = type;
+}
+
+void OpenGLGraphicsManager::initializeGLContext() {
+ // Initialize default state.
+ g_context.reset();
+
+ // Load all functions.
+ // We use horrible trickery to silence C++ compilers.
+ // See backends/plugins/sdl/sdl-provider.cpp for more information.
+ assert(sizeof(void (*)()) == sizeof(void *));
+ void *fn = nullptr;
+
+#define LOAD_FUNC(name, loadName) \
+ fn = getProcAddress(#loadName); \
+ memcpy(&g_context.name, &fn, sizeof(fn))
+
+#define GL_EXT_FUNC_DEF(ret, name, param) LOAD_FUNC(name, name)
+
+#ifdef USE_BUILTIN_OPENGL
+#define GL_FUNC_DEF(ret, name, param) g_context.name = &name
+#define GL_FUNC_2_DEF GL_FUNC_DEF
+#else
+#define GL_FUNC_DEF GL_EXT_FUNC_DEF
+#define GL_FUNC_2_DEF(ret, name, extName, param) \
+ if (g_context.type == kContextGL) { \
+ LOAD_FUNC(name, extName); \
+ } else { \
+ LOAD_FUNC(name, name); \
+ }
+#endif
+#include "backends/graphics/opengl/opengl-func.h"
+#undef GL_FUNC_2_DEF
+#undef GL_FUNC_DEF
+#undef GL_EXT_FUNC_DEF
+#undef LOAD_FUNC
+
+ // Obtain maximum texture size.
+ GL_CALL(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &g_context.maxTextureSize));
+ debug(5, "OpenGL maximum texture size: %d", g_context.maxTextureSize);
+
+ const char *extString = (const char *)g_context.glGetString(GL_EXTENSIONS);
+ debug(5, "OpenGL extensions: %s", extString);
+
+ bool ARBShaderObjects = false;
+ bool ARBShadingLanguage100 = false;
+ bool ARBVertexShader = false;
+ bool ARBFragmentShader = false;
+
+ Common::StringTokenizer tokenizer(extString, " ");
+ while (!tokenizer.empty()) {
+ Common::String token = tokenizer.nextToken();
+
+ if (token == "GL_ARB_texture_non_power_of_two" || token == "GL_OES_texture_npot") {
+ g_context.NPOTSupported = true;
+ } else if (token == "GL_ARB_shader_objects") {
+ ARBShaderObjects = true;
+ } else if (token == "GL_ARB_shading_language_100") {
+ ARBShadingLanguage100 = true;
+ } else if (token == "GL_ARB_vertex_shader") {
+ ARBVertexShader = true;
+ } else if (token == "GL_ARB_fragment_shader") {
+ ARBFragmentShader = true;
+ } else if (token == "GL_ARB_multitexture") {
+ g_context.multitextureSupported = true;
+ } else if (token == "GL_EXT_framebuffer_object") {
+ g_context.framebufferObjectSupported = true;
+ }
+ }
+
+ if (g_context.type == kContextGLES2) {
+ // GLES2 always has (limited) NPOT support.
+ g_context.NPOTSupported = true;
+
+ // GLES2 always has shader support.
+ g_context.shadersSupported = true;
+
+ // GLES2 always has multi texture support.
+ g_context.multitextureSupported = true;
+
+ // GLES2 always has FBO support.
+ g_context.framebufferObjectSupported = true;
+ } else {
+ g_context.shadersSupported = ARBShaderObjects & ARBShadingLanguage100 & ARBVertexShader & ARBFragmentShader;
+ }
+
+ // Log context type.
+ switch (g_context.type) {
+ case kContextGL:
+ debug(5, "OpenGL: GL context initialized");
+ break;
+
+ case kContextGLES:
+ debug(5, "OpenGL: GLES context initialized");
+ break;
+
+ case kContextGLES2:
+ debug(5, "OpenGL: GLES2 context initialized");
+ break;
+ }
+
+ // Log features supported by GL context.
+ debug(5, "OpenGL: NPOT texture support: %d", g_context.NPOTSupported);
+ debug(5, "OpenGL: Shader support: %d", g_context.shadersSupported);
+ debug(5, "OpenGL: Multitexture support: %d", g_context.multitextureSupported);
+ debug(5, "OpenGL: FBO support: %d", g_context.framebufferObjectSupported);
+}
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/debug.cpp b/backends/graphics/opengl/debug.cpp
index d5d73fb5ec..c4319f5e36 100644
--- a/backends/graphics/opengl/debug.cpp
+++ b/backends/graphics/opengl/debug.cpp
@@ -54,7 +54,7 @@ Common::String getGLErrStr(GLenum error) {
void checkGLError(const char *expr, const char *file, int line) {
GLenum error;
- while ((error = glGetError()) != GL_NO_ERROR) {
+ while ((error = g_context.glGetError()) != GL_NO_ERROR) {
// We cannot use error here because we do not know whether we have a
// working screen or not.
warning("GL ERROR: %s on %s (%s:%d)", getGLErrStr(error).c_str(), expr, file, line);
diff --git a/backends/graphics/opengl/debug.h b/backends/graphics/opengl/debug.h
index ff6b678870..abaa6544dc 100644
--- a/backends/graphics/opengl/debug.h
+++ b/backends/graphics/opengl/debug.h
@@ -31,9 +31,9 @@ namespace OpenGL {
void checkGLError(const char *expr, const char *file, int line);
} // End of namespace OpenGL
-#define GLCALL(x) do { (x); OpenGL::checkGLError(#x, __FILE__, __LINE__); } while (false)
+#define GL_WRAP_DEBUG(call, name) do { (call); OpenGL::checkGLError(#name, __FILE__, __LINE__); } while (false)
#else
-#define GLCALL(x) do { (x); } while (false)
+#define GL_WRAP_DEBUG(call, name) do { (call); } while (false)
#endif
#endif
diff --git a/backends/graphics/opengl/framebuffer.cpp b/backends/graphics/opengl/framebuffer.cpp
new file mode 100644
index 0000000000..7191aab8bc
--- /dev/null
+++ b/backends/graphics/opengl/framebuffer.cpp
@@ -0,0 +1,259 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/framebuffer.h"
+#include "backends/graphics/opengl/texture.h"
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+
+namespace OpenGL {
+
+Framebuffer::Framebuffer()
+ : _viewport(), _projectionMatrix(), _isActive(false), _clearColor(),
+ _blendState(false), _scissorTestState(false), _scissorBox() {
+}
+
+void Framebuffer::activate() {
+ _isActive = true;
+
+ applyViewport();
+ applyProjectionMatrix();
+ applyClearColor();
+ applyBlendState();
+ applyScissorTestState();
+ applyScissorBox();
+
+ activateInternal();
+}
+
+void Framebuffer::deactivate() {
+ _isActive = false;
+
+ deactivateInternal();
+}
+
+void Framebuffer::setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ _clearColor[0] = r;
+ _clearColor[1] = g;
+ _clearColor[2] = b;
+ _clearColor[3] = a;
+
+ // Directly apply changes when we are active.
+ if (isActive()) {
+ applyClearColor();
+ }
+}
+
+void Framebuffer::enableBlend(bool enable) {
+ _blendState = enable;
+
+ // Directly apply changes when we are active.
+ if (isActive()) {
+ applyBlendState();
+ }
+}
+
+void Framebuffer::enableScissorTest(bool enable) {
+ _scissorTestState = enable;
+
+ // Directly apply changes when we are active.
+ if (isActive()) {
+ applyScissorTestState();
+ }
+}
+
+void Framebuffer::setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h) {
+ _scissorBox[0] = x;
+ _scissorBox[1] = y;
+ _scissorBox[2] = w;
+ _scissorBox[3] = h;
+
+ // Directly apply changes when we are active.
+ if (isActive()) {
+ applyScissorBox();
+ }
+}
+
+void Framebuffer::applyViewport() {
+ GL_CALL(glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]));
+}
+
+void Framebuffer::applyProjectionMatrix() {
+ g_context.getActivePipeline()->setProjectionMatrix(_projectionMatrix);
+}
+
+void Framebuffer::applyClearColor() {
+ GL_CALL(glClearColor(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]));
+}
+
+void Framebuffer::applyBlendState() {
+ if (_blendState) {
+ GL_CALL(glEnable(GL_BLEND));
+ } else {
+ GL_CALL(glDisable(GL_BLEND));
+ }
+}
+
+void Framebuffer::applyScissorTestState() {
+ if (_scissorTestState) {
+ GL_CALL(glEnable(GL_SCISSOR_TEST));
+ } else {
+ GL_CALL(glDisable(GL_SCISSOR_TEST));
+ }
+}
+
+void Framebuffer::applyScissorBox() {
+ GL_CALL(glScissor(_scissorBox[0], _scissorBox[1], _scissorBox[2], _scissorBox[3]));
+}
+
+//
+// Backbuffer implementation
+//
+
+void Backbuffer::activateInternal() {
+#if !USE_FORCED_GLES
+ if (g_context.framebufferObjectSupported) {
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ }
+#endif
+}
+
+void Backbuffer::setDimensions(uint width, uint height) {
+ // Set viewport dimensions.
+ _viewport[0] = 0;
+ _viewport[1] = 0;
+ _viewport[2] = width;
+ _viewport[3] = height;
+
+ // Setup orthogonal projection matrix.
+ _projectionMatrix[ 0] = 2.0f / width;
+ _projectionMatrix[ 1] = 0.0f;
+ _projectionMatrix[ 2] = 0.0f;
+ _projectionMatrix[ 3] = 0.0f;
+
+ _projectionMatrix[ 4] = 0.0f;
+ _projectionMatrix[ 5] = -2.0f / height;
+ _projectionMatrix[ 6] = 0.0f;
+ _projectionMatrix[ 7] = 0.0f;
+
+ _projectionMatrix[ 8] = 0.0f;
+ _projectionMatrix[ 9] = 0.0f;
+ _projectionMatrix[10] = 0.0f;
+ _projectionMatrix[11] = 0.0f;
+
+ _projectionMatrix[12] = -1.0f;
+ _projectionMatrix[13] = 1.0f;
+ _projectionMatrix[14] = 0.0f;
+ _projectionMatrix[15] = 1.0f;
+
+ // Directly apply changes when we are active.
+ if (isActive()) {
+ applyViewport();
+ applyProjectionMatrix();
+ }
+}
+
+//
+// Render to texture target implementation
+//
+
+#if !USE_FORCED_GLES
+TextureTarget::TextureTarget()
+ : _texture(new GLTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE)), _glFBO(0), _needUpdate(true) {
+}
+
+TextureTarget::~TextureTarget() {
+ delete _texture;
+ GL_CALL_SAFE(glDeleteFramebuffers, (1, &_glFBO));
+}
+
+void TextureTarget::activateInternal() {
+ // Allocate framebuffer object if necessary.
+ if (!_glFBO) {
+ GL_CALL(glGenFramebuffers(1, &_glFBO));
+ _needUpdate = true;
+ }
+
+ // Attach destination texture to FBO.
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO));
+
+ // If required attach texture to FBO.
+ if (_needUpdate) {
+ GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture->getGLTexture(), 0));
+ _needUpdate = false;
+ }
+}
+
+void TextureTarget::destroy() {
+ GL_CALL(glDeleteFramebuffers(1, &_glFBO));
+ _glFBO = 0;
+
+ _texture->destroy();
+}
+
+void TextureTarget::create() {
+ _texture->create();
+
+ _needUpdate = true;
+}
+
+void TextureTarget::setSize(uint width, uint height) {
+ _texture->setSize(width, height);
+
+ const uint texWidth = _texture->getWidth();
+ const uint texHeight = _texture->getHeight();
+
+ // Set viewport dimensions.
+ _viewport[0] = 0;
+ _viewport[1] = 0;
+ _viewport[2] = texWidth;
+ _viewport[3] = texHeight;
+
+ // Setup orthogonal projection matrix.
+ _projectionMatrix[ 0] = 2.0f / texWidth;
+ _projectionMatrix[ 1] = 0.0f;
+ _projectionMatrix[ 2] = 0.0f;
+ _projectionMatrix[ 3] = 0.0f;
+
+ _projectionMatrix[ 4] = 0.0f;
+ _projectionMatrix[ 5] = 2.0f / texHeight;
+ _projectionMatrix[ 6] = 0.0f;
+ _projectionMatrix[ 7] = 0.0f;
+
+ _projectionMatrix[ 8] = 0.0f;
+ _projectionMatrix[ 9] = 0.0f;
+ _projectionMatrix[10] = 0.0f;
+ _projectionMatrix[11] = 0.0f;
+
+ _projectionMatrix[12] = -1.0f;
+ _projectionMatrix[13] = -1.0f;
+ _projectionMatrix[14] = 0.0f;
+ _projectionMatrix[15] = 1.0f;
+
+ // Directly apply changes when we are active.
+ if (isActive()) {
+ applyViewport();
+ applyProjectionMatrix();
+ }
+}
+#endif // !USE_FORCED_GLES
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/framebuffer.h b/backends/graphics/opengl/framebuffer.h
new file mode 100644
index 0000000000..c44c98ddc4
--- /dev/null
+++ b/backends/graphics/opengl/framebuffer.h
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H
+#define BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H
+
+#include "backends/graphics/opengl/opengl-sys.h"
+
+namespace OpenGL {
+
+/**
+ * Object describing a framebuffer OpenGL can render to.
+ */
+class Framebuffer {
+ friend class Pipeline;
+public:
+ Framebuffer();
+ virtual ~Framebuffer() {};
+
+public:
+ /**
+ * Set the clear color of the framebuffer.
+ */
+ void setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+
+ /**
+ * Enable/disable GL_BLEND.
+ */
+ void enableBlend(bool enable);
+
+ /**
+ * Enable/disable GL_SCISSOR_TEST.
+ */
+ void enableScissorTest(bool enable);
+
+ /**
+ * Set scissor box dimensions.
+ */
+ void setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h);
+
+ /**
+ * Obtain projection matrix of the framebuffer.
+ */
+ const GLfloat *getProjectionMatrix() const { return _projectionMatrix; }
+protected:
+ bool isActive() const { return _isActive; }
+
+ GLint _viewport[4];
+ void applyViewport();
+
+ GLfloat _projectionMatrix[4*4];
+ void applyProjectionMatrix();
+
+ /**
+ * Activate framebuffer.
+ *
+ * This is supposed to set all state associated with the framebuffer.
+ */
+ virtual void activateInternal() = 0;
+
+ /**
+ * Deactivate framebuffer.
+ *
+ * This is supposed to make any cleanup required when unbinding the
+ * framebuffer.
+ */
+ virtual void deactivateInternal() {}
+
+private:
+ /**
+ * Accessor to activate framebuffer for pipeline.
+ */
+ void activate();
+
+ /**
+ * Accessor to deactivate framebuffer from pipeline.
+ */
+ void deactivate();
+
+private:
+ bool _isActive;
+
+ GLfloat _clearColor[4];
+ void applyClearColor();
+
+ bool _blendState;
+ void applyBlendState();
+
+ bool _scissorTestState;
+ void applyScissorTestState();
+
+ GLint _scissorBox[4];
+ void applyScissorBox();
+};
+
+/**
+ * Default back buffer implementation.
+ */
+class Backbuffer : public Framebuffer {
+public:
+ /**
+ * Set the dimensions (a.k.a. size) of the back buffer.
+ */
+ void setDimensions(uint width, uint height);
+
+protected:
+ virtual void activateInternal();
+};
+
+#if !USE_FORCED_GLES
+class GLTexture;
+
+/**
+ * Render to texture framebuffer implementation.
+ *
+ * This target allows to render to a texture, which can then be used for
+ * further rendering.
+ */
+class TextureTarget : public Framebuffer {
+public:
+ TextureTarget();
+ virtual ~TextureTarget();
+
+ /**
+ * Notify that the GL context is about to be destroyed.
+ */
+ void destroy();
+
+ /**
+ * Notify that the GL context has been created.
+ */
+ void create();
+
+ /**
+ * Set size of the texture target.
+ */
+ void setSize(uint width, uint height);
+
+ /**
+ * Query pointer to underlying GL texture.
+ */
+ GLTexture *getTexture() const { return _texture; }
+
+protected:
+ virtual void activateInternal();
+
+private:
+ GLTexture *_texture;
+ GLuint _glFBO;
+ bool _needUpdate;
+};
+#endif
+
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/opengl-defs.h b/backends/graphics/opengl/opengl-defs.h
new file mode 100644
index 0000000000..733fc2933c
--- /dev/null
+++ b/backends/graphics/opengl/opengl-defs.h
@@ -0,0 +1,262 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/* This file is based on Mesa 3-D's gl.h and GLES/gl.h from Khronos Registry.
+ *
+ * Mesa 3-D's gl.h file is distributed under the following license:
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
+ * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * GLES/gl.h from Khronos Registry is distributed under the following license:
+ * This document is licensed under the SGI Free Software B License Version
+ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ .
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_DEFS_H
+#define BACKENDS_GRAPHICS_OPENGL_OPENGL_DEFS_H
+
+#include "common/scummsys.h"
+
+/*
+ * Datatypes
+ */
+typedef uint GLenum;
+typedef uint8 GLboolean;
+typedef uint GLbitfield;
+typedef void GLvoid;
+typedef int8 GLbyte; /* 1-byte signed */
+typedef int16 GLshort; /* 2-byte signed */
+typedef int32 GLint; /* 4-byte signed */
+typedef uint8 GLubyte; /* 1-byte unsigned */
+typedef uint16 GLushort; /* 2-byte unsigned */
+typedef uint32 GLuint; /* 4-byte unsigned */
+typedef int32 GLsizei; /* 4-byte signed */
+typedef float GLfloat; /* single precision float */
+typedef float GLclampf; /* single precision float in [0,1] */
+typedef double GLdouble; /* double precision float */
+typedef double GLclampd; /* double precision float in [0,1] */
+typedef char GLchar;
+#if defined(MACOSX)
+typedef void *GLhandleARB;
+#else
+typedef uint GLhandleARB;
+#endif
+
+// This is an addition from us to alias ARB shader object extensions to
+// OpenGL (ES) 2.0 style functions. It only works when GLhandleARB and GLuint
+// are type compatible.
+typedef GLhandleARB GLprogram;
+typedef GLhandleARB GLshader;
+
+/*
+ * Constants
+ */
+
+/* Boolean constants */
+#define GL_FALSE 0
+#define GL_TRUE 1
+
+/* StringName */
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+
+/* ErrorCode */
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_STACK_OVERFLOW 0x0503
+#define GL_STACK_UNDERFLOW 0x0504
+#define GL_OUT_OF_MEMORY 0x0505
+
+/* ClearBufferMask */
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_COLOR_BUFFER_BIT 0x00004000
+
+/* Scissor box */
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_SCISSOR_TEST 0x0C11
+
+/* MatrixMode */
+#define GL_MATRIX_MODE 0x0BA0
+#define GL_MODELVIEW 0x1700
+#define GL_PROJECTION 0x1701
+#define GL_TEXTURE 0x1702
+
+/* EnableCap */
+#define GL_FOG 0x0B60
+#define GL_LIGHTING 0x0B50
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_CULL_FACE 0x0B44
+#define GL_ALPHA_TEST 0x0BC0
+#define GL_BLEND 0x0BE2
+#define GL_DITHER 0x0BD0
+#define GL_DEPTH_TEST 0x0B71
+#define GL_VERTEX_ARRAY 0x8074
+#define GL_COLOR_ARRAY 0x8076
+#define GL_TEXTURE_COORD_ARRAY 0x8078
+
+/* ShadingModel */
+#define GL_FLAT 0x1D00
+#define GL_SMOOTH 0x1D01
+
+/* HintMode */
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+
+/* HintTarget */
+#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50
+#define GL_POINT_SMOOTH_HINT 0x0C51
+#define GL_LINE_SMOOTH_HINT 0x0C52
+#define GL_FOG_HINT 0x0C54
+#define GL_GENERATE_MIPMAP_HINT 0x8192
+
+/* BlendingFactorDest */
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+
+/* BlendingFactorSrc */
+/* GL_ZERO */
+/* GL_ONE */
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+/* GL_SRC_ALPHA */
+/* GL_ONE_MINUS_SRC_ALPHA */
+/* GL_DST_ALPHA */
+/* GL_ONE_MINUS_DST_ALPHA */
+
+/* PixelFormat */
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_BGR 0x80E0
+#define GL_BGRA 0x80E1
+
+#define GL_RED 0x1903
+#define GL_R8 0x8229
+
+/* PixelStoreParameter */
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_ALIGNMENT 0x0D05
+
+/* DataType */
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_FLOAT 0x1406
+#define GL_FIXED 0x140C
+
+/* PixelType */
+/* GL_UNSIGNED_BYTE */
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+
+/* Implementation limits */
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+
+/* TextureMagFilter */
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+
+/* TextureParameterName */
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+
+/* TextureWrapMode */
+#define GL_REPEAT 0x2901
+#define GL_CLAMP_TO_EDGE 0x812F
+
+/* BeginMode */
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+
+/* Shaders */
+#define GL_FRAGMENT_SHADER 0x8B30
+
+#define GL_VERTEX_SHADER 0x8B31
+
+/* Programs */
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_CURRENT_PROGRAM 0x8B8D
+
+/* Textures */
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+
+/* GetPName */
+#define GL_VIEWPORT 0x0BA2
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+
+/* Framebuffer objects */
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_FRAMEBUFFER 0x8D40
+
+#endif
diff --git a/backends/graphics/opengl/opengl-func.h b/backends/graphics/opengl/opengl-func.h
new file mode 100644
index 0000000000..4e44c13d0f
--- /dev/null
+++ b/backends/graphics/opengl/opengl-func.h
@@ -0,0 +1,153 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/* This file is based on Mesa 3-D's gl.h and GLES/gl.h from Khronos Registry.
+ *
+ * Mesa 3-D's gl.h file is distributed under the following license:
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
+ * Copyright (C) 2009 VMware, Inc. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * GLES/gl.h from Khronos Registry is distributed under the following license:
+ * This document is licensed under the SGI Free Software B License Version
+ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ .
+ */
+
+/*
+ * This file is a template file to be used inside specific locations in the
+ * OpenGL graphics code. It is not to be included otherwise. It intentionally
+ * does not contain include guards because it can be required to include it
+ * multiple times in a source file.
+ *
+ * Functions are defined by three different user supplied macros:
+ * GL_FUNC_DEF: Define a (builtin) OpenGL (ES) function.
+ * GL_FUNC_2_DEF: Define a OpenGL (ES) 2.0 function which can be provided by
+ * extensions in OpenGL 1.x contexts.
+ * GL_EXT_FUNC_DEF: Define an OpenGL (ES) extension function.
+ */
+
+#if !defined(GL_FUNC_2_DEF)
+#define GL_FUNC_2_DEF(ret, name, extName, param) GL_FUNC_DEF(ret, name, param)
+#define DEFINED_GL_FUNC_2_DEF
+#endif
+
+#if !defined(GL_EXT_FUNC_DEF)
+#define GL_EXT_FUNC_DEF(ret, name, param) GL_FUNC_DEF(ret, name, param)
+#define DEFINED_GL_EXT_FUNC_DEF
+#endif
+
+GL_FUNC_DEF(void, glEnable, (GLenum cap));
+GL_FUNC_DEF(void, glDisable, (GLenum cap));
+GL_FUNC_DEF(GLboolean, glIsEnabled, (GLenum cap));
+GL_FUNC_DEF(void, glClear, (GLbitfield mask));
+GL_FUNC_DEF(void, glColor4f, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha));
+GL_FUNC_DEF(void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height));
+GL_FUNC_DEF(void, glMatrixMode, (GLenum mode));
+GL_FUNC_DEF(void, glLoadIdentity, ());
+GL_FUNC_DEF(void, glLoadMatrixf, (const GLfloat *m));
+GL_FUNC_DEF(void, glShadeModel, (GLenum mode));
+GL_FUNC_DEF(void, glHint, (GLenum target, GLenum mode));
+GL_FUNC_DEF(void, glClearColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha));
+GL_FUNC_DEF(void, glBlendFunc, (GLenum sfactor, GLenum dfactor));
+GL_FUNC_DEF(void, glEnableClientState, (GLenum array));
+GL_FUNC_DEF(void, glPixelStorei, (GLenum pname, GLint param));
+GL_FUNC_DEF(void, glScissor, (GLint x, GLint y, GLsizei width, GLsizei height));
+GL_FUNC_DEF(void, glReadPixels, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels));
+GL_FUNC_DEF(void, glGetIntegerv, (GLenum pname, GLint *params));
+GL_FUNC_DEF(void, glDeleteTextures, (GLsizei n, const GLuint *textures));
+GL_FUNC_DEF(void, glGenTextures, (GLsizei n, GLuint *textures));
+GL_FUNC_DEF(void, glBindTexture, (GLenum target, GLuint texture));
+GL_FUNC_DEF(void, glTexParameteri, (GLenum target, GLenum pname, GLint param));
+GL_FUNC_DEF(void, glTexImage2D, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels));
+GL_FUNC_DEF(void, glTexCoordPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer));
+GL_FUNC_DEF(void, glVertexPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer));
+GL_FUNC_DEF(void, glDrawArrays, (GLenum mode, GLint first, GLsizei count));
+GL_FUNC_DEF(void, glTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels));
+GL_FUNC_DEF(const GLubyte *, glGetString, (GLenum name));
+GL_FUNC_DEF(GLenum, glGetError, ());
+
+#if !USE_FORCED_GLES
+GL_FUNC_2_DEF(void, glEnableVertexAttribArray, glEnableVertexAttribArrayARB, (GLuint index));
+GL_FUNC_2_DEF(void, glDisableVertexAttribArray, glDisableVertexAttribArrayARB, (GLuint index));
+GL_FUNC_2_DEF(void, glUniform1i, glUniform1iARB, (GLint location, GLint v0));
+GL_FUNC_2_DEF(void, glUniform1f, glUniform1fARB, (GLint location, GLfloat v0));
+GL_FUNC_2_DEF(void, glUniformMatrix4fv, glUniformMatrix4fvARB, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value));
+GL_FUNC_2_DEF(void, glVertexAttrib4f, glVertexAttrib4fARB, (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w));
+GL_FUNC_2_DEF(void, glVertexAttribPointer, glVertexAttribPointerARB, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer));
+
+GL_FUNC_2_DEF(GLprogram, glCreateProgram, glCreateProgramObjectARB, ());
+GL_FUNC_2_DEF(void, glDeleteProgram, glDeleteObjectARB, (GLprogram program));
+GL_FUNC_2_DEF(void, glAttachShader, glAttachObjectARB, (GLprogram program, GLshader shader));
+GL_FUNC_2_DEF(void, glDetachShader, glDetachObjectARB, (GLprogram program, GLshader shader));
+GL_FUNC_2_DEF(void, glLinkProgram, glLinkProgramARB, (GLprogram program));
+GL_FUNC_2_DEF(void, glUseProgram, glUseProgramObjectARB, (GLprogram program));
+GL_FUNC_2_DEF(void, glGetProgramiv, glGetObjectParameterivARB, (GLprogram program, GLenum pname, GLint *params));
+GL_FUNC_2_DEF(void, glGetProgramInfoLog, glGetInfoLogARB, (GLprogram program, GLsizei bufSize, GLsizei *length, GLchar *infoLog));
+GL_FUNC_2_DEF(void, glBindAttribLocation, glBindAttribLocationARB, (GLprogram program, GLuint index, const GLchar *name));
+GL_FUNC_2_DEF(GLint, glGetAttribLocation, glGetAttribLocationARB, (GLprogram program, const GLchar *name));
+GL_FUNC_2_DEF(GLint, glGetUniformLocation, glGetUniformLocationARB, (GLprogram program, const GLchar *name));
+
+GL_FUNC_2_DEF(GLshader, glCreateShader, glCreateShaderObjectARB, (GLenum type));
+GL_FUNC_2_DEF(void, glDeleteShader, glDeleteObjectARB, (GLshader shader));
+GL_FUNC_2_DEF(void, glGetShaderiv, glGetObjectParameterivARB, (GLshader shader, GLenum pname, GLint *params));
+GL_FUNC_2_DEF(void, glGetShaderInfoLog, glGetInfoLogARB, (GLshader shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog));
+GL_FUNC_2_DEF(void, glShaderSource, glShaderSourceARB, (GLshader shader, GLsizei count, const GLchar *const *string, const GLint *length));
+GL_FUNC_2_DEF(void, glCompileShader, glCompileShaderARB, (GLshader shader));
+
+GL_FUNC_2_DEF(void, glBindFramebuffer, glBindFramebufferEXT, (GLenum target, GLuint renderbuffer));
+GL_FUNC_2_DEF(void, glDeleteFramebuffers, glDeleteFramebuffersEXT, (GLsizei n, const GLuint *framebuffers));
+GL_FUNC_2_DEF(void, glGenFramebuffers, glGenFramebuffersEXT, (GLsizei n, GLuint *renderbuffers));
+GL_FUNC_2_DEF(void, glFramebufferTexture2D, glFramebufferTexture2DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level));
+GL_FUNC_2_DEF(GLenum, glCheckFramebufferStatus, glCheckFramebufferStatusEXT, (GLenum target));
+
+GL_FUNC_2_DEF(void, glActiveTexture, glActiveTextureARB, (GLenum texture));
+#endif
+
+#ifdef DEFINED_GL_EXT_FUNC_DEF
+#undef DEFINED_GL_EXT_FUNC_DEF
+#undef GL_EXT_FUNC_DEF
+#endif
+
+#ifdef DEFINED_GL_FUNC_2_DEF
+#undef DEFINED_GL_FUNC_2_DEF
+#undef GL_FUNC_2_DEF
+#endif
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index ac6d41d47d..4d6a00a3b3 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -23,8 +23,10 @@
#include "backends/graphics/opengl/opengl-graphics.h"
#include "backends/graphics/opengl/texture.h"
-#include "backends/graphics/opengl/debug.h"
-#include "backends/graphics/opengl/extensions.h"
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+#include "backends/graphics/opengl/pipelines/fixed.h"
+#include "backends/graphics/opengl/pipelines/shader.h"
+#include "backends/graphics/opengl/shader.h"
#include "common/textconsole.h"
#include "common/translation.h"
@@ -45,6 +47,7 @@ namespace OpenGL {
OpenGLGraphicsManager::OpenGLGraphicsManager()
: _currentState(), _oldState(), _transactionMode(kTransactionNone), _screenChangeID(1 << (sizeof(int) * 8 - 2)),
+ _pipeline(nullptr),
_outputScreenWidth(0), _outputScreenHeight(0), _displayX(0), _displayY(0),
_displayWidth(0), _displayHeight(0), _defaultFormat(), _defaultFormatAlpha(),
_gameScreen(nullptr), _gameScreenShakeOffset(0), _overlay(nullptr),
@@ -58,6 +61,7 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
#endif
{
memset(_gamePalette, 0, sizeof(_gamePalette));
+ g_context.reset();
}
OpenGLGraphicsManager::~OpenGLGraphicsManager() {
@@ -67,6 +71,9 @@ OpenGLGraphicsManager::~OpenGLGraphicsManager() {
#ifdef USE_OSD
delete _osd;
#endif
+#if !USE_FORCED_GLES
+ ShaderManager::destroy();
+#endif
}
bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) {
@@ -215,8 +222,8 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
// a context existing before, which means we don't know the maximum
// supported texture size before this. Thus, we check whether the
// requested game resolution is supported over here.
- || ( _currentState.gameWidth > (uint)Texture::getMaximumTextureSize()
- || _currentState.gameHeight > (uint)Texture::getMaximumTextureSize())) {
+ || ( _currentState.gameWidth > (uint)g_context.maxTextureSize
+ || _currentState.gameHeight > (uint)g_context.maxTextureSize)) {
if (_transactionMode == kTransactionActive) {
// Try to setup the old state in case its valid and is
// actually different from the new one.
@@ -267,9 +274,9 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
_gameScreen = nullptr;
#ifdef USE_RGB_COLOR
- _gameScreen = createTexture(_currentState.gameFormat);
+ _gameScreen = createSurface(_currentState.gameFormat);
#else
- _gameScreen = createTexture(Graphics::PixelFormat::createFormatCLUT8());
+ _gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8());
#endif
assert(_gameScreen);
if (_gameScreen->hasPalette()) {
@@ -365,29 +372,37 @@ void OpenGLGraphicsManager::updateScreen() {
}
_forceRedraw = false;
+ // Update changes to textures.
+ _gameScreen->updateGLTexture();
+ if (_cursor) {
+ _cursor->updateGLTexture();
+ }
+ _overlay->updateGLTexture();
+ _osd->updateGLTexture();
+
// Clear the screen buffer.
if (_scissorOverride && !_overlayVisible) {
// In certain cases we need to assure that the whole screen area is
// cleared. For example, when switching from overlay visible to
// invisible, we need to assure that all contents are cleared to
// properly remove all overlay contents.
- GLCALL(glDisable(GL_SCISSOR_TEST));
- GLCALL(glClear(GL_COLOR_BUFFER_BIT));
- GLCALL(glEnable(GL_SCISSOR_TEST));
+ _backBuffer.enableScissorTest(false);
+ GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
+ _backBuffer.enableScissorTest(true);
--_scissorOverride;
} else {
- GLCALL(glClear(GL_COLOR_BUFFER_BIT));
+ GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
}
const GLfloat shakeOffset = _gameScreenShakeOffset * (GLfloat)_displayHeight / _gameScreen->getHeight();
// First step: Draw the (virtual) game screen.
- _gameScreen->draw(_displayX, _displayY + shakeOffset, _displayWidth, _displayHeight);
+ g_context.getActivePipeline()->drawTexture(_gameScreen->getGLTexture(), _displayX, _displayY + shakeOffset, _displayWidth, _displayHeight);
// Second step: Draw the overlay if visible.
if (_overlayVisible) {
- _overlay->draw(0, 0, _outputScreenWidth, _outputScreenHeight);
+ g_context.getActivePipeline()->drawTexture(_overlay->getGLTexture(), 0, 0, _outputScreenWidth, _outputScreenHeight);
}
// Third step: Draw the cursor if visible.
@@ -396,9 +411,10 @@ void OpenGLGraphicsManager::updateScreen() {
// visible.
const GLfloat cursorOffset = _overlayVisible ? 0 : shakeOffset;
- _cursor->draw(_cursorDisplayX - _cursorHotspotXScaled,
- _cursorDisplayY - _cursorHotspotYScaled + cursorOffset,
- _cursorWidthScaled, _cursorHeightScaled);
+ g_context.getActivePipeline()->drawTexture(_cursor->getGLTexture(),
+ _cursorDisplayX - _cursorHotspotXScaled,
+ _cursorDisplayY - _cursorHotspotYScaled + cursorOffset,
+ _cursorWidthScaled, _cursorHeightScaled);
}
#ifdef USE_OSD
@@ -419,13 +435,13 @@ void OpenGLGraphicsManager::updateScreen() {
}
// Set the OSD transparency.
- GLCALL(glColor4f(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f));
+ g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, _osdAlpha / 100.0f);
// Draw the OSD texture.
- _osd->draw(0, 0, _outputScreenWidth, _outputScreenHeight);
+ g_context.getActivePipeline()->drawTexture(_osd->getGLTexture(), 0, 0, _outputScreenWidth, _outputScreenHeight);
// Reset color.
- GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
+ g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
}
#endif
@@ -467,7 +483,7 @@ void OpenGLGraphicsManager::showOverlay() {
_forceRedraw = true;
// Allow drawing inside full screen area.
- GLCALL(glDisable(GL_SCISSOR_TEST));
+ _backBuffer.enableScissorTest(false);
// Update cursor position.
setMousePosition(_cursorX, _cursorY);
@@ -478,7 +494,7 @@ void OpenGLGraphicsManager::hideOverlay() {
_forceRedraw = true;
// Limit drawing to screen area.
- GLCALL(glEnable(GL_SCISSOR_TEST));
+ _backBuffer.enableScissorTest(true);
_scissorOverride = 3;
// Update cursor position.
@@ -609,7 +625,7 @@ void OpenGLGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int
} else {
textureFormat = _defaultFormatAlpha;
}
- _cursor = createTexture(textureFormat, true);
+ _cursor = createSurface(textureFormat, true);
assert(_cursor);
_cursor->enableLinearFiltering(_currentState.graphicsMode == GFX_LINEAR);
}
@@ -755,18 +771,8 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
_outputScreenWidth = width;
_outputScreenHeight = height;
- // Setup coordinates system.
- GLCALL(glViewport(0, 0, _outputScreenWidth, _outputScreenHeight));
-
- GLCALL(glMatrixMode(GL_PROJECTION));
- GLCALL(glLoadIdentity());
-#ifdef USE_GLES
- GLCALL(glOrthof(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1));
-#else
- GLCALL(glOrtho(0, _outputScreenWidth, _outputScreenHeight, 0, -1, 1));
-#endif
- GLCALL(glMatrixMode(GL_MODELVIEW));
- GLCALL(glLoadIdentity());
+ // Setup backbuffer size.
+ _backBuffer.setDimensions(width, height);
uint overlayWidth = width;
uint overlayHeight = height;
@@ -777,15 +783,15 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
// possible and then scale it to the physical display size. This sounds
// bad but actually all recent chips should support full HD resolution
// anyway. Thus, it should not be a real issue for modern hardware.
- if ( overlayWidth > (uint)Texture::getMaximumTextureSize()
- || overlayHeight > (uint)Texture::getMaximumTextureSize()) {
+ if ( overlayWidth > (uint)g_context.maxTextureSize
+ || overlayHeight > (uint)g_context.maxTextureSize) {
const frac_t outputAspect = intToFrac(_outputScreenWidth) / _outputScreenHeight;
if (outputAspect > (frac_t)FRAC_ONE) {
- overlayWidth = Texture::getMaximumTextureSize();
+ overlayWidth = g_context.maxTextureSize;
overlayHeight = intToFrac(overlayWidth) / outputAspect;
} else {
- overlayHeight = Texture::getMaximumTextureSize();
+ overlayHeight = g_context.maxTextureSize;
overlayWidth = fracToInt(overlayHeight * outputAspect);
}
}
@@ -801,7 +807,7 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
delete _overlay;
_overlay = nullptr;
- _overlay = createTexture(_defaultFormatAlpha);
+ _overlay = createSurface(_defaultFormatAlpha);
assert(_overlay);
// We always filter the overlay with GL_LINEAR. This assures it's
// readable in case it needs to be scaled and does not affect it
@@ -816,7 +822,7 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
delete _osd;
_osd = nullptr;
- _osd = createTexture(_defaultFormatAlpha);
+ _osd = createSurface(_defaultFormatAlpha);
assert(_osd);
// We always filter the osd with GL_LINEAR. This assures it's
// readable in case it needs to be scaled and does not affect it
@@ -836,38 +842,48 @@ void OpenGLGraphicsManager::setActualScreenSize(uint width, uint height) {
}
void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &defaultFormat, const Graphics::PixelFormat &defaultFormatAlpha) {
- // Initialize all extensions.
- initializeGLExtensions();
+ // Initialize context for use.
+ initializeGLContext();
- // Disable 3D properties.
- GLCALL(glDisable(GL_CULL_FACE));
- GLCALL(glDisable(GL_DEPTH_TEST));
- GLCALL(glDisable(GL_LIGHTING));
- GLCALL(glDisable(GL_FOG));
- GLCALL(glDisable(GL_DITHER));
- GLCALL(glShadeModel(GL_FLAT));
- GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
+ // Initialize pipeline.
+ delete _pipeline;
+ _pipeline = nullptr;
- // Default to black as clear color.
- GLCALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
- GLCALL(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
+#if !USE_FORCED_GLES
+ if (g_context.shadersSupported) {
+ ShaderMan.notifyCreate();
+ _pipeline = new ShaderPipeline(ShaderMan.query(ShaderManager::kDefault));
+ }
+#endif
- // Setup alpha blend (for overlay and cursor).
- GLCALL(glEnable(GL_BLEND));
- GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+#if !USE_FORCED_GLES2
+ if (_pipeline == nullptr) {
+ _pipeline = new FixedPipeline();
+ }
+#endif
+
+ g_context.setPipeline(_pipeline);
+
+ // Disable 3D properties.
+ GL_CALL(glDisable(GL_CULL_FACE));
+ GL_CALL(glDisable(GL_DEPTH_TEST));
+ GL_CALL(glDisable(GL_DITHER));
- // Enable rendering with vertex and coord arrays.
- GLCALL(glEnableClientState(GL_VERTEX_ARRAY));
- GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
+ g_context.getActivePipeline()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
- GLCALL(glEnable(GL_TEXTURE_2D));
+ GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
+ // Setup backbuffer state.
+
+ // Default to black as clear color.
+ _backBuffer.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ // Setup alpha blend (for overlay and cursor).
+ _backBuffer.enableBlend(true);
// Setup scissor state accordingly.
- if (_overlayVisible) {
- GLCALL(glDisable(GL_SCISSOR_TEST));
- } else {
- GLCALL(glEnable(GL_SCISSOR_TEST));
- }
+ _backBuffer.enableScissorTest(!_overlayVisible);
+
+ g_context.getActivePipeline()->setFramebuffer(&_backBuffer);
+
// Clear the whole screen for the first three frames to assure any
// leftovers are cleared.
_scissorOverride = 3;
@@ -875,10 +891,7 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def
// We use a "pack" alignment (when reading from textures) to 4 here,
// since the only place where we really use it is the BMP screenshot
// code and that requires the same alignment too.
- GLCALL(glPixelStorei(GL_PACK_ALIGNMENT, 4));
-
- // Query information needed by textures.
- Texture::queryTextureInformation();
+ GL_CALL(glPixelStorei(GL_PACK_ALIGNMENT, 4));
// Refresh the output screen dimensions if some are set up.
if (_outputScreenWidth != 0 && _outputScreenHeight != 0) {
@@ -892,42 +905,56 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def
_defaultFormatAlpha = defaultFormatAlpha;
if (_gameScreen) {
- _gameScreen->recreateInternalTexture();
+ _gameScreen->recreate();
}
if (_overlay) {
- _overlay->recreateInternalTexture();
+ _overlay->recreate();
}
if (_cursor) {
- _cursor->recreateInternalTexture();
+ _cursor->recreate();
}
#ifdef USE_OSD
if (_osd) {
- _osd->recreateInternalTexture();
+ _osd->recreate();
}
#endif
}
void OpenGLGraphicsManager::notifyContextDestroy() {
if (_gameScreen) {
- _gameScreen->releaseInternalTexture();
+ _gameScreen->destroy();
}
if (_overlay) {
- _overlay->releaseInternalTexture();
+ _overlay->destroy();
}
if (_cursor) {
- _cursor->releaseInternalTexture();
+ _cursor->destroy();
}
#ifdef USE_OSD
if (_osd) {
- _osd->releaseInternalTexture();
+ _osd->destroy();
}
#endif
+
+#if !USE_FORCED_GLES
+ if (g_context.shadersSupported) {
+ ShaderMan.notifyDestroy();
+ }
+#endif
+
+ // Destroy rendering pipeline.
+ g_context.setPipeline(nullptr);
+ delete _pipeline;
+ _pipeline = nullptr;
+
+ // Rest our context description since the context is gone soon.
+ g_context.reset();
}
void OpenGLGraphicsManager::adjustMousePosition(int16 &x, int16 &y) {
@@ -970,9 +997,15 @@ void OpenGLGraphicsManager::setMousePosition(int x, int y) {
}
}
-Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &format, bool wantAlpha) {
+Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha) {
GLenum glIntFormat, glFormat, glType;
if (format.bytesPerPixel == 1) {
+#if !USE_FORCED_GLES
+ if (TextureCLUT8GPU::isSupportedByContext()) {
+ return new TextureCLUT8GPU();
+ }
+#endif
+
const Graphics::PixelFormat &virtFormat = wantAlpha ? _defaultFormatAlpha : _defaultFormat;
const bool supported = getGLPixelFormat(virtFormat, glIntFormat, glFormat, glType);
if (!supported) {
@@ -980,6 +1013,15 @@ Texture *OpenGLGraphicsManager::createTexture(const Graphics::PixelFormat &forma
} else {
return new TextureCLUT8(glIntFormat, glFormat, glType, virtFormat);
}
+#if !USE_FORCED_GL
+ } else if (isGLESContext() && format == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) {
+ // OpenGL ES does not support a texture format usable for RGB555.
+ // Since SCUMM uses this pixel format for some games (and there is no
+ // hope for this to change anytime soon) we use pixel format
+ // conversion to a supported texture format. However, this is a one
+ // time exception.
+ return new TextureRGB555();
+#endif // !USE_FORCED_GL
} else {
const bool supported = getGLPixelFormat(format, glIntFormat, glFormat, glType);
if (!supported) {
@@ -1015,7 +1057,11 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF
glFormat = GL_RGBA;
glType = GL_UNSIGNED_SHORT_4_4_4_4;
return true;
-#ifndef USE_GLES
+#if !USE_FORCED_GLES && !USE_FORCED_GLES2
+ // The formats below are not supported by every GLES implementation.
+ // Thus, we do not mark them as supported when a GLES context is setup.
+ } else if (isGLESContext()) {
+ return false;
#ifdef SCUMM_LITTLE_ENDIAN
} else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)) { // RGBA8888
glIntFormat = GL_RGBA;
@@ -1024,17 +1070,10 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF
return true;
#endif
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) { // RGB555
- // GL_BGRA does not exist in every GLES implementation so should not be configured if
- // USE_GLES is set.
glIntFormat = GL_RGB;
glFormat = GL_BGRA;
glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
return true;
- } else if (pixelFormat == Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)) { // ARGB8888
- glIntFormat = GL_RGBA;
- glFormat = GL_BGRA;
- glType = GL_UNSIGNED_INT_8_8_8_8_REV;
- return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)) { // ARGB4444
glIntFormat = GL_RGBA;
glFormat = GL_BGRA;
@@ -1054,8 +1093,8 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF
return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0)) { // BGR565
glIntFormat = GL_RGB;
- glFormat = GL_BGR;
- glType = GL_UNSIGNED_SHORT_5_6_5;
+ glFormat = GL_RGB;
+ glType = GL_UNSIGNED_SHORT_5_6_5_REV;
return true;
} else if (pixelFormat == Graphics::PixelFormat(2, 5, 5, 5, 1, 1, 6, 11, 0)) { // BGRA5551
glIntFormat = GL_RGBA;
@@ -1072,7 +1111,7 @@ bool OpenGLGraphicsManager::getGLPixelFormat(const Graphics::PixelFormat &pixelF
glFormat = GL_BGRA;
glType = GL_UNSIGNED_SHORT_4_4_4_4;
return true;
-#endif
+#endif // !USE_FORCED_GLES && !USE_FORCED_GLES2
} else {
return false;
}
@@ -1119,10 +1158,10 @@ void OpenGLGraphicsManager::recalculateDisplayArea() {
// Setup drawing limitation for game graphics.
// This invovles some trickery because OpenGL's viewport coordinate system
// is upside down compared to ours.
- GLCALL(glScissor(_displayX,
- _outputScreenHeight - _displayHeight - _displayY,
- _displayWidth,
- _displayHeight));
+ _backBuffer.setScissorBox(_displayX,
+ _outputScreenHeight - _displayHeight - _displayY,
+ _displayWidth,
+ _displayHeight);
// Clear the whole screen for the first three frames to remove leftovers.
_scissorOverride = 3;
@@ -1144,20 +1183,7 @@ void OpenGLGraphicsManager::updateCursorPalette() {
_cursor->setPalette(0, 256, _gamePalette);
}
- // We remove all alpha bits from the palette entry of the color key.
- // This makes sure its properly handled as color key.
- const Graphics::PixelFormat &hardwareFormat = _cursor->getHardwareFormat();
- const uint32 aMask = (0xFF >> hardwareFormat.aLoss) << hardwareFormat.aShift;
-
- if (hardwareFormat.bytesPerPixel == 2) {
- uint16 *palette = (uint16 *)_cursor->getPalette() + _cursorKeyColor;
- *palette &= ~aMask;
- } else if (hardwareFormat.bytesPerPixel == 4) {
- uint32 *palette = (uint32 *)_cursor->getPalette() + _cursorKeyColor;
- *palette &= ~aMask;
- } else {
- warning("OpenGLGraphicsManager::updateCursorPalette: Unsupported pixel depth %d", hardwareFormat.bytesPerPixel);
- }
+ _cursor->setColorKey(_cursorKeyColor);
}
void OpenGLGraphicsManager::recalculateCursorScaling() {
@@ -1207,7 +1233,7 @@ void OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const
uint8 *pixels = new uint8[lineSize * height];
// Get pixel data from OpenGL buffer
- GLCALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels));
+ GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels));
// BMP stores as BGR. Since we can't assume that GL_BGR is supported we
// will swap the components from the RGB we read to BGR on our own.
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index 9578839383..35435c156e 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -24,6 +24,7 @@
#define BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
#include "backends/graphics/opengl/opengl-sys.h"
+#include "backends/graphics/opengl/framebuffer.h"
#include "backends/graphics/graphics.h"
#include "common/frac.h"
@@ -40,7 +41,11 @@ namespace OpenGL {
// SurfaceSDL backend enables it and disabling it can cause issues in sdl.cpp.
#define USE_OSD 1
-class Texture;
+class Surface;
+class Pipeline;
+#if !USE_FORCED_GLES
+class Shader;
+#endif
enum {
GFX_LINEAR = 0,
@@ -117,6 +122,11 @@ public:
protected:
/**
+ * Whether an GLES or GLES2 context is active.
+ */
+ bool isGLESContext() const { return g_context.type == kContextGLES || g_context.type == kContextGLES2; }
+
+ /**
* Set up the actual screen size available for the OpenGL code to do any
* drawing.
*
@@ -126,6 +136,16 @@ protected:
void setActualScreenSize(uint width, uint height);
/**
+ * Sets the OpenGL (ES) type the graphics manager shall work with.
+ *
+ * This needs to be called at least once (and before ever calling
+ * notifyContextCreate).
+ *
+ * @param type Type of the OpenGL (ES) contexts to be created.
+ */
+ void setContextType(ContextType type);
+
+ /**
* Notify the manager of a OpenGL context change. This should be the first
* thing to call after you created an OpenGL (ES) context!
*
@@ -172,15 +192,15 @@ protected:
private:
/**
- * Create a texture with the specified pixel format.
+ * Create a surface with the specified pixel format.
*
- * @param format The pixel format the Texture object should accept as
+ * @param format The pixel format the Surface object should accept as
* input.
- * @param wantAlpha For CLUT8 textures this marks whether an alpha
+ * @param wantAlpha For CLUT8 surfaces this marks whether an alpha
* channel should be used.
- * @return A pointer to the texture or nullptr on failure.
+ * @return A pointer to the surface or nullptr on failure.
*/
- Texture *createTexture(const Graphics::PixelFormat &format, bool wantAlpha = false);
+ Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false);
//
// Transaction support
@@ -281,6 +301,36 @@ private:
//
/**
+ * Initialize the active context for use.
+ */
+ void initializeGLContext();
+
+ /**
+ * Render back buffer.
+ */
+ Backbuffer _backBuffer;
+
+ /**
+ * OpenGL pipeline used for rendering.
+ */
+ Pipeline *_pipeline;
+
+protected:
+ /**
+ * Query the address of an OpenGL function by name.
+ *
+ * This can only be used after a context has been created.
+ * Please note that this function can return valid addresses even if the
+ * OpenGL context does not support the function.
+ *
+ * @param name The name of the OpenGL function.
+ * @return An function pointer for the requested OpenGL function or
+ * nullptr in case of failure.
+ */
+ virtual void *getProcAddress(const char *name) const = 0;
+
+private:
+ /**
* Try to determine the internal parameters for a given pixel format.
*
* @return true when the format can be used, false otherwise.
@@ -348,7 +398,7 @@ private:
/**
* The virtual game screen.
*/
- Texture *_gameScreen;
+ Surface *_gameScreen;
/**
* The game palette if in CLUT8 mode.
@@ -367,7 +417,7 @@ private:
/**
* The overlay screen.
*/
- Texture *_overlay;
+ Surface *_overlay;
/**
* Whether the overlay is visible or not.
@@ -386,7 +436,7 @@ private:
/**
* The cursor image.
*/
- Texture *_cursor;
+ Surface *_cursor;
/**
* X coordinate of the cursor in phyiscal coordinates.
@@ -497,7 +547,7 @@ private:
/**
* The OSD's contents.
*/
- Texture *_osd;
+ Surface *_osd;
/**
* Current opacity level of the OSD.
diff --git a/backends/graphics/opengl/opengl-sys.h b/backends/graphics/opengl/opengl-sys.h
index 4e21894380..4495128f32 100644
--- a/backends/graphics/opengl/opengl-sys.h
+++ b/backends/graphics/opengl/opengl-sys.h
@@ -23,35 +23,147 @@
#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_SYS_H
#define BACKENDS_GRAPHICS_OPENGL_OPENGL_SYS_H
-// The purpose of this header is to include the OpenGL headers in an uniform
-// fashion. A notable example for a non standard port is the Tizen port.
-
#include "common/scummsys.h"
-#ifdef WIN32
-#if defined(ARRAYSIZE) && !defined(_WINDOWS_)
-#undef ARRAYSIZE
-#endif
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#undef ARRAYSIZE
+#include "backends/graphics/opengl/debug.h"
+#ifdef SDL_BACKEND
+#include "backends/platform/sdl/sdl-sys.h"
#endif
-// HACK: In case common/util.h has been included already we need to make sure
-// to define ARRAYSIZE again in case of Windows.
-#if !defined(ARRAYSIZE) && defined(COMMON_UTIL_H)
-#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+// On OS X we only support GL contexts. The reason is that Apple's GL interface
+// uses "void *" for GLhandleARB which is not type compatible with GLint. This
+// kills our aliasing trick for extension functions and thus would force us to
+// supply two different Shader class implementations or introduce other
+// wrappers. OS X only supports GL contexts right now anyway (at least
+// according to SDL2 sources), thus it is not much of an issue.
+#if defined(MACOSX) && (!defined(USE_GLES_MODE) || USE_GLES_MODE != 0)
+//#warning "Only forced OpenGL mode is supported on Mac OS X. Overriding settings."
+#undef USE_GLES_MODE
+#define USE_GLES_MODE 0
#endif
+// We allow to force GL or GLES modes on compile time.
+// For this the USE_GLES_MODE define is used. The following values represent
+// the given selection choices:
+// 0 - Force OpenGL context
+// 1 - Force OpenGL ES context
+// 2 - Force OpenGL ES 2.0 context
+#define USE_FORCED_GL (defined(USE_GLES_MODE) && USE_GLES_MODE == 0)
+#define USE_FORCED_GLES (defined(USE_GLES_MODE) && USE_GLES_MODE == 1)
+#define USE_FORCED_GLES2 (defined(USE_GLES_MODE) && USE_GLES_MODE == 2)
+
+// On Tizen we include the toolchain's OpenGL file. This is something we
+// actually want to avoid. However, since Tizen uses eglGetProcAddress which
+// is not required to return valid function pointers to non OpenGL extension
+// functions, we need the system's definitions to resolve all OpenGL
+// functions.
+// TODO: See if there is an alternative which allows us to avoid including
+// Tizen's OpenGL header here.
#if defined(TIZEN)
-#include <FGraphicsOpengl.h>
-using namespace Tizen::Graphics::Opengl;
-#elif defined(USE_GLES)
-#include <GLES/gl.h>
-#elif defined(SDL_BACKEND)
-#include <SDL_opengl.h>
+ #include <FGraphicsOpengl.h>
+ using namespace Tizen::Graphics::Opengl;
+ #define USE_BUILTIN_OPENGL
#else
-#include <GL/gl.h>
+ #include "backends/graphics/opengl/opengl-defs.h"
#endif
+#ifdef SDL_BACKEND
+ // Win32 needs OpenGL functions declared with APIENTRY.
+ // However, SDL does not define APIENTRY in it's SDL.h file on non-Windows
+ // targets, thus if it is not available, we just dummy define it.
+ #ifndef APIENTRY
+ #define APIENTRY
+ #endif
+ #define GL_CALL_CONV APIENTRY
+#else
+ #define GL_CALL_CONV
+#endif
+
+namespace OpenGL {
+
+enum ContextType {
+ kContextGL,
+ kContextGLES,
+ kContextGLES2
+};
+
+class Pipeline;
+class Framebuffer;
+
+/**
+ * Description structure of the OpenGL (ES) context.
+ */
+struct Context {
+ /** The type of the active context. */
+ ContextType type;
+
+ /**
+ * Reset context.
+ *
+ * This marks all extensions as unavailable and clears all function
+ * pointers.
+ */
+ void reset();
+
+ /** The maximum texture size supported by the context. */
+ GLint maxTextureSize;
+
+ /** Whether GL_ARB_texture_non_power_of_two is available or not. */
+ bool NPOTSupported;
+
+ /** Whether shader support is available or not. */
+ bool shadersSupported;
+
+ /** Whether multi texture support is available or not. */
+ bool multitextureSupported;
+
+ /** Whether FBO support is available or not. */
+ bool framebufferObjectSupported;
+
+#define GL_FUNC_DEF(ret, name, param) ret (GL_CALL_CONV *name)param
+#include "backends/graphics/opengl/opengl-func.h"
+#undef GL_FUNC_DEF
+
+ //
+ // Wrapper functionality to handle fixed-function pipelines and
+ // programmable pipelines in the same fashion.
+ //
+
+private:
+ /** Currently active rendering pipeline. */
+ Pipeline *activePipeline;
+
+public:
+ /**
+ * Set new pipeline.
+ *
+ * Client is responsible for any memory management related to pipelines.
+ *
+ * @param pipeline Pipeline to activate.
+ * @return Formerly active pipeline.
+ */
+ Pipeline *setPipeline(Pipeline *pipeline);
+
+ /**
+ * Query the currently active rendering pipeline.
+ */
+ Pipeline *getActivePipeline() const { return activePipeline; }
+};
+
+/**
+ * The (active) OpenGL context.
+ */
+extern Context g_context;
+
+} // End of namespace OpenGL
+
+#define GL_CALL(x) GL_WRAP_DEBUG(g_context.x, x)
+#define GL_CALL_SAFE(func, params) \
+ do { \
+ if (g_context.func) { \
+ GL_CALL(func params); \
+ } \
+ } while (0)
+#define GL_ASSIGN(var, x) GL_WRAP_DEBUG(var = g_context.x, x)
+
#endif
diff --git a/backends/graphics/opengl/pipelines/clut8.cpp b/backends/graphics/opengl/pipelines/clut8.cpp
new file mode 100644
index 0000000000..fca40074f0
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/clut8.cpp
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/pipelines/clut8.h"
+#include "backends/graphics/opengl/shader.h"
+#include "backends/graphics/opengl/framebuffer.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES
+CLUT8LookUpPipeline::CLUT8LookUpPipeline()
+ : ShaderPipeline(ShaderMan.query(ShaderManager::kCLUT8LookUp)), _paletteTexture(nullptr) {
+}
+
+void CLUT8LookUpPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) {
+ // Set the palette texture.
+ GL_CALL(glActiveTexture(GL_TEXTURE1));
+ if (_paletteTexture) {
+ _paletteTexture->bind();
+ }
+
+ GL_CALL(glActiveTexture(GL_TEXTURE0));
+ ShaderPipeline::drawTexture(texture, coordinates);
+}
+#endif // !USE_FORCED_GLES
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/extensions.cpp b/backends/graphics/opengl/pipelines/clut8.h
index 4482ef82b5..16724e4652 100644
--- a/backends/graphics/opengl/extensions.cpp
+++ b/backends/graphics/opengl/pipelines/clut8.h
@@ -20,29 +20,27 @@
*
*/
-#include "backends/graphics/opengl/extensions.h"
-#include "backends/graphics/opengl/opengl-sys.h"
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
-#include "common/tokenizer.h"
+#include "backends/graphics/opengl/pipelines/shader.h"
namespace OpenGL {
-bool g_extNPOTSupported = false;
+#if !USE_FORCED_GLES
+class CLUT8LookUpPipeline : public ShaderPipeline {
+public:
+ CLUT8LookUpPipeline();
-void initializeGLExtensions() {
- const char *extString = (const char *)glGetString(GL_EXTENSIONS);
+ void setPaletteTexture(const GLTexture *paletteTexture) { _paletteTexture = paletteTexture; }
- // Initialize default state.
- g_extNPOTSupported = false;
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates);
- Common::StringTokenizer tokenizer(extString, " ");
- while (!tokenizer.empty()) {
- Common::String token = tokenizer.nextToken();
-
- if (token == "GL_ARB_texture_non_power_of_two") {
- g_extNPOTSupported = true;
- }
- }
-}
+private:
+ const GLTexture *_paletteTexture;
+};
+#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/pipelines/fixed.cpp b/backends/graphics/opengl/pipelines/fixed.cpp
new file mode 100644
index 0000000000..8e3bd7eaee
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/fixed.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/pipelines/fixed.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES2
+void FixedPipeline::activateInternal() {
+ GL_CALL(glDisable(GL_LIGHTING));
+ GL_CALL(glDisable(GL_FOG));
+ GL_CALL(glShadeModel(GL_FLAT));
+ GL_CALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
+
+ GL_CALL(glEnableClientState(GL_VERTEX_ARRAY));
+ GL_CALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
+
+#if !USE_FORCED_GLES
+ if (g_context.multitextureSupported) {
+ GL_CALL(glActiveTexture(GL_TEXTURE0));
+ }
+#endif
+ GL_CALL(glEnable(GL_TEXTURE_2D));
+}
+
+void FixedPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ GL_CALL(glColor4f(r, g, b, a));
+}
+
+void FixedPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) {
+ texture.bind();
+
+ GL_CALL(glTexCoordPointer(2, GL_FLOAT, 0, texture.getTexCoords()));
+ GL_CALL(glVertexPointer(2, GL_FLOAT, 0, coordinates));
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
+}
+
+void FixedPipeline::setProjectionMatrix(const GLfloat *projectionMatrix) {
+ if (!isActive()) {
+ return;
+ }
+
+ GL_CALL(glMatrixMode(GL_PROJECTION));
+ GL_CALL(glLoadMatrixf(projectionMatrix));
+
+ GL_CALL(glMatrixMode(GL_MODELVIEW));
+ GL_CALL(glLoadIdentity());
+}
+#endif // !USE_FORCED_GLES2
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/pipelines/fixed.h b/backends/graphics/opengl/pipelines/fixed.h
new file mode 100644
index 0000000000..6bfe140c19
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/fixed.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
+
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES2
+class FixedPipeline : public Pipeline {
+public:
+ virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates);
+
+ virtual void setProjectionMatrix(const GLfloat *projectionMatrix);
+
+protected:
+ virtual void activateInternal();
+};
+#endif // !USE_FORCED_GLES2
+
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/pipelines/pipeline.cpp b/backends/graphics/opengl/pipelines/pipeline.cpp
new file mode 100644
index 0000000000..6a59cd28e7
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/pipeline.cpp
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+#include "backends/graphics/opengl/framebuffer.h"
+
+namespace OpenGL {
+
+Pipeline::Pipeline()
+ : _activeFramebuffer(nullptr), _isActive(false) {
+}
+
+void Pipeline::activate() {
+ _isActive = true;
+
+ if (_activeFramebuffer) {
+ _activeFramebuffer->activate();
+ }
+
+ activateInternal();
+}
+
+void Pipeline::deactivate() {
+ deactivateInternal();
+
+ if (_activeFramebuffer) {
+ _activeFramebuffer->deactivate();
+ }
+
+ _isActive = false;
+}
+
+Framebuffer *Pipeline::setFramebuffer(Framebuffer *framebuffer) {
+ Framebuffer *oldFramebuffer = _activeFramebuffer;
+ if (_isActive && oldFramebuffer) {
+ oldFramebuffer->deactivate();
+ }
+
+ _activeFramebuffer = framebuffer;
+ if (_isActive && _activeFramebuffer) {
+ _activeFramebuffer->activate();
+ }
+
+ return oldFramebuffer;
+}
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/pipelines/pipeline.h b/backends/graphics/opengl/pipelines/pipeline.h
new file mode 100644
index 0000000000..9f32d33b95
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/pipeline.h
@@ -0,0 +1,126 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
+
+#include "backends/graphics/opengl/opengl-sys.h"
+#include "backends/graphics/opengl/texture.h"
+
+namespace OpenGL {
+
+class Framebuffer;
+
+/**
+ * Interface for OpenGL pipeline functionality.
+ *
+ * This encapsulates differences in various rendering pipelines used for
+ * OpenGL, OpenGL ES 1, and OpenGL ES 2.
+ */
+class Pipeline {
+public:
+ Pipeline();
+ virtual ~Pipeline() {}
+
+ /**
+ * Activate the pipeline.
+ *
+ * This sets the OpenGL state to make use of drawing with the given
+ * OpenGL pipeline.
+ */
+ void activate();
+
+ /**
+ * Deactivate the pipeline.
+ */
+ void deactivate();
+
+ /**
+ * Set framebuffer to render to.
+ *
+ * Client is responsible for any memory management related to framebuffer.
+ *
+ * @param framebuffer Framebuffer to activate.
+ * @return Formerly active framebuffer.
+ */
+ Framebuffer *setFramebuffer(Framebuffer *framebuffer);
+
+ /**
+ * Set modulation color.
+ *
+ * @param r Red component in [0,1].
+ * @param g Green component in [0,1].
+ * @param b Blue component in [0,1].
+ * @param a Alpha component in [0,1].
+ */
+ virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) = 0;
+
+ /**
+ * Draw a texture rectangle to the currently active framebuffer.
+ *
+ * @param texture Texture to use for drawing.
+ * @param coordinates x1, y1, x2, y2 coordinates where to draw the texture.
+ */
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates) = 0;
+
+ void drawTexture(const GLTexture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
+ const GLfloat coordinates[4*2] = {
+ x, y,
+ x + w, y,
+ x, y + h,
+ x + w, y + h
+ };
+ drawTexture(texture, coordinates);
+ }
+
+ /**
+ * Set the projection matrix.
+ *
+ * This is intended to be only ever be used by Framebuffer subclasses.
+ */
+ virtual void setProjectionMatrix(const GLfloat *projectionMatrix) = 0;
+
+protected:
+ /**
+ * Activate the pipeline.
+ *
+ * This sets the OpenGL state to make use of drawing with the given
+ * OpenGL pipeline.
+ */
+ virtual void activateInternal() = 0;
+
+ /**
+ * Deactivate the pipeline.
+ */
+ virtual void deactivateInternal() {}
+
+ bool isActive() const { return _isActive; }
+
+ Framebuffer *_activeFramebuffer;
+
+private:
+ bool _isActive;
+};
+
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/pipelines/shader.cpp b/backends/graphics/opengl/pipelines/shader.cpp
new file mode 100644
index 0000000000..8e38458f73
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/shader.cpp
@@ -0,0 +1,94 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/pipelines/shader.h"
+#include "backends/graphics/opengl/shader.h"
+#include "backends/graphics/opengl/framebuffer.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES
+ShaderPipeline::ShaderPipeline(Shader *shader)
+ : _activeShader(shader), _colorAttributes() {
+ _vertexAttribLocation = shader->getAttributeLocation("position");
+ _texCoordAttribLocation = shader->getAttributeLocation("texCoordIn");
+ _colorAttribLocation = shader->getAttributeLocation("blendColorIn");
+
+ assert(_vertexAttribLocation != -1);
+ assert(_texCoordAttribLocation != -1);
+ assert(_colorAttribLocation != -1);
+
+ // One of the attributes needs to be passed through location 0, otherwise
+ // we get no output for GL contexts due to GL compatibility reasons. Let's
+ // check whether this ever happens. If this ever gets hit, we need to
+ // enable location 0 and pass some dummy values through it to fix output.
+ assert( _vertexAttribLocation == 0
+ || _texCoordAttribLocation == 0
+ || _colorAttribLocation == 0);
+}
+
+void ShaderPipeline::activateInternal() {
+ GL_CALL(glEnableVertexAttribArray(_vertexAttribLocation));
+ GL_CALL(glEnableVertexAttribArray(_texCoordAttribLocation));
+ GL_CALL(glEnableVertexAttribArray(_colorAttribLocation));
+
+ if (g_context.multitextureSupported) {
+ GL_CALL(glActiveTexture(GL_TEXTURE0));
+ }
+
+ _activeShader->activate();
+}
+
+void ShaderPipeline::deactivateInternal() {
+ GL_CALL(glDisableVertexAttribArray(_vertexAttribLocation));
+ GL_CALL(glDisableVertexAttribArray(_texCoordAttribLocation));
+ GL_CALL(glDisableVertexAttribArray(_colorAttribLocation));
+
+ _activeShader->deactivate();
+}
+
+void ShaderPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ GLfloat *dst = _colorAttributes;
+ for (uint i = 0; i < 4; ++i) {
+ *dst++ = r;
+ *dst++ = g;
+ *dst++ = b;
+ *dst++ = a;
+ }
+
+ GL_CALL(glVertexAttribPointer(_colorAttribLocation, 4, GL_FLOAT, GL_FALSE, 0, _colorAttributes));
+}
+
+void ShaderPipeline::drawTexture(const GLTexture &texture, const GLfloat *coordinates) {
+ texture.bind();
+
+ GL_CALL(glVertexAttribPointer(_texCoordAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, texture.getTexCoords()));
+ GL_CALL(glVertexAttribPointer(_vertexAttribLocation, 2, GL_FLOAT, GL_FALSE, 0, coordinates));
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
+}
+
+void ShaderPipeline::setProjectionMatrix(const GLfloat *projectionMatrix) {
+ _activeShader->setUniform("projection", new ShaderUniformMatrix44(projectionMatrix));
+}
+#endif // !USE_FORCED_GLES
+
+} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/pipelines/shader.h b/backends/graphics/opengl/pipelines/shader.h
new file mode 100644
index 0000000000..6159607099
--- /dev/null
+++ b/backends/graphics/opengl/pipelines/shader.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
+#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
+
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+
+namespace OpenGL {
+
+#if !USE_FORCED_GLES
+class Shader;
+
+class ShaderPipeline : public Pipeline {
+public:
+ ShaderPipeline(Shader *shader);
+
+ virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+
+ virtual void drawTexture(const GLTexture &texture, const GLfloat *coordinates);
+
+ virtual void setProjectionMatrix(const GLfloat *projectionMatrix);
+
+protected:
+ virtual void activateInternal();
+ virtual void deactivateInternal();
+
+ GLint _vertexAttribLocation;
+ GLint _texCoordAttribLocation;
+ GLint _colorAttribLocation;
+
+ GLfloat _colorAttributes[4*4];
+
+ Shader *const _activeShader;
+};
+#endif // !USE_FORCED_GLES
+
+} // End of namespace OpenGL
+
+#endif
diff --git a/backends/graphics/opengl/shader.cpp b/backends/graphics/opengl/shader.cpp
new file mode 100644
index 0000000000..27981f25dc
--- /dev/null
+++ b/backends/graphics/opengl/shader.cpp
@@ -0,0 +1,332 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "backends/graphics/opengl/shader.h"
+
+#if !USE_FORCED_GLES
+
+#include "common/textconsole.h"
+#include "common/util.h"
+
+namespace Common {
+DECLARE_SINGLETON(OpenGL::ShaderManager);
+}
+
+namespace OpenGL {
+
+namespace {
+
+#pragma mark - Builtin Shader Sources -
+
+const char *const g_defaultVertexShader =
+ "attribute vec4 position;\n"
+ "attribute vec2 texCoordIn;\n"
+ "attribute vec4 blendColorIn;\n"
+ "\n"
+ "uniform mat4 projection;\n"
+ "\n"
+ "varying vec2 texCoord;\n"
+ "varying vec4 blendColor;\n"
+ "\n"
+ "void main(void) {\n"
+ "\ttexCoord = texCoordIn;\n"
+ "\tblendColor = blendColorIn;\n"
+ "\tgl_Position = projection * position;\n"
+ "}\n";
+
+const char *const g_defaultFragmentShader =
+ "varying vec2 texCoord;\n"
+ "varying vec4 blendColor;\n"
+ "\n"
+ "uniform sampler2D texture;\n"
+ "\n"
+ "void main(void) {\n"
+ "\tgl_FragColor = blendColor * texture2D(texture, texCoord);\n"
+ "}\n";
+
+const char *const g_lookUpFragmentShader =
+ "varying vec2 texCoord;\n"
+ "varying vec4 blendColor;\n"
+ "\n"
+ "uniform sampler2D texture;\n"
+ "uniform sampler2D palette;\n"
+ "\n"
+ "const float adjustFactor = 255.0 / 256.0 + 1.0 / (2.0 * 256.0);"
+ "\n"
+ "void main(void) {\n"
+ "\tvec4 index = texture2D(texture, texCoord);\n"
+ "\tgl_FragColor = blendColor * texture2D(palette, vec2(index.a * adjustFactor, 0.0));\n"
+ "}\n";
+
+
+// Taken from: https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_03#OpenGL_ES_2_portability
+const char *const g_precisionDefines =
+ "#ifdef GL_ES\n"
+ "\t#if defined(GL_FRAGMENT_PRECISION_HIGH) && GL_FRAGMENT_PRECISION_HIGH == 1\n"
+ "\t\tprecision highp float;\n"
+ "\t#else\n"
+ "\t\tprecision mediump float;\n"
+ "\t#endif\n"
+ "#else\n"
+ "\t#define highp\n"
+ "\t#define mediump\n"
+ "\t#define lowp\n"
+ "#endif\n";
+
+} // End of anonymous namespace
+
+#pragma mark - Uniform Values -
+
+void ShaderUniformInteger::set(GLint location) const {
+ GL_CALL(glUniform1i(location, _value));
+}
+
+void ShaderUniformFloat::set(GLint location) const {
+ GL_CALL(glUniform1f(location, _value));
+}
+
+void ShaderUniformMatrix44::set(GLint location) const {
+ GL_CALL(glUniformMatrix4fv(location, 1, GL_FALSE, _matrix));
+}
+
+#pragma mark - Shader Implementation -
+
+Shader::Shader(const Common::String &vertex, const Common::String &fragment)
+ : _vertex(vertex), _fragment(fragment), _isActive(false), _program(0), _uniforms() {
+ recreate();
+}
+
+Shader::~Shader() {
+ // According to extension specification glDeleteObjectARB silently ignores
+ // 0. However, with nVidia drivers this can cause GL_INVALID_VALUE, thus
+ // we do not call it with 0 as parameter to avoid warnings.
+ if (_program) {
+ GL_CALL_SAFE(glDeleteProgram, (_program));
+ }
+}
+
+void Shader::destroy() {
+ // According to extension specification glDeleteObjectARB silently ignores
+ // 0. However, with nVidia drivers this can cause GL_INVALID_VALUE, thus
+ // we do not call it with 0 as parameter to avoid warnings.
+ if (_program) {
+ GL_CALL(glDeleteProgram(_program));
+ _program = 0;
+ }
+}
+
+bool Shader::recreate() {
+ // Make sure any old programs are destroyed properly.
+ destroy();
+
+ GLshader vertexShader = compileShader(_vertex.c_str(), GL_VERTEX_SHADER);
+ if (!vertexShader) {
+ return false;
+ }
+
+ GLshader fragmentShader = compileShader(_fragment.c_str(), GL_FRAGMENT_SHADER);
+ if (!fragmentShader) {
+ GL_CALL(glDeleteShader(vertexShader));
+ return false;
+ }
+
+ GL_ASSIGN(_program, glCreateProgram());
+ if (!_program) {
+ GL_CALL(glDeleteShader(vertexShader));
+ GL_CALL(glDeleteShader(fragmentShader));
+ return false;
+ }
+
+ GL_CALL(glAttachShader(_program, vertexShader));
+ GL_CALL(glAttachShader(_program, fragmentShader));
+
+ GL_CALL(glLinkProgram(_program));
+
+ GL_CALL(glDetachShader(_program, fragmentShader));
+ GL_CALL(glDeleteShader(fragmentShader));
+
+ GL_CALL(glDetachShader(_program, vertexShader));
+ GL_CALL(glDeleteShader(vertexShader));
+
+ GLint result;
+ GL_CALL(glGetProgramiv(_program, GL_LINK_STATUS, &result));
+ if (result == GL_FALSE) {
+ GLint logSize;
+ GL_CALL(glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &logSize));
+
+ GLchar *log = new GLchar[logSize];
+ GL_CALL(glGetProgramInfoLog(_program, logSize, nullptr, log));
+ warning("Could not link shader: \"%s\"", log);
+ delete[] log;
+
+ destroy();
+ return false;
+ }
+
+ // Set program object in case shader is active during recreation.
+ if (_isActive) {
+ GL_CALL(glUseProgram(_program));
+ }
+
+ for (UniformMap::iterator i = _uniforms.begin(), end = _uniforms.end(); i != end; ++i) {
+ i->_value.location = getUniformLocation(i->_key.c_str());
+ i->_value.altered = true;
+ if (_isActive) {
+ i->_value.set();
+ }
+ }
+
+ return true;
+}
+
+void Shader::activate() {
+ // Activate program.
+ GL_CALL(glUseProgram(_program));
+
+ // Reset changed uniform values.
+ for (UniformMap::iterator i = _uniforms.begin(), end = _uniforms.end(); i != end; ++i) {
+ i->_value.set();
+ }
+
+ _isActive = true;
+}
+
+void Shader::deactivate() {
+ _isActive = false;
+}
+
+GLint Shader::getAttributeLocation(const char *name) const {
+ GLint result = -1;
+ GL_ASSIGN(result, glGetAttribLocation(_program, name));
+ return result;
+}
+
+GLint Shader::getUniformLocation(const char *name) const {
+ GLint result = -1;
+ GL_ASSIGN(result, glGetUniformLocation(_program, name));
+ return result;
+}
+
+bool Shader::setUniform(const Common::String &name, ShaderUniformValue *value) {
+ UniformMap::iterator uniformIter = _uniforms.find(name);
+ Uniform *uniform;
+
+ if (uniformIter == _uniforms.end()) {
+ const GLint location = getUniformLocation(name.c_str());
+ if (location == -1) {
+ delete value;
+ return false;
+ }
+
+ uniform = &_uniforms[name];
+ uniform->location = location;
+ } else {
+ uniform = &uniformIter->_value;
+ }
+
+ uniform->value = Common::SharedPtr<ShaderUniformValue>(value);
+ uniform->altered = true;
+ if (_isActive) {
+ uniform->set();
+ }
+
+ return true;
+}
+
+GLshader Shader::compileShader(const char *source, GLenum shaderType) {
+ GLshader handle;
+ GL_ASSIGN(handle, glCreateShader(shaderType));
+ if (!handle) {
+ return 0;
+ }
+
+ const char *const sources[2] = {
+ g_precisionDefines,
+ source
+ };
+
+ GL_CALL(glShaderSource(handle, ARRAYSIZE(sources), sources, nullptr));
+ GL_CALL(glCompileShader(handle));
+
+ GLint result;
+ GL_CALL(glGetShaderiv(handle, GL_COMPILE_STATUS, &result));
+ if (result == GL_FALSE) {
+ GLint logSize;
+ GL_CALL(glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &logSize));
+
+ GLchar *log = new GLchar[logSize];
+ GL_CALL(glGetShaderInfoLog(handle, logSize, nullptr, log));
+ warning("Could not compile shader \"%s\": \"%s\"", source, log);
+ delete[] log;
+
+ GL_CALL(glDeleteShader(handle));
+ return 0;
+ }
+
+ return handle;
+}
+
+ShaderManager::ShaderManager() : _initializeShaders(true) {
+}
+
+ShaderManager::~ShaderManager() {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ delete _builtIn[i];
+ }
+}
+
+void ShaderManager::notifyDestroy() {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ _builtIn[i]->destroy();
+ }
+}
+
+void ShaderManager::notifyCreate() {
+ if (_initializeShaders) {
+ _initializeShaders = false;
+
+ _builtIn[kDefault] = new Shader(g_defaultVertexShader, g_defaultFragmentShader);
+ _builtIn[kCLUT8LookUp] = new Shader(g_defaultVertexShader, g_lookUpFragmentShader);
+ _builtIn[kCLUT8LookUp]->setUniform1I("palette", 1);
+
+ for (uint i = 0; i < kMaxUsages; ++i) {
+ _builtIn[i]->setUniform1I("texture", 0);
+ }
+ } else {
+ for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
+ _builtIn[i]->recreate();
+ }
+ }
+}
+
+Shader *ShaderManager::query(ShaderUsage shader) const {
+ if (shader == kMaxUsages) {
+ warning("OpenGL: ShaderManager::query used with kMaxUsages");
+ return nullptr;
+ }
+
+ return _builtIn[shader];
+}
+
+} // End of namespace OpenGL
+
+#endif // !USE_FORCED_GLES
diff --git a/backends/graphics/opengl/shader.h b/backends/graphics/opengl/shader.h
new file mode 100644
index 0000000000..ec1e516d14
--- /dev/null
+++ b/backends/graphics/opengl/shader.h
@@ -0,0 +1,288 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_OPENGL_SHADER_H
+#define BACKENDS_GRAPHICS_OPENGL_SHADER_H
+
+#include "backends/graphics/opengl/opengl-sys.h"
+
+#if !USE_FORCED_GLES
+
+#include "common/singleton.h"
+#include "common/hash-str.h"
+#include "common/ptr.h"
+
+namespace OpenGL {
+
+/**
+ * A generic uniform value interface for a shader program.
+ */
+class ShaderUniformValue {
+public:
+ virtual ~ShaderUniformValue() {}
+
+ /**
+ * Setup the the value to the given location.
+ *
+ * @param location Location of the uniform.
+ */
+ virtual void set(GLint location) const = 0;
+};
+
+/**
+ * Integer value for a shader uniform.
+ */
+class ShaderUniformInteger : public ShaderUniformValue {
+public:
+ ShaderUniformInteger(GLint value) : _value(value) {}
+
+ virtual void set(GLint location) const override;
+
+private:
+ const GLint _value;
+};
+
+/**
+ * Float value for a shader uniform.
+ */
+class ShaderUniformFloat : public ShaderUniformValue {
+public:
+ ShaderUniformFloat(GLfloat value) : _value(value) {}
+
+ virtual void set(GLint location) const override;
+
+private:
+ const GLfloat _value;
+};
+
+/**
+ * 4x4 Matrix value for a shader uniform.
+ */
+class ShaderUniformMatrix44 : public ShaderUniformValue {
+public:
+ ShaderUniformMatrix44(const GLfloat *mat44) {
+ memcpy(_matrix, mat44, sizeof(_matrix));
+ }
+
+ virtual void set(GLint location) const override;
+
+private:
+ GLfloat _matrix[4*4];
+};
+
+class Shader {
+public:
+ Shader(const Common::String &vertex, const Common::String &fragment);
+ ~Shader();
+
+ /**
+ * Destroy the shader program.
+ *
+ * This keeps the vertex and fragment shader sources around and thus
+ * allows for recreating the shader on context recreation. It also keeps
+ * the uniform state around.
+ */
+ void destroy();
+
+ /**
+ * Recreate shader program.
+ *
+ * @return true on success, false on failure.
+ */
+ bool recreate();
+
+ /**
+ * Make shader active.
+ */
+ void activate();
+
+ /**
+ * Make shader inactive.
+ */
+ void deactivate();
+
+ /**
+ * Return location for attribute with given name.
+ *
+ * @param name Name of the attribute to look up in the shader.
+ * @return The loctaion of -1 if attribute was not found.
+ */
+ GLint getAttributeLocation(const char *name) const;
+ GLint getAttributeLocation(const Common::String &name) const {
+ return getAttributeLocation(name.c_str());
+ }
+
+ /**
+ * Return location for uniform with given name.
+ *
+ * @param name Name of the uniform to look up in the shader.
+ * @return The location or -1 if uniform was not found.
+ */
+ GLint getUniformLocation(const char *name) const;
+ GLint getUniformLocation(const Common::String &name) const {
+ return getUniformLocation(name.c_str());
+ }
+
+ /**
+ * Bind value to uniform.
+ *
+ * @param name The name of the uniform to be set.
+ * @param value The value to be set.
+ * @return 'false' on error (i.e. uniform unknown or otherwise),
+ * 'true' otherwise.
+ */
+ bool setUniform(const Common::String &name, ShaderUniformValue *value);
+
+ /**
+ * Bind integer value to uniform.
+ *
+ * @param name The name of the uniform to be set.
+ * @param value The value to be set.
+ * @return 'false' on error (i.e. uniform unknown or otherwise),
+ * 'true' otherwise.
+ */
+ bool setUniform1I(const Common::String &name, GLint value) {
+ return setUniform(name, new ShaderUniformInteger(value));
+ }
+protected:
+ /**
+ * Vertex shader sources.
+ */
+ const Common::String _vertex;
+
+ /**
+ * Fragment shader sources.
+ */
+ const Common::String _fragment;
+
+ /**
+ * Whether the shader is active or not.
+ */
+ bool _isActive;
+
+ /**
+ * Shader program handle.
+ */
+ GLprogram _program;
+
+ /**
+ * A uniform descriptor.
+ *
+ * This stores the state of a shader uniform. The state is made up of the
+ * uniform location, whether the state was altered since last set, and the
+ * value of the uniform.
+ */
+ struct Uniform {
+ Uniform() : location(-1), altered(false), value() {}
+ Uniform(GLint loc, ShaderUniformValue *val)
+ : location(loc), altered(true), value(val) {}
+
+ /**
+ * Write uniform value into currently active shader.
+ */
+ void set() {
+ if (altered && value) {
+ value->set(location);
+ altered = false;
+ }
+ }
+
+ /**
+ * The location of the uniform or -1 in case it does not exist.
+ */
+ GLint location;
+
+ /**
+ * Whether the uniform state was aletered since last 'set'.
+ */
+ bool altered;
+
+ /**
+ * The value of the uniform.
+ */
+ Common::SharedPtr<ShaderUniformValue> value;
+ };
+
+ typedef Common::HashMap<Common::String, Uniform> UniformMap;
+
+ /**
+ * Map from uniform name to associated uniform description.
+ */
+ UniformMap _uniforms;
+
+ /**
+ * Compile a vertex or fragment shader.
+ *
+ * @param source Sources to the shader.
+ * @param shaderType Type of shader to compile (GL_FRAGMENT_SHADER_ARB or
+ * GL_VERTEX_SHADER_ARB)
+ * @return The shader object or 0 on failure.
+ */
+ static GLshader compileShader(const char *source, GLenum shaderType);
+};
+
+class ShaderManager : public Common::Singleton<ShaderManager> {
+public:
+ enum ShaderUsage {
+ /** Default shader implementing the GL fixed-function pipeline. */
+ kDefault = 0,
+
+ /** CLUT8 look up shader. */
+ kCLUT8LookUp,
+
+ /** Number of built-in shaders. Should not be used for query. */
+ kMaxUsages
+ };
+
+ /**
+ * Notify shader manager about context destruction.
+ */
+ void notifyDestroy();
+
+ /**
+ * Notify shader manager about context creation.
+ */
+ void notifyCreate();
+
+ /**
+ * Query a built-in shader.
+ */
+ Shader *query(ShaderUsage shader) const;
+
+private:
+ friend class Common::Singleton<SingletonBaseType>;
+ ShaderManager();
+ ~ShaderManager();
+
+ bool _initializeShaders;
+
+ Shader *_builtIn[kMaxUsages];
+};
+
+} // End of namespace OpenGL
+
+/** Shortcut for accessing the font manager. */
+#define ShaderMan (OpenGL::ShaderManager::instance())
+
+#endif // !USE_FORCED_GLES
+
+#endif
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp
index 7b0b22d630..8b94549971 100644
--- a/backends/graphics/opengl/texture.cpp
+++ b/backends/graphics/opengl/texture.cpp
@@ -21,8 +21,10 @@
*/
#include "backends/graphics/opengl/texture.h"
-#include "backends/graphics/opengl/extensions.h"
-#include "backends/graphics/opengl/debug.h"
+#include "backends/graphics/opengl/shader.h"
+#include "backends/graphics/opengl/pipelines/pipeline.h"
+#include "backends/graphics/opengl/pipelines/clut8.h"
+#include "backends/graphics/opengl/framebuffer.h"
#include "common/rect.h"
#include "common/textconsole.h"
@@ -41,94 +43,140 @@ static GLuint nextHigher2(GLuint v) {
return ++v;
}
-GLint Texture::_maxTextureSize = 0;
-void Texture::queryTextureInformation() {
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize);
- debug(5, "OpenGL maximum texture size: %d", _maxTextureSize);
+GLTexture::GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType)
+ : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType),
+ _width(0), _height(0), _logicalWidth(0), _logicalHeight(0),
+ _texCoords(), _glFilter(GL_NEAREST),
+ _glTexture(0) {
+ create();
}
-Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
- : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), _format(format), _glFilter(GL_NEAREST),
- _glTexture(0), _textureData(), _userPixelData(), _allDirty(false) {
- recreateInternalTexture();
+GLTexture::~GLTexture() {
+ GL_CALL_SAFE(glDeleteTextures, (1, &_glTexture));
}
-Texture::~Texture() {
- releaseInternalTexture();
- _textureData.free();
+void GLTexture::enableLinearFiltering(bool enable) {
+ if (enable) {
+ _glFilter = GL_LINEAR;
+ } else {
+ _glFilter = GL_NEAREST;
+ }
+
+ bind();
+
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
}
-void Texture::releaseInternalTexture() {
- GLCALL(glDeleteTextures(1, &_glTexture));
+void GLTexture::destroy() {
+ GL_CALL(glDeleteTextures(1, &_glTexture));
_glTexture = 0;
}
-void Texture::recreateInternalTexture() {
+void GLTexture::create() {
// Release old texture name in case it exists.
- releaseInternalTexture();
+ destroy();
// Get a new texture name.
- GLCALL(glGenTextures(1, &_glTexture));
+ GL_CALL(glGenTextures(1, &_glTexture));
// Set up all texture parameters.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
- GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
-
- // In case there is an actual texture setup we reinitialize it.
- if (_textureData.getPixels()) {
+ bind();
+ GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+ // If a size is specified, allocate memory for it.
+ if (_width != 0 && _height != 0) {
// Allocate storage for OpenGL texture.
- GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
- _textureData.h, 0, _glFormat, _glType, NULL));
-
- // Mark dirts such that it will be completely refreshed the next time.
- flagDirty();
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, _height,
+ 0, _glFormat, _glType, NULL));
}
}
-void Texture::enableLinearFiltering(bool enable) {
- if (enable) {
- _glFilter = GL_LINEAR;
+void GLTexture::bind() const {
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+}
+
+void GLTexture::setSize(uint width, uint height) {
+ const uint oldWidth = _width;
+ const uint oldHeight = _height;
+
+ if (!g_context.NPOTSupported) {
+ _width = nextHigher2(width);
+ _height = nextHigher2(height);
} else {
- _glFilter = GL_NEAREST;
+ _width = width;
+ _height = height;
}
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+ _logicalWidth = width;
+ _logicalHeight = height;
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter));
- GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter));
-}
+ // If a size is specified, allocate memory for it.
+ if (width != 0 && height != 0) {
+ const GLfloat texWidth = (GLfloat)width / _width;
+ const GLfloat texHeight = (GLfloat)height / _height;
-void Texture::allocate(uint width, uint height) {
- uint texWidth = width, texHeight = height;
- if (!g_extNPOTSupported) {
- texWidth = nextHigher2(texWidth);
- texHeight = nextHigher2(texHeight);
- }
+ _texCoords[0] = 0;
+ _texCoords[1] = 0;
- // In case the needed texture dimension changed we will reinitialize the
- // texture.
- if (texWidth != _textureData.w || texHeight != _textureData.h) {
- // Create a buffer for the texture data.
- _textureData.create(texWidth, texHeight, _format);
+ _texCoords[2] = texWidth;
+ _texCoords[3] = 0;
- // Set the texture.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
+ _texCoords[4] = 0;
+ _texCoords[5] = texHeight;
- // Allocate storage for OpenGL texture.
- GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w,
- _textureData.h, 0, _glFormat, _glType, NULL));
+ _texCoords[6] = texWidth;
+ _texCoords[7] = texHeight;
+
+ // Allocate storage for OpenGL texture if necessary.
+ if (oldWidth != _width || oldHeight != _height) {
+ bind();
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width,
+ _height, 0, _glFormat, _glType, NULL));
+ }
}
+}
- // Create a sub-buffer for raw access.
- _userPixelData = _textureData.getSubArea(Common::Rect(width, height));
+void GLTexture::updateArea(const Common::Rect &area, const Graphics::Surface &src) {
+ // Set the texture on the active texture unit.
+ bind();
+
+ // Update the actual texture.
+ // Although we have the area of the texture buffer we want to update we
+ // cannot take advantage of the left/right boundries here because it is
+ // not possible to specify a pitch to glTexSubImage2D. To be precise, with
+ // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
+ // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
+ // with the following options:
+ //
+ // 1) (As we do right now) Simply always update the whole texture lines of
+ // rect changed. This is simplest to implement. In case performance is
+ // really an issue we can think of switching to another method.
+ //
+ // 2) Copy the dirty rect to a temporary buffer and upload that by using
+ // glTexSubImage2D. This is what the Android backend does. It is more
+ // complicated though.
+ //
+ // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
+ // graphics manager did but it is much slower! Thus, we do not use it.
+ GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, area.top, src.w, area.height(),
+ _glFormat, _glType, src.getBasePtr(0, area.top)));
+}
+
+//
+// Surface
+//
+
+Surface::Surface()
+ : _allDirty(false), _dirtyArea() {
}
-void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
+void Surface::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
Graphics::Surface *dstSurf = getSurface();
assert(x + w <= dstSurf->w);
assert(y + h <= dstSurf->h);
@@ -159,50 +207,74 @@ void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcP
}
}
-void Texture::fill(uint32 color) {
+void Surface::fill(uint32 color) {
Graphics::Surface *dst = getSurface();
dst->fillRect(Common::Rect(dst->w, dst->h), color);
flagDirty();
}
-void Texture::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
- // Only do any processing when the Texture is initialized.
- if (!_textureData.getPixels()) {
- return;
+Common::Rect Surface::getDirtyArea() const {
+ if (_allDirty) {
+ return Common::Rect(getWidth(), getHeight());
+ } else {
+ return _dirtyArea;
+ }
+}
+
+//
+// Surface implementations
+//
+
+Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
+ : Surface(), _format(format), _glTexture(glIntFormat, glFormat, glType),
+ _textureData(), _userPixelData() {
+}
+
+Texture::~Texture() {
+ _textureData.free();
+}
+
+void Texture::destroy() {
+ _glTexture.destroy();
+}
+
+void Texture::recreate() {
+ _glTexture.create();
+
+ // In case image date exists assure it will be completely refreshed next
+ // time.
+ if (_textureData.getPixels()) {
+ flagDirty();
+ }
+}
+
+void Texture::enableLinearFiltering(bool enable) {
+ _glTexture.enableLinearFiltering(enable);
+}
+
+void Texture::allocate(uint width, uint height) {
+ // Assure the texture can contain our user data.
+ _glTexture.setSize(width, height);
+
+ // In case the needed texture dimension changed we will reinitialize the
+ // texture data buffer.
+ if (_glTexture.getWidth() != _textureData.w || _glTexture.getHeight() != _textureData.h) {
+ // Create a buffer for the texture data.
+ _textureData.create(_glTexture.getWidth(), _glTexture.getHeight(), _format);
}
- // First update any potentional changes.
- updateTexture();
-
- // Set the texture.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
-
- // Calculate the texture rect that will be drawn.
- const GLfloat texWidth = (GLfloat)_userPixelData.w / _textureData.w;
- const GLfloat texHeight = (GLfloat)_userPixelData.h / _textureData.h;
- const GLfloat texcoords[4*2] = {
- 0, 0,
- texWidth, 0,
- 0, texHeight,
- texWidth, texHeight
- };
- GLCALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords));
-
- // Calculate the screen rect where the texture will be drawn.
- const GLfloat vertices[4*2] = {
- x, y,
- x + w, y,
- x, y + h,
- x + w, y + h
- };
- GLCALL(glVertexPointer(2, GL_FLOAT, 0, vertices));
-
- // Draw the texture to the screen buffer.
- GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
-}
-
-void Texture::updateTexture() {
+ // Create a sub-buffer for raw access.
+ _userPixelData = _textureData.getSubArea(Common::Rect(width, height));
+
+ // The whole texture is dirty after we changed the size. This fixes
+ // multiple texture size changes without any actual update in between.
+ // Without this we might try to write a too big texture into the GL
+ // texture.
+ flagDirty();
+}
+
+void Texture::updateGLTexture() {
if (!isDirty()) {
return;
}
@@ -211,7 +283,7 @@ void Texture::updateTexture() {
// In case we use linear filtering we might need to duplicate the last
// pixel row/column to avoid glitches with filtering.
- if (_glFilter == GL_LINEAR) {
+ if (_glTexture.isLinearFilteringEnabled()) {
if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) {
uint height = dirtyArea.height();
@@ -238,42 +310,12 @@ void Texture::updateTexture() {
}
}
- // Set the texture.
- GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture));
-
- // Update the actual texture.
- // Although we keep track of the dirty part of the texture buffer we
- // cannot take advantage of the left/right boundries here because it is
- // not possible to specify a pitch to glTexSubImage2D. To be precise, with
- // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However,
- // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left
- // with the following options:
- //
- // 1) (As we do right now) Simply always update the whole texture lines of
- // rect changed. This is simplest to implement. In case performance is
- // really an issue we can think of switching to another method.
- //
- // 2) Copy the dirty rect to a temporary buffer and upload that by using
- // glTexSubImage2D. This is what the Android backend does. It is more
- // complicated though.
- //
- // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL
- // graphics manager did but it is much slower! Thus, we do not use it.
- GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(),
- _glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top)));
+ _glTexture.updateArea(dirtyArea, _textureData);
// We should have handled everything, thus not dirty anymore.
clearDirty();
}
-Common::Rect Texture::getDirtyArea() const {
- if (_allDirty) {
- return Common::Rect(_userPixelData.w, _userPixelData.h);
- } else {
- return _dirtyArea;
- }
-}
-
TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
: Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) {
memset(_palette, 0, sizeof(byte) * format.bytesPerPixel);
@@ -301,6 +343,25 @@ Graphics::PixelFormat TextureCLUT8::getFormat() const {
return Graphics::PixelFormat::createFormatCLUT8();
}
+void TextureCLUT8::setColorKey(uint colorKey) {
+ // We remove all alpha bits from the palette entry of the color key.
+ // This makes sure its properly handled as color key.
+ const uint32 aMask = (0xFF >> _format.aLoss) << _format.aShift;
+
+ if (_format.bytesPerPixel == 2) {
+ uint16 *palette = (uint16 *)_palette + colorKey;
+ *palette &= ~aMask;
+ } else if (_format.bytesPerPixel == 4) {
+ uint32 *palette = (uint32 *)_palette + colorKey;
+ *palette &= ~aMask;
+ } else {
+ warning("TextureCLUT8::setColorKey: Unsupported pixel depth %d", _format.bytesPerPixel);
+ }
+
+ // A palette changes means we need to refresh the whole surface.
+ flagDirty();
+}
+
namespace {
template<typename ColorType>
inline void convertPalette(ColorType *dst, const byte *src, uint colors, const Graphics::PixelFormat &format) {
@@ -312,14 +373,12 @@ inline void convertPalette(ColorType *dst, const byte *src, uint colors, const G
} // End of anonymous namespace
void TextureCLUT8::setPalette(uint start, uint colors, const byte *palData) {
- const Graphics::PixelFormat &hardwareFormat = getHardwareFormat();
-
- if (hardwareFormat.bytesPerPixel == 2) {
- convertPalette<uint16>((uint16 *)_palette + start, palData, colors, hardwareFormat);
- } else if (hardwareFormat.bytesPerPixel == 4) {
- convertPalette<uint32>((uint32 *)_palette + start, palData, colors, hardwareFormat);
+ if (_format.bytesPerPixel == 2) {
+ convertPalette<uint16>((uint16 *)_palette + start, palData, colors, _format);
+ } else if (_format.bytesPerPixel == 4) {
+ convertPalette<uint32>((uint32 *)_palette + start, palData, colors, _format);
} else {
- warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", hardwareFormat.bytesPerPixel);
+ warning("TextureCLUT8::setPalette: Unsupported pixel depth: %d", _format.bytesPerPixel);
}
// A palette changes means we need to refresh the whole surface.
@@ -343,7 +402,7 @@ inline void doPaletteLookUp(PixelType *dst, const byte *src, uint width, uint he
}
} // End of anonymous namespace
-void TextureCLUT8::updateTexture() {
+void TextureCLUT8::updateGLTexture() {
if (!isDirty()) {
return;
}
@@ -368,7 +427,222 @@ void TextureCLUT8::updateTexture() {
}
// Do generic handling of updating the texture.
- Texture::updateTexture();
+ Texture::updateGLTexture();
+}
+
+#if !USE_FORCED_GL
+TextureRGB555::TextureRGB555()
+ : Texture(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)),
+ _rgb555Data() {
+}
+
+TextureRGB555::~TextureRGB555() {
+ _rgb555Data.free();
+}
+
+void TextureRGB555::allocate(uint width, uint height) {
+ Texture::allocate(width, height);
+
+ // We only need to reinitialize our RGB555 surface when the output size
+ // changed.
+ if (width == _rgb555Data.w && height == _rgb555Data.h) {
+ return;
+ }
+
+ _rgb555Data.create(width, height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
+}
+
+Graphics::PixelFormat TextureRGB555::getFormat() const {
+ return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+}
+
+void TextureRGB555::updateTexture() {
+ if (!isDirty()) {
+ return;
+ }
+
+ // Convert color space.
+ Graphics::Surface *outSurf = Texture::getSurface();
+
+ const Common::Rect dirtyArea = getDirtyArea();
+
+ uint16 *dst = (uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
+ const uint dstAdd = outSurf->pitch - 2 * dirtyArea.width();
+
+ const uint16 *src = (const uint16 *)_rgb555Data.getBasePtr(dirtyArea.left, dirtyArea.top);
+ const uint srcAdd = _rgb555Data.pitch - 2 * dirtyArea.width();
+
+ for (int height = dirtyArea.height(); height > 0; --height) {
+ for (int width = dirtyArea.width(); width > 0; --width) {
+ const uint16 color = *src++;
+
+ *dst++ = ((color & 0x7C00) << 1) // R
+ | (((color & 0x03E0) << 1) | ((color & 0x0200) >> 4)) // G
+ | (color & 0x001F); // B
+ }
+
+ src = (const uint16 *)((const byte *)src + srcAdd);
+ dst = (uint16 *)((byte *)dst + dstAdd);
+ }
+
+ // Do generic handling of updating the texture.
+ Texture::updateGLTexture();
+}
+#endif // !USE_FORCED_GL
+
+#if !USE_FORCED_GLES
+
+// _clut8Texture needs 8 bits internal precision, otherwise graphics glitches
+// can occur. GL_ALPHA does not have any internal precision requirements.
+// However, in practice (according to fuzzie) it's 8bit. If we run into
+// problems, we need to switch to GL_R8 and GL_RED, but that is only supported
+// for ARB_texture_rg and GLES3+ (EXT_rexture_rg does not support GL_R8).
+TextureCLUT8GPU::TextureCLUT8GPU()
+ : _clut8Texture(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE),
+ _paletteTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE),
+ _target(new TextureTarget()), _clut8Pipeline(new CLUT8LookUpPipeline()),
+ _clut8Vertices(), _clut8Data(), _userPixelData(), _palette(),
+ _paletteDirty(false) {
+ // Allocate space for 256 colors.
+ _paletteTexture.setSize(256, 1);
+
+ // Setup pipeline.
+ _clut8Pipeline->setFramebuffer(_target);
+ _clut8Pipeline->setPaletteTexture(&_paletteTexture);
+}
+
+TextureCLUT8GPU::~TextureCLUT8GPU() {
+ delete _clut8Pipeline;
+ delete _target;
+ _clut8Data.free();
+}
+
+void TextureCLUT8GPU::destroy() {
+ _clut8Texture.destroy();
+ _paletteTexture.destroy();
+ _target->destroy();
+}
+
+void TextureCLUT8GPU::recreate() {
+ _clut8Texture.create();
+ _paletteTexture.create();
+ _target->create();
+
+ // In case image date exists assure it will be completely refreshed next
+ // time.
+ if (_clut8Data.getPixels()) {
+ flagDirty();
+ _paletteDirty = true;
+ }
+}
+
+void TextureCLUT8GPU::enableLinearFiltering(bool enable) {
+ _target->getTexture()->enableLinearFiltering(enable);
+}
+
+void TextureCLUT8GPU::allocate(uint width, uint height) {
+ // Assure the texture can contain our user data.
+ _clut8Texture.setSize(width, height);
+ _target->setSize(width, height);
+
+ // In case the needed texture dimension changed we will reinitialize the
+ // texture data buffer.
+ if (_clut8Texture.getWidth() != _clut8Data.w || _clut8Texture.getHeight() != _clut8Data.h) {
+ // Create a buffer for the texture data.
+ _clut8Data.create(_clut8Texture.getWidth(), _clut8Texture.getHeight(), Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ // Create a sub-buffer for raw access.
+ _userPixelData = _clut8Data.getSubArea(Common::Rect(width, height));
+
+ // Setup structures for internal rendering to _glTexture.
+ _clut8Vertices[0] = 0;
+ _clut8Vertices[1] = 0;
+
+ _clut8Vertices[2] = width;
+ _clut8Vertices[3] = 0;
+
+ _clut8Vertices[4] = 0;
+ _clut8Vertices[5] = height;
+
+ _clut8Vertices[6] = width;
+ _clut8Vertices[7] = height;
+
+ // The whole texture is dirty after we changed the size. This fixes
+ // multiple texture size changes without any actual update in between.
+ // Without this we might try to write a too big texture into the GL
+ // texture.
+ flagDirty();
+}
+
+Graphics::PixelFormat TextureCLUT8GPU::getFormat() const {
+ return Graphics::PixelFormat::createFormatCLUT8();
+}
+
+void TextureCLUT8GPU::setColorKey(uint colorKey) {
+ _palette[colorKey * 4 + 3] = 0x00;
+
+ _paletteDirty = true;
+}
+
+void TextureCLUT8GPU::setPalette(uint start, uint colors, const byte *palData) {
+ byte *dst = _palette + start * 4;
+
+ while (colors-- > 0) {
+ memcpy(dst, palData, 3);
+ dst[3] = 0xFF;
+
+ dst += 4;
+ palData += 3;
+ }
+
+ _paletteDirty = true;
+}
+
+const GLTexture &TextureCLUT8GPU::getGLTexture() const {
+ return *_target->getTexture();
+}
+
+void TextureCLUT8GPU::updateGLTexture() {
+ const bool needLookUp = Surface::isDirty() || _paletteDirty;
+
+ // Update CLUT8 texture if necessary.
+ if (Surface::isDirty()) {
+ _clut8Texture.updateArea(getDirtyArea(), _clut8Data);
+ clearDirty();
+ }
+
+ // Update palette if necessary.
+ if (_paletteDirty) {
+ Graphics::Surface palSurface;
+ palSurface.init(256, 1, 256, _palette,
+#ifdef SCUMM_LITTLE_ENDIAN
+ Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24) // ABGR8888
+#else
+ Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0) // RGBA8888
+#endif
+ );
+
+ _paletteTexture.updateArea(Common::Rect(256, 1), palSurface);
+ _paletteDirty = false;
+ }
+
+ // In case any data changed, do color look up and store result in _target.
+ if (needLookUp) {
+ lookUpColors();
+ }
+}
+
+void TextureCLUT8GPU::lookUpColors() {
+ // Setup pipeline to do color look up.
+ Pipeline *oldPipeline = g_context.setPipeline(_clut8Pipeline);
+
+ // Do color look up.
+ g_context.getActivePipeline()->drawTexture(_clut8Texture, _clut8Vertices);
+
+ // Restore old state.
+ g_context.setPipeline(oldPipeline);
}
+#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h
index ad70833544..3be09cb9f9 100644
--- a/backends/graphics/opengl/texture.h
+++ b/backends/graphics/opengl/texture.h
@@ -32,115 +32,267 @@
namespace OpenGL {
+class Shader;
+
/**
- * An OpenGL texture wrapper. It automatically takes care of all OpenGL
- * texture handling issues and also provides access to the texture data.
+ * A simple GL texture object abstraction.
+ *
+ * This is used for low-level GL texture handling.
*/
-class Texture {
+class GLTexture {
public:
/**
- * Create a new texture with the specific internal format.
+ * Constrcut a new GL texture object.
*
* @param glIntFormat The internal format to use.
* @param glFormat The input format.
* @param glType The input type.
- * @param format The format used for the texture input.
*/
- Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
- virtual ~Texture();
+ GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType);
+ ~GLTexture();
+
+ /**
+ * Enable or disable linear texture filtering.
+ *
+ * @param enable true to enable and false to disable.
+ */
+ void enableLinearFiltering(bool enable);
+
+ /**
+ * Test whether linear filtering is enabled.
+ */
+ bool isLinearFilteringEnabled() const { return (_glFilter == GL_LINEAR); }
/**
* Destroy the OpenGL texture name.
*/
- void releaseInternalTexture();
+ void destroy();
+
+ /**
+ * Create the OpenGL texture name.
+ */
+ void create();
+
+ /**
+ * Bind the texture to the active texture unit.
+ */
+ void bind() const;
+
+ /**
+ * Sets the size of the texture in pixels.
+ *
+ * The internal OpenGL texture might have a different size. To query the
+ * actual size use getWidth()/getHeight().
+ *
+ * @param width The desired logical width.
+ * @param height The desired logical height.
+ */
+ void setSize(uint width, uint height);
+
+ /**
+ * Copy image data to the texture.
+ *
+ * @param area The area to update.
+ * @param src Surface for the whole texture containing the pixel data
+ * to upload. Only the area described by area will be
+ * uploaded.
+ */
+ void updateArea(const Common::Rect &area, const Graphics::Surface &src);
/**
- * Create the OpenGL texture name and flag the whole texture as dirty.
+ * Query the GL texture's width.
*/
- void recreateInternalTexture();
+ uint getWidth() const { return _width; }
+
+ /**
+ * Query the GL texture's height.
+ */
+ uint getHeight() const { return _height; }
+
+ /**
+ * Query the logical texture's width.
+ */
+ uint getLogicalWidth() const { return _logicalWidth; }
+
+ /**
+ * Query the logical texture's height.
+ */
+ uint getLogicalHeight() const { return _logicalHeight; }
+
+ /**
+ * Obtain texture coordinates for rectangular drawing.
+ */
+ const GLfloat *getTexCoords() const { return _texCoords; }
+
+ /**
+ * Obtain texture name.
+ *
+ * Beware that the texture name changes whenever create is used.
+ * destroy will invalidate the texture name.
+ */
+ GLuint getGLTexture() const { return _glTexture; }
+private:
+ const GLenum _glIntFormat;
+ const GLenum _glFormat;
+ const GLenum _glType;
+
+ uint _width, _height;
+ uint _logicalWidth, _logicalHeight;
+ GLfloat _texCoords[4*2];
+
+ GLint _glFilter;
+
+ GLuint _glTexture;
+};
+
+/**
+ * Interface for OpenGL implementations of a 2D surface.
+ */
+class Surface {
+public:
+ Surface();
+ virtual ~Surface() {}
+
+ /**
+ * Destroy OpenGL description of surface.
+ */
+ virtual void destroy() = 0;
+
+ /**
+ * Recreate OpenGL description of surface.
+ */
+ virtual void recreate() = 0;
/**
* Enable or disable linear texture filtering.
*
* @param enable true to enable and false to disable.
*/
- void enableLinearFiltering(bool enable);
+ virtual void enableLinearFiltering(bool enable) = 0;
/**
- * Allocate texture space for the desired dimensions. This wraps any
- * handling of requirements for POT textures.
+ * Allocate storage for surface.
*
* @param width The desired logical width.
* @param height The desired logical height.
*/
- virtual void allocate(uint width, uint height);
+ virtual void allocate(uint width, uint height) = 0;
+ /**
+ * Copy image data to the surface.
+ *
+ * The format of the input data needs to match the format returned by
+ * getFormat.
+ *
+ * @param x X coordinate of upper left corner to copy data to.
+ * @param y Y coordinate of upper left corner to copy data to.
+ * @param w Width of the image data to copy.
+ * @param h Height of the image data to copy.
+ * @param src Pointer to image data.
+ * @param srcPitch The number of bytes in a row of the image data.
+ */
void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch);
+ /**
+ * Fill the surface with a fixed color.
+ *
+ * @param color Color value in format returned by getFormat.
+ */
void fill(uint32 color);
- void draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h);
-
void flagDirty() { _allDirty = true; }
- bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
-
- uint getWidth() const { return _userPixelData.w; }
- uint getHeight() const { return _userPixelData.h; }
+ virtual bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
- /**
- * @return The hardware format of the texture data.
- */
- const Graphics::PixelFormat &getHardwareFormat() const { return _format; }
+ virtual uint getWidth() const = 0;
+ virtual uint getHeight() const = 0;
/**
* @return The logical format of the texture data.
*/
- virtual Graphics::PixelFormat getFormat() const { return _format; }
+ virtual Graphics::PixelFormat getFormat() const = 0;
- virtual Graphics::Surface *getSurface() { return &_userPixelData; }
- virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+ virtual Graphics::Surface *getSurface() = 0;
+ virtual const Graphics::Surface *getSurface() const = 0;
/**
- * @return Whether the texture data is using a palette.
+ * @return Whether the surface is having a palette.
*/
virtual bool hasPalette() const { return false; }
+ /**
+ * Set color key for paletted textures.
+ *
+ * This needs to be called after any palette update affecting the color
+ * key. Calling this multiple times will result in multiple color indices
+ * to be treated as color keys.
+ */
+ virtual void setColorKey(uint colorKey) {}
virtual void setPalette(uint start, uint colors, const byte *palData) {}
- virtual void *getPalette() { return 0; }
- virtual const void *getPalette() const { return 0; }
-
/**
- * Query texture related OpenGL information from the context. This only
- * queries the maximum texture size for now.
+ * Update underlying OpenGL texture to reflect current state.
*/
- static void queryTextureInformation();
+ virtual void updateGLTexture() = 0;
/**
- * @return Return the maximum texture dimensions supported.
+ * Obtain underlying OpenGL texture.
*/
- static GLint getMaximumTextureSize() { return _maxTextureSize; }
+ virtual const GLTexture &getGLTexture() const = 0;
protected:
- virtual void updateTexture();
+ void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
Common::Rect getDirtyArea() const;
private:
- const GLenum _glIntFormat;
- const GLenum _glFormat;
- const GLenum _glType;
+ bool _allDirty;
+ Common::Rect _dirtyArea;
+};
+
+/**
+ * An OpenGL texture wrapper. It automatically takes care of all OpenGL
+ * texture handling issues and also provides access to the texture data.
+ */
+class Texture : public Surface {
+public:
+ /**
+ * Create a new texture with the specific internal format.
+ *
+ * @param glIntFormat The internal format to use.
+ * @param glFormat The input format.
+ * @param glType The input type.
+ * @param format The format used for the texture input.
+ */
+ Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
+ virtual ~Texture();
+
+ virtual void destroy();
+
+ virtual void recreate();
+
+ virtual void enableLinearFiltering(bool enable);
+
+ virtual void allocate(uint width, uint height);
+
+ virtual uint getWidth() const { return _userPixelData.w; }
+ virtual uint getHeight() const { return _userPixelData.h; }
+
+ /**
+ * @return The logical format of the texture data.
+ */
+ virtual Graphics::PixelFormat getFormat() const { return _format; }
+
+ virtual Graphics::Surface *getSurface() { return &_userPixelData; }
+ virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+
+ virtual void updateGLTexture();
+ virtual const GLTexture &getGLTexture() const { return _glTexture; }
+protected:
const Graphics::PixelFormat _format;
- GLint _glFilter;
- GLuint _glTexture;
+private:
+ GLTexture _glTexture;
Graphics::Surface _textureData;
Graphics::Surface _userPixelData;
-
- bool _allDirty;
- Common::Rect _dirtyArea;
- void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
-
- static GLint _maxTextureSize;
};
class TextureCLUT8 : public Texture {
@@ -154,21 +306,95 @@ public:
virtual bool hasPalette() const { return true; }
+ virtual void setColorKey(uint colorKey);
virtual void setPalette(uint start, uint colors, const byte *palData);
- virtual void *getPalette() { return _palette; }
- virtual const void *getPalette() const { return _palette; }
-
virtual Graphics::Surface *getSurface() { return &_clut8Data; }
virtual const Graphics::Surface *getSurface() const { return &_clut8Data; }
-protected:
+ virtual void updateGLTexture();
+private:
+ Graphics::Surface _clut8Data;
+ byte *_palette;
+};
+
+#if !USE_FORCED_GL
+class TextureRGB555 : public Texture {
+public:
+ TextureRGB555();
+ virtual ~TextureRGB555();
+
+ virtual void allocate(uint width, uint height);
+
+ virtual Graphics::PixelFormat getFormat() const;
+
+ virtual Graphics::Surface *getSurface() { return &_rgb555Data; }
+ virtual const Graphics::Surface *getSurface() const { return &_rgb555Data; }
+
virtual void updateTexture();
+private:
+ Graphics::Surface _rgb555Data;
+};
+#endif // !USE_FORCED_GL
+
+#if !USE_FORCED_GLES
+class TextureTarget;
+class CLUT8LookUpPipeline;
+
+class TextureCLUT8GPU : public Surface {
+public:
+ TextureCLUT8GPU();
+ virtual ~TextureCLUT8GPU();
+
+ virtual void destroy();
+
+ virtual void recreate();
+ virtual void enableLinearFiltering(bool enable);
+
+ virtual void allocate(uint width, uint height);
+
+ virtual bool isDirty() const { return _paletteDirty || Surface::isDirty(); }
+
+ virtual uint getWidth() const { return _userPixelData.w; }
+ virtual uint getHeight() const { return _userPixelData.h; }
+
+ virtual Graphics::PixelFormat getFormat() const;
+
+ virtual bool hasPalette() const { return true; }
+
+ virtual void setColorKey(uint colorKey);
+ virtual void setPalette(uint start, uint colors, const byte *palData);
+
+ virtual Graphics::Surface *getSurface() { return &_userPixelData; }
+ virtual const Graphics::Surface *getSurface() const { return &_userPixelData; }
+
+ virtual void updateGLTexture();
+ virtual const GLTexture &getGLTexture() const;
+
+ static bool isSupportedByContext() {
+ return g_context.shadersSupported
+ && g_context.multitextureSupported
+ && g_context.framebufferObjectSupported;
+ }
private:
+ void lookUpColors();
+
+ GLTexture _clut8Texture;
+ GLTexture _paletteTexture;
+
+ TextureTarget *_target;
+ CLUT8LookUpPipeline *_clut8Pipeline;
+
+ GLfloat _clut8Vertices[4*2];
+
Graphics::Surface _clut8Data;
- byte *_palette;
+ Graphics::Surface _userPixelData;
+
+ byte _palette[4 * 256];
+ bool _paletteDirty;
};
+#endif // !USE_FORCED_GLES
} // End of namespace OpenGL
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index 0d140ee4d7..7ea1860d93 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -45,6 +45,100 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ // Setup proper SDL OpenGL context creation.
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ OpenGL::ContextType glContextType;
+
+ // Context version 1.4 is choosen arbitrarily based on what most shader
+ // extensions were written against.
+#define DEFAULT_GL_MAJOR 1
+#define DEFAULT_GL_MINOR 4
+
+#define DEFAULT_GLES_MAJOR 1
+#define DEFAULT_GLES_MINOR 1
+
+#define DEFAULT_GLES2_MAJOR 2
+#define DEFAULT_GLES2_MINOR 0
+
+#if USE_FORCED_GL
+ glContextType = OpenGL::kContextGL;
+ _glContextProfileMask = 0;
+ _glContextMajor = DEFAULT_GL_MAJOR;
+ _glContextMinor = DEFAULT_GL_MINOR;
+#elif USE_FORCED_GLES
+ glContextType = OpenGL::kContextGLES;
+ _glContextProfileMask = SDL_GL_CONTEXT_PROFILE_ES;
+ _glContextMajor = DEFAULT_GLES_MAJOR;
+ _glContextMinor = DEFAULT_GLES_MINOR;
+#elif USE_FORCED_GLES2
+ glContextType = OpenGL::kContextGLES2;
+ _glContextProfileMask = SDL_GL_CONTEXT_PROFILE_ES;
+ _glContextMajor = DEFAULT_GLES2_MAJOR;
+ _glContextMinor = DEFAULT_GLES2_MINOR;
+#else
+ bool noDefaults = false;
+
+ // Obtain the default GL(ES) context SDL2 tries to setup.
+ //
+ // Please note this might not actually be SDL2's defaults when multiple
+ // instances of this object have been created. But that is no issue
+ // because then we already set up what we want to use.
+ //
+ // In case no defaults are given we prefer OpenGL over OpenGL ES.
+ if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &_glContextProfileMask) != 0) {
+ _glContextProfileMask = 0;
+ noDefaults = true;
+ }
+
+ if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &_glContextMajor) != 0) {
+ noDefaults = true;
+ }
+
+ if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &_glContextMinor) != 0) {
+ noDefaults = true;
+ }
+
+ if (noDefaults) {
+ if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) {
+ _glContextMajor = DEFAULT_GLES_MAJOR;
+ _glContextMinor = DEFAULT_GLES_MINOR;
+ } else {
+ _glContextProfileMask = 0;
+ _glContextMajor = DEFAULT_GL_MAJOR;
+ _glContextMinor = DEFAULT_GL_MINOR;
+ }
+ }
+
+ if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_ES) {
+ if (_glContextMajor >= 2) {
+ glContextType = OpenGL::kContextGLES2;
+ } else {
+ glContextType = OpenGL::kContextGLES;
+ }
+ } else if (_glContextProfileMask == SDL_GL_CONTEXT_PROFILE_CORE) {
+ glContextType = OpenGL::kContextGL;
+
+ // Core profile does not allow legacy functionality, which we use.
+ // Thus we request a standard OpenGL context.
+ _glContextProfileMask = 0;
+ _glContextMajor = DEFAULT_GL_MAJOR;
+ _glContextMinor = DEFAULT_GL_MINOR;
+ } else {
+ glContextType = OpenGL::kContextGL;
+ }
+#undef DEFAULT_GL_MAJOR
+#undef DEFAULT_GL_MINOR
+#undef DEFAULT_GLES_MAJOR
+#undef DEFAULT_GLES_MINOR
+#undef DEFAULT_GLES2_MAJOR
+#undef DEFAULT_GLES2_MINOR
+#endif
+
+ setContextType(glContextType);
+#else
+ setContextType(OpenGL::kContextGL);
+#endif
+
// Retrieve a list of working fullscreen modes
#if SDL_VERSION_ATLEAST(2, 0, 0)
const int numModes = SDL_GetNumDisplayModes(0);
@@ -100,6 +194,10 @@ OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(uint desktopWidth, uint deskt
}
OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() {
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ notifyContextDestroy();
+ SDL_GL_DeleteContext(_glContext);
+#endif
}
void OpenGLSdlGraphicsManager::activateManager() {
@@ -210,20 +308,26 @@ Common::List<Graphics::PixelFormat> OpenGLSdlGraphicsManager::getSupportedFormat
// RGBA4444
formats.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0));
-#ifndef USE_GLES
+#if !USE_FORCED_GLES && !USE_FORCED_GLES2
+#if !USE_FORCED_GL
+ if (!isGLESContext()) {
+#endif
#ifdef SCUMM_LITTLE_ENDIAN
- // RGBA8888
- formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+ // RGBA8888
+ formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
#else
- // ABGR8888
- formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+ // ABGR8888
+ formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+#endif
+#if !USE_FORCED_GL
+ }
+#endif
#endif
- // ARGB8888, this should not be here, but Sword25 requires it. :-/
- formats.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
// RGB555, this is used by SCUMM HE 16 bit games.
+ // This is not natively supported by OpenGL ES implementations, we convert
+ // the pixel format internally.
formats.push_back(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
-#endif
formats.push_back(Graphics::PixelFormat::createFormatCLUT8());
@@ -305,6 +409,10 @@ void OpenGLSdlGraphicsManager::refreshScreen() {
#endif
}
+void *OpenGLSdlGraphicsManager::getProcAddress(const char *name) const {
+ return SDL_GL_GetProcAddress(name);
+}
+
bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
// In case we request a fullscreen mode we will use the mode the user
// has chosen last time or the biggest mode available.
@@ -378,6 +486,11 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
flags |= SDL_WINDOW_RESIZABLE;
}
+ // Request a OpenGL (ES) context we can use.
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, _glContextProfileMask);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, _glContextMajor);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, _glContextMinor);
+
if (!_window->createWindow(width, height, flags)) {
// We treat fullscreen requests as a "hint" for now. This means in
// case it is not available we simply ignore it.
@@ -390,13 +503,6 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
}
}
-#ifdef USE_GLES
- // SDL2 will create a GLES2 context by default, so this is needed for GLES1-profile
- // functions to work.
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
-#endif
_glContext = SDL_GL_CreateContext(_window->getSDLWindow());
if (!_glContext) {
return false;
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.h b/backends/graphics/openglsdl/openglsdl-graphics.h
index 1552593575..51edcb4363 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.h
+++ b/backends/graphics/openglsdl/openglsdl-graphics.h
@@ -67,10 +67,13 @@ protected:
virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, const Graphics::PixelFormat &format);
virtual void refreshScreen();
+
+ virtual void *getProcAddress(const char *name) const;
private:
bool setupMode(uint width, uint height);
#if SDL_VERSION_ATLEAST(2, 0, 0)
+ int _glContextProfileMask, _glContextMajor, _glContextMinor;
SDL_GLContext _glContext;
#else
uint32 _lastVideoModeLoad;
diff --git a/backends/module.mk b/backends/module.mk
index 3f7620e2a8..2e100d215d 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -53,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.
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/configure b/configure
index 8d778db278..1a873b213a 100755
--- a/configure
+++ b/configure
@@ -133,8 +133,7 @@ _png=auto
_theoradec=auto
_faad=auto
_fluidsynth=auto
-_opengl=auto
-_opengles=auto
+_opengl_mode=auto
_readline=auto
_freetype2=auto
_taskbar=auto
@@ -940,6 +939,15 @@ Optional Features:
--enable-verbose-build enable regular echoing of commands during build
process
--disable-bink don't build with Bink video support
+ --opengl-mode=MODE OpenGL (ES) mode to use for OpenGL output [auto]
+ available modes: auto for autodetection
+ none for disabling any OpenGL usage
+ any for runtime detection
+ gl for forcing OpenGL
+ gles for forcing OpenGL ES
+ gles2 for forcing OpenGL ES 2
+ WARNING: only specify this manually if you know what
+ you are doing!
Optional Libraries:
--with-alsa-prefix=DIR Prefix where alsa is installed (optional)
@@ -964,9 +972,6 @@ Optional Libraries:
--with-mpeg2-prefix=DIR Prefix where libmpeg2 is installed (optional)
--enable-mpeg2 enable mpeg2 codec for cutscenes [autodetect]
- --with-opengl-prefix=DIR Prefix where OpenGL (ES) is installed (optional)
- --disable-opengl disable OpenGL (ES) support [autodetect]
-
--with-jpeg-prefix=DIR Prefix where libjpeg is installed (optional)
--disable-jpeg disable JPEG decoder [autodetect]
@@ -1071,10 +1076,11 @@ for ac_option in $@; do
--disable-updates) _updates=no ;;
--enable-libunity) _libunity=yes ;;
--disable-libunity) _libunity=no ;;
- --enable-opengl) _opengl=yes ;;
- --disable-opengl) _opengl=no ;;
--enable-bink) _bink=yes ;;
--disable-bink) _bink=no ;;
+ --opengl-mode=*)
+ _opengl_mode=`echo $ac_option | cut -d '=' -f 2`
+ ;;
--enable-verbose-build) _verbose_build=yes ;;
--enable-plugins) _dynamic_modules=yes ;;
--default-dynamic) _plugins_default=dynamic ;;
@@ -1175,11 +1181,6 @@ for ac_option in $@; do
LIBUNITY_CFLAGS="-I$arg/include"
LIBUNITY_LIBS="-L$arg/lib"
;;
- --with-opengl-prefix=*)
- arg=`echo $ac_option | cut -d '=' -f 2`
- OPENGL_CFLAGS="-I$arg/include"
- OPENGL_LIBS="-L$arg/lib"
- ;;
--backend=*)
_backend=`echo $ac_option | cut -d '=' -f 2`
;;
@@ -2659,10 +2660,11 @@ if test -n "$_host"; then
# since SDL2 manages dispmanx/GLES2 very well internally.
# SDL1 is bit-rotten on this platform.
_sdlconfig=sdl2-config
- # OpenGL(ES) support is mature enough as to be the best option on
+ # OpenGL ES support is mature enough as to be the best option on
# the Raspberry Pi, so it's enabled by default.
- _opengl=yes
- _opengles=yes
+ # The Raspberry Pi always supports OpenGL ES 2.0 contexts, thus we
+ # take advantage of those.
+ _opengl_mode=gles2
;;
dreamcast)
append_var DEFINES "-DDISABLE_DEFAULT_SAVEFILEMANAGER"
@@ -2990,6 +2992,8 @@ if test -n "$_host"; then
_mt32emu=no
_timidity=no
_vkeybd=yes
+ # Tizen relies on the OpenGL ES output thus we always enable it.
+ _opengl_mode=gles
;;
webos)
_backend="webos"
@@ -4150,113 +4154,92 @@ echocheck "OpenGL"
case $_backend in
openpandora)
- # Only enable OpenGL ES on the OpanPandora if --enable-opengl is passed in explicitly.
- if test "$_opengl" = yes ; then
- _opengl=yes
- _opengles=yes
- OPENGL_LIBS="-lGLES_CM -lEGL -lX11"
- OPENGL_CFLAGS="$OPENGL_LIBS"
- append_var LIBS "$OPENGL_LIBS"
- append_var INCLUDES "$OPENGL_CFLAGS"
+ # Only enable OpenGL ES on the OpanPandora if --opengl-mode=gles is passed in explicitly.
+ if test "$_opengl_mode" = "gles" ; then
+ append_var LIBS "-lGLES_CM -lEGL -lX11"
+ else
+ _opengl_mode=none
fi
;;
esac
-if test "$_opengl" = auto ; then
- _opengl=no
- if test "$_backend" = "sdl" ; then
- # Try different header filenames
- # 1) GL/gl.h This is usually used on POSIX and Windows systems
- # 2) OpenGL/gl.h This is used on Mac OS X
- # 3) GLES/gl.h This is used for OpenGL ES 1.x
- for i in "GL/gl.h" "OpenGL/gl.h" "GLES/gl.h"; do
- # Test the current header for OpenGL
- cat > $TMPC << EOF
-#include <$i>
-#include <stdio.h>
-int main(void) { printf("ANTIVIRUS FALSE POSITIVE WORKAROUND"); return GL_VERSION_1_1; }
-EOF
- cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && break
+if test "$_opengl_mode" = auto ; then
+ case $_backend in
+ sdl)
+ case $_sdlversion in
+ 1.2.*)
+ # Stock SDL 1.2 only supports OpenGL contexts.
+ _opengl_mode=gl
+ ;;
- # Test the current header for OpenGL ES
- cat > $TMPC << EOF
-#include <$i>
-int main(void) { return GL_OES_VERSION_1_1; }
-EOF
- cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && _opengles=yes && break
+ 2.0.*)
+ # SDL2 supports both OpenGL + OpenGL ES contexts.
+ # However, Mac OS X only allows OpenGL context creation at
+ # this time, thus we limit us to OpenGL on that platform.
+ case $_host_os in
+ darwin*)
+ _opengl_mode=gl
+ ;;
+
+ *)
+ _opengl_mode=any
+ ;;
+ esac
+ ;;
+ esac
+ ;;
- # Test the current header for OpenGL ES on SBCs (Raspberry Pi, Cubieboard, etc)
- cat > $TMPC << EOF
-#include <$i>
-int main(void) { return GL_VERSION_ES_CM_1_1; }
-EOF
- cc_check $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS && _opengl=yes && _opengles=yes && break
- done
- fi
+ tizen)
+ # Tizen always runs in GLES mode
+ _opengl_mode=gles
+ ;;
+
+ *)
+ _opengl_mode=none
+ ;;
+ esac
fi
-if test "$_opengl" = yes ; then
- # Our simple test case
- cat > $TMPC << EOF
-int main(void) { return 0; }
-EOF
- _opengl=no
- # Try different library names
- if test "$_opengles" = "yes" ; then
- # 1) GLES_CM This is usually used for OpenGL ES 1.1 (Common profile)
- # 2) GLESv1_CM This is used by the Windows Mali OpenGL ES 1.1 Emulator
- # 3) glesv1 This is used by the Linux Mali OpenGL ES 1.1 Emulator
- _opengles=no
- for lib in "-lGLES_CM" "-lGLESv1_CM" "-lglesv1"; do
- if cc_check_no_clean $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS $lib
- then
- _opengl=yes
- _opengles=yes
- OPENGL_LIBS="$OPENGL_LIBS $lib"
- break
- fi
- done
- else
- # 1) -framework OpenGL This is used on Mac OS X
- # 2) GL This is usually used on POSIX systems
- # 3) opengl32 This is used on Windows
- #
- # We try "-framework OpenGL" first here to assure it will always be
- # picked up by the configure script on Mac OS X, even when a libGL
- # exists.
- for lib in "-framework OpenGL" "-lGL" "-lopengl32"; do
- if cc_check_no_clean $DEFINES $OPENGL_CFLAGS $OPENGL_LIBS $lib
- then
- _opengl=yes
- OPENGL_LIBS="$OPENGL_LIBS $lib"
- break
- fi
- done
- fi
- cc_check_clean
+_opengl=yes
+case $_opengl_mode in
+ auto)
+ # This case should never occur but better safe than sorry.
+ echo "no"
+ _opengl=no
+ ;;
- if test "$_opengl" = yes ; then
- append_var LIBS "$OPENGL_LIBS"
- append_var INCLUDES "$OPENGL_CFLAGS"
- fi
-fi
+ none)
+ echo "no"
+ _opengl=no
+ ;;
-case $_host_os in
- tizen)
- # components live in non-standard locations so just assume sane SDK
- _opengl=yes
- _opengles=yes
+ any)
+ echo "yes (runtime detection)"
+ add_line_to_config_h "#undef USE_GLES_MODE"
;;
-esac
-if test "$_opengles" = "yes" ; then
- echo "yes (OpenGL ES)"
-else
- echo "$_opengl"
-fi
+ gl)
+ echo "yes (OpenGL)"
+ add_line_to_config_h "#define USE_GLES_MODE 0"
+ ;;
+
+ gles)
+ echo "yes (OpenGL ES)"
+ add_line_to_config_h "#define USE_GLES_MODE 1"
+ ;;
+
+ gles2)
+ echo "yes (OpenGL ES 2)"
+ add_line_to_config_h "#define USE_GLES_MODE 2"
+ ;;
+
+ *)
+ echo "invalid mode specification '$_opengl_mode'. Aborting."
+ exit 1
+ ;;
+esac
define_in_config_if_yes "$_opengl" "USE_OPENGL"
-define_in_config_if_yes "$_opengles" "USE_GLES"
#
# Check for Linux CD-ROM support
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index aa450f1461..0242af1cfb 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -125,7 +125,6 @@ int main(int argc, char *argv[]) {
ProjectType projectType = kProjectNone;
int msvcVersion = 12;
- bool useSDL2 = false;
// Parse command line arguments
using std::cout;
@@ -269,7 +268,7 @@ int main(int argc, char *argv[]) {
} else if (!std::strcmp(argv[i], "--tests")) {
setup.tests = true;
} else if (!std::strcmp(argv[i], "--sdl2")) {
- useSDL2 = true;
+ setup.useSDL2 = true;
} else {
std::cerr << "ERROR: Unknown parameter \"" << argv[i] << "\"\n";
return -1;
@@ -349,11 +348,15 @@ int main(int argc, char *argv[]) {
setup.defines.push_back("IPHONE");
}
setup.defines.push_back("SDL_BACKEND");
- if (!useSDL2) {
- cout << "\nLinking to SDL 1.2\n\n";
+ if (!setup.useSDL2) {
+ cout << "\nBuilding against SDL 1.2\n\n";
setup.libraries.push_back("sdl");
} else {
- cout << "\nLinking to SDL 2.0\n\n";
+ cout << "\nBuilding against SDL 2.0\n\n";
+ // TODO: This also defines USE_SDL2 in the preprocessor, we don't do
+ // this in our configure/make based build system. Adapt create_project
+ // to replicate this behavior.
+ setup.defines.push_back("USE_SDL2");
setup.libraries.push_back("sdl2");
}
setup.libraries.push_back("winmm");
@@ -954,7 +957,8 @@ const Feature s_features[] = {
{ "16bit", "USE_RGB_COLOR", "", true, "16bit color support" },
{ "mt32emu", "USE_MT32EMU", "", true, "integrated MT-32 emulator" },
{ "nasm", "USE_NASM", "", true, "IA-32 assembly support" }, // This feature is special in the regard, that it needs additional handling.
- { "opengl", "USE_OPENGL", "opengl32", true, "OpenGL support" },
+ { "opengl", "USE_OPENGL", "", true, "OpenGL support" },
+ { "opengles", "USE_GLES", "", true, "forced OpenGL ES mode" },
{ "taskbar", "USE_TASKBAR", "", true, "Taskbar integration support" },
{ "translation", "USE_TRANSLATION", "", true, "Translation support" },
{ "vkeybd", "ENABLE_VKEYBD", "", false, "Virtual keyboard support"},
diff --git a/devtools/create_project/create_project.h b/devtools/create_project/create_project.h
index fb207f3f59..1e417d485b 100644
--- a/devtools/create_project/create_project.h
+++ b/devtools/create_project/create_project.h
@@ -229,15 +229,17 @@ struct BuildSetup {
StringList testDirs; ///< List of all folders containing tests
bool devTools; ///< Generate project files for the tools
- bool tests; ///< Generate project files for the tests
+ bool tests; ///< Generate project files for the tests
bool runBuildEvents; ///< Run build events as part of the build (generate revision number and copy engine/theme data & needed files to the build folder
bool createInstaller; ///< Create NSIS installer after the build
+ bool useSDL2; ///< Whether to use SDL2 or not.
BuildSetup() {
devTools = false;
tests = false;
runBuildEvents = false;
createInstaller = false;
+ useSDL2 = false;
}
};
diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp
index a804205c42..2c6a89543f 100644
--- a/devtools/create_project/msbuild.cpp
+++ b/devtools/create_project/msbuild.cpp
@@ -366,7 +366,7 @@ void MSBuildProvider::outputGlobalPropFile(const BuildSetup &setup, std::ofstrea
"\t\t<_PropertySheetDisplayName>" << setup.projectDescription << "_Global</_PropertySheetDisplayName>\n"
"\t\t<ExecutablePath>$(" << LIBS_DEFINE << ")\\bin;$(" << LIBS_DEFINE << ")\\bin\\" << (bits == 32 ? "x86" : "x64") << ";$(ExecutablePath)</ExecutablePath>\n"
"\t\t<LibraryPath>$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << ";$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << "\\$(Configuration);$(LibraryPath)</LibraryPath>\n"
- "\t\t<IncludePath>$(" << LIBS_DEFINE << ")\\include;$(" << LIBS_DEFINE << ")\\include\\SDL;$(IncludePath)</IncludePath>\n"
+ "\t\t<IncludePath>$(" << LIBS_DEFINE << ")\\include;$(" << LIBS_DEFINE << ")\\include\\" << (setup.useSDL2 ? "SDL2" : "SDL") << ";$(IncludePath)</IncludePath>\n"
"\t\t<OutDir>$(Configuration)" << bits << "\\</OutDir>\n"
"\t\t<IntDir>$(Configuration)" << bits << "\\$(ProjectName)\\</IntDir>\n"
"\t</PropertyGroup>\n"
diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp
index a43730fbe2..bfe7f522f0 100644
--- a/devtools/create_project/xcode.cpp
+++ b/devtools/create_project/xcode.cpp
@@ -447,9 +447,6 @@ void XcodeProvider::setupFrameworksBuildPhase(const BuildSetup &setup) {
DEF_SYSFRAMEWORK("UIKit");
DEF_SYSTBD("libiconv");
- // Optionals:
- DEF_SYSFRAMEWORK("OpenGL");
-
// Local libraries
DEF_LOCALLIB_STATIC("libFLAC");
DEF_LOCALLIB_STATIC("libmad");
@@ -570,8 +567,6 @@ void XcodeProvider::setupFrameworksBuildPhase(const BuildSetup &setup) {
frameworks_osx.push_back("IOKit.framework");
frameworks_osx.push_back("Cocoa.framework");
frameworks_osx.push_back("AudioUnit.framework");
- // Optionals:
- frameworks_osx.push_back("OpenGL.framework");
order = 0;
for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) {
@@ -932,7 +927,11 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) {
ADD_SETTING_LIST(scummvmOSX_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvmOSX_defines, kSettingsNoQuote | kSettingsAsList, 5);
ADD_SETTING_QUOTE(scummvmOSX_Debug, "GCC_VERSION", "");
ValueList scummvmOSX_HeaderPaths;
- scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL");
+ if (setup.useSDL2) {
+ scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL2");
+ } else {
+ scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL");
+ }
scummvmOSX_HeaderPaths.push_back("/opt/local/include");
scummvmOSX_HeaderPaths.push_back("/opt/local/include/freetype2");
scummvmOSX_HeaderPaths.push_back("include/");
@@ -948,7 +947,6 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) {
ADD_SETTING_LIST(scummvmOSX_Debug, "LIBRARY_SEARCH_PATHS", scummvmOSX_LibPaths, kSettingsNoQuote | kSettingsAsList, 5);
ADD_SETTING_QUOTE(scummvmOSX_Debug, "OTHER_CFLAGS", "");
ValueList scummvmOSX_LdFlags;
- scummvmOSX_LdFlags.push_back("-lSDLmain");
scummvmOSX_LdFlags.push_back("-logg");
scummvmOSX_LdFlags.push_back("-lpng");
scummvmOSX_LdFlags.push_back("-ljpeg");
@@ -958,7 +956,13 @@ void XcodeProvider::setupBuildConfiguration(const BuildSetup &setup) {
scummvmOSX_LdFlags.push_back("-lvorbis");
scummvmOSX_LdFlags.push_back("-lmad");
scummvmOSX_LdFlags.push_back("-lFLAC");
- scummvmOSX_LdFlags.push_back("-lSDL");
+ if (setup.useSDL2) {
+ scummvmOSX_LdFlags.push_back("-lSDL2main");
+ scummvmOSX_LdFlags.push_back("-lSDL2");
+ } else {
+ scummvmOSX_LdFlags.push_back("-lSDLmain");
+ scummvmOSX_LdFlags.push_back("-lSDL");
+ }
scummvmOSX_LdFlags.push_back("-lz");
ADD_SETTING_LIST(scummvmOSX_Debug, "OTHER_LDFLAGS", scummvmOSX_LdFlags, kSettingsAsList, 5);
ADD_SETTING(scummvmOSX_Debug, "PREBINDING", "NO");
diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp
new file mode 100644
index 0000000000..1ab74c3cf6
--- /dev/null
+++ b/engines/adl/adl.cpp
@@ -0,0 +1,1038 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/stream.h"
+#include "common/savefile.h"
+
+#include "engines/util.h"
+
+#include "graphics/palette.h"
+#include "graphics/thumbnail.h"
+
+#include "adl/adl.h"
+#include "adl/display.h"
+#include "adl/detection.h"
+
+namespace Adl {
+
+AdlEngine::~AdlEngine() {
+ delete _display;
+}
+
+AdlEngine::AdlEngine(OSystem *syst, const AdlGameDescription *gd) :
+ Engine(syst),
+ _display(nullptr),
+ _gameDescription(gd),
+ _isRestarting(false),
+ _isRestoring(false),
+ _saveVerb(0),
+ _saveNoun(0),
+ _restoreVerb(0),
+ _restoreNoun(0),
+ _canSaveNow(false),
+ _canRestoreNow(false) {
+}
+
+Common::String AdlEngine::readString(Common::ReadStream &stream, byte until) const {
+ Common::String str;
+
+ while (1) {
+ byte b = stream.readByte();
+
+ if (stream.eos() || stream.err())
+ error("Error reading string");
+
+ if (b == until)
+ break;
+
+ str += b;
+ };
+
+ return str;
+}
+
+Common::String AdlEngine::readStringAt(Common::SeekableReadStream &stream, uint offset, byte until) const {
+ stream.seek(offset);
+ return readString(stream, until);
+}
+
+void AdlEngine::printMessage(uint idx, bool wait) const {
+ Common::String msg = _messages[idx - 1];
+ wordWrap(msg);
+ _display->printString(msg);
+
+ if (wait)
+ delay(14 * 166018 / 1000);
+}
+
+void AdlEngine::delay(uint32 ms) const {
+ Common::EventManager *ev = g_system->getEventManager();
+
+ uint32 start = g_system->getMillis();
+
+ while (!g_engine->shouldQuit() && g_system->getMillis() - start < ms) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) {
+ switch(event.kbd.keycode) {
+ case Common::KEYCODE_q:
+ g_engine->quitGame();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ g_system->delayMillis(16);
+ }
+}
+
+Common::String AdlEngine::inputString(byte prompt) const {
+ Common::String s;
+
+ if (prompt > 0)
+ _display->printString(Common::String(prompt));
+
+ while (1) {
+ byte b = inputKey();
+
+ if (g_engine->shouldQuit() || _isRestoring)
+ return 0;
+
+ if (b == 0)
+ continue;
+
+ if (b == ('\r' | 0x80)) {
+ s += b;
+ _display->printString(Common::String(b));
+ return s;
+ }
+
+ if (b < 0xa0) {
+ switch (b) {
+ case Common::KEYCODE_BACKSPACE | 0x80:
+ if (!s.empty()) {
+ _display->moveCursorBackward();
+ _display->setCharAtCursor(APPLECHAR(' '));
+ s.deleteLastChar();
+ }
+ break;
+ };
+ } else {
+ if (s.size() < 255) {
+ s += b;
+ _display->printString(Common::String(b));
+ }
+ }
+ }
+}
+
+byte AdlEngine::inputKey() const {
+ Common::EventManager *ev = g_system->getEventManager();
+
+ byte key = 0;
+
+ _display->showCursor(true);
+
+ while (!g_engine->shouldQuit() && !_isRestoring && key == 0) {
+ Common::Event event;
+ if (ev->pollEvent(event)) {
+ if (event.type != Common::EVENT_KEYDOWN)
+ continue;
+
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ if (event.kbd.keycode == Common::KEYCODE_q)
+ g_engine->quitGame();
+ continue;
+ }
+
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_BACKSPACE:
+ case Common::KEYCODE_RETURN:
+ key = convertKey(event.kbd.keycode);
+ break;
+ default:
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii < 0x80)
+ key = convertKey(event.kbd.ascii);
+ };
+ }
+
+ _display->updateTextScreen();
+ g_system->delayMillis(16);
+ }
+
+ _display->showCursor(false);
+
+ return key;
+}
+
+void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map) const {
+ uint index = 0;
+
+ while (1) {
+ ++index;
+
+ byte buf[IDI_WORD_SIZE];
+
+ if (stream.read(buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
+ error("Error reading word list");
+
+ Common::String word((char *)buf, IDI_WORD_SIZE);
+
+ if (!map.contains(word))
+ map[word] = index;
+
+ byte synonyms = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Error reading word list");
+
+ if (synonyms == 0xff)
+ break;
+
+ for (uint i = 0; i < synonyms; ++i) {
+ if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
+ error("Error reading word list");
+
+ word = Common::String((char *)buf, IDI_WORD_SIZE);
+
+ if (!map.contains(word))
+ map[word] = index;
+ }
+ }
+}
+
+void AdlEngine::readCommands(Common::ReadStream &stream, Commands &commands) {
+ while (1) {
+ Command command;
+ command.room = stream.readByte();
+
+ if (command.room == 0xff)
+ return;
+
+ command.verb = stream.readByte();
+ command.noun = stream.readByte();
+
+ byte scriptSize = stream.readByte() - 6;
+
+ command.numCond = stream.readByte();
+ command.numAct = stream.readByte();
+
+ for (uint i = 0; i < scriptSize; ++i)
+ command.script.push_back(stream.readByte());
+
+ if (stream.eos() || stream.err())
+ error("Failed to read commands");
+
+ if (command.numCond == 0 && command.script[0] == IDO_ACT_SAVE) {
+ _saveVerb = command.verb;
+ _saveNoun = command.noun;
+ }
+
+ if (command.numCond == 0 && command.script[0] == IDO_ACT_LOAD) {
+ _restoreVerb = command.verb;
+ _restoreNoun = command.noun;
+ }
+
+ commands.push_back(command);
+ }
+}
+
+Common::Error AdlEngine::run() {
+ _display = new Display();
+
+ loadData();
+
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot >= 0) {
+ if (loadGameState(saveSlot).getCode() != Common::kNoError)
+ error("Failed to load save game from slot %i", saveSlot);
+ _display->moveCursorTo(Common::Point(0, 23));
+ _isRestoring = true;
+ } else {
+ runIntro();
+ initState();
+ }
+
+ _display->setMode(DISPLAY_MODE_MIXED);
+ _display->printAsciiString("\r\r\r\r\r");
+
+ while (1) {
+ uint verb = 0, noun = 0;
+
+ // When restoring from the launcher, we don't read
+ // input on the first iteration. This is needed to
+ // ensure that restoring from the launcher and
+ // restoring in-game brings us to the same game state.
+ // (Also see comment below.)
+ if (!_isRestoring) {
+ clearScreen();
+ showRoom();
+
+ _canSaveNow = _canRestoreNow = true;
+ getInput(verb, noun);
+ _canSaveNow = _canRestoreNow = false;
+
+ if (shouldQuit())
+ break;
+
+ // If we just restored from the GMM, we skip this command
+ // set, as no command has been input by the user
+ if (!_isRestoring)
+ if (!doOneCommand(_roomCommands, verb, noun))
+ printMessage(_messageIds.dontUnderstand);
+ }
+
+ if (_isRestoring) {
+ // We restored from the GMM or launcher. As restoring
+ // with "RESTORE GAME" does not end command processing,
+ // we don't break it off here either. This essentially
+ // means that restoring a game will always run through
+ // the global commands and increase the move counter
+ // before the first user input.
+ _display->printAsciiString("\r");
+ _isRestoring = false;
+ verb = _restoreVerb;
+ noun = _restoreNoun;
+ }
+
+ // Restarting does end command processing
+ if (_isRestarting) {
+ _isRestarting = false;
+ continue;
+ }
+
+ doAllCommands(_globalCommands, verb, noun);
+ _state.moves++;
+ }
+
+ return Common::kNoError;
+}
+
+bool AdlEngine::hasFeature(EngineFeature f) const {
+ switch (f) {
+ case kSupportsLoadingDuringRuntime:
+ case kSupportsSavingDuringRuntime:
+ case kSupportsRTL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Common::Error AdlEngine::loadGameState(int slot) {
+ Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
+ Common::InSaveFile *inFile = getSaveFileManager()->openForLoading(fileName);
+
+ if (!inFile) {
+ warning("Failed to open file '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
+
+ if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
+ warning("No header found in '%s'", fileName.c_str());
+ delete inFile;
+ return Common::kUnknownError;
+ }
+
+ byte saveVersion = inFile->readByte();
+ if (saveVersion != SAVEGAME_VERSION) {
+ warning("Save game version %i not supported", saveVersion);
+ delete inFile;
+ return Common::kUnknownError;
+ }
+
+ // Skip description
+ inFile->seek(SAVEGAME_NAME_LEN, SEEK_CUR);
+ // Skip save time
+ inFile->seek(6, SEEK_CUR);
+
+ uint32 playTime = inFile->readUint32BE();
+
+ Graphics::skipThumbnail(*inFile);
+
+ initState();
+
+ _state.room = inFile->readByte();
+ _state.moves = inFile->readByte();
+ _state.isDark = inFile->readByte();
+
+ uint32 size = inFile->readUint32BE();
+ if (size != _state.rooms.size())
+ error("Room count mismatch (expected %i; found %i)", _state.rooms.size(), size);
+
+ for (uint i = 0; i < size; ++i) {
+ _state.rooms[i].picture = inFile->readByte();
+ _state.rooms[i].curPicture = inFile->readByte();
+ }
+
+ size = inFile->readUint32BE();
+ if (size != _state.items.size())
+ error("Item count mismatch (expected %i; found %i)", _state.items.size(), size);
+
+ for (uint i = 0; i < size; ++i) {
+ _state.items[i].room = inFile->readByte();
+ _state.items[i].picture = inFile->readByte();
+ _state.items[i].position.x = inFile->readByte();
+ _state.items[i].position.y = inFile->readByte();
+ _state.items[i].state = inFile->readByte();
+ }
+
+ size = inFile->readUint32BE();
+ if (size != _state.vars.size())
+ error("Variable count mismatch (expected %i; found %i)", _state.vars.size(), size);
+
+ for (uint i = 0; i < size; ++i)
+ _state.vars[i] = inFile->readByte();
+
+ if (inFile->err() || inFile->eos())
+ error("Failed to load game '%s'", fileName.c_str());
+
+ delete inFile;
+
+ setTotalPlayTime(playTime);
+
+ _isRestoring = true;
+ return Common::kNoError;
+}
+
+bool AdlEngine::canLoadGameStateCurrently() {
+ return _canRestoreNow;
+}
+
+Common::Error AdlEngine::saveGameState(int slot, const Common::String &desc) {
+ Common::String fileName = Common::String::format("%s.s%02d", _targetName.c_str(), slot);
+ Common::OutSaveFile *outFile = getSaveFileManager()->openForSaving(fileName);
+
+ if (!outFile) {
+ warning("Failed to open file '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
+
+ outFile->writeUint32BE(MKTAG('A', 'D', 'L', ':'));
+ outFile->writeByte(SAVEGAME_VERSION);
+
+ char name[SAVEGAME_NAME_LEN] = { };
+
+ if (!desc.empty())
+ strncpy(name, desc.c_str(), sizeof(name) - 1);
+ else {
+ Common::String defaultName("Save ");
+ defaultName += 'A' + slot;
+ strncpy(name, defaultName.c_str(), sizeof(name) - 1);
+ }
+
+ outFile->write(name, sizeof(name));
+
+ TimeDate t;
+ g_system->getTimeAndDate(t);
+
+ outFile->writeUint16BE(t.tm_year);
+ outFile->writeByte(t.tm_mon);
+ outFile->writeByte(t.tm_mday);
+ outFile->writeByte(t.tm_hour);
+ outFile->writeByte(t.tm_min);
+
+ uint32 playTime = getTotalPlayTime();
+ outFile->writeUint32BE(playTime);
+
+ _display->saveThumbnail(*outFile);
+
+ outFile->writeByte(_state.room);
+ outFile->writeByte(_state.moves);
+ outFile->writeByte(_state.isDark);
+
+ outFile->writeUint32BE(_state.rooms.size());
+ for (uint i = 0; i < _state.rooms.size(); ++i) {
+ outFile->writeByte(_state.rooms[i].picture);
+ outFile->writeByte(_state.rooms[i].curPicture);
+ }
+
+ outFile->writeUint32BE(_state.items.size());
+ for (uint i = 0; i < _state.items.size(); ++i) {
+ outFile->writeByte(_state.items[i].room);
+ outFile->writeByte(_state.items[i].picture);
+ outFile->writeByte(_state.items[i].position.x);
+ outFile->writeByte(_state.items[i].position.y);
+ outFile->writeByte(_state.items[i].state);
+ }
+
+ outFile->writeUint32BE(_state.vars.size());
+ for (uint i = 0; i < _state.vars.size(); ++i)
+ outFile->writeByte(_state.vars[i]);
+
+ outFile->finalize();
+
+ if (outFile->err()) {
+ delete outFile;
+ warning("Failed to save game '%s'", fileName.c_str());
+ return Common::kUnknownError;
+ }
+
+ delete outFile;
+ return Common::kNoError;
+}
+
+bool AdlEngine::canSaveGameStateCurrently() {
+ if (!_canSaveNow)
+ return false;
+
+ Commands::const_iterator cmd;
+
+ // Here we check whether or not the game currently accepts the command
+ // "SAVE GAME". This prevents saving via the GMM in situations where
+ // it wouldn't otherwise be possible to do so.
+ for (cmd = _roomCommands.begin(); cmd != _roomCommands.end(); ++cmd) {
+ uint offset;
+ if (matchCommand(*cmd, _saveVerb, _saveNoun, &offset))
+ return cmd->script[offset] == IDO_ACT_SAVE;
+ }
+
+ return false;
+}
+
+void AdlEngine::wordWrap(Common::String &str) const {
+ uint end = 39;
+
+ while (1) {
+ if (str.size() <= end)
+ return;
+
+ while (str[end] != APPLECHAR(' '))
+ --end;
+
+ str.setChar(APPLECHAR('\r'), end);
+ end += 40;
+ }
+}
+
+byte AdlEngine::convertKey(uint16 ascii) const {
+ ascii = toupper(ascii);
+
+ if (ascii >= 0x80)
+ return 0;
+
+ ascii |= 0x80;
+
+ if (ascii >= 0x80 && ascii <= 0xe0)
+ return ascii;
+
+ return 0;
+}
+
+Common::String AdlEngine::getLine() const {
+ // Original engine uses a global here, which isn't reset between
+ // calls and may not match actual mode
+ bool textMode = false;
+
+ while (1) {
+ Common::String line = inputString(APPLECHAR('?'));
+
+ if (shouldQuit() || _isRestoring)
+ return "";
+
+ if ((byte)line[0] == ('\r' | 0x80)) {
+ textMode = !textMode;
+ _display->setMode(textMode ? DISPLAY_MODE_TEXT : DISPLAY_MODE_MIXED);
+ continue;
+ }
+
+ // Remove the return
+ line.deleteLastChar();
+ return line;
+ }
+}
+
+Common::String AdlEngine::getWord(const Common::String &line, uint &index) const {
+ Common::String str;
+
+ for (uint i = 0; i < 8; ++i)
+ str += APPLECHAR(' ');
+
+ int copied = 0;
+
+ // Skip initial whitespace
+ while (1) {
+ if (index == line.size())
+ return str;
+ if (line[index] != APPLECHAR(' '))
+ break;
+ ++index;
+ }
+
+ // Copy up to 8 characters
+ while (1) {
+ if (copied < 8)
+ str.setChar(line[index], copied++);
+
+ index++;
+
+ if (index == line.size() || line[index] == APPLECHAR(' '))
+ return str;
+ }
+}
+
+void AdlEngine::getInput(uint &verb, uint &noun) {
+ while (1) {
+ _display->printString(_strings.enterCommand);
+ Common::String line = getLine();
+
+ if (shouldQuit() || _isRestoring)
+ return;
+
+ uint index = 0;
+ Common::String verbStr = getWord(line, index);
+
+ if (!_verbs.contains(verbStr)) {
+ Common::String err = _strings.verbError;
+ for (uint i = 0; i < verbStr.size(); ++i)
+ err.setChar(verbStr[i], i + 19);
+ _display->printString(err);
+ continue;
+ }
+
+ verb = _verbs[verbStr];
+
+ Common::String nounStr = getWord(line, index);
+
+ if (!_nouns.contains(nounStr)) {
+ Common::String err = _strings.nounError;
+ for (uint i = 0; i < verbStr.size(); ++i)
+ err.setChar(verbStr[i], i + 19);
+ for (uint i = 0; i < nounStr.size(); ++i)
+ err.setChar(nounStr[i], i + 30);
+ _display->printString(err);
+ continue;
+ }
+
+ noun = _nouns[nounStr];
+ return;
+ }
+}
+
+void AdlEngine::showRoom() const {
+ if (!_state.isDark) {
+ drawPic(getCurRoom().curPicture);
+ drawItems();
+ }
+
+ _display->updateHiResScreen();
+ printMessage(getCurRoom().description, false);
+}
+
+void AdlEngine::clearScreen() const {
+ _display->setMode(DISPLAY_MODE_MIXED);
+ _display->clear(0x00);
+}
+
+void AdlEngine::drawItems() const {
+ Common::Array<Item>::const_iterator item;
+
+ uint dropped = 0;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->room != _state.room)
+ continue;
+
+ if (item->state == IDI_ITEM_MOVED) {
+ if (getCurRoom().picture == getCurRoom().curPicture) {
+ const Common::Point &p = _itemOffsets[dropped];
+ if (item->isLineArt)
+ drawLineArt(_lineArt[item->picture - 1], p);
+ else
+ drawPic(item->picture, p);
+ ++dropped;
+ }
+ continue;
+ }
+
+ Common::Array<byte>::const_iterator pic;
+
+ for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
+ if (*pic == getCurRoom().curPicture) {
+ if (item->isLineArt)
+ drawLineArt(_lineArt[item->picture - 1], item->position);
+ else
+ drawPic(item->picture, item->position);
+ continue;
+ }
+ }
+ }
+}
+
+void AdlEngine::drawNextPixel(Common::Point &p, byte color, byte bits, byte quadrant) const {
+ if (bits & 4)
+ _display->putPixel(p, color);
+
+ bits += quadrant;
+
+ if (bits & 1)
+ p.x += (bits & 2 ? -1 : 1);
+ else
+ p.y += (bits & 2 ? 1 : -1);
+}
+
+void AdlEngine::drawLineArt(const Common::Array<byte> &lineArt, const Common::Point &pos, byte rotation, byte scaling, byte color) const {
+ const byte stepping[] = {
+ 0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5,
+ 0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18,
+ 0xff
+ };
+
+ byte quadrant = rotation >> 4;
+ rotation &= 0xf;
+ byte xStep = stepping[rotation];
+ byte yStep = stepping[(rotation ^ 0xf) + 1] + 1;
+
+ Common::Point p(pos);
+
+ for (uint i = 0; i < lineArt.size(); ++i) {
+ byte b = lineArt[i];
+
+ do {
+ byte xFrac = 0x80;
+ byte yFrac = 0x80;
+ for (uint j = 0; j < scaling; ++j) {
+ if (xFrac + xStep + 1 > 255)
+ drawNextPixel(p, color, b, quadrant);
+ xFrac += xStep + 1;
+ if (yFrac + yStep > 255)
+ drawNextPixel(p, color, b, quadrant + 1);
+ yFrac += yStep;
+ }
+ b >>= 3;
+ } while (b != 0);
+ }
+}
+
+const Room &AdlEngine::getRoom(uint i) const {
+ if (i < 1 || i > _state.rooms.size())
+ error("Room %i out of range [1, %i]", i, _state.rooms.size());
+
+ return _state.rooms[i - 1];
+}
+
+Room &AdlEngine::getRoom(uint i) {
+ if (i < 1 || i > _state.rooms.size())
+ error("Room %i out of range [1, %i]", i, _state.rooms.size());
+
+ return _state.rooms[i - 1];
+}
+
+const Room &AdlEngine::getCurRoom() const {
+ return getRoom(_state.room);
+}
+
+Room &AdlEngine::getCurRoom() {
+ return getRoom(_state.room);
+}
+
+const Item &AdlEngine::getItem(uint i) const {
+ if (i < 1 || i > _state.items.size())
+ error("Item %i out of range [1, %i]", i, _state.items.size());
+
+ return _state.items[i - 1];
+}
+
+Item &AdlEngine::getItem(uint i) {
+ if (i < 1 || i > _state.items.size())
+ error("Item %i out of range [1, %i]", i, _state.items.size());
+
+ return _state.items[i - 1];
+}
+
+byte AdlEngine::getVar(uint i) const {
+ if (i >= _state.vars.size())
+ error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+
+ return _state.vars[i];
+}
+
+void AdlEngine::setVar(uint i, byte value) {
+ if (i >= _state.vars.size())
+ error("Variable %i out of range [0, %i]", i, _state.vars.size() - 1);
+
+ _state.vars[i] = value;
+}
+
+void AdlEngine::takeItem(byte noun) {
+ Common::Array<Item>::iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->noun != noun || item->room != _state.room)
+ continue;
+
+ if (item->state == IDI_ITEM_DOESNT_MOVE) {
+ printMessage(_messageIds.itemDoesntMove);
+ return;
+ }
+
+ if (item->state == IDI_ITEM_MOVED) {
+ item->room = IDI_NONE;
+ return;
+ }
+
+ Common::Array<byte>::const_iterator pic;
+ for (pic = item->roomPictures.begin(); pic != item->roomPictures.end(); ++pic) {
+ if (*pic == getCurRoom().curPicture) {
+ item->room = IDI_NONE;
+ item->state = IDI_ITEM_MOVED;
+ return;
+ }
+ }
+ }
+
+ printMessage(_messageIds.itemNotHere);
+}
+
+void AdlEngine::dropItem(byte noun) {
+ Common::Array<Item>::iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item) {
+ if (item->noun != noun || item->room != IDI_NONE)
+ continue;
+
+ item->room = _state.room;
+ item->state = IDI_ITEM_MOVED;
+ return;
+ }
+
+ printMessage(_messageIds.dontUnderstand);
+}
+
+#define ARG(N) (command.script[offset + (N)])
+
+bool AdlEngine::matchCommand(const Command &command, byte verb, byte noun, uint *actions) const {
+ if (command.room != IDI_NONE && command.room != _state.room)
+ return false;
+
+ if (command.verb != IDI_NONE && command.verb != verb)
+ return false;
+
+ if (command.noun != IDI_NONE && command.noun != noun)
+ return false;
+
+ uint offset = 0;
+ for (uint i = 0; i < command.numCond; ++i) {
+ switch (ARG(0)) {
+ case IDO_CND_ITEM_IN_ROOM:
+ if (getItem(ARG(1)).room != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ case IDO_CND_MOVES_GE:
+ if (ARG(1) > _state.moves)
+ return false;
+ offset += 2;
+ break;
+ case IDO_CND_VAR_EQ:
+ if (getVar(ARG(1)) != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ case IDO_CND_CUR_PIC_EQ:
+ if (getCurRoom().curPicture != ARG(1))
+ return false;
+ offset += 2;
+ break;
+ case IDO_CND_ITEM_PIC_EQ:
+ if (getItem(ARG(1)).picture != ARG(2))
+ return false;
+ offset += 3;
+ break;
+ default:
+ error("Invalid condition opcode %02x", command.script[offset]);
+ }
+ }
+
+ if (actions)
+ *actions = offset;
+
+ return true;
+}
+
+void AdlEngine::doActions(const Command &command, byte noun, byte offset) {
+ for (uint i = 0; i < command.numAct; ++i) {
+ switch (ARG(0)) {
+ case IDO_ACT_VAR_ADD:
+ setVar(ARG(2), getVar(ARG(2) + ARG(1)));
+ offset += 3;
+ break;
+ case IDO_ACT_VAR_SUB:
+ setVar(ARG(2), getVar(ARG(2)) - ARG(1));
+ offset += 3;
+ break;
+ case IDO_ACT_VAR_SET:
+ setVar(ARG(1), ARG(2));
+ offset += 3;
+ break;
+ case IDO_ACT_LIST_ITEMS: {
+ Common::Array<Item>::const_iterator item;
+
+ for (item = _state.items.begin(); item != _state.items.end(); ++item)
+ if (item->room == IDI_NONE)
+ printMessage(item->description);
+
+ ++offset;
+ break;
+ }
+ case IDO_ACT_MOVE_ITEM:
+ getItem(ARG(1)).room = ARG(2);
+ offset += 3;
+ break;
+ case IDO_ACT_SET_ROOM:
+ getCurRoom().curPicture = getCurRoom().picture;
+ _state.room = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_SET_CUR_PIC:
+ getCurRoom().curPicture = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_SET_PIC:
+ getCurRoom().picture = getCurRoom().curPicture = ARG(1);
+ offset += 2;
+ break;
+ case IDO_ACT_PRINT_MSG:
+ printMessage(ARG(1));
+ offset += 2;
+ break;
+ case IDO_ACT_SET_LIGHT:
+ _state.isDark = false;
+ ++offset;
+ break;
+ case IDO_ACT_SET_DARK:
+ _state.isDark = true;
+ ++offset;
+ break;
+ case IDO_ACT_SAVE:
+ saveGameState(0, "");
+ ++offset;
+ break;
+ case IDO_ACT_LOAD:
+ loadGameState(0);
+ ++offset;
+ // Original engine does not jump out of the loop,
+ // so we don't either.
+ // We reset the restore flag, as the restore game
+ // process is complete
+ _isRestoring = false;
+ break;
+ case IDO_ACT_RESTART: {
+ _display->printString(_strings.playAgain);
+ Common::String input = inputString();
+ if (input.size() == 0 || input[0] != APPLECHAR('N')) {
+ _isRestarting = true;
+ _display->clear(0x00);
+ _display->updateHiResScreen();
+ restartGame();
+ return;
+ }
+ // Fall-through
+ }
+ case IDO_ACT_QUIT:
+ printMessage(_messageIds.thanksForPlaying);
+ quitGame();
+ return;
+ case IDO_ACT_PLACE_ITEM:
+ getItem(ARG(1)).room = ARG(2);
+ getItem(ARG(1)).position.x = ARG(3);
+ getItem(ARG(1)).position.y = ARG(4);
+ offset += 5;
+ break;
+ case IDO_ACT_SET_ITEM_PIC:
+ getItem(ARG(2)).picture = ARG(1);
+ offset += 3;
+ break;
+ case IDO_ACT_RESET_PIC:
+ getCurRoom().curPicture = getCurRoom().picture;
+ ++offset;
+ break;
+ case IDO_ACT_GO_NORTH:
+ case IDO_ACT_GO_SOUTH:
+ case IDO_ACT_GO_EAST:
+ case IDO_ACT_GO_WEST:
+ case IDO_ACT_GO_UP:
+ case IDO_ACT_GO_DOWN: {
+ byte room = getCurRoom().connections[ARG(0) - IDO_ACT_GO_NORTH];
+
+ if (room == 0) {
+ printMessage(_messageIds.cantGoThere);
+ return;
+ }
+
+ getCurRoom().curPicture = getCurRoom().picture;
+ _state.room = room;
+ return;
+ }
+ case IDO_ACT_TAKE_ITEM:
+ takeItem(noun);
+ ++offset;
+ break;
+ case IDO_ACT_DROP_ITEM:
+ dropItem(noun);
+ ++offset;
+ break;
+ case IDO_ACT_SET_ROOM_PIC:
+ getRoom(ARG(1)).picture = getRoom(ARG(1)).curPicture = ARG(2);
+ offset += 3;
+ break;
+ default:
+ error("Invalid action opcode %02x", ARG(0));
+ }
+ }
+}
+
+#undef ARG
+
+bool AdlEngine::doOneCommand(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator cmd;
+
+ for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
+ uint offset = 0;
+ if (matchCommand(*cmd, verb, noun, &offset)) {
+ doActions(*cmd, noun, offset);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AdlEngine::doAllCommands(const Commands &commands, byte verb, byte noun) {
+ Commands::const_iterator cmd;
+
+ for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
+ uint offset = 0;
+ if (matchCommand(*cmd, verb, noun, &offset))
+ doActions(*cmd, noun, offset);
+ }
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/adl.h b/engines/adl/adl.h
new file mode 100644
index 0000000000..4ea7566669
--- /dev/null
+++ b/engines/adl/adl.h
@@ -0,0 +1,247 @@
+/* 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 ADL_ADL_H
+#define ADL_ADL_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str.h"
+
+#include "engines/engine.h"
+
+namespace Common {
+class ReadStream;
+class SeekableReadStream;
+}
+
+namespace Adl {
+
+class Display;
+struct AdlGameDescription;
+
+// Conditional opcodes
+#define IDO_CND_ITEM_IN_ROOM 0x03
+#define IDO_CND_MOVES_GE 0x05
+#define IDO_CND_VAR_EQ 0x06
+#define IDO_CND_CUR_PIC_EQ 0x09
+#define IDO_CND_ITEM_PIC_EQ 0x0a
+
+// Action opcodes
+#define IDO_ACT_VAR_ADD 0x01
+#define IDO_ACT_VAR_SUB 0x02
+#define IDO_ACT_VAR_SET 0x03
+#define IDO_ACT_LIST_ITEMS 0x04
+#define IDO_ACT_MOVE_ITEM 0x05
+#define IDO_ACT_SET_ROOM 0x06
+#define IDO_ACT_SET_CUR_PIC 0x07
+#define IDO_ACT_SET_PIC 0x08
+#define IDO_ACT_PRINT_MSG 0x09
+#define IDO_ACT_SET_LIGHT 0x0a
+#define IDO_ACT_SET_DARK 0x0b
+#define IDO_ACT_QUIT 0x0d
+#define IDO_ACT_SAVE 0x0f
+#define IDO_ACT_LOAD 0x10
+#define IDO_ACT_RESTART 0x11
+#define IDO_ACT_PLACE_ITEM 0x12
+#define IDO_ACT_SET_ITEM_PIC 0x13
+#define IDO_ACT_RESET_PIC 0x14
+#define IDO_ACT_GO_NORTH 0x15
+#define IDO_ACT_GO_SOUTH 0x16
+#define IDO_ACT_GO_EAST 0x17
+#define IDO_ACT_GO_WEST 0x18
+#define IDO_ACT_GO_UP 0x19
+#define IDO_ACT_GO_DOWN 0x1a
+#define IDO_ACT_TAKE_ITEM 0x1b
+#define IDO_ACT_DROP_ITEM 0x1c
+#define IDO_ACT_SET_ROOM_PIC 0x1d
+
+#define IDI_WORD_SIZE 8
+
+struct Room {
+ byte description;
+ byte connections[6];
+ byte picture;
+ byte curPicture;
+};
+
+struct Picture {
+ byte block;
+ uint16 offset;
+};
+
+struct Command {
+ byte room;
+ byte verb, noun;
+ byte numCond, numAct;
+ Common::Array<byte> script;
+};
+
+enum {
+ IDI_ITEM_NOT_MOVED,
+ IDI_ITEM_MOVED,
+ IDI_ITEM_DOESNT_MOVE
+};
+
+#define IDI_NONE 0xfe
+
+struct Item {
+ byte noun;
+ byte room;
+ byte picture;
+ bool isLineArt;
+ Common::Point position;
+ int state;
+ byte description;
+ Common::Array<byte> roomPictures;
+};
+
+struct State {
+ Common::Array<Room> rooms;
+ Common::Array<Item> items;
+ Common::Array<byte> vars;
+
+ byte room;
+ uint16 moves;
+ bool isDark;
+
+ State() : room(1), moves(0), isDark(false) { }
+};
+
+typedef Common::List<Command> Commands;
+typedef Common::HashMap<Common::String, uint> WordMap;
+
+class AdlEngine : public Engine {
+public:
+ virtual ~AdlEngine();
+
+protected:
+ AdlEngine(OSystem *syst, const AdlGameDescription *gd);
+
+ Common::String readString(Common::ReadStream &stream, byte until = 0) const;
+ Common::String readStringAt(Common::SeekableReadStream &stream, uint offset, byte until = 0) const;
+
+ virtual void printMessage(uint idx, bool wait = true) const;
+ void delay(uint32 ms) const;
+
+ Common::String inputString(byte prompt = 0) const;
+ byte inputKey() const;
+
+ void loadWords(Common::ReadStream &stream, WordMap &map) const;
+ void readCommands(Common::ReadStream &stream, Commands &commands);
+
+ Display *_display;
+
+ // Message strings in data file
+ Common::Array<Common::String> _messages;
+ // Picture data
+ Common::Array<Picture> _pictures;
+ // Dropped item screen offsets
+ Common::Array<Common::Point> _itemOffsets;
+ // Drawings consisting of horizontal and vertical lines only, but
+ // supporting scaling and rotation
+ Common::Array<Common::Array<byte> > _lineArt;
+ // <room, verb, noun, script> lists
+ Commands _roomCommands;
+ Commands _globalCommands;
+
+ WordMap _verbs;
+ WordMap _nouns;
+
+ struct {
+ Common::String enterCommand;
+ Common::String dontHaveIt;
+ Common::String gettingDark;
+ Common::String verbError;
+ Common::String nounError;
+ Common::String playAgain;
+ } _strings;
+
+ struct {
+ uint cantGoThere;
+ uint dontUnderstand;
+ uint itemDoesntMove;
+ uint itemNotHere;
+ uint thanksForPlaying;
+ } _messageIds;
+
+ // Game state
+ State _state;
+
+private:
+ virtual void runIntro() const { }
+ virtual void loadData() = 0;
+ virtual void initState() = 0;
+ virtual void restartGame() = 0;
+ virtual void drawPic(byte pic, Common::Point pos = Common::Point()) const = 0;
+
+ // Engine
+ Common::Error run();
+ bool hasFeature(EngineFeature f) const;
+ Common::Error loadGameState(int slot);
+ bool canLoadGameStateCurrently();
+ Common::Error saveGameState(int slot, const Common::String &desc);
+ bool canSaveGameStateCurrently();
+
+ // Text output
+ void wordWrap(Common::String &str) const;
+
+ // Text input
+ byte convertKey(uint16 ascii) const;
+ Common::String getLine() const;
+ Common::String getWord(const Common::String &line, uint &index) const;
+ void getInput(uint &verb, uint &noun);
+
+ // Graphics
+ void showRoom() const;
+ void clearScreen() const;
+ void drawItems() const;
+ void drawNextPixel(Common::Point &p, byte color, byte bits, byte quadrant) const;
+ void drawLineArt(const Common::Array<byte> &lineArt, const Common::Point &pos, byte rotation = 0, byte scaling = 1, byte color = 0x7f) const;
+
+ // Game state functions
+ const Room &getRoom(uint i) const;
+ Room &getRoom(uint i);
+ const Room &getCurRoom() const;
+ Room &getCurRoom();
+ const Item &getItem(uint i) const;
+ Item &getItem(uint i);
+ byte getVar(uint i) const;
+ void setVar(uint i, byte value);
+ void takeItem(byte noun);
+ void dropItem(byte noun);
+ bool matchCommand(const Command &command, byte verb, byte noun, uint *actions = nullptr) const;
+ void doActions(const Command &command, byte noun, byte offset);
+ bool doOneCommand(const Commands &commands, byte verb, byte noun);
+ void doAllCommands(const Commands &commands, byte verb, byte noun);
+
+ const AdlGameDescription *_gameDescription;
+ bool _isRestarting, _isRestoring;
+ byte _saveVerb, _saveNoun, _restoreVerb, _restoreNoun;
+ bool _canSaveNow, _canRestoreNow;
+};
+
+AdlEngine *HiRes1Engine__create(OSystem *syst, const AdlGameDescription *gd);
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/configure.engine b/engines/adl/configure.engine
new file mode 100644
index 0000000000..844e2b8e6a
--- /dev/null
+++ b/engines/adl/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine adl "ADL" no
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
new file mode 100644
index 0000000000..1a8c5025e8
--- /dev/null
+++ b/engines/adl/detection.cpp
@@ -0,0 +1,247 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/translation.h"
+
+#include "graphics/thumbnail.h"
+
+#include "engines/advancedDetector.h"
+
+#include "adl/detection.h"
+
+namespace Adl {
+
+#define GAMEOPTION_COLOR GUIO_GAMEOPTIONS1
+#define GAMEOPTION_SCANLINES GUIO_GAMEOPTIONS2
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_COLOR,
+ {
+ _s("Color mode"),
+ _s("Use color graphics"),
+ "color",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_SCANLINES,
+ {
+ _s("Scanlines"),
+ _s("Show scanlines"),
+ "scanlines",
+ false
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+static const PlainGameDescriptor adlGames[] = {
+ {"hires1", "Hi-Res Adventure #1: Mystery House"},
+ {0, 0}
+};
+
+static const AdlGameDescription gameDescriptions[] = {
+ { // Hi-Res Adventure #1: Mystery House - Apple II - 1987 PD release
+ {
+ "hires1", 0,
+ {
+ {"ADVENTURE", 0, "22d9e63a11d69fa033ba1738715ad09a", 29952},
+ {"AUTO LOAD OBJ", 0, "23bfccfe9fcff9b22cf6c41bde9078ac", 12291},
+ {"MYSTERY.HELLO", 0, "2289b7fea300b506e902a4c597968369", 836},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformApple2GS, // FIXME
+ ADGF_NO_FLAGS,
+ GUIO2(GAMEOPTION_COLOR, GAMEOPTION_SCANLINES)
+ },
+ GAME_TYPE_HIRES1
+ },
+ { AD_TABLE_END_MARKER, GAME_TYPE_NONE }
+};
+
+class AdlMetaEngine : public AdvancedMetaEngine {
+public:
+ AdlMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(AdlGameDescription), adlGames, optionsList) { }
+
+ const char *getName() const {
+ return "ADL";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "Copyright (C) Sierra On-Line";
+ }
+
+ bool hasFeature(MetaEngineFeature f) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+ int getMaximumSaveSlot() const { return 'O' - 'A'; }
+ SaveStateList listSaves(const char *target) const;
+ void removeSaveState(const char *target, int slot) const;
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+};
+
+bool AdlMetaEngine::hasFeature(MetaEngineFeature f) const {
+ switch(f) {
+ case kSupportsListSaves:
+ case kSupportsLoadingDuringStartup:
+ case kSupportsDeleteSave:
+ case kSavesSupportMetaInfo:
+ case kSavesSupportThumbnail:
+ case kSavesSupportCreationDate:
+ case kSavesSupportPlayTime:
+ return true;
+ default:
+ return false;
+ }
+}
+
+SaveStateDescriptor AdlMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.s%02d", target, slot);
+ Common::InSaveFile *inFile = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (!inFile)
+ return SaveStateDescriptor();
+
+ if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ byte saveVersion = inFile->readByte();
+ if (saveVersion != SAVEGAME_VERSION) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ char name[SAVEGAME_NAME_LEN] = { };
+ inFile->read(name, sizeof(name) - 1);
+ inFile->readByte();
+
+ if (inFile->eos() || inFile->err()) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ SaveStateDescriptor sd(slot, name);
+
+ int year = inFile->readUint16BE();
+ int month = inFile->readByte();
+ int day = inFile->readByte();
+ sd.setSaveDate(year + 1900, month + 1, day);
+
+ int hour = inFile->readByte();
+ int minutes = inFile->readByte();
+ sd.setSaveTime(hour, minutes);
+
+ uint32 playTime = inFile->readUint32BE();
+ sd.setPlayTime(playTime);
+
+ if (inFile->eos() || inFile->err()) {
+ delete inFile;
+ return SaveStateDescriptor();
+ }
+
+ Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*inFile);
+ sd.setThumbnail(thumbnail);
+
+ delete inFile;
+ return sd;
+}
+
+SaveStateList AdlMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray files = saveFileMan->listSavefiles(Common::String(target) + ".s##");
+
+ SaveStateList saveList;
+
+ for (uint i = 0; i < files.size(); ++i) {
+ const Common::String &fileName = files[i];
+ Common::InSaveFile *inFile = saveFileMan->openForLoading(fileName);
+ if (!inFile) {
+ warning("Cannot open save file '%s'", fileName.c_str());
+ continue;
+ }
+
+ if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
+ warning("No header found in '%s'", fileName.c_str());
+ delete inFile;
+ continue;
+ }
+
+ byte saveVersion = inFile->readByte();
+ if (saveVersion != SAVEGAME_VERSION) {
+ warning("Unsupported save game version %i found in '%s'", saveVersion, fileName.c_str());
+ delete inFile;
+ continue;
+ }
+
+ char name[SAVEGAME_NAME_LEN] = { };
+ inFile->read(name, sizeof(name) - 1);
+ delete inFile;
+
+ int slotNum = atoi(fileName.c_str() + fileName.size() - 2);
+ SaveStateDescriptor sd(slotNum, name);
+ saveList.push_back(sd);
+ }
+
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+ return saveList;
+}
+
+void AdlMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.s%02d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd);
+
+bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (!gd)
+ return false;
+
+ const AdlGameDescription *adlGd = (const AdlGameDescription *)gd;
+
+ switch (adlGd->gameType) {
+ case GAME_TYPE_HIRES1:
+ *engine = HiRes1Engine_create(syst, adlGd);
+ break;
+ default:
+ error("Unknown GameType");
+ }
+
+ return true;
+}
+
+} // End of namespace Adl
+
+#if PLUGIN_ENABLED_DYNAMIC(ADL)
+ REGISTER_PLUGIN_DYNAMIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
+#endif
diff --git a/backends/graphics/opengl/extensions.h b/engines/adl/detection.h
index 87452429e2..c646aeb5b9 100644
--- a/backends/graphics/opengl/extensions.h
+++ b/engines/adl/detection.h
@@ -20,22 +20,26 @@
*
*/
-#ifndef BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
-#define BACKENDS_GRAPHICS_OPENGL_EXTENSIONS_H
+#ifndef ADL_DETECTION_H
+#define ADL_DETECTION_H
-namespace OpenGL {
+#include "engines/advancedDetector.h"
-/**
- * Checks for availability of extensions we want to use and initializes them
- * when available.
- */
-void initializeGLExtensions();
+namespace Adl {
-/**
- * Whether non power of two textures are supported
- */
-extern bool g_extNPOTSupported;
+#define SAVEGAME_VERSION 0
+#define SAVEGAME_NAME_LEN 32
+
+enum GameType {
+ GAME_TYPE_NONE,
+ GAME_TYPE_HIRES1
+};
+
+struct AdlGameDescription {
+ ADGameDescription desc;
+ GameType gameType;
+};
-} // End of namespace OpenGL
+} // End of namespace Adl
#endif
diff --git a/engines/adl/display.cpp b/engines/adl/display.cpp
new file mode 100644
index 0000000000..6342504bc3
--- /dev/null
+++ b/engines/adl/display.cpp
@@ -0,0 +1,520 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stream.h"
+#include "common/rect.h"
+#include "common/system.h"
+#include "common/str.h"
+#include "common/config-manager.h"
+
+#include "graphics/surface.h"
+#include "graphics/palette.h"
+#include "graphics/thumbnail.h"
+
+#include "engines/util.h"
+
+#include "adl/display.h"
+
+namespace Adl {
+
+// This implements the Apple II "Hi-Res" display mode
+
+#define DISPLAY_PITCH (DISPLAY_WIDTH / 7)
+#define DISPLAY_SIZE (DISPLAY_PITCH * DISPLAY_HEIGHT)
+
+#define TEXT_WIDTH 40
+#define TEXT_HEIGHT 24
+#define TEXT_BUF_SIZE (TEXT_WIDTH * TEXT_HEIGHT)
+
+#define COLOR_PALETTE_ENTRIES 8
+static const byte colorPalette[COLOR_PALETTE_ENTRIES * 3] = {
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0xc7, 0x34, 0xff,
+ 0x38, 0xcb, 0x00,
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0x0d, 0xa1, 0xff,
+ 0xf2, 0x5e, 0x00
+};
+
+// Corresponding color in second palette
+#define PAL2(X) ((X) | 0x04)
+
+// Alternate color for odd pixel rows (for scanlines)
+#define ALTCOL(X) ((X) | 0x08)
+
+// Green monochrome palette
+#define MONO_PALETTE_ENTRIES 2
+static const byte monoPalette[MONO_PALETTE_ENTRIES * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0x01
+};
+
+// Uppercase-only Apple II font (manually created).
+static const byte font[64][5] = {
+ { 0x7c, 0x82, 0xba, 0xb2, 0x9c }, { 0xf8, 0x24, 0x22, 0x24, 0xf8 }, // @A
+ { 0xfe, 0x92, 0x92, 0x92, 0x6c }, { 0x7c, 0x82, 0x82, 0x82, 0x44 }, // BC
+ { 0xfe, 0x82, 0x82, 0x82, 0x7c }, { 0xfe, 0x92, 0x92, 0x92, 0x82 }, // DE
+ { 0xfe, 0x12, 0x12, 0x12, 0x02 }, { 0x7c, 0x82, 0x82, 0xa2, 0xe2 }, // FG
+ { 0xfe, 0x10, 0x10, 0x10, 0xfe }, { 0x00, 0x82, 0xfe, 0x82, 0x00 }, // HI
+ { 0x40, 0x80, 0x80, 0x80, 0x7e }, { 0xfe, 0x10, 0x28, 0x44, 0x82 }, // JK
+ { 0xfe, 0x80, 0x80, 0x80, 0x80 }, { 0xfe, 0x04, 0x18, 0x04, 0xfe }, // LM
+ { 0xfe, 0x08, 0x10, 0x20, 0xfe }, { 0x7c, 0x82, 0x82, 0x82, 0x7c }, // NO
+ { 0xfe, 0x12, 0x12, 0x12, 0x0c }, { 0x7c, 0x82, 0xa2, 0x42, 0xbc }, // PQ
+ { 0xfe, 0x12, 0x32, 0x52, 0x8c }, { 0x4c, 0x92, 0x92, 0x92, 0x64 }, // RS
+ { 0x02, 0x02, 0xfe, 0x02, 0x02 }, { 0x7e, 0x80, 0x80, 0x80, 0x7e }, // TU
+ { 0x3e, 0x40, 0x80, 0x40, 0x3e }, { 0xfe, 0x40, 0x30, 0x40, 0xfe }, // VW
+ { 0xc6, 0x28, 0x10, 0x28, 0xc6 }, { 0x06, 0x08, 0xf0, 0x08, 0x06 }, // XY
+ { 0xc2, 0xa2, 0x92, 0x8a, 0x86 }, { 0xfe, 0xfe, 0x82, 0x82, 0x82 }, // Z[
+ { 0x04, 0x08, 0x10, 0x20, 0x40 }, { 0x82, 0x82, 0x82, 0xfe, 0xfe }, // \]
+ { 0x20, 0x10, 0x08, 0x10, 0x20 }, { 0x80, 0x80, 0x80, 0x80, 0x80 }, // ^_
+ { 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xbe, 0x00, 0x00 }, // !
+ { 0x00, 0x0e, 0x00, 0x0e, 0x00 }, { 0x28, 0xfe, 0x28, 0xfe, 0x28 }, // "#
+ { 0x48, 0x54, 0xfe, 0x54, 0x24 }, { 0x46, 0x26, 0x10, 0xc8, 0xc4 }, // $%
+ { 0x6c, 0x92, 0xac, 0x40, 0xa0 }, { 0x00, 0x00, 0x0e, 0x00, 0x00 }, // &'
+ { 0x38, 0x44, 0x82, 0x00, 0x00 }, { 0x00, 0x00, 0x82, 0x44, 0x38 }, // ()
+ { 0x44, 0x28, 0xfe, 0x28, 0x44 }, { 0x10, 0x10, 0x7c, 0x10, 0x10 }, // *+
+ { 0x00, 0x80, 0x60, 0x00, 0x00 }, { 0x10, 0x10, 0x10, 0x10, 0x10 }, // ,-
+ { 0x00, 0x00, 0x80, 0x00, 0x00 }, { 0x40, 0x20, 0x10, 0x08, 0x04 }, // ./
+ { 0x7c, 0xa2, 0x92, 0x8a, 0x7c }, { 0x00, 0x84, 0xfe, 0x80, 0x00 }, // 01
+ { 0xc4, 0xa2, 0x92, 0x92, 0x8c }, { 0x42, 0x82, 0x92, 0x9a, 0x66 }, // 23
+ { 0x30, 0x28, 0x24, 0xfe, 0x20 }, { 0x4e, 0x8a, 0x8a, 0x8a, 0x72 }, // 45
+ { 0x78, 0x94, 0x92, 0x92, 0x62 }, { 0x02, 0xe2, 0x12, 0x0a, 0x06 }, // 67
+ { 0x6c, 0x92, 0x92, 0x92, 0x6c }, { 0x8c, 0x92, 0x92, 0x52, 0x3c }, // 89
+ { 0x00, 0x00, 0x28, 0x00, 0x00 }, { 0x00, 0x80, 0x68, 0x00, 0x00 }, // :;
+ { 0x10, 0x28, 0x44, 0x82, 0x00 }, { 0x28, 0x28, 0x28, 0x28, 0x28 }, // <=
+ { 0x00, 0x82, 0x44, 0x28, 0x10 }, { 0x04, 0x02, 0xb2, 0x0a, 0x04 } // >?
+};
+
+Display::Display() :
+ _mode(DISPLAY_MODE_TEXT),
+ _cursorPos(0),
+ _showCursor(false) {
+
+ initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, true);
+
+ _monochrome = !ConfMan.getBool("color");
+ _scanlines = ConfMan.getBool("scanlines");
+
+ if (_monochrome)
+ g_system->getPaletteManager()->setPalette(monoPalette, 0, MONO_PALETTE_ENTRIES);
+ else
+ g_system->getPaletteManager()->setPalette(colorPalette, 0, COLOR_PALETTE_ENTRIES);
+
+ showScanlines(_scanlines);
+
+ _frameBuf = new byte[DISPLAY_SIZE];
+ memset(_frameBuf, 0, DISPLAY_SIZE);
+ _frameBufSurface = new Graphics::Surface;
+ // We need 2x scaling to properly render the half-pixel shift
+ // of the second palette
+ _frameBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ _textBuf = new byte[TEXT_BUF_SIZE];
+ memset(_textBuf, APPLECHAR(' '), TEXT_BUF_SIZE);
+ _textBufSurface = new Graphics::Surface;
+ // For ease of copying, also use 2x scaling here
+ _textBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ createFont();
+}
+
+Display::~Display() {
+ delete[] _frameBuf;
+ _frameBufSurface->free();
+ delete _frameBufSurface;
+
+ delete[] _textBuf;
+ _textBufSurface->free();
+ delete _textBufSurface;
+
+ _font->free();
+ delete _font;
+}
+
+void Display::setMode(DisplayMode mode) {
+ _mode = mode;
+
+ if (_mode == DISPLAY_MODE_TEXT || _mode == DISPLAY_MODE_MIXED)
+ updateTextScreen();
+ if (_mode == DISPLAY_MODE_HIRES || _mode == DISPLAY_MODE_MIXED)
+ updateHiResScreen();
+}
+
+void Display::updateTextScreen() {
+ updateTextSurface();
+
+ if (_mode == DISPLAY_MODE_TEXT)
+ g_system->copyRectToScreen(_textBufSurface->getPixels(), _textBufSurface->pitch, 0, 0, _textBufSurface->w, _textBufSurface->h);
+ else if (_mode == DISPLAY_MODE_MIXED)
+ g_system->copyRectToScreen(_textBufSurface->getBasePtr(0, _textBufSurface->h - 4 * 8 * 2), _textBufSurface->pitch, 0, _textBufSurface->h - 4 * 8 * 2, _textBufSurface->w, 4 * 8 * 2);
+
+ g_system->updateScreen();
+}
+
+void Display::updateHiResScreen() {
+ updateHiResSurface();
+
+ if (_mode == DISPLAY_MODE_HIRES)
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h);
+ else if (_mode == DISPLAY_MODE_MIXED)
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h - 4 * 8 * 2);
+
+ g_system->updateScreen();
+}
+
+bool Display::saveThumbnail(Common::WriteStream &out) {
+ if (_scanlines) {
+ showScanlines(false);
+ g_system->updateScreen();
+ }
+
+ bool retval = Graphics::saveThumbnail(out);
+
+ if (_scanlines) {
+ showScanlines(true);
+ g_system->updateScreen();
+ }
+
+ return retval;
+}
+
+void Display::loadFrameBuffer(Common::ReadStream &stream) {
+ byte *dst = _frameBuf;
+ for (uint j = 0; j < 8; ++j) {
+ for (uint i = 0; i < 8; ++i) {
+ stream.read(dst, DISPLAY_PITCH);
+ dst += DISPLAY_PITCH * 64;
+ stream.read(dst, DISPLAY_PITCH);
+ dst += DISPLAY_PITCH * 64;
+ stream.read(dst, DISPLAY_PITCH);
+ stream.readUint32LE();
+ stream.readUint32LE();
+ dst -= DISPLAY_PITCH * 120;
+ }
+ dst -= DISPLAY_PITCH * 63;
+ }
+
+ if (stream.eos() || stream.err())
+ error("Failed to read frame buffer");
+}
+
+void Display::putPixel(const Common::Point &p, byte color) {
+ byte offset = p.x / 7;
+
+ if (offset & 1) {
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ color ^= 0x7f;
+ }
+
+ byte *b = _frameBuf + p.y * DISPLAY_PITCH + offset;
+ color ^= *b;
+ color &= 1 << (p.x % 7);
+ *b ^= color;
+}
+
+void Display::clear(byte color) {
+ byte val = 0;
+
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ val = 0x7f;
+
+ for (uint i = 0; i < DISPLAY_SIZE; ++i) {
+ _frameBuf[i] = color;
+ color ^= val;
+ }
+}
+
+void Display::home() {
+ memset(_textBuf, APPLECHAR(' '), TEXT_BUF_SIZE);
+ _cursorPos = 0;
+}
+
+void Display::moveCursorForward() {
+ ++_cursorPos;
+
+ if (_cursorPos >= TEXT_BUF_SIZE)
+ scrollUp();
+}
+
+void Display::moveCursorBackward() {
+ if (_cursorPos > 0)
+ --_cursorPos;
+}
+
+void Display::moveCursorTo(const Common::Point &pos) {
+ _cursorPos = pos.y * TEXT_WIDTH + pos.x;
+
+ if (_cursorPos >= TEXT_BUF_SIZE)
+ error("Cursor position (%i, %i) out of bounds", pos.x, pos.y);
+}
+
+void Display::printString(const Common::String &str) {
+ Common::String::const_iterator c;
+ for (c = str.begin(); c != str.end(); ++c) {
+ byte b = *c;
+
+ if (*c == APPLECHAR('\r'))
+ _cursorPos = (_cursorPos / TEXT_WIDTH + 1) * TEXT_WIDTH;
+ else if (b < 0x80 || b >= 0xa0) {
+ setCharAtCursor(b);
+ ++_cursorPos;
+ }
+
+ if (_cursorPos == TEXT_BUF_SIZE)
+ scrollUp();
+ }
+
+ updateTextScreen();
+}
+
+void Display::printAsciiString(const Common::String &str) {
+ Common::String aStr;
+
+ Common::String::const_iterator it;
+ for (it = str.begin(); it != str.end(); ++it)
+ aStr += APPLECHAR(*it);
+
+ printString(aStr);
+}
+
+void Display::setCharAtCursor(byte c) {
+ _textBuf[_cursorPos] = c;
+}
+
+void Display::showCursor(bool enable) {
+ _showCursor = enable;
+}
+
+void Display::showScanlines(bool enable) {
+ byte pal[COLOR_PALETTE_ENTRIES * 3] = { };
+
+ if (enable)
+ g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ else {
+ g_system->getPaletteManager()->grabPalette(pal, 0, COLOR_PALETTE_ENTRIES);
+ g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ }
+}
+
+static byte processColorBits(uint16 &bits, bool &odd, bool secondPal) {
+ byte color = 0;
+
+ switch (bits & 0x7) {
+ case 0x3: // 011 (white)
+ case 0x6: // 110
+ case 0x7: // 111
+ color = 1;
+ break;
+ case 0x2: // 010 (color)
+ color = 2 + odd;
+ break;
+ case 0x5: // 101 (color)
+ color = 2 + !odd;
+ }
+
+ if (secondPal)
+ color = PAL2(color);
+
+ odd = !odd;
+ bits >>= 1;
+
+ return color;
+}
+
+static void renderPixelRowColor(byte *dst, byte *src) {
+ uint16 bits = (src[0] & 0x7f) << 1;
+ byte pal = src[0] >> 7;
+
+ if (pal != 0)
+ *dst++ = 0;
+
+ bool odd = false;
+
+ for (uint i = 0; i < DISPLAY_PITCH; ++i) {
+ if (i != DISPLAY_PITCH - 1) {
+ bits |= (src[i + 1] & 0x7f) << 8;
+ pal |= (src[i + 1] >> 7) << 1;
+ }
+
+ // For the first 6 bits in the block we draw two pixels
+ for (uint j = 0; j < 6; ++j) {
+ byte color = processColorBits(bits, odd, pal & 1);
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ // Last bit of the block, draw one, two or three pixels
+ byte color = processColorBits(bits, odd, pal & 1);
+
+ // Draw the first pixel
+ *dst++ = color;
+
+ switch (pal) {
+ case 0x0:
+ case 0x3:
+ // If palette stays the same, draw a second pixel
+ *dst++ = color;
+ break;
+ case 0x2:
+ // If we're moving from first to second palette,
+ // draw a second pixel, and a third in the second
+ // palette.
+ *dst++ = color;
+ *dst++ = PAL2(color);
+ }
+
+ pal >>= 1;
+ }
+}
+
+static void renderPixelRowMono(byte *dst, byte *src) {
+ byte pal = src[0] >> 7;
+
+ if (pal != 0)
+ *dst++ = 0;
+
+ for (uint i = 0; i < DISPLAY_PITCH; ++i) {
+ if (i != DISPLAY_PITCH - 1)
+ pal |= (src[i + 1] >> 7) << 1;
+
+ for (uint j = 0; j < 6; ++j) {
+ bool color = src[i] & (1 << j);
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ bool color = src[i] & (1 << 6);
+
+ *dst++ = color;
+
+ switch (pal) {
+ case 0x0:
+ case 0x3:
+ *dst++ = color;
+ break;
+ case 0x2:
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ pal >>= 1;
+ }
+}
+
+static void copyEvenSurfaceRows(Graphics::Surface &surf) {
+ byte *src = (byte *)surf.getPixels();
+
+ for (uint y = 0; y < surf.h / 2; ++y) {
+ byte *dst = src + surf.pitch;
+ for (uint x = 0; x < surf.w; ++x)
+ dst[x] = ALTCOL(src[x]);
+ src += surf.pitch * 2;
+ }
+}
+
+void Display::updateHiResSurface() {
+ byte *src = _frameBuf;
+ byte *dst = (byte *)_frameBufSurface->getPixels();
+
+ for (uint i = 0; i < DISPLAY_HEIGHT; ++i) {
+ if (_monochrome)
+ renderPixelRowMono(dst, src);
+ else
+ renderPixelRowColor(dst, src);
+ src += DISPLAY_PITCH;
+ dst += _frameBufSurface->pitch * 2;
+ }
+
+ copyEvenSurfaceRows(*_frameBufSurface);
+}
+
+void Display::updateTextSurface() {
+ for (uint row = 0; row < 24; ++row)
+ for (uint col = 0; col < TEXT_WIDTH; ++col) {
+ uint charPos = row * TEXT_WIDTH + col;
+ char c = _textBuf[row * TEXT_WIDTH + col];
+
+ if (charPos == _cursorPos && _showCursor)
+ c = (c & 0x3f) | 0x40;
+
+ Common::Rect r(7 * 2, 8 * 2);
+ r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2);
+
+ if (!(c & 0x80)) {
+ if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1))
+ r.translate(0, 4 * 8 * 2);
+ }
+
+ _textBufSurface->copyRectToSurface(*_font, col * 7 * 2, row * 8 * 2, r);
+ }
+}
+
+void Display::drawChar(byte c, int x, int y) {
+ byte *buf = (byte *)_font->getPixels() + y * _font->pitch + x;
+
+ for (uint row = 0; row < 8; ++row) {
+ for (uint col = 1; col < 6; ++col) {
+ if (font[c][col - 1] & (1 << row)) {
+ buf[col * 2] = 1;
+ buf[col * 2 + 1] = 1;
+ }
+ }
+
+ buf += 2 * _font->pitch;
+ }
+}
+
+void Display::createFont() {
+ _font = new Graphics::Surface;
+ _font->create(16 * 7 * 2, 4 * 8 * 2 * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ for (uint i = 0; i < 4; ++i)
+ for (uint j = 0; j < 16; ++j)
+ drawChar(i * 16 + j, j * 7 * 2, i * 8 * 2);
+
+ // Create inverted font
+ byte *buf = (byte *)_font->getPixels();
+ byte *bufInv = buf + (_font->h / 2) * _font->pitch;
+
+ for (uint row = 0; row < _font->h / 2; row += 2) {
+ for (uint col = 0; col < _font->w; ++col)
+ bufInv[col] = (buf[col] ? 0 : 1);
+
+ buf += _font->pitch * 2;
+ bufInv += _font->pitch * 2;
+ }
+
+ copyEvenSurfaceRows(*_font);
+}
+
+void Display::scrollUp() {
+ memmove(_textBuf, _textBuf + TEXT_WIDTH, TEXT_BUF_SIZE - TEXT_WIDTH);
+ memset(_textBuf + TEXT_BUF_SIZE - TEXT_WIDTH, APPLECHAR(' '), TEXT_WIDTH);
+ if (_cursorPos >= TEXT_WIDTH)
+ _cursorPos -= TEXT_WIDTH;
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/display.h b/engines/adl/display.h
new file mode 100644
index 0000000000..e61477da84
--- /dev/null
+++ b/engines/adl/display.h
@@ -0,0 +1,102 @@
+/* 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 ADL_DISPLAY_H
+#define ADL_DISPLAY_H
+
+#include <common/types.h>
+
+namespace Common {
+class ReadStream;
+class WriteStream;
+class String;
+class Point;
+}
+
+namespace Graphics {
+class Surface;
+}
+
+namespace Adl {
+
+#define DISPLAY_WIDTH 280
+#define DISPLAY_HEIGHT 192
+
+enum DisplayMode {
+ DISPLAY_MODE_HIRES,
+ DISPLAY_MODE_TEXT,
+ DISPLAY_MODE_MIXED
+};
+
+#define APPLECHAR(C) ((char)((C) | 0x80))
+
+class Display {
+public:
+ Display();
+ ~Display();
+
+ void setMode(DisplayMode mode);
+ void updateTextScreen();
+ void updateHiResScreen();
+ bool saveThumbnail(Common::WriteStream &out);
+
+ // Graphics
+ void loadFrameBuffer(Common::ReadStream &stream);
+ void putPixel(const Common::Point &p, byte color);
+ void clear(byte color);
+
+ // Text
+ void home();
+ void moveCursorTo(const Common::Point &pos);
+ void moveCursorForward();
+ void moveCursorBackward();
+ void printString(const Common::String &str);
+ void printAsciiString(const Common::String &str);
+ void setCharAtCursor(byte c);
+ void showCursor(bool enable);
+
+private:
+ void updateHiResSurface();
+ void showScanlines(bool enable);
+
+ void updateTextSurface();
+ void drawChar(byte c, int x, int y);
+ void createFont();
+ void scrollUp();
+
+ DisplayMode _mode;
+
+ byte *_frameBuf;
+ Graphics::Surface *_frameBufSurface;
+ bool _scanlines;
+ bool _monochrome;
+
+ byte *_textBuf;
+ Graphics::Surface *_textBufSurface;
+ Graphics::Surface *_font;
+ uint _cursorPos;
+ bool _showCursor;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/hires1.cpp b/engines/adl/hires1.cpp
new file mode 100644
index 0000000000..6e1e31df9f
--- /dev/null
+++ b/engines/adl/hires1.cpp
@@ -0,0 +1,396 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/debug.h"
+#include "common/error.h"
+#include "common/file.h"
+#include "common/stream.h"
+
+#include "adl/hires1.h"
+#include "adl/display.h"
+
+namespace Adl {
+
+void HiRes1Engine::runIntro() const {
+ Common::File file;
+
+ if (!file.open(IDS_HR1_EXE_0))
+ error("Failed to open file '" IDS_HR1_EXE_0 "'");
+
+ file.seek(IDI_HR1_OFS_LOGO_0);
+ _display->setMode(DISPLAY_MODE_HIRES);
+ _display->loadFrameBuffer(file);
+ _display->updateHiResScreen();
+ delay(4000);
+
+ if (shouldQuit())
+ return;
+
+ _display->setMode(DISPLAY_MODE_TEXT);
+
+ Common::File basic;
+ if (!basic.open(IDS_HR1_LOADER))
+ error("Failed to open file '" IDS_HR1_LOADER "'");
+
+ Common::String str;
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_0, '"');
+ _display->printAsciiString(str + '\r');
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_1, '"');
+ _display->printAsciiString(str + "\r\r");
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_2, '"');
+ _display->printAsciiString(str + "\r\r");
+
+ str = readStringAt(basic, IDI_HR1_OFS_PD_TEXT_3, '"');
+ _display->printAsciiString(str + '\r');
+
+ inputKey();
+ if (g_engine->shouldQuit())
+ return;
+
+ _display->setMode(DISPLAY_MODE_MIXED);
+
+ str = readStringAt(file, IDI_HR1_OFS_GAME_OR_HELP);
+
+ bool instructions = false;
+
+ while (1) {
+ _display->printString(str);
+ Common::String s = inputString();
+
+ if (g_engine->shouldQuit())
+ break;
+
+ if (s.empty())
+ continue;
+
+ if (s[0] == APPLECHAR('I')) {
+ instructions = true;
+ break;
+ } else if (s[0] == APPLECHAR('G')) {
+ break;
+ }
+ };
+
+ if (instructions) {
+ _display->setMode(DISPLAY_MODE_TEXT);
+ file.seek(IDI_HR1_OFS_INTRO_TEXT);
+
+ const uint pages[] = { 6, 6, 4, 5, 8, 7, 0 };
+
+ uint page = 0;
+ while (pages[page] != 0) {
+ _display->home();
+
+ uint count = pages[page++];
+ for (uint i = 0; i < count; ++i) {
+ str = readString(file);
+ _display->printString(str);
+ file.seek(3, SEEK_CUR);
+ }
+
+ inputString();
+
+ if (g_engine->shouldQuit())
+ return;
+
+ file.seek(6, SEEK_CUR);
+ }
+ }
+
+ _display->printAsciiString("\r");
+
+ file.close();
+
+ _display->setMode(DISPLAY_MODE_MIXED);
+
+ if (!file.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Title screen shown during loading
+ file.seek(IDI_HR1_OFS_LOGO_1);
+ _display->loadFrameBuffer(file);
+ _display->updateHiResScreen();
+ delay(2000);
+}
+
+void HiRes1Engine::loadData() {
+ Common::File f;
+
+ if (!f.open(IDS_HR1_MESSAGES))
+ error("Failed to open file '" IDS_HR1_MESSAGES "'");
+
+ for (uint i = 0; i < IDI_HR1_NUM_MESSAGES; ++i)
+ _messages.push_back(readString(f, APPLECHAR('\r')) + APPLECHAR('\r'));
+
+ f.close();
+
+ if (!f.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Some messages have overrides inside the executable
+ _messages[IDI_HR1_MSG_CANT_GO_THERE - 1] = readStringAt(f, IDI_HR1_OFS_STR_CANT_GO_THERE);
+ _messages[IDI_HR1_MSG_DONT_HAVE_IT - 1] = readStringAt(f, IDI_HR1_OFS_STR_DONT_HAVE_IT);
+ _messages[IDI_HR1_MSG_DONT_UNDERSTAND - 1] = readStringAt(f, IDI_HR1_OFS_STR_DONT_UNDERSTAND);
+ _messages[IDI_HR1_MSG_GETTING_DARK - 1] = readStringAt(f, IDI_HR1_OFS_STR_GETTING_DARK);
+
+ // Load other strings from executable
+ _strings.enterCommand = readStringAt(f, IDI_HR1_OFS_STR_ENTER_COMMAND);
+ _strings.dontHaveIt = readStringAt(f, IDI_HR1_OFS_STR_DONT_HAVE_IT);
+ _strings.gettingDark = readStringAt(f, IDI_HR1_OFS_STR_GETTING_DARK);
+ _strings.verbError = readStringAt(f, IDI_HR1_OFS_STR_VERB_ERROR);
+ _strings.nounError = readStringAt(f, IDI_HR1_OFS_STR_NOUN_ERROR);
+ _strings.playAgain = readStringAt(f, IDI_HR1_OFS_STR_PLAY_AGAIN);
+ _gameStrings.pressReturn = readStringAt(f, IDI_HR1_OFS_STR_PRESS_RETURN);
+
+ // Set message IDs
+ _messageIds.cantGoThere = IDI_HR1_MSG_CANT_GO_THERE;
+ _messageIds.dontUnderstand = IDI_HR1_MSG_DONT_UNDERSTAND;
+ _messageIds.itemDoesntMove = IDI_HR1_MSG_ITEM_DOESNT_MOVE;
+ _messageIds.itemNotHere = IDI_HR1_MSG_ITEM_NOT_HERE;
+ _messageIds.thanksForPlaying = IDI_HR1_MSG_THANKS_FOR_PLAYING;
+
+ // Load picture data from executable
+ f.seek(IDI_HR1_OFS_PICS);
+ for (uint i = 0; i < IDI_HR1_NUM_PICS; ++i) {
+ struct Picture pic;
+ pic.block = f.readByte();
+ pic.offset = f.readUint16LE();
+ _pictures.push_back(pic);
+ }
+
+ // Load commands from executable
+ f.seek(IDI_HR1_OFS_CMDS_1);
+ readCommands(f, _roomCommands);
+
+ f.seek(IDI_HR1_OFS_CMDS_0);
+ readCommands(f, _globalCommands);
+
+ // Load dropped item offsets
+ f.seek(IDI_HR1_OFS_ITEM_OFFSETS);
+ for (uint i = 0; i < IDI_HR1_NUM_ITEM_OFFSETS; ++i) {
+ Common::Point p;
+ p.x = f.readByte();
+ p.y = f.readByte();
+ _itemOffsets.push_back(p);
+ }
+
+ // Load right-angle line art
+ f.seek(IDI_HR1_OFS_LINE_ART);
+ uint16 lineArtTotal = f.readUint16LE();
+ for (uint i = 0; i < lineArtTotal; ++i) {
+ f.seek(IDI_HR1_OFS_LINE_ART + 2 + i * 2);
+ uint16 offset = f.readUint16LE();
+ f.seek(IDI_HR1_OFS_LINE_ART + offset);
+
+ Common::Array<byte> lineArt;
+ byte b = f.readByte();
+ while (b != 0) {
+ lineArt.push_back(b);
+ b = f.readByte();
+ }
+ _lineArt.push_back(lineArt);
+ }
+
+ if (f.eos() || f.err())
+ error("Failed to read game data from '" IDS_HR1_EXE_1 "'");
+
+ f.seek(IDI_HR1_OFS_VERBS);
+ loadWords(f, _verbs);
+
+ f.seek(IDI_HR1_OFS_NOUNS);
+ loadWords(f, _nouns);
+}
+
+void HiRes1Engine::initState() {
+ Common::File f;
+
+ _state.room = 1;
+ _state.moves = 0;
+ _state.isDark = false;
+
+ _state.vars.clear();
+ _state.vars.resize(IDI_HR1_NUM_VARS);
+
+ if (!f.open(IDS_HR1_EXE_1))
+ error("Failed to open file '" IDS_HR1_EXE_1 "'");
+
+ // Load room data from executable
+ _state.rooms.clear();
+ f.seek(IDI_HR1_OFS_ROOMS);
+ for (uint i = 0; i < IDI_HR1_NUM_ROOMS; ++i) {
+ Room room;
+ f.readByte();
+ room.description = f.readByte();
+ for (uint j = 0; j < 6; ++j)
+ room.connections[j] = f.readByte();
+ room.picture = f.readByte();
+ room.curPicture = f.readByte();
+ _state.rooms.push_back(room);
+ }
+
+ // Load item data from executable
+ _state.items.clear();
+ f.seek(IDI_HR1_OFS_ITEMS);
+ while (f.readByte() != 0xff) {
+ Item item;
+ item.noun = f.readByte();
+ item.room = f.readByte();
+ item.picture = f.readByte();
+ item.isLineArt = f.readByte();
+ item.position.x = f.readByte();
+ item.position.y = f.readByte();
+ item.state = f.readByte();
+ item.description = f.readByte();
+
+ f.readByte();
+
+ byte size = f.readByte();
+
+ for (uint i = 0; i < size; ++i)
+ item.roomPictures.push_back(f.readByte());
+
+ _state.items.push_back(item);
+ }
+}
+
+void HiRes1Engine::restartGame() {
+ initState();
+ _display->printString(_gameStrings.pressReturn);
+ inputString(); // Missing in the original
+ _display->printAsciiString("\r\r\r\r\r");
+}
+
+void HiRes1Engine::drawPic(byte pic, Common::Point pos) const {
+ Common::File f;
+ Common::String name = Common::String::format("BLOCK%i", _pictures[pic].block);
+
+ if (!f.open(name))
+ error("Failed to open file '%s'", name.c_str());
+
+ f.seek(_pictures[pic].offset);
+ drawPic(f, pos);
+}
+
+void HiRes1Engine::printMessage(uint idx, bool wait) const {
+ // Messages with hardcoded overrides don't delay after printing.
+ // It's unclear if this is a bug or not. In some cases the result
+ // is that these strings will scroll past the four-line text window
+ // before the user gets a chance to read them.
+ // NOTE: later games seem to wait for a key when the text window
+ // overflows and don't use delays. It might be better to use
+ // that system for this game as well.
+ switch (idx) {
+ case IDI_HR1_MSG_CANT_GO_THERE:
+ case IDI_HR1_MSG_DONT_HAVE_IT:
+ case IDI_HR1_MSG_DONT_UNDERSTAND:
+ case IDI_HR1_MSG_GETTING_DARK:
+ wait = false;
+ }
+
+ AdlEngine::printMessage(idx, wait);
+}
+
+void HiRes1Engine::drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const {
+ // This draws a four-connected line
+
+ int16 deltaX = p2.x - p1.x;
+ int8 xStep = 1;
+
+ if (deltaX < 0) {
+ deltaX = -deltaX;
+ xStep = -1;
+ }
+
+ int16 deltaY = p2.y - p1.y;
+ int8 yStep = -1;
+
+ if (deltaY > 0) {
+ deltaY = -deltaY;
+ yStep = 1;
+ }
+
+ Common::Point p(p1);
+ int16 steps = deltaX - deltaY + 1;
+ int16 err = deltaX + deltaY;
+
+ while (1) {
+ _display->putPixel(p, color);
+
+ if (--steps == 0)
+ return;
+
+ if (err < 0) {
+ p.y += yStep;
+ err += deltaX;
+ } else {
+ p.x += xStep;
+ err += deltaY;
+ }
+ }
+}
+
+void HiRes1Engine::drawPic(Common::ReadStream &stream, const Common::Point &pos) const {
+ byte x, y;
+ bool bNewLine = false;
+ byte oldX = 0, oldY = 0;
+ while (1) {
+ x = stream.readByte();
+ y = stream.readByte();
+
+ if (stream.err() || stream.eos())
+ error("Failed to read picture");
+
+ if (x == 0xff && y == 0xff)
+ return;
+
+ if (x == 0 && y == 0) {
+ bNewLine = true;
+ continue;
+ }
+
+ x += pos.x;
+ y += pos.y;
+
+ if (y > 160)
+ y = 160;
+
+ if (bNewLine) {
+ _display->putPixel(Common::Point(x, y), 0x7f);
+ bNewLine = false;
+ } else {
+ drawLine(Common::Point(oldX, oldY), Common::Point(x, y), 0x7f);
+ }
+
+ oldX = x;
+ oldY = y;
+ }
+}
+
+Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd) {
+ return new HiRes1Engine(syst, gd);
+}
+
+} // End of namespace Adl
diff --git a/engines/adl/hires1.h b/engines/adl/hires1.h
new file mode 100644
index 0000000000..d9d67c46e4
--- /dev/null
+++ b/engines/adl/hires1.h
@@ -0,0 +1,113 @@
+/* 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 ADL_HIRES1_H
+#define ADL_HIRES1_H
+
+#include "common/str.h"
+
+#include "adl/adl.h"
+
+namespace Common {
+class ReadStream;
+class Point;
+}
+
+namespace Adl {
+
+#define IDS_HR1_EXE_0 "AUTO LOAD OBJ"
+#define IDS_HR1_EXE_1 "ADVENTURE"
+#define IDS_HR1_LOADER "MYSTERY.HELLO"
+#define IDS_HR1_MESSAGES "MESSAGES"
+
+#define IDI_HR1_NUM_ROOMS 41
+#define IDI_HR1_NUM_PICS 98
+#define IDI_HR1_NUM_VARS 20
+#define IDI_HR1_NUM_ITEM_OFFSETS 21
+#define IDI_HR1_NUM_MESSAGES 167
+
+// Messages used outside of scripts
+#define IDI_HR1_MSG_CANT_GO_THERE 137
+#define IDI_HR1_MSG_DONT_UNDERSTAND 37
+#define IDI_HR1_MSG_ITEM_DOESNT_MOVE 151
+#define IDI_HR1_MSG_ITEM_NOT_HERE 152
+#define IDI_HR1_MSG_THANKS_FOR_PLAYING 140
+#define IDI_HR1_MSG_DONT_HAVE_IT 127
+#define IDI_HR1_MSG_GETTING_DARK 7
+
+#define IDI_HR1_OFS_STR_ENTER_COMMAND 0x5bbc
+#define IDI_HR1_OFS_STR_VERB_ERROR 0x5b4f
+#define IDI_HR1_OFS_STR_NOUN_ERROR 0x5b8e
+#define IDI_HR1_OFS_STR_PLAY_AGAIN 0x5f1e
+#define IDI_HR1_OFS_STR_CANT_GO_THERE 0x6c0a
+#define IDI_HR1_OFS_STR_DONT_HAVE_IT 0x6c31
+#define IDI_HR1_OFS_STR_DONT_UNDERSTAND 0x6c51
+#define IDI_HR1_OFS_STR_GETTING_DARK 0x6c7c
+#define IDI_HR1_OFS_STR_PRESS_RETURN 0x5f68
+
+#define IDI_HR1_OFS_PD_TEXT_0 0x005d
+#define IDI_HR1_OFS_PD_TEXT_1 0x012b
+#define IDI_HR1_OFS_PD_TEXT_2 0x016d
+#define IDI_HR1_OFS_PD_TEXT_3 0x0259
+
+#define IDI_HR1_OFS_INTRO_TEXT 0x0066
+#define IDI_HR1_OFS_GAME_OR_HELP 0x000f
+
+#define IDI_HR1_OFS_LOGO_0 0x1003
+#define IDI_HR1_OFS_LOGO_1 0x1800
+
+#define IDI_HR1_OFS_ITEMS 0x0100
+#define IDI_HR1_OFS_ROOMS 0x050a
+#define IDI_HR1_OFS_PICS 0x4b00
+#define IDI_HR1_OFS_CMDS_0 0x3c00
+#define IDI_HR1_OFS_CMDS_1 0x3d00
+
+#define IDI_HR1_OFS_ITEM_OFFSETS 0x68ff
+#define IDI_HR1_OFS_LINE_ART 0x4f00
+
+#define IDI_HR1_OFS_VERBS 0x3800
+#define IDI_HR1_OFS_NOUNS 0x0f00
+
+class HiRes1Engine : public AdlEngine {
+public:
+ HiRes1Engine(OSystem *syst, const AdlGameDescription *gd) : AdlEngine(syst, gd) { }
+
+private:
+ // AdlEngine
+ void runIntro() const;
+ void loadData();
+ void initState();
+ void restartGame();
+ void drawPic(byte pic, Common::Point pos) const;
+ void printMessage(uint idx, bool wait = true) const;
+
+ void drawLine(const Common::Point &p1, const Common::Point &p2, byte color) const;
+ void drawPic(Common::ReadStream &stream, const Common::Point &pos) const;
+
+ struct {
+ Common::String pressReturn;
+ } _gameStrings;
+};
+
+} // End of namespace Adl
+
+#endif
diff --git a/engines/adl/module.mk b/engines/adl/module.mk
new file mode 100644
index 0000000000..6acd06f6de
--- /dev/null
+++ b/engines/adl/module.mk
@@ -0,0 +1,18 @@
+MODULE := engines/adl
+
+MODULE_OBJS := \
+ adl.o \
+ detection.o \
+ display.o \
+ hires1.o
+
+MODULE_DIRS += \
+ engines/adl
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_ADL), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index 0cacce2421..110ba10632 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -885,6 +885,12 @@ void TextMgr::stringEdit(int16 stringMaxLen) {
_inputStringRow = _textPos.row;
_inputStringColumn = _textPos.column;
+ if (_inputCursorChar) {
+ // Cursor character is shown, which means we are one beyond the start of the input
+ // Adjust the column for predictive input dialog
+ _inputStringColumn--;
+ }
+
// Caller can set the input string
_inputStringCursorPos = 0;
while (_inputStringCursorPos < inputStringLen) {
diff --git a/engines/sherlock/tattoo/widget_inventory.cpp b/engines/sherlock/tattoo/widget_inventory.cpp
index 34331f0eae..9f126cf7a7 100644
--- a/engines/sherlock/tattoo/widget_inventory.cpp
+++ b/engines/sherlock/tattoo/widget_inventory.cpp
@@ -546,7 +546,7 @@ void WidgetInventory::drawBars() {
_surface.SHtransBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2));
}
- _surface.hLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
+ _surface.vLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
}
void WidgetInventory::drawInventory() {
diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h
index e164ea70cd..530b56c962 100644
--- a/engines/wage/detection_tables.h
+++ b/engines/wage/detection_tables.h
@@ -35,6 +35,9 @@ static const ADGameDescription gameDescriptions[] = {
FANGAME("3rd Floor", "913812a1ac7a6b0e48dadd1afa1c7763", 281409),
BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "94a9c4f8b3dabd1846d76215a49bd221", 1420723),
BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "26207bdf0bb539464f136f0669af885f", 1843104),
+ // No Next on the first screen?
+ FANGAME("Brownie's Dream", "94a9c4f8b3dabd1846d76215a49bd221", 440704),
+ FANGAMEN("Brownie's Time Travels", "Brownie's Time Travels v1.2", "94a9c4f8b3dabd1846d76215a49bd221", 471589),
FANGAME("Bug Hunt", "595117cbed33e8de1ab3714b33880205", 195699),
BIGGAME("cantitoe", "", "Camp Cantitoe", "913812a1ac7a6b0e48dadd1afa1c7763", 616985),
// Problems with letter rendering
@@ -51,14 +54,21 @@ static const ADGameDescription gameDescriptions[] = {
// Crash at start in GUI rendering
FANGAME("Dune Eternity", "94a9c4f8b3dabd1846d76215a49bd221", 290201), // Original file name is "***DUNE ETERNITY*** "
FANGAMEN("Dungeon World II", "DungeonWorld2", "0154ea11d3cbb536c13b4ae9e6902d48", 230199),
+ FANGAME("Edg's World", "913812a1ac7a6b0e48dadd1afa1c7763", 106769),
FANGAME("Eidisi I", "595117cbed33e8de1ab3714b33880205", 172552),
// Problems(?) with text on the first screen
FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "595117cbed33e8de1ab3714b33880205", 408913),
FANGAME("Escape from School!", "913812a1ac7a6b0e48dadd1afa1c7763", 50105),
+ FANGAME("Everyman 1", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 335705),
FANGAME("Exploration Zeta!", "c477921aeee6ed0f8997ba44447eb2d0", 366599),
// Crash in console rendering on the first scene
FANGAME("Fantasy Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 762754),
FANGAME("Find the Heart", "595117cbed33e8de1ab3714b33880205", 106235), // From Joshua's Worlds 1.0
+ FANGAMEN("Fortune Teller", "Fortune Teller 1.1", "e5df11bfec42dd12b675ad4d98479ef3", 73931),
+ // Cropped graphics on first scene
+ FANGAME("Intro to Gothic", "d81f2d03a1e863f04fb1e3a5495b720e", 208067),
+ // No Next button in intro
+ FANGAME("Jamie the Demon Slayer", "94a9c4f8b3dabd1846d76215a49bd221", 232789),
// Problems with window overlay
FANGAMEN("Jumble", "LSJUMBLE", "e12ec4d76d48bdc86567c5e63750547e", 647339), // Original file name is "LSJUMBLE† "
FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96711),
@@ -66,15 +76,25 @@ static const ADGameDescription gameDescriptions[] = {
FANGAME("Karth of the Jungle II", "c106835ab4436de054e03aec3ce904ce", 201053),
FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "94a9c4f8b3dabd1846d76215a49bd221", 628821),
FANGAME("Lost Crystal", "8174c81ea1858d0079ae040dae2cefd3", 771072),
+ // Crash in design drawing on startup
+ FANGAMEN("Lost In Kookyville", "Lost In Kookyville 1.2.4", "e6cea2234cee9d0dba7be10bc1ad6055", 721569),
FANGAME("Magic Rings", "913812a1ac7a6b0e48dadd1afa1c7763", 109044),
// No way to click on the house
FANGAME("Messy House", "913812a1ac7a6b0e48dadd1afa1c7763", 177120),
FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67952),
FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67966), // Alt version
FANGAME("Minitorian", "913812a1ac7a6b0e48dadd1afa1c7763", 586464),
+ FANGAME("M'Lord's Warrior", "7d30b6e68ecf197b2d15492630bdeb89", 465639), // Original file name is "M'Lord's Warrior †"
+ // Unhandled comparison case
+ FANGAME("Mountain of Mayhem", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 750003), // Original file name "Mountain of Mayhem †"
// No way to pass through the first screen
FANGAME("Nightcrawler Ned", "94a9c4f8b3dabd1846d76215a49bd221", 366542),
+ // Crash on startup
+ FANGAMEN("Parrot Talk", "PARROT TALK V1", "d81f2d03a1e863f04fb1e3a5495b720e", 118936),
+ // Crash on startup
+ FANGAMEN("Parrot Talk", "PARROT TALKV2", "d81f2d03a1e863f04fb1e3a5495b720e", 118884),
FANGAME("Pavilion", "4d991d7d1534d48d90598d86ea6d5d97", 231687),
+ FANGAMEN("Pencils", "Pencils.99", "913812a1ac7a6b0e48dadd1afa1c7763", 408551),
// Polygons with byte 1
FANGAME("Periapt", "913812a1ac7a6b0e48dadd1afa1c7763", 406006),
FANGAME("Puzzle Piece Search", "595117cbed33e8de1ab3714b33880205", 247693), // From Joshua's Worlds 1.0
@@ -88,6 +108,8 @@ static const ADGameDescription gameDescriptions[] = {
FANGAME("Radical Castle 1.0", "677bfee4afeca2f7152eb8b76c85ca8d", 347278),
BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "064b16d8c20724f8debbbdc3aafde538", 1408516),
BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "92cca777800c3d31a77b5ed7f6ee49ad", 1408516),
+ // Unhandled comparison case
+ FANGAME("Sands of Time", "913812a1ac7a6b0e48dadd1afa1c7763", 122672), // Original file name "Sands of Time†"
BIGGAME("scepters", "", "Scepters", "3311deef8bf82f0b4b1cfa15a3b3289d", 346595),
// ??? problems with dog bitmap?
FANGAMEN("Space Adventure", "SpaceAdventure", "f9f3f1c419f56955f7966355b34ea5c8", 155356),
@@ -97,9 +119,12 @@ static const ADGameDescription gameDescriptions[] = {
// Code 0x03 in text
FANGAME("Swamp Witch", "913812a1ac7a6b0e48dadd1afa1c7763", 739781), // Original file name "Swamp Witch†"
FANGAME("Sweetspace Now!", "e12ec4d76d48bdc86567c5e63750547e", 123813), // Comes with Jumble
+ // Wrong scrolling in the first console text
+ FANGAMEN("Sword of Siegfried", "Sword of Siegfried 1.0", "913812a1ac7a6b0e48dadd1afa1c7763", 234763),
FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64564),
FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64578), // Alt version
FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "913812a1ac7a6b0e48dadd1afa1c7763", 145023), // Original file name "The Ashland Revolution Demo†"
+ FANGAME("The Axe-orcist", "94a9c4f8b3dabd1846d76215a49bd221", 308764),
FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "595117cbed33e8de1ab3714b33880205", 231969),
// Invalid rect in scene "Access Tube 1"
FANGAMEN("The Phoenix v1.2", "The Phoenix", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 431640),
diff --git a/engines/wage/gui.cpp b/engines/wage/gui.cpp
index 9dd1a24b3c..15d82a68fb 100644
--- a/engines/wage/gui.cpp
+++ b/engines/wage/gui.cpp
@@ -50,6 +50,7 @@
#include "graphics/cursorman.h"
#include "graphics/fonts/bdf.h"
#include "graphics/palette.h"
+#include "graphics/primitives.h"
#include "wage/wage.h"
#include "wage/design.h"
@@ -310,7 +311,17 @@ const int arrowPixels[ARROW_H][ARROW_W] = {
{0,1,1,1,1,1,1,1,1,1,1,0},
{1,1,1,1,1,1,1,1,1,1,1,1}};
-void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart) {
+static void drawPixelInverted(int x, int y, int color, void *data) {
+ Graphics::Surface *surface = (Graphics::Surface *)data;
+
+ if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) {
+ byte *p = (byte *)surface->getBasePtr(x, y);
+
+ *p = *p == kColorWhite ? kColorBlack : kColorWhite;
+ }
+}
+
+void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart, float scrollPos, float scrollSize) {
bool active = false, scrollable = false, closeable = false, drawTitle = false;
const int size = kBorderWidth;
int x = r.left - size;
@@ -351,39 +362,28 @@ void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowTy
} else {
int x1 = x + width - 15;
int y1 = y + size + 1;
- int color1 = kColorBlack;
- int color2 = kColorWhite;
- if (highlightedPart == kBorderScrollUp) {
- SWAP(color1, color2);
- fillRect(g, x + width - kBorderWidth + 2, y + size, size - 4, r.height() / 2);
- }
+
for (int yy = 0; yy < ARROW_H; yy++) {
- for (int xx = 0; xx < ARROW_W; xx++) {
- if (arrowPixels[yy][xx] != 0) {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color1);
- } else {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color2);
- }
- }
+ for (int xx = 0; xx < ARROW_W; xx++)
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[yy][xx] != 0 ? kColorBlack : kColorWhite));
}
- fillRect(g, x + width - 13, y + size + ARROW_H, 8, r.height() / 2 - ARROW_H, color1);
- color1 = kColorBlack;
- color2 = kColorWhite;
- if (highlightedPart == kBorderScrollDown) {
- SWAP(color1, color2);
- fillRect(g, x + width - kBorderWidth + 2, y + size + r.height() / 2, size - 4, r.height() / 2);
- }
- fillRect(g, x + width - 13, y + size + r.height() / 2, 8, r.height() / 2 - ARROW_H, color1);
+ fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2);
+
y1 += height - 2 * size - ARROW_H - 2;
for (int yy = 0; yy < ARROW_H; yy++) {
- for (int xx = 0; xx < ARROW_W; xx++) {
- if (arrowPixels[ARROW_H - yy - 1][xx] != 0) {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color1);
- } else {
- g->hLine(x1 + xx, y1 + yy, x1 + xx, color2);
- }
- }
+ for (int xx = 0; xx < ARROW_W; xx++)
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[ARROW_H - yy - 1][xx] != 0 ? kColorBlack : kColorWhite));
+ }
+
+ if (highlightedPart == kBorderScrollUp || highlightedPart == kBorderScrollDown) {
+ int rx1 = x + width - kBorderWidth + 2;
+ int ry1 = y + size + r.height() * scrollPos;
+ int rx2 = rx1 + size - 4;
+ int ry2 = ry1 + r.height() * scrollSize;
+ Common::Rect rr(rx1, ry1, rx2, ry2);
+
+ Graphics::drawFilledRect(rr, kColorBlack, drawPixelInverted, g);
}
}
if (closeable) {
@@ -619,7 +619,11 @@ void Gui::mouseDown(int x, int y) {
} else if (_consoleTextArea.contains(x, y)) {
startMarking(x, y);
} else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) {
- paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick);
+ int textFullSize = _lines.size() * _consoleLineHeight + _consoleTextArea.height();
+ float scrollPos = (float)_scrollPos / textFullSize;
+ float scrollSize = (float)_consoleTextArea.height() / textFullSize;
+
+ paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick, scrollPos, scrollSize);
}
}
diff --git a/engines/wage/gui.h b/engines/wage/gui.h
index 73814d39b4..c136163951 100644
--- a/engines/wage/gui.h
+++ b/engines/wage/gui.h
@@ -123,7 +123,8 @@ public:
private:
void undrawCursor();
void drawDesktop();
- void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone);
+ void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone,
+ float scrollPos = 0.0, float scrollSize = 0.0);
void renderConsole(Graphics::Surface *g, Common::Rect &r);
void drawBox(Graphics::Surface *g, int x, int y, int w, int h);
void fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color = kColorBlack);
diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp
index 39462f7a15..5a694e7ce2 100644
--- a/engines/wintermute/base/base_persistence_manager.cpp
+++ b/engines/wintermute/base/base_persistence_manager.cpp
@@ -183,7 +183,7 @@ void BasePersistenceManager::getSaveStateDesc(int slot, SaveStateDescriptor &des
}
}
- desc.setSaveDate(_savedTimestamp.tm_year, _savedTimestamp.tm_mon, _savedTimestamp.tm_mday);
+ desc.setSaveDate(_savedTimestamp.tm_year + 1900, _savedTimestamp.tm_mon + 1, _savedTimestamp.tm_mday);
desc.setSaveTime(_savedTimestamp.tm_hour, _savedTimestamp.tm_min);
desc.setPlayTime(0);
}
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 1b95a11e03..c2deb8c61e 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -164,7 +164,7 @@ struct DrawDataInfo {
DrawData id; ///< The actual ID of the DrawData item.
const char *name; ///< The name of the DrawData item as it appears in the Theme Description files
bool buffer; ///< Sets whether this item is buffered on the backbuffer or drawn directly to the screen.
- DrawData parent; ///< Parent DrawData item, for items that overlay. E.g. kButtonIdle -> kButtonHover
+ DrawData parent; ///< Parent DrawData item, for items that overlay. E.g. kDDButtonIdle -> kDDButtonHover
};
/**