code:
/* MegaZeux
 *
 * Copyright (C) 2008 Joel Bouchard Lamontagne <logicow@gmail.com>
 * Copyright (C) 2006-2007 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2009 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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 <stdlib.h>
#include <string.h>
#include <assert.h>

#include "platform.h"
#include "render.h"
#include "renderers.h"
#include "util.h"
#include "data.h"

#ifdef CONFIG_SDL
#include "render_sdl.h"
#endif

#ifdef CONFIG_EGL
#include <GLES2/gl2.h>
#include "render_egl.h"
typedef GLenum GLiftype;
#else
typedef GLint GLiftype;
#endif

#include "render_gl.h"

enum
{
  ATTRIB_POSITION,
  ATTRIB_TEXCOORD,
  ATTRIB_COLOR,
};

static struct
{
  void (GL_APIENTRY *glAttachShader)(GLuint program, GLuint shader);
  void (GL_APIENTRY *glBindAttribLocation)(GLuint program, GLuint index,
   const char *name);
  void (GL_APIENTRY *glBindTexture)(GLenum target, GLuint texture);
  void (GL_APIENTRY *glBlendFunc)(GLenum sfactor, GLenum dfactor);
  void (GL_APIENTRY *glClear)(GLbitfield mask);
  void (GL_APIENTRY *glCompileShader)(GLuint shader);
  void (GL_APIENTRY *glCopyTexImage2D)(GLenum target, GLint level,
   GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height,
   GLint border);
  GLenum (GL_APIENTRY *glCreateProgram)(void);
  GLenum (GL_APIENTRY *glCreateShader)(GLenum type);
  void (GL_APIENTRY *glDisable)(GLenum cap);
  void (GL_APIENTRY *glDisableVertexAttribArray)(GLuint index);
  void (GL_APIENTRY *glDrawArrays)(GLenum mode, GLint first, GLsizei count);
  void (GL_APIENTRY *glEnable)(GLenum cap);
  void (GL_APIENTRY *glEnableVertexAttribArray)(GLuint index);
  void (GL_APIENTRY *glGenTextures)(GLsizei n, GLuint *textures);
  GLenum (GL_APIENTRY *glGetError)(void);
  void (GL_APIENTRY *glGetProgramInfoLog)(GLuint program, GLsizei bufsize,
   GLsizei *len, char *infolog);
  void (GL_APIENTRY *glGetShaderInfoLog)(GLuint shader, GLsizei bufsize,
   GLsizei *len, char *infolog);
  const GLubyte* (GL_APIENTRY *glGetString)(GLenum name);
  void (GL_APIENTRY *glLinkProgram)(GLuint program);
  void (GL_APIENTRY *glShaderSource)(GLuint shader, GLsizei count,
   const char **strings, const GLint *length);
  void (GL_APIENTRY *glTexImage2D)(GLenum target, GLint level,
   GLiftype internalformat, GLsizei width, GLsizei height, GLint border,
   GLenum format, GLenum type, const GLvoid *pixels);
  void (GL_APIENTRY *glTexParameterf)(GLenum target, GLenum pname,
   GLfloat param);
  void (GL_APIENTRY *glTexSubImage2D)(GLenum target, GLint level,
   GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
   GLenum format, GLenum type, const void *pixels);
  void (GL_APIENTRY *glUseProgram)(GLuint program);
  void (GL_APIENTRY *glVertexAttribPointer)(GLuint index, GLint size,
   GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
  void (GL_APIENTRY *glViewport)(GLint x, GLint y, GLsizei width,
   GLsizei height);
}
glsl;

static const struct dso_syms_map glsl_syms_map[] =
{
  { "glAttachShader",             (fn_ptr *)&glsl.glAttachShader },
  { "glBindAttribLocation",       (fn_ptr *)&glsl.glBindAttribLocation },
  { "glBindTexture",              (fn_ptr *)&glsl.glBindTexture },
  { "glBlendFunc",                (fn_ptr *)&glsl.glBlendFunc },
  { "glClear",                    (fn_ptr *)&glsl.glClear },
  { "glCompileShader",            (fn_ptr *)&glsl.glCompileShader },
  { "glCopyTexImage2D",           (fn_ptr *)&glsl.glCopyTexImage2D },
  { "glCreateProgram",            (fn_ptr *)&glsl.glCreateProgram },
  { "glCreateShader",             (fn_ptr *)&glsl.glCreateShader },
  { "glDisable",                  (fn_ptr *)&glsl.glDisable },
  { "glDisableVertexAttribArray", (fn_ptr *)&glsl.glDisableVertexAttribArray },
  { "glDrawArrays",               (fn_ptr *)&glsl.glDrawArrays },
  { "glEnable",                   (fn_ptr *)&glsl.glEnable },
  { "glEnableVertexAttribArray",  (fn_ptr *)&glsl.glEnableVertexAttribArray },
  { "glGenTextures",              (fn_ptr *)&glsl.glGenTextures },
  { "glGetError",                 (fn_ptr *)&glsl.glGetError },
  { "glGetProgramInfoLog",        (fn_ptr *)&glsl.glGetProgramInfoLog },
  { "glGetShaderInfoLog",         (fn_ptr *)&glsl.glGetShaderInfoLog },
  { "glGetString",                (fn_ptr *)&glsl.glGetString },
  { "glLinkProgram",              (fn_ptr *)&glsl.glLinkProgram },
  { "glShaderSource",             (fn_ptr *)&glsl.glShaderSource },
  { "glTexImage2D",               (fn_ptr *)&glsl.glTexImage2D },
  { "glTexParameterf",            (fn_ptr *)&glsl.glTexParameterf },
  { "glTexSubImage2D",            (fn_ptr *)&glsl.glTexSubImage2D },
  { "glUseProgram",               (fn_ptr *)&glsl.glUseProgram },
  { "glVertexAttribPointer",      (fn_ptr *)&glsl.glVertexAttribPointer },
  { "glViewport",                 (fn_ptr *)&glsl.glViewport },
  { NULL, NULL}
};

#define gl_check_error() gl_error(__FILE__, __LINE__, glsl.glGetError)

struct glsl_render_data
{
#ifdef CONFIG_EGL
  struct egl_render_data egl;
#endif
  Uint32 *pixels;
  Uint32 charset_texture[CHAR_H * CHARSET_SIZE * CHAR_W * 2];
  Uint32 background_texture[SCREEN_W * SCREEN_H];
  GLuint texture_number[3];
  GLubyte palette[3 * SMZX_PAL_SIZE];
  Uint8 remap_texture;
  Uint8 remap_char[CHARSET_SIZE * 2];
  enum ratio_type ratio;
  GLuint scaler_program;
  GLuint tilemap_program;
  GLuint tilemap_smzx12_program;
  GLuint tilemap_smzx3_program;
  GLuint mouse_program;
  GLuint cursor_program;
};

static const char *source_cache[SHADERS_CURSOR_FRAG - SHADERS_SCALER_VERT + 1];

#define INFO_MAX 1000

static void glsl_verify_compile(struct glsl_render_data *render_data,
 GLenum shader)
{
  char buffer[INFO_MAX];
  int len = 0;

  glsl.glGetShaderInfoLog(shader, INFO_MAX - 1, &len, buffer);
  buffer[len] = 0;

  if(len > 0)
    warn("%s", buffer);
}

static void glsl_verify_link(struct glsl_render_data *render_data,
 GLenum program)
{
  char buffer[INFO_MAX];
  int len = 0;

  glsl.glGetProgramInfoLog(program, INFO_MAX - 1, &len, buffer);
  buffer[len] = 0;

  if(len > 0)
    warn("%s", buffer);
}

static char *glsl_load_string(const char *filename)
{
  char *buffer = NULL;
  unsigned long size;
  FILE *f;

  f = fopen_unsafe(filename, "rb");
  if(!f)
    goto err_out;

  size = ftell_and_rewind(f);
  if(!size)
    goto err_close;

  buffer = cmalloc(size + 1);
  buffer[size] = '\0';

  if(fread(buffer, size, 1, f) != 1)
  {
    free(buffer);
    buffer = NULL;
  }

err_close:
  fclose(f);
err_out:
  return buffer;
}

static GLenum glsl_load_shader(struct graphics_data *graphics,
 enum resource_id res, GLenum type)
{
  struct glsl_render_data *render_data = graphics->render_data;
  int index = res - SHADERS_SCALER_VERT;
  GLenum shader;

  assert(res >= SHADERS_SCALER_VERT && res <= SHADERS_CURSOR_FRAG);

  // If we've already seen this shader, it's loaded, and we don't
  // need to do any more file I/O.

  if(!source_cache[index])
  {
    source_cache[index] = glsl_load_string(mzx_res_get_by_id(res));
    if(!source_cache[index])
    {
      warn("Failed to load shader '%s'\n", mzx_res_get_by_id(res));
      return 0;
    }
  }

  shader = glsl.glCreateShader(type);

#ifdef CONFIG_EGL
  {
    const GLchar *sources[2];
    GLint lengths[2];

    sources[0] = "precision mediump float;";
    sources[1] = source_cache[index];

    lengths[0] = (GLint)strlen(sources[0]);
    lengths[1] = (GLint)strlen(source_cache[index]);

    glsl.glShaderSource(shader, 2, sources, lengths);
  }
#else
  {
    GLint length = (GLint)strlen(source_cache[index]);
    glsl.glShaderSource(shader, 1, &source_cache[index], &length);
  }
#endif

  glsl.glCompileShader(shader);
  glsl_verify_compile(render_data, shader);

  return shader;
}

static GLuint glsl_load_program(struct graphics_data *graphics,
 enum resource_id v_res, enum resource_id f_res)
{
  GLenum vertex, fragment;
  GLuint program = 0;
  char *path;

  path = cmalloc(MAX_PATH);

  vertex = glsl_load_shader(graphics, v_res, GL_VERTEX_SHADER);
  if(!vertex)
    goto err_free_path;

  fragment = glsl_load_shader(graphics, f_res, GL_FRAGMENT_SHADER);
  if(!fragment)
    goto err_free_path;

  program = glsl.glCreateProgram();

  glsl.glAttachShader(program, vertex);
  glsl.glAttachShader(program, fragment);

err_free_path:
  free(path);
  return program;
}

static void glsl_load_shaders(struct graphics_data *graphics)
{
  struct glsl_render_data *render_data = graphics->render_data;

  render_data->scaler_program = glsl_load_program(graphics,
   SHADERS_SCALER_VERT, SHADERS_SCALER_FRAG);
  if(render_data->scaler_program)
  {
    glsl.glBindAttribLocation(render_data->scaler_program,
     ATTRIB_POSITION, "Position");
    glsl.glBindAttribLocation(render_data->scaler_program,
     ATTRIB_TEXCOORD, "Texcoord");
    glsl.glLinkProgram(render_data->scaler_program);
    glsl_verify_link(render_data, render_data->scaler_program);
  }

  render_data->tilemap_program = glsl_load_program(graphics,
   SHADERS_TILEMAP_VERT, SHADERS_TILEMAP_FRAG);
  if(render_data->tilemap_program)
  {
    glsl.glBindAttribLocation(render_data->tilemap_program,
     ATTRIB_POSITION, "Position");
    glsl.glBindAttribLocation(render_data->tilemap_program,
     ATTRIB_TEXCOORD, "Texcoord");
    glsl.glLinkProgram(render_data->tilemap_program);
    glsl_verify_link(render_data, render_data->tilemap_program);
  }

  render_data->tilemap_smzx12_program = glsl_load_program(graphics,
   SHADERS_TILEMAP_VERT, SHADERS_TILEMAP_SMZX12_FRAG);
  if(render_data->tilemap_smzx12_program)
  {
    glsl.glBindAttribLocation(render_data->tilemap_smzx12_program,
     ATTRIB_POSITION, "Position");
    glsl.glBindAttribLocation(render_data->tilemap_smzx12_program,
     ATTRIB_TEXCOORD, "Texcoord");
    glsl.glLinkProgram(render_data->tilemap_smzx12_program);
    glsl_verify_link(render_data, render_data->tilemap_smzx12_program);
  }

  render_data->tilemap_smzx3_program = glsl_load_program(graphics,
   SHADERS_TILEMAP_VERT, SHADERS_TILEMAP_SMZX3_FRAG);
  if(render_data->tilemap_smzx3_program)
  {
    glsl.glBindAttribLocation(render_data->tilemap_smzx3_program,
     ATTRIB_POSITION, "Position");
    glsl.glBindAttribLocation(render_data->tilemap_smzx3_program,
     ATTRIB_TEXCOORD, "Texcoord");
    glsl.glLinkProgram(render_data->tilemap_smzx3_program);
    glsl_verify_link(render_data, render_data->tilemap_smzx3_program);
  }

  render_data->mouse_program = glsl_load_program(graphics,
   SHADERS_MOUSE_VERT, SHADERS_MOUSE_FRAG);
  if(render_data->mouse_program)
  {
    glsl.glBindAttribLocation(render_data->mouse_program,
     ATTRIB_POSITION, "Position");
    glsl.glLinkProgram(render_data->mouse_program);
    glsl_verify_link(render_data, render_data->mouse_program);
  }

  render_data->cursor_program  = glsl_load_program(graphics,
   SHADERS_CURSOR_VERT, SHADERS_CURSOR_FRAG);
  if(render_data->cursor_program)
  {
    glsl.glBindAttribLocation(render_data->cursor_program,
     ATTRIB_POSITION, "Position");
    glsl.glBindAttribLocation(render_data->cursor_program,
     ATTRIB_COLOR, "Color");
    glsl.glLinkProgram(render_data->cursor_program);
    glsl_verify_link(render_data, render_data->cursor_program);
  }
}

static bool glsl_init_video(struct graphics_data *graphics,
 struct config_info *conf)
{
  struct glsl_render_data *render_data;

  render_data = cmalloc(sizeof(struct glsl_render_data));
  if(!render_data)
    return false;

  if(!GL_LoadLibrary(GL_LIB_PROGRAMMABLE))
    goto err_free;

  graphics->render_data = render_data;

  graphics->gl_vsync = conf->gl_vsync;
  graphics->allow_resize = conf->allow_resize;
  graphics->gl_filter_method = conf->gl_filter_method;
  graphics->bits_per_pixel = 32;

  // OpenGL only supports 16/32bit colour
  if(conf->force_bpp == 16 || conf->force_bpp == 32)
    graphics->bits_per_pixel = conf->force_bpp;

  render_data->pixels = cmalloc(sizeof(Uint32) * GL_POWER_2_WIDTH *
   GL_POWER_2_HEIGHT);
  if(!render_data->pixels)
    goto err_free;

  render_data->ratio = conf->video_ratio;
  if(!set_video_mode())
    goto err_free;

  return true;

err_free:
  free(render_data);
  return false;
}

static void glsl_free_video(struct graphics_data *graphics)
{
  int i;

  for(i = 0; i < SHADERS_CURSOR_FRAG - SHADERS_SCALER_VERT + 1; i++)
    if(source_cache[i])
      free((void *)source_cache[i]);

  free(graphics->render_data);
  graphics->render_data = NULL;
}

static void glsl_remap_charsets(struct graphics_data *graphics)
{
  struct glsl_render_data *render_data = graphics->render_data;
  render_data->remap_texture = true;
}

// FIXME: Many magic numbers
static void glsl_resize_screen(struct graphics_data *graphics,
 int width, int height)
{
  struct glsl_render_data *render_data = graphics->render_data;

  glsl.glViewport(0, 0, width, height);
  gl_check_error();

  glsl.glGenTextures(3, render_data->texture_number);
  gl_check_error();

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[0]);
  gl_check_error();

  gl_set_filter_method(CONFIG_GL_FILTER_LINEAR, glsl.glTexParameterf);

  glsl.glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);

  memset(render_data->pixels, 255,
   sizeof(Uint32) * GL_POWER_2_WIDTH * GL_POWER_2_HEIGHT);

  glsl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GL_POWER_2_WIDTH,
   GL_POWER_2_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
   render_data->pixels);
  gl_check_error();

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
  gl_check_error();

  gl_set_filter_method(CONFIG_GL_FILTER_NEAREST, glsl.glTexParameterf);

  glsl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA,
   GL_UNSIGNED_BYTE, render_data->pixels);
  gl_check_error();

  glsl_remap_charsets(graphics);

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[2]);
  gl_check_error();

  gl_set_filter_method(CONFIG_GL_FILTER_NEAREST, glsl.glTexParameterf);

  glsl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 32, 0, GL_RGBA,
   GL_UNSIGNED_BYTE, render_data->pixels);
  gl_check_error();

  glsl_load_shaders(graphics);
}

static bool glsl_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  gl_set_attributes(graphics);

  if(!gl_set_video_mode(graphics, width, height, depth, fullscreen, resize))
    return false;

  if(!gl_load_syms(glsl_syms_map))
    return false;

  // We need a specific version of OpenGL; desktop GL must be 2.0.
  // All OpenGL ES 2.0 implementations are supported, so don't do
  // the check with EGL configurations (EGL implies OpenGL ES).
#ifndef CONFIG_EGL
  {
    static bool initialized = false;

    if(!initialized)
    {
      const char *version;
      double version_float;

      version = (const char *)glsl.glGetString(GL_VERSION);
      if(!version)
        return false;

      version_float = atof(version);
      if(version_float < 2.0)
      {
        warn("Need >= OpenGL 2.0, got OpenGL %.1f.\n", version_float);
        return false;
      }

      initialized = true;
    }
  }
#endif

  glsl_resize_screen(graphics, width, height);
  return true;
}

static void glsl_remap_char(struct graphics_data *graphics, Uint16 chr)
{
  struct glsl_render_data *render_data = graphics->render_data;
  render_data->remap_char[chr] = true;
}

static void glsl_remap_charbyte(struct graphics_data *graphics,
 Uint16 chr, Uint8 byte)
{
  glsl_remap_char(graphics, chr);
}

static int *glsl_char_bitmask_to_texture(signed char *c, int *p)
{
  // This tests the 7th bit, if 0, result is 0.
  // If 1, result is 255 (because of sign extended bit shift!).
  // Note the use of char constants to force 8bit calculation.
  *(p++) = *c << 24 >> 31;
  *(p++) = *c << 25 >> 31;
  *(p++) = *c << 26 >> 31;
  *(p++) = *c << 27 >> 31;
  *(p++) = *c << 28 >> 31;
  *(p++) = *c << 29 >> 31;
  *(p++) = *c << 30 >> 31;
  *(p++) = *c << 31 >> 31;
  return p;
}

static inline void glsl_do_remap_charsets(struct graphics_data *graphics)
{
  struct glsl_render_data *render_data = graphics->render_data;
  signed char *c = (signed char *)graphics->charset;
  int *p = (int *)render_data->charset_texture;
  unsigned int i, j, k;

  for(i = 0; i < 16; i++, c += -14 + 32 * 14)
    for(j = 0; j < 14; j++, c += -32 * 14 + 1)
      for(k = 0; k < 32; k++, c += 14)
        p = glsl_char_bitmask_to_texture(c, p);

  glsl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 32 * 8, 16 * 14, GL_RGBA,
    GL_UNSIGNED_BYTE, render_data->charset_texture);
  gl_check_error();
}

static inline void glsl_do_remap_char(struct graphics_data *graphics,
 Uint16 chr)
{
  struct glsl_render_data *render_data = graphics->render_data;
  signed char *c = (signed char *)graphics->charset;
  int *p = (int *)render_data->charset_texture;
  unsigned int i;

  c += chr * 14;

  for(i = 0; i < 14; i++, c++)
    p = glsl_char_bitmask_to_texture(c, p);

  glsl.glTexSubImage2D(GL_TEXTURE_2D, 0, chr % 32 * 8, chr / 32 * 14, 8, 14,
   GL_RGBA, GL_UNSIGNED_BYTE, render_data->charset_texture);
  gl_check_error();
}

static void glsl_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  struct glsl_render_data *render_data = graphics->render_data;
  Uint32 i;
  for(i = 0; i < count; i++)
  {
#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
    graphics->flat_intensity_palette[i] = (palette[i].r << 24) |
     (palette[i].g << 16) | (palette[i].b << 8);
#else
    graphics->flat_intensity_palette[i] = (palette[i].b << 16) |
     (palette[i].g << 8) | palette[i].r;
#endif
    render_data->palette[i*3  ] = (GLubyte)palette[i].r;
    render_data->palette[i*3+1] = (GLubyte)palette[i].g;
    render_data->palette[i*3+2] = (GLubyte)palette[i].b;
  }
}

static void glsl_render_graph(struct graphics_data *graphics)
{
  struct glsl_render_data *render_data = graphics->render_data;
  struct char_element *src = graphics->text_video;
  Uint32 *colorptr, *dest, i;
  int width, height;

  static const float tex_coord_array_single[2 * 4] = {
    0.0f,          0.0f,
    0.0f,          25.0f,
    25.0f / 80.0f, 0.0f,
    25.0f / 80.0f, 25.0f,
  };

  get_context_width_height(graphics, &width, &height);
  if(width < 640 || height < 350)
  {
    if(width >= 1024)
      width = 1024;
    if(height >= 512)
      height = 512;
  }
  else
  {
    width = 640;
    height = 350;
  }

  glsl.glViewport(0, 0, width, height);
  gl_check_error();

  if(graphics->screen_mode == 0)
    glsl.glUseProgram(render_data->tilemap_program);
  else if(graphics->screen_mode == 1 || graphics->screen_mode == 2)
    glsl.glUseProgram(render_data->tilemap_smzx12_program);
  else
    glsl.glUseProgram(render_data->tilemap_smzx3_program);
  gl_check_error();

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
  gl_check_error();

  if(render_data->remap_texture)
  {
    glsl_do_remap_charsets(graphics);
    render_data->remap_texture = false;
    memset(render_data->remap_char, false, sizeof(Uint8) * CHARSET_SIZE * 2);
  }
  else
  {
    for(i = 0; i < CHARSET_SIZE * 2; i++)
    {
      if(render_data->remap_char[i])
      {
        glsl_do_remap_char(graphics, i);
        render_data->remap_char[i] = false;
      }
    }
  }

  dest = render_data->background_texture;

  for(i = 0; i < SCREEN_W * SCREEN_H; i++, dest++, src++)
    *dest = (src->char_value<<16) + (src->bg_color<<8) + src->fg_color;

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
  gl_check_error();

  glsl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 225, SCREEN_W, SCREEN_H, GL_RGBA,
   GL_UNSIGNED_BYTE, render_data->background_texture);
  gl_check_error();

  colorptr = graphics->flat_intensity_palette;
  dest = render_data->background_texture;

  for(i = 0; i < SMZX_PAL_SIZE; i++, dest++, colorptr++)
    *dest = *colorptr;

  glsl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 224, SMZX_PAL_SIZE, 1, GL_RGBA,
   GL_UNSIGNED_BYTE, render_data->background_texture);
  gl_check_error();

  glsl.glEnableVertexAttribArray(ATTRIB_POSITION);
  glsl.glEnableVertexAttribArray(ATTRIB_TEXCOORD);

  glsl.glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0,
   vertex_array_single);
  gl_check_error();

  glsl.glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0,
   tex_coord_array_single);
  gl_check_error();

  glsl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  glsl.glDisableVertexAttribArray(ATTRIB_POSITION);
  glsl.glDisableVertexAttribArray(ATTRIB_TEXCOORD);
}

static void glsl_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct glsl_render_data *render_data = graphics->render_data;
  GLubyte *pal_base = &render_data->palette[color * 3];

  const float vertex_array[2 * 4] = {
    (x * 8)*2.0f/640.0f-1.0f,     (y * 14 + offset)*-2.0f/350.0f+1.0f,
    (x * 8)*2.0f/640.0f-1.0f,     (y * 14 + lines + offset)*-2.0f/350.0f+1.0f,
    (x * 8 + 8)*2.0f/640.0f-1.0f, (y * 14 + offset)*-2.0f/350.0f+1.0f,
    (x * 8 + 8)*2.0f/640.0f-1.0f, (y * 14 + lines + offset)*-2.0f/350.0f+1.0f
  };

  const GLubyte color_array[3 * 4] = {
    pal_base[0], pal_base[1], pal_base[2],
    pal_base[0], pal_base[1], pal_base[2],
    pal_base[0], pal_base[1], pal_base[2],
    pal_base[0], pal_base[1], pal_base[2],
  };

  glsl.glDisable(GL_TEXTURE_2D);

  glsl.glUseProgram(render_data->cursor_program);
  gl_check_error();

  glsl.glEnableVertexAttribArray(ATTRIB_POSITION);
  glsl.glEnableVertexAttribArray(ATTRIB_COLOR);

  glsl.glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0,
   vertex_array);
  gl_check_error();

  glsl.glVertexAttribPointer(ATTRIB_COLOR, 3, GL_UNSIGNED_BYTE, GL_FALSE, 0,
   color_array);
  gl_check_error();

  glsl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  glsl.glDisableVertexAttribArray(ATTRIB_POSITION);
  glsl.glDisableVertexAttribArray(ATTRIB_COLOR);
}

static void glsl_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  struct glsl_render_data *render_data = graphics->render_data;

  const float vertex_array[2 * 4] = {
     x*2.0f/640.0f-1.0f,       y*-2.0f/350.0f+1.0f,
     x*2.0f/640.0f-1.0f,      (y + h)*-2.0f/350.0f+1.0f,
    (x + w)*2.0f/640.0f-1.0f,  y*-2.0f/350.0f+1.0f,
    (x + w)*2.0f/640.0f-1.0f, (y + h)*-2.0f/350.0f+1.0f
  };

  glsl.glEnable(GL_BLEND);

  glsl.glUseProgram(render_data->mouse_program);
  gl_check_error();

  glsl.glEnableVertexAttribArray(ATTRIB_POSITION);

  glsl.glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0,
   vertex_array);
  gl_check_error();

  glsl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  glsl.glDisableVertexAttribArray(ATTRIB_POSITION);

  glsl.glDisable(GL_BLEND);
}

static void glsl_sync_screen(struct graphics_data *graphics)
{
  struct glsl_render_data *render_data = graphics->render_data;
  int v_width, v_height, width, height;

  get_context_width_height(graphics, &width, &height);
  fix_viewport_ratio(width, height, &v_width, &v_height, render_data->ratio);
  glsl.glViewport((width - v_width) >> 1, (height - v_height) >> 1,
   v_width, v_height);

  if(width < 640 || height < 350)
  {
    width = (width < 1024) ? width : 1024;
    height = (height < 512) ? height : 512;
  }
  else
  {
    width = 640;
    height = 350;
  }

  glsl.glUseProgram(render_data->scaler_program);
  gl_check_error();

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[0]);
  gl_check_error();

  glsl.glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1024, 512, 0);
  gl_check_error();

  glsl.glClear(GL_COLOR_BUFFER_BIT);
  gl_check_error();

  glsl.glEnableVertexAttribArray(ATTRIB_POSITION);
  glsl.glEnableVertexAttribArray(ATTRIB_TEXCOORD);

  glsl.glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0,
   vertex_array_single);
  gl_check_error();

  {
    const float tex_coord_array_single[2 * 4] = {
       0.0f,            height / 512.0f,
       0.0f,            0.0f,
       width / 1024.0f, height / 512.0f,
       width / 1024.0f, 0.0f,
    };

    glsl.glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0,
     tex_coord_array_single);
    gl_check_error();
  }

  glsl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  gl_check_error();

  glsl.glDisableVertexAttribArray(ATTRIB_POSITION);
  glsl.glDisableVertexAttribArray(ATTRIB_TEXCOORD);

  glsl.glBindTexture(GL_TEXTURE_2D, render_data->texture_number[1]);
  gl_check_error();

  gl_swap_buffers(graphics);

  glsl.glClear(GL_COLOR_BUFFER_BIT);
  gl_check_error();
}

void render_glsl_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = glsl_init_video;
  renderer->free_video = glsl_free_video;
  renderer->check_video_mode = gl_check_video_mode;
  renderer->set_video_mode = glsl_set_video_mode;
  renderer->update_colors = glsl_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->remap_charsets = glsl_remap_charsets;
  renderer->remap_char = glsl_remap_char;
  renderer->remap_charbyte = glsl_remap_charbyte;
  renderer->get_screen_coords = get_screen_coords_scaled;
  renderer->set_screen_coords = set_screen_coords_scaled;
  renderer->render_graph = glsl_render_graph;
  renderer->render_cursor = glsl_render_cursor;
  renderer->render_mouse = glsl_render_mouse;
  renderer->sync_screen = glsl_sync_screen;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * 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 "platform.h"
#include "graphics.h"
#include "render.h"
#include "render_sdl.h"
#include "renderers.h"

#include <stdlib.h>
#include <string.h>

#include "SDL.h"

struct gp2x_render_data
{
  SDL_Surface *screen;
  Uint32 halfmask;
  Uint16 buffer[320*350];
};

static void gp2x_set_colors_mzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  Uint32 cb_bg, cb_fg, cb_mx;

  cb_bg = graphics->flat_intensity_palette[bg];
  cb_fg = graphics->flat_intensity_palette[fg];
  if(cb_bg == cb_fg)
    cb_mx = cb_bg;
  else
    cb_mx = ((cb_bg >> 1) & render_data->halfmask) +
     ((cb_fg >> 1) & render_data->halfmask);

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
  char_colors[0] = (cb_bg << 16) | cb_bg;
  char_colors[1] = (cb_bg << 16) | cb_mx;
  char_colors[2] = (cb_bg << 16) | cb_mx;
  char_colors[3] = (cb_bg << 16) | cb_fg;
  char_colors[4] = (cb_mx << 16) | cb_bg;
  char_colors[5] = (cb_mx << 16) | cb_mx;
  char_colors[6] = (cb_mx << 16) | cb_mx;
  char_colors[7] = (cb_mx << 16) | cb_fg;
  char_colors[8] = (cb_mx << 16) | cb_bg;
  char_colors[9] = (cb_mx << 16) | cb_mx;
  char_colors[10] = (cb_mx << 16) | cb_mx;
  char_colors[11] = (cb_mx << 16) | cb_fg;
  char_colors[12] = (cb_fg << 16) | cb_bg;
  char_colors[13] = (cb_fg << 16) | cb_mx;
  char_colors[14] = (cb_fg << 16) | cb_mx;
  char_colors[15] = (cb_fg << 16) | cb_fg;
#else
  char_colors[0] = (cb_bg << 16) | cb_bg;
  char_colors[1] = (cb_mx << 16) | cb_bg;
  char_colors[2] = (cb_mx << 16) | cb_bg;
  char_colors[3] = (cb_fg << 16) | cb_bg;
  char_colors[4] = (cb_bg << 16) | cb_mx;
  char_colors[5] = (cb_mx << 16) | cb_mx;
  char_colors[6] = (cb_mx << 16) | cb_mx;
  char_colors[7] = (cb_fg << 16) | cb_mx;
  char_colors[8] = (cb_bg << 16) | cb_mx;
  char_colors[9] = (cb_mx << 16) | cb_mx;
  char_colors[10] = (cb_mx << 16) | cb_mx;
  char_colors[11] = (cb_fg << 16) | cb_mx;
  char_colors[12] = (cb_bg << 16) | cb_fg;
  char_colors[13] = (cb_mx << 16) | cb_fg;
  char_colors[14] = (cb_mx << 16) | cb_fg;
  char_colors[15] = (cb_fg << 16) | cb_fg;
#endif
}

static void gp2x_set_colors_smzx (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint32 bb, bf, fb, ff;

  bg &= 0x0F;
  fg &= 0x0F;

  bb = graphics->flat_intensity_palette[(bg << 4) | bg];
  bf = graphics->flat_intensity_palette[(bg << 4) | fg];
  fb = graphics->flat_intensity_palette[(fg << 4) | bg];
  ff = graphics->flat_intensity_palette[(fg << 4) | fg];

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
  char_colors[0] = (bb << 16) | bb;
  char_colors[1] = (bb << 16) | bf;
  char_colors[2] = (bb << 16) | fb;
  char_colors[3] = (bb << 16) | ff;
  char_colors[4] = (bf << 16) | bb;
  char_colors[5] = (bf << 16) | bf;
  char_colors[6] = (bf << 16) | fb;
  char_colors[7] = (bf << 16) | ff;
  char_colors[8] = (fb << 16) | bb;
  char_colors[9] = (fb << 16) | bf;
  char_colors[10] = (fb << 16) | fb;
  char_colors[11] = (fb << 16) | ff;
  char_colors[12] = (ff << 16) | bb;
  char_colors[13] = (ff << 16) | bf;
  char_colors[14] = (ff << 16) | fb;
  char_colors[15] = (ff << 16) | ff;
#else
  char_colors[0] = (bb << 16) | bb;
  char_colors[1] = (bf << 16) | bb;
  char_colors[2] = (fb << 16) | bb;
  char_colors[3] = (ff << 16) | bb;
  char_colors[4] = (bb << 16) | bf;
  char_colors[5] = (bf << 16) | bf;
  char_colors[6] = (fb << 16) | bf;
  char_colors[7] = (ff << 16) | bf;
  char_colors[8] = (bb << 16) | fb;
  char_colors[9] = (bf << 16) | fb;
  char_colors[10] = (fb << 16) | fb;
  char_colors[11] = (ff << 16) | fb;
  char_colors[12] = (bb << 16) | ff;
  char_colors[13] = (bf << 16) | ff;
  char_colors[14] = (fb << 16) | ff;
  char_colors[15] = (ff << 16) | ff;
#endif
}

static void gp2x_set_colors_smzx3 (struct graphics_data *graphics,
 Uint32 *char_colors, Uint8 bg, Uint8 fg)
{
  Uint8 base;
  Uint32 c0, c1, c2, c3;

  base = (bg << 4) | (fg & 0x0F);

  c0 = graphics->flat_intensity_palette[base];
  c1 = graphics->flat_intensity_palette[(base + 1) & 0xFF];
  c2 = graphics->flat_intensity_palette[(base + 2) & 0xFF];
  c3 = graphics->flat_intensity_palette[(base + 3) & 0xFF];

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
  char_colors[0] = (c0 << 16) | c0;
  char_colors[1] = (c0 << 16) | c2;
  char_colors[2] = (c0 << 16) | c1;
  char_colors[3] = (c0 << 16) | c3;
  char_colors[4] = (c2 << 16) | c0;
  char_colors[5] = (c2 << 16) | c2;
  char_colors[6] = (c2 << 16) | c1;
  char_colors[7] = (c2 << 16) | c3;
  char_colors[8] = (c1 << 16) | c0;
  char_colors[9] = (c1 << 16) | c2;
  char_colors[10] = (c1 << 16) | c1;
  char_colors[11] = (c1 << 16) | c3;
  char_colors[12] = (c3 << 16) | c0;
  char_colors[13] = (c3 << 16) | c2;
  char_colors[14] = (c3 << 16) | c1;
  char_colors[15] = (c3 << 16) | c3;
#else
  char_colors[0] = (c0 << 16) | c0;
  char_colors[1] = (c2 << 16) | c0;
  char_colors[2] = (c1 << 16) | c0;
  char_colors[3] = (c3 << 16) | c0;
  char_colors[4] = (c0 << 16) | c2;
  char_colors[5] = (c2 << 16) | c2;
  char_colors[6] = (c1 << 16) | c2;
  char_colors[7] = (c3 << 16) | c2;
  char_colors[8] = (c0 << 16) | c1;
  char_colors[9] = (c2 << 16) | c1;
  char_colors[10] = (c1 << 16) | c1;
  char_colors[11] = (c3 << 16) | c1;
  char_colors[12] = (c0 << 16) | c3;
  char_colors[13] = (c2 << 16) | c3;
  char_colors[14] = (c1 << 16) | c3;
  char_colors[15] = (c3 << 16) | c3;
#endif
}

static void (*gp2x_set_colors[4])
 (struct graphics_data *, Uint32 *, Uint8, Uint8) =
{
  gp2x_set_colors_mzx,
  gp2x_set_colors_smzx,
  gp2x_set_colors_smzx,
  gp2x_set_colors_smzx3
};

static bool gp2x_init_video(struct graphics_data *graphics,
 struct config_info *conf)
{
  struct gp2x_render_data *render_data =
   cmalloc(sizeof(struct gp2x_render_data));

  if(!render_data)
    return false;

  graphics->render_data = render_data;

  graphics->allow_resize = 0;

  graphics->bits_per_pixel = 16;

  graphics->resolution_width = 320;
  graphics->resolution_height = 240;
  graphics->window_width = 320;
  graphics->window_height = 240;

  return set_video_mode();
}

static void gp2x_free_video(struct graphics_data *graphics)
{
  free(graphics->render_data);
  graphics->render_data = NULL;
}

static bool gp2x_check_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  return SDL_VideoModeOK(width, height, 16,
   sdl_flags(depth, fullscreen, resize));
}

static bool gp2x_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  SDL_PixelFormat *format;
  Uint32 halfmask;

  render_data->screen = SDL_SetVideoMode(width, height, 16,
   sdl_flags(depth, fullscreen, resize));
  if(render_data->screen)
  {
    format = render_data->screen->format;
    halfmask = (format->Rmask >> 1) & format->Rmask;
    halfmask |= (format->Gmask >> 1) & format->Gmask;
    halfmask |= (format->Bmask >> 1) & format->Bmask;
    render_data->halfmask = halfmask;
    return true;
  }

  return false;
}

static void gp2x_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  Uint32 i;

  for(i = 0; i < count; i++)
  {
    graphics->flat_intensity_palette[i] =
     SDL_MapRGBA(render_data->screen->format, palette[i].r, palette[i].g,
     palette[i].b, 255);
  }
}

static void gp2x_get_screen_coords(struct graphics_data *graphics,
 int screen_x, int screen_y, int *x, int *y, int *min_x, int *min_y,
 int *max_x, int *max_y)
{
  *x = screen_x * 2;
  *y = screen_y * 35 / 24;
  *min_x = 0;
  *min_y = 0;
  *max_x = 319;
  *max_y = 239;
}

static void gp2x_set_screen_coords(struct graphics_data *graphics,
 int x, int y, int *screen_x, int *screen_y)
{
  *screen_x = x / 2;
  *screen_y = y * 24 / 35;
}

static void gp2x_render_graph(struct graphics_data *graphics)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  render_graph8((Uint8 *)render_data->buffer, 640, graphics,
   gp2x_set_colors[graphics->screen_mode]);
}

static void gp2x_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  Uint32 flatcolor = graphics->flat_intensity_palette[color];
  flatcolor |= flatcolor << 16;
  render_cursor((Uint32 *)render_data->buffer, 640, 8, x, y, flatcolor, lines,
   offset);
}

static void gp2x_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  render_mouse((Uint32 *)render_data->buffer, 640, 8, x, y, 0xFFFFFFFF, w, h);
}

static void gp2x_sync_screen(struct graphics_data *graphics)
{
  struct gp2x_render_data *render_data = graphics->render_data;
  Uint32 line_advance = render_data->screen->pitch / 2;
  Uint16 *dest = (Uint16*)render_data->screen->pixels;
  Uint16 *src = render_data->buffer;
  Uint32 i, j, skip = 0;
  SDL_LockSurface(render_data->screen);
  for(i = 0; i < 240; i++)
  {
    skip += 35 - 24;
    if(skip < 24)
      memcpy(dest, src, sizeof(Uint16) * 320);
    else
    {
      for(j = 0; j < 320; j++)
      {
        if(src[j] == src[j+320])
          dest[j] = src[j];
        else
          dest[j] = ((src[j] >> 1) & render_data->halfmask) +
           ((src[j+320] >> 1) & render_data->halfmask);
      }
      skip -= 24;
      src += 320;
    }
    src += 320;
    dest += line_advance;
  }
  SDL_UnlockSurface(render_data->screen);
  SDL_Flip(render_data->screen);
}

void render_gp2x_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = gp2x_init_video;
  renderer->free_video = gp2x_free_video;
  renderer->check_video_mode = gp2x_check_video_mode;
  renderer->set_video_mode = gp2x_set_video_mode;
  renderer->update_colors = gp2x_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->get_screen_coords = gp2x_get_screen_coords;
  renderer->set_screen_coords = gp2x_set_screen_coords;
  renderer->render_graph = gp2x_render_graph;
  renderer->render_cursor = gp2x_render_cursor;
  renderer->render_mouse = gp2x_render_mouse;
  renderer->sync_screen = gp2x_sync_screen;
}

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 2008 Alan Williams <mralert@gmail.com>
 *
 * 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 "platform.h"
#include "graphics.h"
#include "render.h"
#include "renderers.h"

#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#define BOOL _BOOL
#include <ogc/system.h>
#include <ogc/conf.h>
#include <ogc/cache.h>
#include <ogc/video.h>
#include <ogc/gx.h>
#include <ogc/gu.h>
#undef BOOL

#define DEFAULT_FIFO_SIZE (1024 * 1024)
#define CHAR_TEX_SIZE (sizeof(u32) * 512 * 16 * 2)
#define SMZX_TEX_OFFSET (512 * 16)

#define SCALE_TEX_SIZE (sizeof(u32) * 1024 * 512)
#define SCALE_TEX_X0 (0.0 / 1024.0)
#define SCALE_TEX_Y0 (1.0 / 512.0)
#define SCALE_TEX_X1 (640.0 / 1024.0)
#define SCALE_TEX_Y1 (351.0 / 512.0)

// Must be multiple of 32 bytes
struct ci4tlut
{
  u16 pal[16];
};

struct gx_render_data
{
  void *xfb[2];
  void *fifo;
  GXRModeObj *rmode;
  struct ci4tlut *smzxtlut;
  void *charimg;
  void *scaleimg;
  GXTlutObj mzxtlutobj;
  GXTlutObj smzxtlutobj[512];
  GXTexObj chartex;
  GXTexObj scaletex;
  GXColor palette[SMZX_PAL_SIZE];
  u16 tlutpal[SMZX_PAL_SIZE];
  int chrdirty;
  int paldirty;
  int curfb;
  enum ratio_type ratio;
  s16 sx0, sy0, sx1, sy1;
};

// IA8 TLUT for MZX/SMZX mode 1
static struct ci4tlut mzxtlut ATTRIBUTE_ALIGN(32) =
{{
  0x00FF,
  0x0000,
  0x0000,
  0x0000,
  0x0000,
  0xAAFF,
  0x0000,
  0x0000,
  0x0000,
  0x0000,
  0x55FF,
  0x0000,
  0x0000,
  0x0000,
  0x0000,
  0xFFFF
}};

// Lookup table for generating CI4 charset textures
static u32 mzxtexline[256] =
{
  0x00000000, 0x0000000F, 0x000000F0, 0x000000FF,
  0x00000F00, 0x00000F0F, 0x00000FF0, 0x00000FFF,
  0x0000F000, 0x0000F00F, 0x0000F0F0, 0x0000F0FF,
  0x0000FF00, 0x0000FF0F, 0x0000FFF0, 0x0000FFFF,
  0x000F0000, 0x000F000F, 0x000F00F0, 0x000F00FF,
  0x000F0F00, 0x000F0F0F, 0x000F0FF0, 0x000F0FFF,
  0x000FF000, 0x000FF00F, 0x000FF0F0, 0x000FF0FF,
  0x000FFF00, 0x000FFF0F, 0x000FFFF0, 0x000FFFFF,
  0x00F00000, 0x00F0000F, 0x00F000F0, 0x00F000FF,
  0x00F00F00, 0x00F00F0F, 0x00F00FF0, 0x00F00FFF,
  0x00F0F000, 0x00F0F00F, 0x00F0F0F0, 0x00F0F0FF,
  0x00F0FF00, 0x00F0FF0F, 0x00F0FFF0, 0x00F0FFFF,
  0x00FF0000, 0x00FF000F, 0x00FF00F0, 0x00FF00FF,
  0x00FF0F00, 0x00FF0F0F, 0x00FF0FF0, 0x00FF0FFF,
  0x00FFF000, 0x00FFF00F, 0x00FFF0F0, 0x00FFF0FF,
  0x00FFFF00, 0x00FFFF0F, 0x00FFFFF0, 0x00FFFFFF,
  0x0F000000, 0x0F00000F, 0x0F0000F0, 0x0F0000FF,
  0x0F000F00, 0x0F000F0F, 0x0F000FF0, 0x0F000FFF,
  0x0F00F000, 0x0F00F00F, 0x0F00F0F0, 0x0F00F0FF,
  0x0F00FF00, 0x0F00FF0F, 0x0F00FFF0, 0x0F00FFFF,
  0x0F0F0000, 0x0F0F000F, 0x0F0F00F0, 0x0F0F00FF,
  0x0F0F0F00, 0x0F0F0F0F, 0x0F0F0FF0, 0x0F0F0FFF,
  0x0F0FF000, 0x0F0FF00F, 0x0F0FF0F0, 0x0F0FF0FF,
  0x0F0FFF00, 0x0F0FFF0F, 0x0F0FFFF0, 0x0F0FFFFF,
  0x0FF00000, 0x0FF0000F, 0x0FF000F0, 0x0FF000FF,
  0x0FF00F00, 0x0FF00F0F, 0x0FF00FF0, 0x0FF00FFF,
  0x0FF0F000, 0x0FF0F00F, 0x0FF0F0F0, 0x0FF0F0FF,
  0x0FF0FF00, 0x0FF0FF0F, 0x0FF0FFF0, 0x0FF0FFFF,
  0x0FFF0000, 0x0FFF000F, 0x0FFF00F0, 0x0FFF00FF,
  0x0FFF0F00, 0x0FFF0F0F, 0x0FFF0FF0, 0x0FFF0FFF,
  0x0FFFF000, 0x0FFFF00F, 0x0FFFF0F0, 0x0FFFF0FF,
  0x0FFFFF00, 0x0FFFFF0F, 0x0FFFFFF0, 0x0FFFFFFF,
  0xF0000000, 0xF000000F, 0xF00000F0, 0xF00000FF,
  0xF0000F00, 0xF0000F0F, 0xF0000FF0, 0xF0000FFF,
  0xF000F000, 0xF000F00F, 0xF000F0F0, 0xF000F0FF,
  0xF000FF00, 0xF000FF0F, 0xF000FFF0, 0xF000FFFF,
  0xF00F0000, 0xF00F000F, 0xF00F00F0, 0xF00F00FF,
  0xF00F0F00, 0xF00F0F0F, 0xF00F0FF0, 0xF00F0FFF,
  0xF00FF000, 0xF00FF00F, 0xF00FF0F0, 0xF00FF0FF,
  0xF00FFF00, 0xF00FFF0F, 0xF00FFFF0, 0xF00FFFFF,
  0xF0F00000, 0xF0F0000F, 0xF0F000F0, 0xF0F000FF,
  0xF0F00F00, 0xF0F00F0F, 0xF0F00FF0, 0xF0F00FFF,
  0xF0F0F000, 0xF0F0F00F, 0xF0F0F0F0, 0xF0F0F0FF,
  0xF0F0FF00, 0xF0F0FF0F, 0xF0F0FFF0, 0xF0F0FFFF,
  0xF0FF0000, 0xF0FF000F, 0xF0FF00F0, 0xF0FF00FF,
  0xF0FF0F00, 0xF0FF0F0F, 0xF0FF0FF0, 0xF0FF0FFF,
  0xF0FFF000, 0xF0FFF00F, 0xF0FFF0F0, 0xF0FFF0FF,
  0xF0FFFF00, 0xF0FFFF0F, 0xF0FFFFF0, 0xF0FFFFFF,
  0xFF000000, 0xFF00000F, 0xFF0000F0, 0xFF0000FF,
  0xFF000F00, 0xFF000F0F, 0xFF000FF0, 0xFF000FFF,
  0xFF00F000, 0xFF00F00F, 0xFF00F0F0, 0xFF00F0FF,
  0xFF00FF00, 0xFF00FF0F, 0xFF00FFF0, 0xFF00FFFF,
  0xFF0F0000, 0xFF0F000F, 0xFF0F00F0, 0xFF0F00FF,
  0xFF0F0F00, 0xFF0F0F0F, 0xFF0F0FF0, 0xFF0F0FFF,
  0xFF0FF000, 0xFF0FF00F, 0xFF0FF0F0, 0xFF0FF0FF,
  0xFF0FFF00, 0xFF0FFF0F, 0xFF0FFFF0, 0xFF0FFFFF,
  0xFFF00000, 0xFFF0000F, 0xFFF000F0, 0xFFF000FF,
  0xFFF00F00, 0xFFF00F0F, 0xFFF00FF0, 0xFFF00FFF,
  0xFFF0F000, 0xFFF0F00F, 0xFFF0F0F0, 0xFFF0F0FF,
  0xFFF0FF00, 0xFFF0FF0F, 0xFFF0FFF0, 0xFFF0FFFF,
  0xFFFF0000, 0xFFFF000F, 0xFFFF00F0, 0xFFFF00FF,
  0xFFFF0F00, 0xFFFF0F0F, 0xFFFF0FF0, 0xFFFF0FFF,
  0xFFFFF000, 0xFFFFF00F, 0xFFFFF0F0, 0xFFFFF0FF,
  0xFFFFFF00, 0xFFFFFF0F, 0xFFFFFFF0, 0xFFFFFFFF
};

// Lookup table for generating CI4 SMZX charset textures
static u32 smzxtexline[256] =
{
  0x00000000, 0x00000055, 0x000000AA, 0x000000FF,
  0x00005500, 0x00005555, 0x000055AA, 0x000055FF,
  0x0000AA00, 0x0000AA55, 0x0000AAAA, 0x0000AAFF,
  0x0000FF00, 0x0000FF55, 0x0000FFAA, 0x0000FFFF,
  0x00550000, 0x00550055, 0x005500AA, 0x005500FF,
  0x00555500, 0x00555555, 0x005555AA, 0x005555FF,
  0x0055AA00, 0x0055AA55, 0x0055AAAA, 0x0055AAFF,
  0x0055FF00, 0x0055FF55, 0x0055FFAA, 0x0055FFFF,
  0x00AA0000, 0x00AA0055, 0x00AA00AA, 0x00AA00FF,
  0x00AA5500, 0x00AA5555, 0x00AA55AA, 0x00AA55FF,
  0x00AAAA00, 0x00AAAA55, 0x00AAAAAA, 0x00AAAAFF,
  0x00AAFF00, 0x00AAFF55, 0x00AAFFAA, 0x00AAFFFF,
  0x00FF0000, 0x00FF0055, 0x00FF00AA, 0x00FF00FF,
  0x00FF5500, 0x00FF5555, 0x00FF55AA, 0x00FF55FF,
  0x00FFAA00, 0x00FFAA55, 0x00FFAAAA, 0x00FFAAFF,
  0x00FFFF00, 0x00FFFF55, 0x00FFFFAA, 0x00FFFFFF,
  0x55000000, 0x55000055, 0x550000AA, 0x550000FF,
  0x55005500, 0x55005555, 0x550055AA, 0x550055FF,
  0x5500AA00, 0x5500AA55, 0x5500AAAA, 0x5500AAFF,
  0x5500FF00, 0x5500FF55, 0x5500FFAA, 0x5500FFFF,
  0x55550000, 0x55550055, 0x555500AA, 0x555500FF,
  0x55555500, 0x55555555, 0x555555AA, 0x555555FF,
  0x5555AA00, 0x5555AA55, 0x5555AAAA, 0x5555AAFF,
  0x5555FF00, 0x5555FF55, 0x5555FFAA, 0x5555FFFF,
  0x55AA0000, 0x55AA0055, 0x55AA00AA, 0x55AA00FF,
  0x55AA5500, 0x55AA5555, 0x55AA55AA, 0x55AA55FF,
  0x55AAAA00, 0x55AAAA55, 0x55AAAAAA, 0x55AAAAFF,
  0x55AAFF00, 0x55AAFF55, 0x55AAFFAA, 0x55AAFFFF,
  0x55FF0000, 0x55FF0055, 0x55FF00AA, 0x55FF00FF,
  0x55FF5500, 0x55FF5555, 0x55FF55AA, 0x55FF55FF,
  0x55FFAA00, 0x55FFAA55, 0x55FFAAAA, 0x55FFAAFF,
  0x55FFFF00, 0x55FFFF55, 0x55FFFFAA, 0x55FFFFFF,
  0xAA000000, 0xAA000055, 0xAA0000AA, 0xAA0000FF,
  0xAA005500, 0xAA005555, 0xAA0055AA, 0xAA0055FF,
  0xAA00AA00, 0xAA00AA55, 0xAA00AAAA, 0xAA00AAFF,
  0xAA00FF00, 0xAA00FF55, 0xAA00FFAA, 0xAA00FFFF,
  0xAA550000, 0xAA550055, 0xAA5500AA, 0xAA5500FF,
  0xAA555500, 0xAA555555, 0xAA5555AA, 0xAA5555FF,
  0xAA55AA00, 0xAA55AA55, 0xAA55AAAA, 0xAA55AAFF,
  0xAA55FF00, 0xAA55FF55, 0xAA55FFAA, 0xAA55FFFF,
  0xAAAA0000, 0xAAAA0055, 0xAAAA00AA, 0xAAAA00FF,
  0xAAAA5500, 0xAAAA5555, 0xAAAA55AA, 0xAAAA55FF,
  0xAAAAAA00, 0xAAAAAA55, 0xAAAAAAAA, 0xAAAAAAFF,
  0xAAAAFF00, 0xAAAAFF55, 0xAAAAFFAA, 0xAAAAFFFF,
  0xAAFF0000, 0xAAFF0055, 0xAAFF00AA, 0xAAFF00FF,
  0xAAFF5500, 0xAAFF5555, 0xAAFF55AA, 0xAAFF55FF,
  0xAAFFAA00, 0xAAFFAA55, 0xAAFFAAAA, 0xAAFFAAFF,
  0xAAFFFF00, 0xAAFFFF55, 0xAAFFFFAA, 0xAAFFFFFF,
  0xFF000000, 0xFF000055, 0xFF0000AA, 0xFF0000FF,
  0xFF005500, 0xFF005555, 0xFF0055AA, 0xFF0055FF,
  0xFF00AA00, 0xFF00AA55, 0xFF00AAAA, 0xFF00AAFF,
  0xFF00FF00, 0xFF00FF55, 0xFF00FFAA, 0xFF00FFFF,
  0xFF550000, 0xFF550055, 0xFF5500AA, 0xFF5500FF,
  0xFF555500, 0xFF555555, 0xFF5555AA, 0xFF5555FF,
  0xFF55AA00, 0xFF55AA55, 0xFF55AAAA, 0xFF55AAFF,
  0xFF55FF00, 0xFF55FF55, 0xFF55FFAA, 0xFF55FFFF,
  0xFFAA0000, 0xFFAA0055, 0xFFAA00AA, 0xFFAA00FF,
  0xFFAA5500, 0xFFAA5555, 0xFFAA55AA, 0xFFAA55FF,
  0xFFAAAA00, 0xFFAAAA55, 0xFFAAAAAA, 0xFFAAAAFF,
  0xFFAAFF00, 0xFFAAFF55, 0xFFAAFFAA, 0xFFAAFFFF,
  0xFFFF0000, 0xFFFF0055, 0xFFFF00AA, 0xFFFF00FF,
  0xFFFF5500, 0xFFFF5555, 0xFFFF55AA, 0xFFFF55FF,
  0xFFFFAA00, 0xFFFFAA55, 0xFFFFAAAA, 0xFFFFAAFF,
  0xFFFFFF00, 0xFFFFFF55, 0xFFFFFFAA, 0xFFFFFFFF
};

static bool gx_init_video(struct graphics_data *graphics,
 struct config_info *conf)
{
  const GXColor black = {0, 0, 0, 255};

  struct gx_render_data *render_data;
  f32 yscale;
  u32 xfbHeight;
  Mtx identmtx;
  GXRModeObj *rmode;
  Mtx44 projmtx;
  u16 sw, sh;
  int i;

  graphics->resolution_width = 640;
  graphics->resolution_height = 350;
  graphics->window_width = 640;
  graphics->window_height = 350;

  render_data = cmalloc(sizeof(struct gx_render_data));
  graphics->render_data = render_data;
  render_data->ratio = conf->video_ratio;

  VIDEO_Init();

  rmode = render_data->rmode = VIDEO_GetPreferredMode(NULL);
  render_data->xfb[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
  render_data->xfb[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
  render_data->curfb = 0;

  render_data->charimg = memalign(32, CHAR_TEX_SIZE);
  memset(render_data->charimg, 0, CHAR_TEX_SIZE);
  render_data->scaleimg = memalign(32, SCALE_TEX_SIZE);
  memset(render_data->scaleimg, 0, SCALE_TEX_SIZE);
  render_data->smzxtlut = memalign(32, sizeof(struct ci4tlut) * 512);
  memset(render_data->smzxtlut, 0, sizeof(struct ci4tlut) * 512);

  GX_InitTlutObj(&render_data->mzxtlutobj, &mzxtlut, GX_TL_IA8, 16);
  for(i = 0; i < 512; i++)
    GX_InitTlutObj(render_data->smzxtlutobj + i, render_data->smzxtlut + i,
     GX_TL_RGB565, 16);
  GX_InitTexObjCI(&render_data->chartex, render_data->charimg, 256, 512,
   GX_TF_CI4, GX_REPEAT, GX_REPEAT, GX_FALSE, GX_TLUT0);
  GX_InitTexObjLOD(&render_data->chartex, GX_NEAR, GX_NEAR, 0, 0, 0, GX_FALSE,
   GX_TRUE, GX_ANISO_1);
  GX_InitTexObj(&render_data->scaletex, render_data->scaleimg, 1024, 512,
   GX_TF_RGBA8, GX_REPEAT, GX_REPEAT, GX_FALSE);

  VIDEO_Configure(rmode);
  VIDEO_SetNextFramebuffer(render_data->xfb[0]);
  VIDEO_SetBlack(FALSE);
  VIDEO_Flush();
  VIDEO_WaitVSync();
  if(rmode->viTVMode & VI_NON_INTERLACE)
    VIDEO_WaitVSync();

  render_data->fifo = memalign(32, DEFAULT_FIFO_SIZE);
  memset(render_data->fifo, 0, DEFAULT_FIFO_SIZE);
  GX_Init(render_data->fifo, DEFAULT_FIFO_SIZE);

  yscale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight);
  xfbHeight = GX_SetDispCopyYScale(yscale);
  sw = ((rmode->fbWidth > 640) ? rmode->fbWidth : 640);
  sh = ((rmode->efbHeight > 352) ? rmode->efbHeight : 352);
  GX_SetScissor(0, 0, sw, sh);
  GX_SetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight);
  GX_SetDispCopyDst(rmode->fbWidth, xfbHeight);
  GX_SetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter);
  GX_SetFieldMode(rmode->field_rendering,
   ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
  if(rmode->aa)
    GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
  else
    GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
  GX_SetDispCopyGamma(GX_GM_1_0);
  GX_SetCopyClear(black, GX_MAX_Z24);
  GX_SetTexCopySrc(0, 0, 640, 352);
  GX_SetTexCopyDst(1024, 512, GX_TF_RGBA8, GX_FALSE);

  GX_ClearVtxDesc();
  GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
  GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);

  GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
  GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);

  GX_SetNumChans(1);
  GX_SetNumTexGens(1);
  GX_SetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHTNULL,
   GX_DF_NONE, GX_AF_NONE);
  GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
  GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
  GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);

  guMtxIdentity(identmtx);
  GX_LoadPosMtxImm(identmtx, GX_PNMTX0);

  GX_SetViewport(0, 0, sw, sh, 0, 1);
  GX_SetAlphaUpdate(GX_TRUE);
  GX_SetCullMode(GX_CULL_NONE);

  GX_CopyDisp(render_data->xfb[1], GX_TRUE);
  GX_CopyDisp(render_data->xfb[0], GX_FALSE);
  GX_Flush();

  render_data->chrdirty = 1;
  render_data->paldirty = 1;

  guOrtho(projmtx, -1, sh - 1, 0, sw, -1.0, 1.0);
  GX_LoadProjectionMtx(projmtx, GX_ORTHOGRAPHIC);

  return set_video_mode();
}

static void gx_free_video(struct graphics_data *graphics)
{
  free(graphics->render_data);
  graphics->render_data = NULL;
}

static bool gx_check_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  return true;
}

static bool gx_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  struct gx_render_data *render_data = graphics->render_data;
  float x, y, w, h, scale, xscale, yscale;
  int fh, fs, sw, sh, dw, dh;

  if(fullscreen)
    scale = 8.0 / 9.0;
  else
    scale = 8.0 / 10.0;

  if(render_data->rmode->viTVMode == VI_TVMODE_PAL_INT)
    fh = 574;
  else
    fh = 480;
  fs = fh * 720;

  if(CONF_GetAspectRatio() == CONF_ASPECT_16_9)
  {
    sw = fs * 16;
    sh = fs * 9;
  }
  else
  {
    sw = fs * 4;
    sh = fs * 3;
  }

  fix_viewport_ratio(sw, sh, &dw, &dh, render_data->ratio);

  if(render_data->rmode->viWidth > render_data->rmode->fbWidth)
    xscale = (float)render_data->rmode->fbWidth / render_data->rmode->viWidth;
  else
    xscale = 1.0;
  yscale = (float)render_data->rmode->efbHeight / render_data->rmode->viHeight;

  w = (float)dw * 720 / sw * scale;
  h = (float)dh * fh / sh * scale;
  x = (720 - w) / 2 - render_data->rmode->viXOrigin;
  y = (fh - h) / 2 - render_data->rmode->viYOrigin;
  w *= xscale; h *= yscale; x *= xscale; y *= yscale;

  render_data->sx0 = x;
  render_data->sy0 = y + 1;
  render_data->sx1 = x + w;
  render_data->sy1 = y + h + 1;

  return true;
}

static void gx_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  struct gx_render_data *render_data = graphics->render_data;
  Uint32 i;

  render_data->paldirty = 1;
  for(i = 0; i < count; i++)
  {
    render_data->palette[i].r = palette[i].r;
    render_data->palette[i].g = palette[i].g;
    render_data->palette[i].b = palette[i].b;
    render_data->palette[i].a = 255;
    render_data->tlutpal[i] = (palette[i].b >> 3)         |
                             ((palette[i].g & 0xFC) << 3) |
                             ((palette[i].r & 0xF8) << 8);
  }
}

static void gx_draw_char(u32 *tex, Uint8 *chr)
{
  u32 *dest = tex;
  Uint8 *src = chr;
  int i;
  for(i = 0; i < 14; i++)
  {
    *dest = mzxtexline[*src];
    src++; dest++;
    if((i & 0x7) == 0x7)
      dest += 31 * 8;
  }

  dest = tex + SMZX_TEX_OFFSET;
  src = chr;
  for(i = 0; i < 14; i++)
  {
    *dest = smzxtexline[*src];
    src++; dest++;
    if((i & 0x7) == 0x7)
      dest += 31 * 8;
  }
}

static void gx_draw_charsets(struct graphics_data *graphics)
{
  struct gx_render_data *render_data = graphics->render_data;
  Uint8 *src = graphics->charset;
  u32 *dest = render_data->charimg;
  int i;

  for(i = 0; i < 512; i++)
  {
    gx_draw_char(dest, src);
    src += 14;
    dest += 8;
    if((i & 0x1F) == 0x1F)
      dest += 32 * 8;
  }
}

static void gx_remap_charsets(struct graphics_data *graphics)
{
  struct gx_render_data *render_data = graphics->render_data;
  render_data->chrdirty = 1;
}

static void gx_remap_char(struct graphics_data *graphics, Uint16 chr)
{
  struct gx_render_data *render_data = graphics->render_data;
  render_data->chrdirty = 1;
}

static void gx_remap_charbyte(struct graphics_data *graphics,
 Uint16 chr, Uint8 byte)
{
  struct gx_render_data *render_data = graphics->render_data;
  render_data->chrdirty = 1;
}

static void gx_render_graph(struct graphics_data *graphics)
{
  struct gx_render_data *render_data = graphics->render_data;
  GXColor *pal = render_data->palette;
  int i, j, x, y;
  float u, v;
  struct char_element *src = graphics->text_video;

  if(render_data->chrdirty)
  {
    gx_draw_charsets(graphics);
    DCFlushRange(render_data->charimg, CHAR_TEX_SIZE);
    GX_InvalidateTexAll();
  }

  GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);

  if(graphics->screen_mode > 1)
  {
    GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
    if(render_data->paldirty)
    {
      if(!render_data->chrdirty)
        GX_InvalidateTexAll();
      for(i = 0; i < 512; i++)
        render_data->smzxtlut[i].pal[1] = 0;
      render_data->paldirty = 0;
    }
  }
  else
  {
    GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
    GX_LoadTlut(&render_data->mzxtlutobj, GX_TLUT0);
    GX_LoadTexObj(&render_data->chartex, GX_TEXMAP0);
  }

  render_data->chrdirty = 0;

  switch(graphics->screen_mode)
  {
    case 0:
    {
      for(y = 0; y < 25; y++)
      {
        for(x = 0; x < 80; x++)
        {
          GX_SetChanMatColor(GX_COLOR0A0, pal[src->bg_color]);
          GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
            GX_Position2s16(x * 8, y * 14);
            GX_TexCoord2f32(0, 0);
            GX_Position2s16(x * 8 + 8, y * 14);
            GX_TexCoord2f32(0, 0);
            GX_Position2s16(x * 8 + 8, y * 14 + 14);
            GX_TexCoord2f32(0, 0);
            GX_Position2s16(x * 8, y * 14 + 14);
            GX_TexCoord2f32(0, 0);
          GX_End();
          src++;
        }
      }

      GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);

      src = graphics->text_video;
      for(y = 0; y < 25; y++)
      {
        for(x = 0; x < 80; x++)
        {
          GX_SetChanMatColor(GX_COLOR0A0, pal[src->fg_color]);
          u = ((int)src->char_value & 0x1f) / 32.0;
          v = ((int)src->char_value >> 5) / 32.0;
          GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
            GX_Position2s16(x * 8, y * 14);
            GX_TexCoord2f32(u, v);
            GX_Position2s16(x * 8 + 8, y * 14);
            GX_TexCoord2f32(u + 1 / 32.0, v);
            GX_Position2s16(x * 8 + 8, y * 14 + 14);
            GX_TexCoord2f32(u + 1 / 32.0, v + 7 / 256.0);
            GX_Position2s16(x * 8, y * 14 + 14);
            GX_TexCoord2f32(u, v + 7 / 256.0);
          GX_End();
          src++;
        }
      }
      break;
    }
    case 1:
    {
      for(y = 0; y < 25; y++)
      {
        for(x = 0; x < 80; x++)
        {
          GX_SetChanMatColor(GX_COLOR0A0, pal[(src->bg_color & 0xF) * 0x11]);
          GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
            GX_Position2s16(x * 8, y * 14);
            GX_TexCoord2f32(0, 0);
            GX_Position2s16(x * 8 + 8, y * 14);
            GX_TexCoord2f32(0, 0);
            GX_Position2s16(x * 8 + 8, y * 14 + 14);
            GX_TexCoord2f32(0, 0);
            GX_Position2s16(x * 8, y * 14 + 14);
            GX_TexCoord2f32(0, 0);
          GX_End();
          src++;
        }
      }

      GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);

      src = graphics->text_video;
      for(y = 0; y < 25; y++)
      {
        for(x = 0; x < 80; x++)
        {
          GX_SetChanMatColor(GX_COLOR0A0, pal[(src->fg_color & 0xF) * 0x11]);
          u = ((int)src->char_value & 0x1f) / 32.0;
          v = ((int)src->char_value >> 5) / 32.0 + 0.5;
          GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
            GX_Position2s16(x * 8, y * 14);
            GX_TexCoord2f32(u, v);
            GX_Position2s16(x * 8 + 8, y * 14);
            GX_TexCoord2f32(u + 1 / 32.0, v);
            GX_Position2s16(x * 8 + 8, y * 14 + 14);
            GX_TexCoord2f32(u + 1 / 32.0, v + 7 / 256.0);
            GX_Position2s16(x * 8, y * 14 + 14);
            GX_TexCoord2f32(u, v + 7 / 256.0);
          GX_End();
          src++;
        }
      }
      break;
    }
    case 2:
    {
      j = -1;
      for(y = 0; y < 25; y++)
      {
        for(x = 0; x < 80; x++)
        {
          i = ((src->fg_color & 0xF) | (src->bg_color << 4)) & 0xFF;
          if(i != j)
          {
            if(!render_data->smzxtlut[i].pal[1])
            {
              render_data->smzxtlut[i].pal[0] =
               render_data->tlutpal[(i >> 4) * 0x11];
              render_data->smzxtlut[i].pal[5] = render_data->tlutpal[i];
              render_data->smzxtlut[i].pal[10] =
               render_data->tlutpal[((i << 4) | (i >> 4)) & 0xFF];
              render_data->smzxtlut[i].pal[15] =
               render_data->tlutpal[(i & 0xF) * 0x11];
              render_data->smzxtlut[i].pal[1] = 0xFFFF;
              DCFlushRange(render_data->smzxtlut + i, sizeof(struct ci4tlut));
            }
            GX_LoadTlut(&render_data->smzxtlutobj[i], GX_TLUT0);
            GX_LoadTexObj(&render_data->chartex, GX_TEXMAP0);
            j = i;
          }
          u = ((int)src->char_value & 0x1f) / 32.0;
          v = ((int)src->char_value >> 5) / 32.0 + 0.5;
          GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
            GX_Position2s16(x * 8, y * 14);
            GX_TexCoord2f32(u, v);
            GX_Position2s16(x * 8 + 8, y * 14);
            GX_TexCoord2f32(u + 1 / 32.0, v);
            GX_Position2s16(x * 8 + 8, y * 14 + 14);
            GX_TexCoord2f32(u + 1 / 32.0, v + 7 / 256.0);
            GX_Position2s16(x * 8, y * 14 + 14);
            GX_TexCoord2f32(u, v + 7 / 256.0);
          GX_End();
          src++;
        }
      }
      break;
    }
    default:
    case 3:
    {
      j = -1;
      for(y = 0; y < 25; y++)
      {
        for(x = 0; x < 80; x++)
        {
          i = (((src->fg_color & 0xF) | (src->bg_color << 4)) & 0xFF) | 0x100;
          if(i != j)
          {
            if(!render_data->smzxtlut[i].pal[1])
            {
              render_data->smzxtlut[i].pal[0] = render_data->tlutpal[i & 0xFF];
              render_data->smzxtlut[i].pal[5] =
               render_data->tlutpal[(i + 2) & 0xFF];
              render_data->smzxtlut[i].pal[10] =
               render_data->tlutpal[(i + 1) & 0xFF];
              render_data->smzxtlut[i].pal[15] =
               render_data->tlutpal[(i + 3) & 0xFF];
              render_data->smzxtlut[i].pal[1] = 0xFFFF;
              DCFlushRange(render_data->smzxtlut + i, sizeof(struct ci4tlut));
            }
            GX_LoadTlut(&render_data->smzxtlutobj[i], GX_TLUT0);
            GX_LoadTexObj(&render_data->chartex, GX_TEXMAP0);
            j = i;
          }
          u = ((int)src->char_value & 0x1f) / 32.0;
          v = ((int)src->char_value >> 5) / 32.0 + 0.5;
          GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
            GX_Position2s16(x * 8, y * 14);
            GX_TexCoord2f32(u, v);
            GX_Position2s16(x * 8 + 8, y * 14);
            GX_TexCoord2f32(u + 1 / 32.0, v);
            GX_Position2s16(x * 8 + 8, y * 14 + 14);
            GX_TexCoord2f32(u + 1 / 32.0, v + 7 / 256.0);
            GX_Position2s16(x * 8, y * 14 + 14);
            GX_TexCoord2f32(u, v + 7 / 256.0);
          GX_End();
          src++;
        }
      }
      break;
    }
  }

  GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
}

static void gx_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct gx_render_data *render_data = graphics->render_data;
  GXColor *pal = render_data->palette;

  GX_SetChanMatColor(GX_COLOR0A0, pal[color]);
  GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
    GX_Position2s16(x * 8, y * 14 + offset);
    GX_TexCoord2f32(0, 0);
    GX_Position2s16(x * 8 + 8, y * 14 + offset);
    GX_TexCoord2f32(0, 0);
    GX_Position2s16(x * 8 + 8, y * 14 + offset + lines);
    GX_TexCoord2f32(0, 0);
    GX_Position2s16(x * 8, y * 14 + offset + lines);
    GX_TexCoord2f32(0, 0);
  GX_End();
}

static void gx_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  const GXColor white = {255, 255, 255, 255};

  GX_SetBlendMode(GX_BM_BLEND, GX_BL_INVDSTCLR, GX_BL_ZERO, GX_LO_CLEAR);

  GX_SetChanMatColor(GX_COLOR0A0, white);
  GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
    GX_Position2s16(x, y);
    GX_TexCoord2f32(0, 0);
    GX_Position2s16(x + w, y);
    GX_TexCoord2f32(0, 0);
    GX_Position2s16(x + w, y + h);
    GX_TexCoord2f32(0, 0);
    GX_Position2s16(x, y + h);
    GX_TexCoord2f32(0, 0);
  GX_End();
}

static void gx_sync_screen(struct graphics_data *graphics)
{
  struct gx_render_data *render_data = graphics->render_data;
  static u8 tex_ptn[12][2] = {
    {6, 6}, {6, 6}, {6, 6},
    {6, 6}, {6, 6}, {6, 6},
    {6, 6}, {6, 6}, {6, 6},
    {6, 6}, {6, 6}, {6, 6},
  };
  static u8 tex_vf[7] = {0, 0, 21, 22, 21, 0, 0};
  render_data->curfb ^= 1;
  GX_SetCopyFilter(GX_FALSE, tex_ptn, GX_FALSE, tex_vf);
  GX_CopyTex(render_data->scaleimg, GX_TRUE);
  GX_Flush();
  GX_PixModeSync();
  GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
  GX_LoadTexObj(&render_data->scaletex, GX_TEXMAP0);
  GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
    GX_Position2s16(render_data->sx0, render_data->sy0);
    GX_TexCoord2f32(SCALE_TEX_X0, SCALE_TEX_Y0);
    GX_Position2s16(render_data->sx1, render_data->sy0);
    GX_TexCoord2f32(SCALE_TEX_X1, SCALE_TEX_Y0);
    GX_Position2s16(render_data->sx1, render_data->sy1);
    GX_TexCoord2f32(SCALE_TEX_X1, SCALE_TEX_Y1);
    GX_Position2s16(render_data->sx0, render_data->sy1);
    GX_TexCoord2f32(SCALE_TEX_X0, SCALE_TEX_Y1);
  GX_End();
  GX_DrawDone();
  GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
  GX_SetColorUpdate(GX_TRUE);
  GX_SetCopyFilter(render_data->rmode->aa, render_data->rmode->sample_pattern,
    GX_TRUE, render_data->rmode->vfilter);
  GX_CopyDisp(render_data->xfb[render_data->curfb], GX_TRUE);
  GX_Flush();
  VIDEO_SetNextFramebuffer(render_data->xfb[render_data->curfb]);
  VIDEO_Flush();
  VIDEO_WaitVSync();
}

void render_gx_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = gx_init_video;
  renderer->free_video = gx_free_video;
  renderer->check_video_mode = gx_check_video_mode;
  renderer->set_video_mode = gx_set_video_mode;
  renderer->update_colors = gx_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->remap_charsets = gx_remap_charsets;
  renderer->remap_char = gx_remap_char;
  renderer->remap_charbyte = gx_remap_charbyte;
  renderer->get_screen_coords = get_screen_coords_centered;
  renderer->set_screen_coords = set_screen_coords_centered;
  renderer->render_graph = gx_render_graph;
  renderer->render_cursor = gx_render_cursor;
  renderer->render_mouse = gx_render_mouse;
  renderer->sync_screen = gx_sync_screen;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007-2008 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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 "render_sdl.h"

#include "SDL.h"

int sdl_flags(int depth, bool fullscreen, bool resize)
{
  int flags = 0;

  if(fullscreen)
  {
    flags |= SDL_FULLSCREEN;
    if(depth == 8)
      flags |= SDL_HWPALETTE;
  }
  else
    if(resize)
      flags |= SDL_RESIZABLE;

  return flags;
}

#if defined(CONFIG_RENDER_GL_FIXED) || defined(CONFIG_RENDER_GL_PROGRAM)

bool gl_set_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize)
{
  return SDL_SetVideoMode(width, height, depth,
   GL_STRIP_FLAGS(sdl_flags(depth, fullscreen, resize))) != NULL;
}

bool gl_check_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize)
{
  return SDL_VideoModeOK(width, height, depth,
   GL_STRIP_FLAGS(sdl_flags(depth, fullscreen, resize)));
}

void gl_set_attributes(struct graphics_data *graphics)
{
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

  if(graphics->gl_vsync == 0)
    SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
  else if(graphics->gl_vsync >= 1)
    SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
}

bool gl_swap_buffers(struct graphics_data *graphics)
{
  SDL_GL_SwapBuffers();
  return true;
}

#endif // CONFIG_RENDER_GL_FIXED || CONFIG_RENDER_GL_PROGRAM


code:
/* MegaZeux
 *
 * Copyright (C) 2007-2008 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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 __RENDER_SDL_H
#define __RENDER_SDL_H

#include "compat.h"

__M_BEGIN_DECLS

#include "graphics.h"

#include "SDL.h"

int sdl_flags(int depth, bool fullscreen, bool resize);

#if defined(CONFIG_RENDER_GL_FIXED) || defined(CONFIG_RENDER_GL_PROGRAM)

#include "render_gl.h"

#define GL_STRIP_FLAGS(A) ((A & (SDL_FULLSCREEN | SDL_RESIZABLE)) | SDL_OPENGL)

bool gl_set_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize);
bool gl_check_video_mode(struct graphics_data *graphics, int width, int height,
 int depth, bool fullscreen, bool resize);
void gl_set_attributes(struct graphics_data *graphics);
bool gl_swap_buffers(struct graphics_data *graphics);

static inline void gl_cleanup(struct graphics_data *graphics) { }

static inline bool GL_LoadLibrary(enum gl_lib_type type)
{
  return SDL_GL_LoadLibrary(NULL) == 0;
}

static inline void *GL_GetProcAddress(const char *proc)
{
  return SDL_GL_GetProcAddress(proc);
}

#endif // CONFIG_RENDER_GL_FIXED || CONFIG_RENDER_GL_PROGRAM

__M_END_DECLS

#endif // __RENDER_SDL_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004-2006 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007 Alistair John Strachan <alistair@devzero.co.uk>
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "graphics.h"
#include "render.h"
#include "render_sdl.h"
#include "renderers.h"
#include "util.h"

#include "SDL.h"

static SDL_Color sdlpal[SMZX_PAL_SIZE];

static bool soft_init_video(struct graphics_data *graphics,
 struct config_info *conf)
{
  graphics->allow_resize = 0;
  graphics->bits_per_pixel = 32;

  // Screens smaller than 640x350 do weird things with the software renderer
  if(graphics->resolution_width < 640)
    graphics->resolution_width = 640;
  if(graphics->resolution_height < 350)
    graphics->resolution_height = 350;
  if(graphics->window_width < 640)
    graphics->window_width = 640;
  if(graphics->window_height < 350)
    graphics->window_height = 350;

  // We have 8-bit, 16-bit, and 32-bit software renderers
  if(conf->force_bpp == 8 || conf->force_bpp == 16 || conf->force_bpp == 32)
    graphics->bits_per_pixel = conf->force_bpp;

  return set_video_mode();
}

static bool soft_check_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  return SDL_VideoModeOK(width, height, depth,
   sdl_flags(depth, fullscreen, resize));
}

static bool soft_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  graphics->render_data = SDL_SetVideoMode(width, height, depth,
   sdl_flags(depth, fullscreen, resize));

  if(!graphics->render_data)
  {
    warn("Failed to set video mode: %s\n", SDL_GetError());
    return false;
  }

  return true;
}

static void soft_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  SDL_Surface *screen = graphics->render_data;
  Uint32 i;

  if(graphics->bits_per_pixel != 8)
  {
    for(i = 0; i < count; i++)
    {
      graphics->flat_intensity_palette[i] =
       SDL_MapRGBA(screen->format, palette[i].r, palette[i].g, palette[i].b,
       255);
    }
  }
  else
  {
    for(i = 0; i < count; i++)
    {
      sdlpal[i].r = palette[i].r;
      sdlpal[i].g = palette[i].g;
      sdlpal[i].b = palette[i].b;
    }
    SDL_SetColors(screen, sdlpal, 0, count);
  }
}

static void soft_render_graph(struct graphics_data *graphics)
{
  SDL_Surface *screen = graphics->render_data;
  Uint32 *pixels = (Uint32 *)screen->pixels;
  Uint32 pitch = screen->pitch;
  Uint32 bpp = screen->format->BitsPerPixel;
  Uint32 mode = graphics->screen_mode;

  pixels += pitch * ((screen->h - 350) / 8);
  pixels += (screen->w - 640) * bpp / 64;

  SDL_LockSurface(screen);
  if(bpp == 8)
    render_graph8((Uint8 *)pixels, pitch, graphics, set_colors8[mode]);
  else if(bpp == 16)
    render_graph16((Uint16 *)pixels, pitch, graphics, set_colors16[mode]);
  else if(bpp == 32)
  {
    if(!mode)
      render_graph32(pixels, pitch, graphics, set_colors32[mode]);
    else
      render_graph32s(pixels, pitch, graphics, set_colors32[mode]);
  }
  SDL_UnlockSurface(screen);
}

static void soft_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  SDL_Surface *screen = graphics->render_data;
  Uint32 *pixels = (Uint32 *)screen->pixels;
  Uint32 pitch = screen->pitch;
  Uint32 bpp = screen->format->BitsPerPixel;
  Uint32 flatcolor;

  pixels += pitch * ((screen->h - 350) / 8);
  pixels += (screen->w - 640) * bpp / 64;

  if(bpp == 8)
    flatcolor = (color << 24) | (color << 16) | (color << 8) | color;
  else
    flatcolor = graphics->flat_intensity_palette[color];

  if(bpp == 16)
    flatcolor |= flatcolor << 16;

  SDL_LockSurface(screen);
  render_cursor(pixels, pitch, bpp, x, y, flatcolor, lines, offset);
  SDL_UnlockSurface(screen);
}

static void soft_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  SDL_Surface *screen = graphics->render_data;
  Uint32 *pixels = (Uint32 *)screen->pixels;
  Uint32 pitch = screen->pitch;
  Uint32 bpp = screen->format->BitsPerPixel;
  Uint32 mask;

  pixels += pitch * ((screen->h - 350) / 8);
  pixels += (screen->w - 640) * bpp / 64;

  if((bpp == 8) && !graphics->screen_mode)
    mask = 0x0F0F0F0F;
  else
    mask = 0xFFFFFFFF;

  SDL_LockSurface(screen);
  render_mouse(pixels, pitch, bpp, x, y, mask, w, h);
  SDL_UnlockSurface(screen);
}

static void soft_sync_screen(struct graphics_data *graphics)
{
  SDL_Flip(graphics->render_data);
}

void render_soft_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = soft_init_video;
  renderer->check_video_mode = soft_check_video_mode;
  renderer->set_video_mode = soft_set_video_mode;
  renderer->update_colors = soft_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->get_screen_coords = get_screen_coords_centered;
  renderer->set_screen_coords = set_screen_coords_centered;
  renderer->render_graph = soft_render_graph;
  renderer->render_cursor = soft_render_cursor;
  renderer->render_mouse = soft_render_mouse;
  renderer->sync_screen = soft_sync_screen;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * 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 <stdlib.h>

#include "platform.h"
#include "graphics.h"
#include "render.h"
#include "render_sdl.h"
#include "render_yuv.h"

bool yuv_set_video_mode_size(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize,
 int yuv_width, int yuv_height)
{
  struct yuv_render_data *render_data = graphics->render_data;

  // the YUV renderer _requires_ 32bit colour
  render_data->screen = SDL_SetVideoMode(width, height, 32,
   sdl_flags(depth, fullscreen, resize) | SDL_ANYFORMAT);

  if(!render_data->screen)
  {
    free(render_data);
    return false;
  }

  // try with a YUY2 pixel format first
  render_data->overlay = SDL_CreateYUVOverlay(yuv_width, yuv_height,
    SDL_YUY2_OVERLAY, render_data->screen);

  // didn't work, try with a UYVY pixel format next
  if(!render_data->overlay)
    render_data->overlay = SDL_CreateYUVOverlay(yuv_width, yuv_height,
      SDL_UYVY_OVERLAY, render_data->screen);

  // failed to create an overlay
  if(!render_data->overlay)
  {
    free(render_data);
    return false;
  }

  return true;
}

bool yuv_init_video(struct graphics_data *graphics, struct config_info *conf)
{
  struct yuv_render_data *render_data = cmalloc(sizeof(struct yuv_render_data));
  if(!render_data)
    return false;

  graphics->render_data = render_data;
  graphics->allow_resize = conf->allow_resize;
  render_data->ratio = conf->video_ratio;

  if(!set_video_mode())
  {
    free(render_data);
    return false;
  }

  return true;
}

void yuv_free_video(struct graphics_data *graphics)
{
  free(graphics->render_data);
  graphics->render_data = NULL;
}

bool yuv_check_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  // requires 32bit colour
  return SDL_VideoModeOK(width, height, 32,
   sdl_flags(depth, fullscreen, resize) | SDL_ANYFORMAT);
}

static inline void rgb_to_yuv(Uint8 r, Uint8 g, Uint8 b,
                              Uint8 *y, Uint8 *u, Uint8 *v)
{
  *y = (9797 * r + 19237 * g + 3734 * b) >> 15;
  *u = ((18492 * (b - *y)) >> 15) + 128;
  *v = ((23372 * (r - *y)) >> 15) + 128;
}

void yuv_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count)
{
  struct yuv_render_data *render_data = graphics->render_data;
  Uint8 y, u, v;
  Uint32 i;

  // it's a YUY2 overlay
  if(render_data->overlay->format == SDL_YUY2_OVERLAY)
  {
    for(i = 0; i < count; i++)
    {
      rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b, &y, &u, &v);

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
      graphics->flat_intensity_palette[i] =
        (v << 0) | (y << 8) | (u << 16) | (y << 24);
#else
      graphics->flat_intensity_palette[i] =
        (y << 0) | (u << 8) | (y << 16) | (v << 24);
#endif
    }
  }

  // it's a UYVY overlay
  else
  {
    for(i = 0; i < count; i++)
    {
      rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b, &y, &u, &v);

#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
      graphics->flat_intensity_palette[i] =
        (y << 0) | (v << 8) | (y << 16) | (u << 24);
#else
      graphics->flat_intensity_palette[i] =
        (u << 0) | (y << 8) | (v << 16) | (y << 24);
#endif
    }
  }
}

void yuv_sync_screen(struct graphics_data *graphics)
{
  struct yuv_render_data *render_data = graphics->render_data;
  int width = render_data->screen->w, v_width;
  int height = render_data->screen->h, v_height;
  SDL_Rect rect;

  // FIXME: Putting this here is suboptimal
  fix_viewport_ratio(width, height, &v_width, &v_height, render_data->ratio);

  rect.x = (width - v_width) >> 1;
  rect.y = (height - v_height) >> 1;
  rect.w = v_width;
  rect.h = v_height;
  SDL_DisplayYUVOverlay(render_data->overlay, &rect);
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * 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 __RENDER_YUV_H
#define __RENDER_YUV_H

#define SOFT_SOURCE_WIDTH    640
#define SOFT_SOURCE_HEIGHT   350

#define YUV1_OVERLAY_WIDTH   (SOFT_SOURCE_WIDTH * 2)
#define YUV1_OVERLAY_HEIGHT  SOFT_SOURCE_HEIGHT

#define YUV2_OVERLAY_WIDTH   SOFT_SOURCE_WIDTH
#define YUV2_OVERLAY_HEIGHT  SOFT_SOURCE_HEIGHT

#include "compat.h"

#include "graphics.h"

#include "SDL.h"

__M_BEGIN_DECLS

struct yuv_render_data
{
  SDL_Surface *screen;
  SDL_Overlay *overlay;
  Uint32 y0mask;
  Uint32 y1mask;
  Uint32 uvmask;
  enum ratio_type ratio;
};

bool yuv_set_video_mode_size(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize,
 int yuv_width, int yuv_height);
bool yuv_init_video(struct graphics_data *graphics, struct config_info *conf);
void yuv_free_video(struct graphics_data *graphics);
bool yuv_check_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize);
void yuv_update_colors(struct graphics_data *graphics,
 struct rgb_color *palette, Uint32 count);
void yuv_sync_screen (struct graphics_data *graphics);

__M_END_DECLS

#endif // __RENDER_YUV_H


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "graphics.h"
#include "render.h"
#include "render_yuv.h"
#include "renderers.h"

static bool yuv1_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  return yuv_set_video_mode_size(graphics, width, height, depth, fullscreen,
   resize, YUV1_OVERLAY_WIDTH, YUV1_OVERLAY_HEIGHT);
}

static void yuv1_render_graph(struct graphics_data *graphics)
{
  struct yuv_render_data *render_data = graphics->render_data;
  Uint32 mode = graphics->screen_mode;

  SDL_LockYUVOverlay(render_data->overlay);

  if(!mode)
  {
    render_graph32((Uint32 *)render_data->overlay->pixels[0],
     render_data->overlay->pitches[0], graphics, set_colors32[mode]);
  }
  else
  {
    render_graph32s((Uint32 *)render_data->overlay->pixels[0],
     render_data->overlay->pitches[0], graphics, set_colors32[mode]);
  }

  SDL_UnlockYUVOverlay(render_data->overlay);
}

static void yuv1_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct yuv_render_data *render_data = graphics->render_data;

  SDL_LockYUVOverlay(render_data->overlay);
  render_cursor((Uint32 *)render_data->overlay->pixels[0],
   render_data->overlay->pitches[0], 32, x, y,
   graphics->flat_intensity_palette[color], lines, offset);
  SDL_UnlockYUVOverlay(render_data->overlay);
}

static void yuv1_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  struct yuv_render_data *render_data = graphics->render_data;

  SDL_LockYUVOverlay(render_data->overlay);
  render_mouse((Uint32 *)render_data->overlay->pixels[0],
   render_data->overlay->pitches[0], 32, x, y, 0xFFFFFFFF, w, h);
  SDL_UnlockYUVOverlay(render_data->overlay);
}

void render_yuv1_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = yuv_init_video;
  renderer->free_video = yuv_free_video;
  renderer->check_video_mode = yuv_check_video_mode;
  renderer->set_video_mode = yuv1_set_video_mode;
  renderer->update_colors = yuv_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->get_screen_coords = get_screen_coords_scaled;
  renderer->set_screen_coords = set_screen_coords_scaled;
  renderer->render_graph = yuv1_render_graph;
  renderer->render_cursor = yuv1_render_cursor;
  renderer->render_mouse = yuv1_render_mouse;
  renderer->sync_screen = yuv_sync_screen;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * 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 "platform.h"
#include "graphics.h"
#include "render.h"
#include "render_yuv.h"
#include "renderers.h"

static bool yuv2_set_video_mode(struct graphics_data *graphics,
 int width, int height, int depth, bool fullscreen, bool resize)
{
  struct yuv_render_data *render_data = graphics->render_data;

  if(yuv_set_video_mode_size(graphics, width, height, depth, fullscreen,
   resize, YUV2_OVERLAY_WIDTH, YUV2_OVERLAY_HEIGHT))
  {
    if(render_data->overlay->format == SDL_YUY2_OVERLAY)
    {
#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
      render_data->y0mask = 0xFF000000;
      render_data->y1mask = 0x0000FF00;
#else
      render_data->y0mask = 0x000000FF;
      render_data->y1mask = 0x00FF0000;
#endif
    }
    else
    {
#if PLATFORM_BYTE_ORDER == PLATFORM_BIG_ENDIAN
      render_data->y0mask = 0x00FF0000;
      render_data->y1mask = 0x000000FF;
#else
      render_data->y0mask = 0x0000FF00;
      render_data->y1mask = 0xFF000000;
#endif
    }
    render_data->uvmask = ~(render_data->y0mask | render_data->y1mask);
    return true;
  }

  return false;
}

static void yuv2_render_graph(struct graphics_data *graphics)
{
  struct yuv_render_data *render_data = graphics->render_data;

  SDL_LockYUVOverlay(render_data->overlay);

  render_graph16((Uint16 *)render_data->overlay->pixels[0],
   render_data->overlay->pitches[0], graphics,
   yuv2_set_colors[graphics->screen_mode]);

  SDL_UnlockYUVOverlay(render_data->overlay);
}

static void yuv2_render_cursor(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 color, Uint8 lines, Uint8 offset)
{
  struct yuv_render_data *render_data = graphics->render_data;

  SDL_LockYUVOverlay(render_data->overlay);
  render_cursor((Uint32 *)render_data->overlay->pixels[0],
   render_data->overlay->pitches[0], 16, x, y,
   graphics->flat_intensity_palette[color], lines, offset);
  SDL_UnlockYUVOverlay(render_data->overlay);
}

static void yuv2_render_mouse(struct graphics_data *graphics,
 Uint32 x, Uint32 y, Uint8 w, Uint8 h)
{
  struct yuv_render_data *render_data = graphics->render_data;

  SDL_LockYUVOverlay(render_data->overlay);
  render_mouse((Uint32 *)render_data->overlay->pixels[0],
   render_data->overlay->pitches[0], 16, x, y, 0xFFFFFFFF, w, h);
  SDL_UnlockYUVOverlay(render_data->overlay);
}

void render_yuv2_register(struct renderer *renderer)
{
  memset(renderer, 0, sizeof(struct renderer));
  renderer->init_video = yuv_init_video;
  renderer->free_video = yuv_free_video;
  renderer->check_video_mode = yuv_check_video_mode;
  renderer->set_video_mode = yuv2_set_video_mode;
  renderer->update_colors = yuv_update_colors;
  renderer->resize_screen = resize_screen_standard;
  renderer->get_screen_coords = get_screen_coords_scaled;
  renderer->set_screen_coords = set_screen_coords_scaled;
  renderer->render_graph = yuv2_render_graph;
  renderer->render_cursor = yuv2_render_cursor;
  renderer->render_mouse = yuv2_render_mouse;
  renderer->sync_screen = yuv_sync_screen;
}


code:
/* MegaZeux
 *
 *   Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * 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 __RENDERERS_H
#define __RENDERERS_H

#include "compat.h"

__M_BEGIN_DECLS

#include "graphics.h"

struct renderer_data
{
  const char *name;
  void (*reg)(struct renderer *renderer);
};

#if defined(CONFIG_RENDER_SOFT)
void render_soft_register(struct renderer *renderer);
#endif
#if defined(CONFIG_RENDER_GL_FIXED)
void render_gl1_register(struct renderer *renderer);
void render_gl2_register(struct renderer *renderer);
#endif
#if defined(CONFIG_RENDER_GL_PROGRAM)
void render_glsl_register(struct renderer *renderer);
#endif
#if defined(CONFIG_RENDER_YUV)
void render_yuv1_register(struct renderer *renderer);
void render_yuv2_register(struct renderer *renderer);
#endif
#if defined(CONFIG_RENDER_GP2X)
void render_gp2x_register(struct renderer *renderer);
#endif
#if defined(CONFIG_NDS)
void render_nds_register(struct renderer *renderer);
#endif
#if defined(CONFIG_RENDER_GX)
void render_gx_register(struct renderer *renderer);
#endif

__M_END_DECLS

#endif // __RENDERERS_H