chiark / gitweb /
New upstream version 1.18
[chroma-debian.git] / sdldisplay.c
diff --git a/sdldisplay.c b/sdldisplay.c
new file mode 100644 (file)
index 0000000..f95cc54
--- /dev/null
@@ -0,0 +1,3918 @@
+/*  
+    sdldisplay.c
+
+    Copyright (C) 2010-2019 Amf
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "chroma.h"
+#include "menu.h"
+#include "level.h"
+#include "display.h"
+#include "graphics.h"
+#include "colours.h"
+#include "sdlfont.h"
+#include "sdlscreen.h"
+#include "util.h"
+#include "actions.h"
+#include "xmlparser.h"
+
+#define MOUSE_TIMEOUT_CLICK 200
+#define MOUSE_TIMEOUT_MOVE 1000
+
+char options_colours[FILENAME_MAX] = COLOURS_DEFAULT;
+char options_graphics[FILENAME_MAX] = GRAPHICS_DEFAULT;
+int options_graphic_level = 0;
+int options_sdl_fullscreen = 1;
+int options_sdl_width = 0;
+int options_sdl_height = 0;
+int options_sdl_delay = 100;
+int options_sdl_player_delay = 200;
+int options_sdl_replay_delay = 200;
+int options_sdl_undo_delay = 200;
+int options_sdl_size_x = 0;
+int options_sdl_size_y = 0;
+int options_debug = 0;
+#ifdef XOR_COMPATIBILITY
+int options_xor_options = 0;
+int options_xor_mode = 1;
+int options_xor_display = 0;
+
+int xor_map_scale = 4;
+int xor_map_x_offset = 0;
+int xor_map_y_offset = 0;
+#endif
+
+#ifdef ENIGMA_COMPATIBILITY
+int options_enigma_options = 0;
+int options_enigma_mode = 1;
+#endif
+
+int actions[SDLK_LAST];
+int actions_mouse[3][MOUSE_BUTTONS_MAX];
+
+int display_offset_x;
+int display_offset_y;
+int display_offset_pixels_x = 0;
+int display_offset_pixels_y = 0;
+int display_start_x;
+int display_start_y;
+int display_end_x;
+int display_end_y;
+int display_pieces_x;
+int display_pieces_y;
+int display_focus_x;
+int display_focus_y;
+int display_bar_pixels = 0;
+float display_animation;
+int display_animation_x;
+int display_animation_y;
+int display_border_x = 3;
+int display_border_y = 3;
+
+struct SDL_Surface* psurfacelogo = NULL;
+struct SDL_Surface* psurfacelogosmall = NULL;
+
+extern int font_height;
+
+extern SDL_Surface *screen_surface;
+
+extern int screen_width;
+extern int screen_height;
+extern int screen_fullscreen;
+
+extern int font_height;
+extern int font_width;
+extern int font_border;
+extern int font_padding;
+
+extern int font_size_game;
+
+extern char *piece_name[];
+extern char *action_name[];
+extern char *action_shortname[];
+
+extern int move_x[];
+extern int move_y[];
+
+extern struct graphics* pdisplaygraphics;
+extern struct colours* pdisplaycolours;
+
+extern char options_graphics[];
+extern char options_colours[];
+
+extern int *editor_piece_maps[];
+
+void display_movers(struct level* plevel, int redraw);
+void displayshadowed_movers(struct level* plevel, int redraw);
+
+void displayshadowed_level(struct level* plevel);
+
+void display_clip(struct level* plevel, int clip);
+void display_screensizemenu();
+
+void display_initactions();
+char *display_keyname(SDLKey key);
+void display_addkeytomenu(struct menu* pmenu, int action, char *text);
+void display_options_keys();
+void display_options_mouse();
+void display_options_size();
+void display_options_debug();
+
+#ifdef XOR_COMPATIBILITY
+void xor_focus(struct level* plevel);
+#endif
+
+void display_options_othergames();
+
+#define SCREENREDRAW_LEVEL        1
+#define SCREENREDRAW_BAR        2
+#define SCREENREDRAW_ALL        (SCREENREDRAW_BAR | SCREENREDRAW_LEVEL)
+
+void display_init()
+{
+    char buffer[256];
+    struct SDL_Surface* psurfaceicon;
+
+    if(SDL_Init(SDL_INIT_VIDEO) < 0)
+    {
+        snprintf(buffer, 256, gettext("Unable to initalise SDL: %s"), SDL_GetError());
+        fatal(buffer);
+    }
+
+    display_options_load();
+
+    atexit(display_quit);
+
+    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+    SDL_EnableUNICODE(1);
+
+    psurfaceicon = graphics_loadimage("icon.png");
+    if(psurfaceicon != NULL)
+        SDL_WM_SetIcon(psurfaceicon, NULL);
+
+    screen_size(options_sdl_width, options_sdl_height, options_sdl_fullscreen);
+    screen_clear(255, 255, 255);
+
+    font_init();
+    font_resize();
+    colours_init();
+    graphics_init();
+
+}
+
+void display_quit()
+{
+    SDL_Quit();
+}
+
+void display_piece(struct level* plevel, int p, int x, int y, int d)
+{
+    SDL_Surface *image;
+
+    SDL_Rect srect;
+    SDL_Rect drect;
+    int alpha;
+    int px, py;
+    int b;
+    int bimage[4];
+    int i;
+    int bsizex, bsizey, boffset;
+    int op;
+
+    int xstart = 0, xend = 0, xsize = 0, xpos = 0;
+
+    if(p < PIECE_SPACE || p >= PIECE_MAX)
+        return;
+
+#ifdef XOR_COMPATIBILITY
+    if(plevel->switched && (p == PIECE_WALL || p == PIECE_SPACE))
+        p = PIECE_DARKNESS;
+#endif
+
+    op = p;
+
+    px = x * pdisplaygraphics->size_x + display_offset_pixels_x; 
+    py = y * pdisplaygraphics->size_y + display_offset_pixels_y; 
+    if(d != MOVE_NONE
+            && p != PIECE_SPACE && !isexplosion(p)
+#ifdef XOR_COMPATIBILITY
+            && p != PIECE_TELEPORT
+#endif
+            ) 
+    { 
+        if(d == MOVE_LEFT)
+            px = px - display_animation_x;
+        if(d == MOVE_RIGHT)
+            px = px + display_animation_x;
+        if(d == MOVE_UP)
+            py = py - display_animation_y;
+        if(d == MOVE_DOWN)
+            py = py + display_animation_y;
+    } 
+
+    if(isexplosion(p) && !(pdisplaygraphics->image_flags[p] & GRAPHICS_KEY))
+    {
+        alpha = 255 * (1 - display_animation);
+        SDL_SetAlpha(pdisplaygraphics->image[p][IMAGE_PIECE], SDL_SRCALPHA, alpha);
+    }
+    if(isnewexplosion(p))
+    {
+        p += PIECE_EXPLOSION_FIRST - PIECE_EXPLOSION_NEW_FIRST;
+        if(!(pdisplaygraphics->image_flags[p] & GRAPHICS_KEY))
+        {
+            alpha = 255 * display_animation;
+            SDL_SetAlpha(pdisplaygraphics->image[p][IMAGE_PIECE], SDL_SRCALPHA, alpha);
+        }
+    }
+
+    image = pdisplaygraphics->image[p][IMAGE_PIECE];
+
+    srect.x = 0;
+    srect.y = 0;
+    srect.w = pdisplaygraphics->size_x;
+    srect.h = pdisplaygraphics->size_y;
+
+    if(image->w > pdisplaygraphics->size_x)
+    {
+        xstart = 0;
+        xend = image->w / pdisplaygraphics->size_x;
+        xsize = 1;
+        xpos = 0;
+
+        if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL)
+            xend -= 4;
+
+        if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL16)
+            xsize = 16;
+
+        if(pdisplaygraphics->image_flags[p] & GRAPHICS_MOVER)
+        {
+            xsize = 5;
+
+            if(d == MOVE_LEFT)
+                xpos += 1;
+            if(d == MOVE_UP)
+                xpos += 2;
+            if(d == MOVE_RIGHT)
+                xpos += 3;
+            if(d == MOVE_DOWN)
+                xpos += 4;
+        }
+
+        /* If we're plotting the players */
+        if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
+        {
+            /* and there's an image for the swapped player */
+            if(xend > xstart + xsize)
+            {
+                /* then use it if the player is swapped out */
+                if(plevel->player != (p & 1) && plevel->player != 2)
+                    xpos += xsize;
+            }
+        }
+
+        if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL16)
+        {
+            i = 15;
+                b = level_data(plevel, x, y) & BEVEL_ALL;
+
+            if(b & BEVEL_U)
+                i -= 1;
+            if(b & BEVEL_R)
+                i -= 2;
+            if(b & BEVEL_D)
+                i -= 4;
+            if(b & BEVEL_L)
+                i -= 8;
+
+            xpos += i;
+        }
+
+        if(pdisplaygraphics->image_flags[p] & GRAPHICS_ANIMATE)
+        {
+            b = (xend - xstart) / xsize;
+
+            if(!isexplosion(p))
+                b = b * display_animation;
+            else
+                  b = b * ((display_animation + (isnewexplosion(op) ? 0 : 1)) * 0.5);
+
+            xpos += b * xsize;
+        }
+        else if(pdisplaygraphics->image_flags[p] & GRAPHICS_RANDOM)
+        {
+            b = (xend - xstart) / xsize;
+
+            if(p == PIECE_SPACE)
+                b = (level_data(plevel, x, y) & 0xff) % b;
+            else
+                b = ((level_data(plevel, x, y) & 0xff00) / 0x100) % b;
+
+            xpos += b * xsize;
+        }
+        else if(pdisplaygraphics->image_flags[p] & GRAPHICS_TILE)
+        {
+            b = x % ((xend - xstart) / xsize);
+            if(b < 0)
+                b += (xend - xstart) / xsize;
+            xpos += b * xsize;
+
+            b = y % (image->h / pdisplaygraphics->size_y);
+            if(b < 0)
+                b += image->h / pdisplaygraphics->size_y;
+            srect.y = b * pdisplaygraphics->size_y;
+        }
+
+        srect.x = (xstart + xpos) * pdisplaygraphics->size_x;
+    }
+
+    drect.x = px; 
+    drect.y = py;
+    drect.w = pdisplaygraphics->size_x;
+    drect.h = pdisplaygraphics->size_y;
+
+    /* Plot piece */
+    SDL_BlitSurface(image, &srect, screen_surface, &drect);
+
+    /* Plot bevelling */
+    if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL)
+    {
+        b = level_data(plevel, x, y) & BEVEL_ALL;
+        if(b != 0)
+        {
+            bsizex = pdisplaygraphics->size_x / 2;
+            bsizey = pdisplaygraphics->size_y / 2;
+            boffset = (xend - 1) * pdisplaygraphics->size_x;
+
+            for(i = 0; i < 4; i ++)
+                bimage[i] = 0;
+
+            if(b & BEVEL_L)
+            {
+                if(b & BEVEL_U)
+                    bimage[0] = 3 * pdisplaygraphics->size_x;
+                else
+                    bimage[0] = 1 * pdisplaygraphics->size_x;
+
+                if(b & BEVEL_D)
+                    bimage[2] = 3 * pdisplaygraphics->size_x;
+                else
+                    bimage[2] = 1 * pdisplaygraphics->size_x;
+            }
+            else
+            {
+                if(b & BEVEL_U)
+                    bimage[0] = 2 * pdisplaygraphics->size_x;
+                if(b & BEVEL_D)
+                    bimage[2] = 2 * pdisplaygraphics->size_x;
+            }
+
+            if(b & BEVEL_R)
+            {
+                if(b & BEVEL_U)
+                    bimage[1] = 3 * pdisplaygraphics->size_x;
+                else
+                    bimage[1] = 1 * pdisplaygraphics->size_x;
+
+                if(b & BEVEL_D)
+                    bimage[3] = 3 * pdisplaygraphics->size_x;
+                else
+                    bimage[3] = 1 * pdisplaygraphics->size_x;
+            }
+            else
+            {
+                if(b & BEVEL_U)
+                    bimage[1] = 2 * pdisplaygraphics->size_x;
+                if(b & BEVEL_D)
+                    bimage[3] = 2 * pdisplaygraphics->size_x;
+            }
+
+            if(b & BEVEL_TL)
+                bimage[0] = 4 * pdisplaygraphics->size_x;
+            if(b & BEVEL_TR)
+                bimage[1] = 4 * pdisplaygraphics->size_x;
+            if(b & BEVEL_BL)
+                bimage[2] = 4 * pdisplaygraphics->size_x;
+            if(b & BEVEL_BR)
+                bimage[3] = 4 * pdisplaygraphics->size_x;
+
+            for(i = 0; i < 4; i ++)
+            {
+                if(bimage[i] != 0)
+                {
+                    srect.x = boffset + bimage[i] + ((i & 1) ? bsizex : 0);
+                    srect.y = (i & 2) ? bsizey : 0;
+                    srect.w = bsizex;
+                    srect.h = bsizey;
+
+                    drect.x = px + ((i & 1) ? bsizex : 0);
+                    drect.y = py + ((i & 2) ? bsizey : 0);
+                    drect.w = bsizex;
+                    drect.h = bsizey;
+
+                    SDL_BlitSurface(image, &srect, screen_surface, &drect);
+                }
+            }
+        }
+    }
+}
+
+void display_redrawpiece(int p, int x, int y, int d)
+{
+    int dx, dy;
+
+    dx = x * pdisplaygraphics->size_x + display_offset_pixels_x;
+    dy = y * pdisplaygraphics->size_y + display_offset_pixels_y;
+    if(d != MOVE_NONE && p != PIECE_SPACE && !isexplosion(p))
+    {
+        if(d == MOVE_LEFT) { dx = dx - display_animation_x; }
+        if(d == MOVE_RIGHT) { dx = dx + display_animation_x; }
+        if(d == MOVE_UP) { dy = dy - display_animation_y; }
+        if(d == MOVE_DOWN) { dy = dy + display_animation_y; }
+    }
+
+    screen_redraw(dx, dy, pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+}
+
+void display_pieceabsolute(int p, int x, int y, int redraw)
+{
+    SDL_Rect srect;
+    SDL_Rect drect;
+
+    srect.x = 0;
+    srect.y = 0;
+    srect.w = pdisplaygraphics->size_x;
+    srect.h = pdisplaygraphics->size_y;
+
+    drect.x = x;
+    drect.y = y;
+    drect.w = pdisplaygraphics->size_x;
+    drect.h = pdisplaygraphics->size_y;
+
+    if(p != PIECE_SPACE && p!= PIECE_CURSOR)
+        SDL_BlitSurface(pdisplaygraphics->image[PIECE_SPACE][IMAGE_PIECE], &srect, screen_surface, &drect);
+
+    SDL_BlitSurface(pdisplaygraphics->image[p][IMAGE_PIECE], &srect, screen_surface, &drect);
+
+    if(redraw)
+        screen_redraw(x, y, pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+}
+
+int display_focus(struct level* plevel, int refocus)
+{
+    int ox, oy;
+    int px, py;
+
+    int maxx, maxy;
+
+#ifdef XOR_COMPATIBILITY
+    if(plevel->mode == MODE_XOR && options_xor_display)
+    {
+        ox = display_offset_pixels_x;
+        oy = display_offset_pixels_y;
+
+        display_start_x = plevel->view_x[plevel->player];
+        display_start_y = plevel->view_y[plevel->player];
+
+        display_end_x = display_start_x + 8;
+        display_end_y = display_start_y + 8;
+
+        display_offset_pixels_x = (screen_width - pdisplaygraphics->size_x * 8) / 2;
+        display_offset_pixels_y = (screen_height - display_bar_pixels - pdisplaygraphics->size_y * 8) / 2;
+
+        display_offset_pixels_x -= display_start_x * pdisplaygraphics->size_x;
+        display_offset_pixels_y -= display_start_y * pdisplaygraphics->size_y;
+
+        if(display_offset_pixels_x != ox || display_offset_pixels_y != oy)
+            return 1;
+        else
+            return 0;
+    }
+#endif
+
+    px = plevel->player_x[plevel->player] * pdisplaygraphics->size_x;
+    py = plevel->player_y[plevel->player] * pdisplaygraphics->size_y;
+    ox = display_offset_pixels_x;
+    oy = display_offset_pixels_y;
+
+    display_border_x = pdisplaygraphics->size_x * 3;
+    display_border_y = pdisplaygraphics->size_y * 3;
+
+    maxx = (plevel->size_x * pdisplaygraphics->size_x - screen_width);
+    maxy = (plevel->size_y * pdisplaygraphics->size_y - screen_height + display_bar_pixels);
+
+    if((plevel->size_x - 1) * pdisplaygraphics->size_x < screen_width)
+    {
+        display_offset_pixels_x = (screen_width - (plevel->size_x * pdisplaygraphics->size_x)) / 2;
+    }
+    else
+    {
+        if(refocus)
+        {
+            if(px < -(display_offset_pixels_x - display_border_x))
+                display_offset_pixels_x = -(px - display_border_x);
+            if(px >= -(display_offset_pixels_x - screen_width + display_border_x + pdisplaygraphics->size_x))
+                display_offset_pixels_x = -(px - screen_width + display_border_x + pdisplaygraphics->size_x);
+        }
+        
+        if(display_offset_pixels_x > 0)
+            display_offset_pixels_x = 0;
+        if(display_offset_pixels_x < -maxx)
+            display_offset_pixels_x = -maxx;
+
+    }
+
+    if((plevel->size_y - 1) * pdisplaygraphics->size_y < screen_height)
+    {
+        display_offset_pixels_y = (screen_height - display_bar_pixels - (plevel->size_y * pdisplaygraphics->size_y)) / 2;
+    }
+    else
+    {
+        if(refocus)
+        {
+            if(py < -(display_offset_pixels_y - display_border_y))
+                display_offset_pixels_y = -(py - display_border_y);
+            if(py >= -(display_offset_pixels_y - screen_height + display_bar_pixels + display_border_y + pdisplaygraphics->size_y))
+                display_offset_pixels_y = -(py - screen_height + display_bar_pixels + display_border_y + pdisplaygraphics->size_y);
+        }
+
+        if(display_offset_pixels_y > 0)
+            display_offset_pixels_y = 0;
+        if(display_offset_pixels_y < -maxy)
+            display_offset_pixels_y = -maxy;
+    }
+
+    /* Calculate start and end points */
+    display_start_x = -display_offset_pixels_x / pdisplaygraphics->size_x;
+    display_end_x = (-display_offset_pixels_x + screen_width + pdisplaygraphics->size_x - 1) / pdisplaygraphics->size_x;
+    if(display_offset_pixels_x > 0)
+        display_start_x --;
+
+    display_start_y = -display_offset_pixels_y / pdisplaygraphics->size_y;
+    display_end_y = (-display_offset_pixels_y + screen_height + pdisplaygraphics->size_y - display_bar_pixels - 1) / pdisplaygraphics->size_y;
+    if(display_offset_pixels_y > 0)
+        display_start_y --;
+
+    if(pdisplaygraphics->flags & GRAPHICS_BACKGROUND)
+    {
+        if(display_start_x < 0)
+            display_start_x = 0;
+        if(display_end_x > plevel->size_x)
+            display_end_x = plevel->size_x;
+        if(display_start_y < 0)
+            display_start_y = 0;
+        if(display_end_y > plevel->size_y)
+            display_end_y = plevel->size_y;
+    }
+
+    if(display_offset_pixels_x != ox || display_offset_pixels_y != oy)
+        return 1;
+    else
+        return 0;
+}
+
+#ifdef XOR_COMPATIBILITY
+void display_map_piece(struct level* plevel, int p, int x, int y, int redraw)
+{
+    SDL_Rect rect;
+
+    rect.x = xor_map_x_offset + x * xor_map_scale;
+    rect.y = xor_map_x_offset + y * xor_map_scale;
+    rect.w = xor_map_scale;
+    rect.h = xor_map_scale;
+
+    if(plevel->player != 2)
+    {
+    if(x < plevel->size_x / 2 && y < plevel->size_y / 2 && !(plevel->mapped & MAPPED_TOP_LEFT))
+        p = PIECE_UNKNOWN;
+    if(x >= plevel->size_x / 2 && y < plevel->size_y / 2 && !(plevel->mapped & MAPPED_TOP_RIGHT))
+        p = PIECE_UNKNOWN;
+    if(x < plevel->size_x / 2 && y >= plevel->size_y / 2 && !(plevel->mapped & MAPPED_BOTTOM_LEFT))
+        p = PIECE_UNKNOWN;
+    if(x >= plevel->size_x / 2 && y >= plevel->size_y / 2 && !(plevel->mapped & MAPPED_BOTTOM_RIGHT))
+        p = PIECE_UNKNOWN;
+    }
+
+    switch(p)
+    {
+        case PIECE_UNKNOWN:
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x00, 0x00, 0x00));
+            break;
+
+        case PIECE_WALL:
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x7f, 0x7f, 0x7f));
+            break;
+
+        case PIECE_STAR:
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0xff, 0xa0, 0x00));
+            if(xor_map_scale > 2)
+            {
+                rect.x ++;
+                rect.y ++;
+                rect.w -= 2;
+                rect.h -= 2;
+                SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0x33));
+            }
+            break;
+
+        case PIECE_DOOR:
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x00, 0x80, 0xff));
+            if(xor_map_scale > 2)
+            {
+                rect.x ++;
+                rect.y ++;
+                rect.w -= 2;
+                rect.h -= 2;
+                SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x66, 0xb3, 0xff));
+            }
+            break;
+
+        default:
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0xff));
+            break;
+    }
+
+    if(redraw)
+    {
+
+    rect.x = xor_map_x_offset + x * xor_map_scale;
+    rect.y = xor_map_x_offset + y * xor_map_scale;
+    rect.w = xor_map_scale;
+    rect.h = xor_map_scale;
+        screen_redraw(rect.x, rect.y, rect.w, rect.h);
+        }
+}
+
+void display_map(struct level* plevel)
+{
+    int i, j;
+
+    xor_map_scale = screen_width / 256;
+    if(xor_map_scale < 1)
+        xor_map_scale = 1;
+
+    display_clip(plevel, 0);
+
+    for(j = 0; j < plevel->size_y; j ++)
+    {
+        for(i = 0; i < plevel->size_y; i ++)
+        {
+            display_map_piece(plevel, level_piece(plevel, i, j), i, j, 0);
+        }
+    }
+
+    /* Redraw map */
+    screen_redraw(xor_map_x_offset, xor_map_y_offset, plevel->size_x * xor_map_scale, plevel->size_y * xor_map_scale);
+}
+#endif
+
+void display_level(struct level* plevel, int redraw)
+{
+    int x, y;
+    int p;
+    SDL_Rect rect;
+
+
+    display_focus(plevel, 0);
+
+   if(redraw == SCREENREDRAW_ALL && (
+          (pdisplaygraphics->flags & GRAPHICS_BACKGROUND)
+#ifdef XOR_COMPATIBILITY
+           || (plevel->mode == MODE_XOR && options_xor_display)
+#endif
+          ))
+    { 
+        rect.x = 0;
+        rect.y = 0;
+        rect.w = screen_width;
+        rect.h = screen_height - display_bar_pixels;
+
+#ifdef XOR_COMPATIBILITY
+        if(plevel->switched)
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+        else
+#endif
+            SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, pdisplaygraphics->background[0], pdisplaygraphics->background[1], pdisplaygraphics->background[2]));
+    }
+
+    if(pdisplaygraphics->shadows != NULL)
+    {
+        displayshadowed_level(plevel);
+#ifdef XOR_COMPATIBILITY
+    if(plevel->mode == MODE_XOR && options_xor_display)
+        display_map(plevel);
+#endif
+        return;
+    }
+
+    display_clip(plevel, 1);
+
+    for(y = display_start_y; y < display_end_y; y ++)
+    {
+        for(x = display_start_x; x < display_end_x; x ++)
+        {
+            p = level_piece(plevel, x, y);
+
+            /* Moving pieces will be redrawn shortly */
+            if(level_moving(plevel, x, y) != MOVE_NONE)
+                p = level_previous(plevel, x, y); 
+
+#ifdef XOR_COMPATIBILITY
+            if(plevel->switched && (p == PIECE_WALL || p == PIECE_SPACE))
+                p = PIECE_DARKNESS;
+#endif
+
+            /* If the piece is transparent, plot a background */
+            if(pdisplaygraphics->image[p][IMAGE_PIECE]->flags & SDL_SRCALPHA)
+            {
+#ifdef XOR_COMPATIBILITY
+                if(plevel->switched)
+                    display_piece(plevel, PIECE_DARKNESS, x, y, MOVE_NONE);
+                else
+#endif
+                    display_piece(plevel, PIECE_SPACE, x, y, MOVE_NONE);
+            }
+
+            /* Plot the piece itself */
+            display_piece(plevel, p, x, y, MOVE_NONE);
+        }
+    }
+
+    display_clip(plevel, 0);
+
+    if(plevel->mover_first != NULL)
+        display_movers(plevel, 0);
+
+#ifdef XOR_COMPATIBILITY
+    if(plevel->mode == MODE_XOR && options_xor_display)
+        display_map(plevel);
+#endif
+
+    screen_redraw(0, 0, screen_width, screen_height);
+
+}
+
+void display_title(struct level* plevel)
+{
+    SDL_Surface *psurface;
+    SDL_Surface *psurfacetest;
+    SDL_Rect drect;
+    int w;
+
+    if(options_debug & DEBUG_SPEED)
+        return;
+
+    if(plevel->title != NULL)
+    {
+        if((strncmp(gettext(plevel->title), "chroma", 6) == 0))
+            psurface = font_render(gettext(plevel->title), -8);
+        else
+            psurface = font_render(plevel->title, COLOUR_WHITE);
+
+        if(plevel->flags & LEVELFLAG_TESTING)
+        {
+            psurfacetest = font_render(gettext("testing: "), COLOUR_CYAN);
+            w = psurfacetest->w;
+
+            drect.x = (screen_width - psurface->w - w) / 2;
+            drect.y = screen_height - font_height;;
+            drect.w = psurfacetest->w;;
+            drect.h = psurfacetest->h;
+            SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+            SDL_BlitSurface(psurfacetest, NULL, screen_surface, &drect);
+            SDL_UpdateRects(screen_surface, 1, &drect);
+            SDL_FreeSurface(psurfacetest);
+        }
+        else
+            w = 0;
+
+        drect.x = ((screen_width - psurface->w - w) / 2) + w;
+        drect.y = screen_height - font_height;
+        drect.w = psurface->w;
+        drect.h = psurface->h;
+        SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+        SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+        SDL_UpdateRects(screen_surface, 1, &drect);
+        SDL_FreeSurface(psurface);
+    }
+}
+
+void display_moves(struct level* plevel, struct level* plevelreplay)
+{
+    static int length = 0;
+    char buffer[256];
+    SDL_Surface *psurface;
+    SDL_Surface *pimage;
+    SDL_Rect srect, drect;
+    int w;
+    int moves, moves2;
+
+    moves = 0;
+    if(plevel->move_current != NULL)
+        moves = plevel->move_current->count;
+
+    moves2 = -1;
+    if(plevelreplay != NULL)
+    {
+        moves2 = 0;
+        if(plevelreplay->move_last != NULL)
+            moves2 = plevelreplay->move_last->count;
+    }
+    else if(plevel->move_current != plevel->move_last)
+    {
+        if(plevel->move_last != NULL)
+            moves2 = plevel->move_last->count;
+    }
+
+    if(moves2 != -1)
+        sprintf(buffer, "%s%d/%d",
+                plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") :
+                plevelreplay != NULL ? gettext("replay ") : "",
+                moves, moves2);
+    else
+        sprintf(buffer, "%s%d",
+                plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") : "",
+                moves);
+
+    if(plevel->flags & LEVELFLAG_FAILED)
+        sprintf(buffer, gettext("failed"));
+
+    psurface = font_render(buffer, COLOUR_CYAN);
+
+    pimage = pdisplaygraphics->image[PIECE_PLAYER_ONE + plevel->player][IMAGE_SMALL];
+    if(pimage != NULL)
+        w = pdisplaygraphics->small_size_x;
+    else
+        w = 0;
+
+    drect.w = length > (psurface->w + w) ? length : (psurface->w + w);
+    drect.h = psurface->h;
+    drect.x = screen_width - drect.w;
+    drect.y = screen_height - font_height;
+    SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+    drect.x = screen_width - w - psurface->w;
+    SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+
+    if(pimage != NULL)
+    {
+        srect.x = 0;
+        srect.y = 0;
+        srect.w = pdisplaygraphics->small_size_x;
+        srect.h = pdisplaygraphics->small_size_y;
+
+        /* If there is a second small image, use it for a dead player */
+        if(plevel->alive[plevel->player] == 0 && pimage->w > pdisplaygraphics->small_size_x)
+            srect.x += pdisplaygraphics->small_size_x;
+
+        drect.x = screen_width - w;
+        if(pimage->h < font_height)
+            drect.y += (font_height - pimage->h) / 2;
+
+        SDL_BlitSurface(pimage, &srect, screen_surface, &drect);
+    }
+
+    drect.w = (length > psurface->w ? length : psurface->w) + w;
+    drect.h = psurface->h;
+    drect.x = screen_width - drect.w;
+    drect.y = screen_height - font_height;
+    SDL_UpdateRects(screen_surface, 1, &drect);
+    length = psurface->w + w;
+    SDL_FreeSurface(psurface);
+}
+
+void display_stars(struct level* plevel)
+{
+    static int length = 0;
+    char buffer[256];
+
+    SDL_Surface *psurface;
+    SDL_Rect drect;
+    SDL_Surface *pimage;
+    int w;
+    int p;
+
+    sprintf(buffer, "%d/%d", plevel->stars_caught, plevel->stars_total);
+
+    if(plevel->stars_exploded != 0)
+        sprintf(buffer, gettext("%d lost"), plevel->stars_exploded);
+
+    if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED))
+        sprintf(buffer, gettext("solved"));
+
+    psurface = font_render(buffer, COLOUR_YELLOW);
+
+    /* If solved, and there is a small door, use that */
+    if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED) && pdisplaygraphics->image[PIECE_DOOR][IMAGE_SMALL] != NULL)
+        p = PIECE_DOOR;
+    /* otherwise use a small star */
+    else
+        p = PIECE_STAR;
+
+    pimage = pdisplaygraphics->image[p][IMAGE_SMALL];
+
+    if(pimage != NULL)
+        w = pdisplaygraphics->small_size_x;
+    else
+        w = 0;
+
+    drect.w = length > psurface->w ? length : psurface->w + w;
+    drect.h = psurface->h;
+    drect.x = 0;
+    drect.y = screen_height - font_height;
+
+    SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+    drect.x = w;
+    SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+
+    if(pimage != NULL)
+    {
+        drect.x = 0;
+        if(pimage->h < font_height)
+            drect.y += (font_height - pimage->h) / 2;
+
+        SDL_BlitSurface(pimage, NULL, screen_surface, &drect);
+    }
+
+    drect.w = (length > psurface->w ? length : psurface->w) + w;
+    drect.h = psurface->h;
+    drect.x = 0;
+    drect.y = screen_height - font_height;
+    SDL_UpdateRects(screen_surface, 1, &drect);
+    length = psurface->w + w;
+    SDL_FreeSurface(psurface);
+}
+
+int display_bevelsquare(struct level* plevel, int x, int y)
+{
+    int bevel;
+
+    bevel = 0;
+
+    if(level_piece(plevel, x, y) == PIECE_WALL)
+    {
+        if(level_piece(plevel, x - 1, y) != PIECE_WALL)
+            bevel |= BEVEL_L;
+        if(level_piece(plevel, x + 1, y) != PIECE_WALL)
+            bevel |= BEVEL_R;
+        if(level_piece(plevel, x, y - 1) != PIECE_WALL)
+            bevel |= BEVEL_U;
+        if(level_piece(plevel, x, y + 1) != PIECE_WALL)
+            bevel |= BEVEL_D;
+
+        if(((bevel & (BEVEL_L | BEVEL_U)) == 0) && level_piece(plevel, x - 1, y - 1) != PIECE_WALL)
+            bevel |= BEVEL_TL;
+        if(((bevel & (BEVEL_R | BEVEL_U)) == 0) && level_piece(plevel, x + 1, y - 1) != PIECE_WALL)
+            bevel |= BEVEL_TR;
+        if(((bevel & (BEVEL_L | BEVEL_D)) == 0) && level_piece(plevel, x - 1, y + 1) != PIECE_WALL)
+            bevel |= BEVEL_BL;
+        if(((bevel & (BEVEL_R | BEVEL_D)) == 0) && level_piece(plevel, x + 1, y + 1) != PIECE_WALL)
+            bevel |= BEVEL_BR;
+    }
+    else
+    {
+        if(level_piece(plevel, x - 1, y) == PIECE_WALL)
+            bevel |= BEVEL_L;
+        if(level_piece(plevel, x + 1, y) == PIECE_WALL)
+            bevel |= BEVEL_R;
+        if(level_piece(plevel, x, y - 1) == PIECE_WALL)
+            bevel |= BEVEL_U;
+        if(level_piece(plevel, x, y + 1) == PIECE_WALL)
+            bevel |= BEVEL_D;
+
+        if(((bevel & (BEVEL_L | BEVEL_U)) == 0) && level_piece(plevel, x - 1, y - 1) == PIECE_WALL)
+            bevel |= BEVEL_TL;
+        if(((bevel & (BEVEL_R | BEVEL_U)) == 0) && level_piece(plevel, x + 1, y - 1) == PIECE_WALL)
+            bevel |= BEVEL_TR;
+        if(((bevel & (BEVEL_L | BEVEL_D)) == 0) && level_piece(plevel, x - 1, y + 1) == PIECE_WALL)
+            bevel |= BEVEL_BL;
+        if(((bevel & (BEVEL_R | BEVEL_D)) == 0) && level_piece(plevel, x + 1, y + 1) == PIECE_WALL)
+            bevel |= BEVEL_BR;
+    }
+
+    return bevel;
+}
+
+void display_bevellevel(struct level* plevel)
+{
+    int x, y;
+    int bevel;
+
+    for(x = 0; x < plevel->size_x; x ++)
+    {
+        for(y = 0; y < plevel->size_y; y ++)
+        {
+            bevel = level_data(plevel, x, y) & ~BEVEL_ALL;
+            bevel = bevel | display_bevelsquare(plevel, x, y);
+            level_setdata(plevel, x, y, bevel);
+        }
+    }
+}
+
+
+void display_movers(struct level* plevel, int redraw)
+{
+    struct mover* pmover;
+    int x, y, p, pm;
+    int i, j;
+    char buffer[16];
+    int bevel;
+    struct SDL_Surface *psurface;
+    SDL_Rect srect, drect;
+
+    if(pdisplaygraphics->shadows != NULL)
+    {
+        displayshadowed_movers(plevel, redraw);
+        return;
+    }
+
+    display_clip(plevel, 1);
+
+    display_animation_x = - pdisplaygraphics->size_x + (int)((float) pdisplaygraphics->size_x * display_animation);
+    display_animation_y = - pdisplaygraphics->size_y + (int)((float) pdisplaygraphics->size_y * display_animation);
+
+    /* First, plot spaces for all moving pieces */
+    pmover = plevel->mover_first;
+    while(pmover != NULL)
+    {
+#ifdef XOR_COMPATIBILITY
+        if(plevel->switched)
+            display_piece(plevel, PIECE_DARKNESS, pmover->x, pmover->y, MOVE_NONE);
+        else
+#endif
+            display_piece(plevel, PIECE_SPACE, pmover->x, pmover->y, MOVE_NONE);
+
+        pmover = pmover->next;
+    }
+
+    /* Plot moving piece */
+    pmover = plevel->mover_first;
+    while(pmover != NULL)
+    {
+        x = pmover->x;
+        y = pmover->y;
+
+        if(isexplosion(pmover->piece))
+        {
+            /* Plot any piece destroyed by the explosion, or the bomb itself */
+            p = level_previous(plevel, x, y);
+            pm = level_previousmoving(plevel, x, y);
+            if(p != PIECE_SPACE)
+                display_piece(plevel, p, x, y, pm);
+
+            /* Plot the detonator */
+            p = level_detonator(plevel, x, y);
+            pm = level_detonatormoving(plevel, x, y);
+            if(p != PIECE_SPACE)
+                display_piece(plevel, p, x, y, pm);
+        }
+        /* Spaces have already been covered */
+        else if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_GONE)
+        {
+            if(display_animation < 1)
+            {
+                /* Pieces being collected, earth being eaten */
+                p = level_previous(plevel, x, y);
+                pm = level_previousmoving(plevel, x, y);
+                if((p != PIECE_SPACE && !isexplosion(p) && pm == MOVE_NONE)
+#ifdef XOR_COMPATIBILITY
+                        || pmover->piece == PIECE_TELEPORT
+#endif
+                  )
+                    display_piece(plevel, p, x, y, pm);
+            }
+
+            display_piece(plevel, pmover->piece, x, y, pmover->direction);
+        }
+
+        pmover = pmover->next;
+    }
+
+    /* Plot explosions */
+    pmover = plevel->mover_first;
+    while(pmover != NULL)
+    {
+        x = pmover->x;
+        y = pmover->y;
+        /* Plot growing explosion */
+        if(isexplosion(pmover->piece))
+            display_piece(plevel, pmover->piece + PIECE_EXPLOSION_NEW_FIRST - PIECE_EXPLOSION_FIRST, x, y, MOVE_NONE);
+
+        /* Plot dying explosion */
+        p = level_previous(plevel, x, y);
+        if(isexplosion(p) && display_animation < 1)
+            display_piece(plevel, p, x, y, MOVE_NONE);
+
+        pmover = pmover->next;
+    }
+
+    /* Plot order of movers if debugging (but not if editing) */
+    if(options_debug & DEBUG_ORDER && display_animation < 1 && plevel->player != 2)
+    {
+        pmover = plevel->mover_first;
+        i = 0;
+        while(pmover != NULL)
+        {
+            if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_GONE)
+            {
+                pm = pmover->direction;
+                if(isexplosion(pmover->piece) || isnewexplosion(pmover->piece))
+                    pm = MOVE_NONE;
+    
+                x = pmover->x * pdisplaygraphics->size_x + display_offset_pixels_x + ((-1 + display_animation) * move_x[pm] * pdisplaygraphics->size_x);
+                y = pmover->y * pdisplaygraphics->size_y + display_offset_pixels_y + ((-1 + display_animation) * move_y[pm] * pdisplaygraphics->size_y);
+    
+                sprintf(buffer, "%X", i++);
+                switch(pmover->direction)
+                {
+                    case MOVE_UP:
+                        strcat(buffer, ARROW_UP);
+                        break;
+                    case MOVE_DOWN:
+                        strcat(buffer, ARROW_DOWN);
+                        break;
+                    case MOVE_LEFT:
+                        strcat(buffer, ARROW_LEFT);
+                        break;
+                    case MOVE_RIGHT:
+                        strcat(buffer, ARROW_RIGHT);
+                        break;
+                    default:
+                        break;
+                }
+                psurface = font_render(buffer, COLOUR_WHITE | COLOUR_BOLD);
+                srect.w = psurface->w > pdisplaygraphics->size_x ? pdisplaygraphics->size_x : psurface->w;
+                srect.h = psurface->h > pdisplaygraphics->size_y ? pdisplaygraphics->size_y : psurface->h;
+                srect.x = psurface->w - srect.w;
+                srect.y = 0;
+                drect.x = x + pdisplaygraphics->size_x - srect.w;
+                drect.y = y;
+                SDL_BlitSurface(psurface, &srect, screen_surface, &drect);
+                SDL_FreeSurface(psurface);
+            }
+            else
+                i ++;
+
+            pmover = pmover->next;
+        }
+    }
+
+    if(redraw == 0)
+        return;
+
+    /* Redraw screen */
+    pmover = plevel->mover_first;
+
+    while(pmover != NULL)
+    {
+        x = pmover->x;
+        y = pmover->y;
+        display_redrawpiece(pmover->piece, x, y, pmover->direction);
+
+        if(isexplosion(pmover->piece))
+        {
+            p = level_previous(plevel, x, y);
+            pm = level_previousmoving(plevel, x, y);
+            if(pm != MOVE_NONE && p != PIECE_SPACE)
+                display_redrawpiece(p, x, y, pm);
+
+            p = level_detonator(plevel, x, y);
+            pm = level_detonatormoving(plevel, x, y);
+            if(pm != MOVE_NONE && p != PIECE_SPACE)
+                display_redrawpiece(p, x, y, pm);
+        }
+
+        p = level_previous(plevel, x, y);
+        if(isexplosion(p))
+            display_redrawpiece(p, x, y, MOVE_NONE);
+
+        pmover = pmover->next;
+    }
+
+    /* At the peak of the explosion, rebevel any walls that have been
+       destroyed. When undoing, rebevel any walls that have been recreated. */
+    if(display_animation == 0 || display_animation == 1)
+    {
+        /* When undoing, we have to create the wall prior to rebevelling, as it
+           wouldn't otherwise exist until after the end of the animation. */
+        pmover = plevel->mover_first;
+        while(pmover != NULL)
+        {
+            if(pmover->piece == PIECE_WALL)
+                level_setpiece(plevel, pmover->x, pmover->y, pmover->piece);
+            pmover = pmover->next;
+        }
+
+        pmover = plevel->mover_first;
+        while(pmover != NULL)
+        {
+            x = pmover->x;
+            y = pmover->y;
+            if(pmover->piece == PIECE_WALL ||
+                (isexplosion(pmover->piece) && display_animation == 1))
+            {
+                for(i = -1; i < 2; i ++)
+                {
+                    for(j = - 1; j < 2; j ++)
+                    {
+                        bevel = display_bevelsquare(plevel, x + i, y + j);
+                        if(bevel != (level_data(plevel, x + i, y + j) & BEVEL_ALL))
+                        {
+                            level_setdata(plevel, x + i, y + j, bevel | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
+                            p = level_piece(plevel, x + i, y + j);
+                            if(p == PIECE_WALL)
+                            {
+#ifdef XOR_COMPATIBILITY
+                                if(plevel->switched)
+                                    display_piece(plevel, PIECE_DARKNESS, x + i, y + j, MOVE_NONE);
+                                else
+#endif
+                                    display_piece(plevel, PIECE_WALL, x + i, y + j, MOVE_NONE);
+                            }
+                            else
+                            {  
+#ifdef XOR_COMPATIBILITY
+                                if(plevel->switched)
+                                    display_piece(plevel, PIECE_DARKNESS, x + i, y + j, MOVE_NONE);
+                                else
+#endif
+                                    display_piece(plevel, PIECE_SPACE, x + i, y + j, MOVE_NONE);
+                                /* Moving pieces will be replotted when they next move. */
+                                if(p != PIECE_SPACE && level_moving(plevel, x + i, y + j) == MOVE_NONE)
+                                    display_piece(plevel, p, x + i, y + j, MOVE_NONE);
+                            }
+                            display_redrawpiece(p, x + i, y + j, MOVE_NONE);
+                        }
+                    }
+                }
+            }
+            pmover = pmover->next;
+        }
+    }
+
+    display_clip(plevel, 0);
+}
+
+void display_play(struct level* plevel, struct level* plevelreplay)
+{
+    SDL_Event event;
+    SDL_Surface *psurface;
+    SDL_Rect drect;
+    int quit;
+    int redraw;
+    int playermove;
+    struct mover* pmover;
+    Uint32 basetime;
+    Uint32 nowtime;
+    Uint32 pausetime = 0;
+    int delay, delayold;
+    int keymod;
+    int frames = 0;
+    int events;
+    char buffer[256];
+    int action;
+    int fast;
+
+    int mouse_x = 0;
+    int mouse_y = 0;
+    int mouse_destination_x = 0;
+    int mouse_destination_y = 0;
+    int mouse_button = 0;
+    int mouse_time;
+
+    int swap = 0;
+
+    graphics_reload();
+
+    display_bevellevel(plevel);
+
+    /* Force full redraw */
+    redraw = SCREENREDRAW_ALL;
+
+    playermove = MOVE_NONE;
+    action = ACTION_NONE;
+    delayold = options_sdl_delay;
+
+    mouse_time = SDL_GetTicks();
+
+    /* Force all animation to end */
+    basetime = SDL_GetTicks() - delayold;
+
+    keymod = 0;
+    fast = 0;
+
+    plevel->flags &= ~LEVELFLAG_PAUSED;
+
+    font_set_size(font_size_game);
+    display_bar_pixels = font_height;
+    display_focus(plevel, 1);
+
+    quit = 0;
+    while(!quit)
+    {
+        //////// Screen redraw ////////
+
+        if(redraw & SCREENREDRAW_ALL)
+        {
+            font_set_size(font_size_game);
+            display_bar_pixels = font_height;
+        }
+        if(redraw & SCREENREDRAW_BAR)
+        {
+            /* Clear bar */
+            drect.x = 0;
+            drect.y = screen_height - display_bar_pixels;
+            drect.w = screen_width;
+            drect.h = display_bar_pixels;
+            SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+
+            display_title(plevel);
+            display_stars(plevel);
+            display_moves(plevel, plevelreplay);
+        }
+        if(redraw & SCREENREDRAW_LEVEL)
+            display_level(plevel, redraw);
+
+        redraw = 0;
+        
+        //////// Delay calculation ////////
+
+        /* Calculate what the delay should be, defaulting to the Move Speed. */
+        delay = options_sdl_delay;
+        /* If we're replaying, use the Replay Speed */
+        if(plevelreplay != NULL && plevelreplay->moves != -1)
+            delay = options_sdl_replay_delay;
+        else
+        {
+            /* Otherwise */
+            pmover = plevel->mover_first;
+            while(pmover != NULL)
+            {
+                /* Use the Player Speed if the player is still moving */
+                if(pmover->piece == PIECE_PLAYER_ONE || pmover->piece == PIECE_PLAYER_TWO)
+                    delay = options_sdl_player_delay;
+                /* unless there's a piece following in their trail */
+                else if(pmover->piece != PIECE_SPACE && pmover->fast == 1)
+                    delay = options_sdl_delay;
+
+                pmover = pmover->next;
+            }
+            /* If we're undoing, use the Undo Speed */
+            if(plevel->flags & LEVELFLAG_UNDO)
+                delay = options_sdl_undo_delay;
+        }
+        /* If SHIFT is pressed, speed things up. */
+        if(keymod & 1)
+            delay = delay / 10;
+        /* If CTRL is pressed, slow things down. */
+        if(keymod & 2)
+            delay = delay * 4;
+        /* If the delay has changed, preserve our position in the animation */
+        if(delay != delayold)
+        {
+            nowtime = SDL_GetTicks();
+            if((plevel->flags & LEVELFLAG_PAUSED))
+                nowtime = pausetime;
+            if(delayold != 0)
+                basetime = nowtime - (((nowtime - basetime) * delay) / delayold );
+            delayold = delay;
+        }
+        if(fast && !(plevel->flags & LEVELFLAG_PAUSED))
+            basetime = 0;
+
+        //////// Movers ////////
+
+        /* If there are movers, plot and then evolve them */
+        if(plevel->mover_first != NULL)
+        {
+            nowtime = SDL_GetTicks();
+            if((plevel->flags & LEVELFLAG_PAUSED))
+                nowtime = pausetime;
+            display_animation = (float)(nowtime - basetime) / delay;
+            if(display_animation > 1)
+                display_animation = 1;
+            frames ++;
+            display_movers(plevel, 1);
+        }
+
+        if(plevel->mover_first != NULL)
+        {
+            /* Is it time for the next stage of this move? */
+            if(nowtime > basetime + delay && !(plevel->flags & LEVELFLAG_PAUSED))
+            {
+#ifdef XOR_COMPATIBILITY
+                if(plevel->mode == MODE_XOR && options_xor_display)
+                {
+                    pmover = plevel->mover_first;
+                    while(pmover != NULL)
+                    {
+                        display_map_piece(plevel, pmover->piece, pmover->x, pmover->y, 1);
+                        pmover = pmover->next;
+                    }
+                }
+#endif
+
+                /* Evolve movers */
+                if(!(plevel->flags & LEVELFLAG_UNDO))
+                {
+                    if(level_evolve(plevel))
+                        redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+                    level_storemovers(plevel);
+
+                    if(options_debug & DEBUG_SPEED)
+                    {
+                        sprintf(buffer, "    %4dfps (%d frames / %d ms)    ", 1000 * frames / (nowtime - basetime), frames, nowtime - basetime);
+                        psurface = font_render(buffer, COLOUR_WHITE);
+                        drect.x = (screen_width - psurface->w) / 2;
+                        drect.y = screen_height - font_height;
+                        drect.w = psurface->w;
+                        drect.h = psurface->h;
+                        SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+                        SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+                        SDL_UpdateRects(screen_surface, 1, &drect);
+                        SDL_FreeSurface(psurface);
+
+                        printf("%s\n", buffer);
+                    }
+                    frames = 0;
+                }
+                else
+                {
+                    if(level_undo(plevel))
+                        plevel->flags |= LEVELFLAG_UNDO;
+                    else
+                        plevel->flags &= ~LEVELFLAG_UNDO;
+
+                        /* Refocus in case we've moved offscreen */
+                        redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+                }
+                
+                basetime = SDL_GetTicks();
+
+                /* Reset animation in case we redraw before it is next calculated */
+                display_animation = 0;
+            }
+        }
+
+        //////// Events ////////
+
+        /* Hide the mouse if not used whilst in full screen mode */
+        if(SDL_GetTicks() > mouse_time + MOUSE_TIMEOUT_MOVE)
+            screen_cursor(0);
+
+        /* Poll if there are movers, otherwise wait so as to reduce load */
+        if(plevel->mover_first != NULL || SDL_GetTicks() < basetime + delay)
+            events = SDL_PollEvent(&event);
+        else
+            events = SDL_WaitEvent(&event);
+
+        while(events)
+        {
+            switch(event.type)
+            {
+                case SDL_KEYDOWN:
+    
+                    switch(actions[event.key.keysym.sym])
+                    {   
+                        case ACTION_FASTER:
+                            keymod |= 1;
+                            break;
+    
+                        case ACTION_SLOWER:
+                            keymod |= 2;
+                            break;
+    
+                        case ACTION_QUIT:
+                            quit = 1;
+                            break;
+    
+                        case ACTION_FAST:
+                            fast = 1;
+                            break;
+    
+                        case ACTION_PAUSE:
+                            if(plevel->flags & LEVELFLAG_PAUSED)
+                            {
+                                plevel->flags &= ~LEVELFLAG_PAUSED;
+                                basetime = SDL_GetTicks() - (pausetime - basetime);
+                            }
+                            else
+                            {
+                                plevel->flags |= LEVELFLAG_PAUSED;
+                                pausetime = SDL_GetTicks();
+                            }
+                            redraw |= SCREENREDRAW_BAR;
+                            break;
+    
+                        case ACTION_REDRAW:
+                            redraw |= SCREENREDRAW_ALL;
+                            break;
+    
+                        case ACTION_HIDE:
+                            SDL_WM_IconifyWindow();
+                            break;
+    
+                        case ACTION_UNDO:
+                            if(plevelreplay != NULL)
+                            {
+                                plevelreplay->flags |= LEVELFLAG_UNDO;
+                                plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+                            }
+                            else
+                                action = ACTION_UNDO;
+                            break;
+    
+    
+                    case ACTION_REDO:
+                            if(plevelreplay!= NULL)
+                            {
+                                plevelreplay->flags &= ~LEVELFLAG_UNDO;
+                                plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+                            }
+                            else
+                                action = ACTION_REDO;
+                            break;
+    
+                        case ACTION_LEFT:
+                            if(plevelreplay != NULL)
+                            {
+                                plevelreplay->flags |= LEVELFLAG_UNDO;
+                                plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+                            }
+                            else
+                                action = ACTION_LEFT;
+                            break;
+    
+                        case ACTION_RIGHT:
+                            if(plevelreplay != NULL)
+                            {
+                                plevelreplay->flags &= ~LEVELFLAG_UNDO;
+                                plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+                            }
+                            else
+                                action = ACTION_RIGHT;
+                            break;
+    
+                        case ACTION_UP:
+                            if(plevelreplay != NULL)
+                                plevelreplay->flags |= LEVELFLAG_PAUSED;
+                            else
+                                action = ACTION_UP;
+                            break;
+    
+                        case ACTION_DOWN:
+                            if(plevelreplay != NULL)
+                                plevelreplay->flags |= LEVELFLAG_PAUSED;
+                            else
+                                action = ACTION_DOWN;
+                            break;
+    
+                        case ACTION_SWAP:
+                            action = ACTION_SWAP;
+                            break;
+    
+                        default:
+                            break;
+                    }
+                    break;
+    
+                case SDL_KEYUP:
+                    switch(actions[event.key.keysym.sym])
+                    {   
+                        case ACTION_SWAP:
+                            swap = 0;
+                        case ACTION_UP:
+                        case ACTION_DOWN:
+                        case ACTION_LEFT:
+                        case ACTION_RIGHT:
+                        case ACTION_UNDO:
+                        case ACTION_REDO:
+                            action = ACTION_NONE;
+                            break;
+    
+                           case ACTION_FASTER:
+                            keymod &= ~1;
+                            break;
+    
+                        case ACTION_SLOWER:
+                            keymod &= ~2;
+                            break;
+
+                        case ACTION_FAST:
+                            fast = 0;
+                            break;
+    
+                        default:
+                            break;
+                    }
+                    break;
+    
+                case SDL_QUIT:
+                    exit(0);
+    
+                case SDL_VIDEORESIZE:
+                    screen_resizeevent(&event);
+                    redraw = SCREENREDRAW_ALL;
+                    break;
+
+                case SDL_ACTIVEEVENT:
+                    if((event.active.state & SDL_APPACTIVE) && event.active.gain == 1)
+                        redraw = SCREENREDRAW_ALL;
+                    break;
+    
+                case SDL_MOUSEBUTTONDOWN:
+                    mouse_x = event.button.x;
+                    mouse_y = event.button.y;
+                    mouse_button = event.button.button;
+                    mouse_time = SDL_GetTicks();
+                    screen_cursor(1);
+
+                    if(mouse_button > 0 && mouse_button < MOUSE_BUTTONS_MAX)
+                        action = actions_mouse[ACTIONS_GAME][mouse_button];
+
+                    if(action == ACTION_HIDE)
+                    {
+                        SDL_WM_IconifyWindow();
+                        action = ACTION_NONE;
+                    }
+                    if(action == ACTION_QUIT)
+                        quit = 1;
+                    break;
+
+                case SDL_MOUSEBUTTONUP:
+                    if(action == ACTION_MOUSE_CLICK ||
+                            (action == ACTION_MOUSE_DRAG_OR_CLICK && (SDL_GetTicks() < mouse_time + MOUSE_TIMEOUT_CLICK)))
+                    {
+                        mouse_destination_x = (mouse_x - display_offset_pixels_x) / pdisplaygraphics->size_x;
+                        mouse_destination_y = (mouse_y - display_offset_pixels_y) / pdisplaygraphics->size_y;
+                        if(mouse_destination_x == plevel->player_x[plevel->player])
+                        {
+                            if(mouse_destination_y < plevel->player_y[plevel->player])
+                                action = ACTION_UP;
+                            if(mouse_destination_y > plevel->player_y[plevel->player])
+                                action = ACTION_DOWN;
+                        }
+                        if(mouse_destination_y == plevel->player_y[plevel->player])
+                        {
+                            if(mouse_destination_x < plevel->player_x[plevel->player])
+                                action = ACTION_LEFT;
+                            if(mouse_destination_x > plevel->player_x[plevel->player])
+                                action = ACTION_RIGHT;
+                        }
+                        if(mouse_destination_x == plevel->player_x[1 - plevel->player] && mouse_destination_y == plevel->player_y[1 - plevel->player])
+                            action = ACTION_SWAP;
+                    }
+                    /* Buttons 4 and 5 can't be held down */
+                    else if(mouse_button != 4 && mouse_button != 5)
+                    {
+                        mouse_button = 0;
+                        action = ACTION_NONE;
+                    }
+                    break;
+                case SDL_MOUSEMOTION:
+                    /* Are we dragging? */
+                    if(action == ACTION_MOUSE_DRAG || action == ACTION_MOUSE_DRAG_OR_CLICK)
+                    {
+                        display_offset_pixels_y -= (mouse_y - event.motion.y);
+                        display_offset_pixels_x -= (mouse_x - event.motion.x);
+
+                        mouse_x = event.motion.x;
+                        mouse_y = event.motion.y;
+
+                        redraw |= SCREENREDRAW_LEVEL;
+                    }
+                    else
+                    {
+                        mouse_time = SDL_GetTicks();
+                        screen_cursor(1);
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+            events = SDL_PollEvent(&event);
+        }
+
+        //////// Actions ////////
+
+        /* Are we replaying the level? */
+        if(plevelreplay != NULL)
+        {
+            /* Prevent the user from moving during the replay */
+            playermove = MOVE_NONE;
+
+            /* Is it time for another move? */
+            if(plevel->mover_first == NULL && !(plevelreplay->flags & LEVELFLAG_PAUSED))
+            {
+                /* Moving backwards through replay */
+                if(plevelreplay->flags & LEVELFLAG_UNDO)
+                {
+                    if(level_undo(plevel))
+                    {
+                        plevel->flags |= LEVELFLAG_UNDO;
+                        if(plevelreplay->move_current != NULL)
+                            plevelreplay->move_current = plevelreplay->move_current->previous;
+                        else
+                            plevelreplay->move_current = plevelreplay->move_last;
+                    }
+                    else
+                        plevel->flags &= ~LEVELFLAG_UNDO;
+                }
+                /* Moving forwards through replay */
+                else
+                {
+                    if(plevelreplay->move_current != NULL)
+                    {
+                        playermove = plevelreplay->move_current->direction;
+                        plevelreplay->move_current = plevelreplay->move_current->next;
+                    }
+                }
+            }
+        }
+        /* otherwise, see what action the user has asked for */
+        else
+        {
+            playermove = MOVE_NONE;
+
+            switch(action)
+            {
+                case ACTION_LEFT:
+                    playermove = MOVE_LEFT;
+                    break;
+                case ACTION_RIGHT:
+                    playermove = MOVE_RIGHT;
+                    break;
+                case ACTION_UP:
+                    playermove = MOVE_UP;
+                    break;
+                case ACTION_DOWN:
+                    playermove = MOVE_DOWN;
+                    break;
+                case ACTION_SWAP:
+                    /* Swap only once per keypress */
+                    if(plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_PAUSED) && !swap)
+                    {
+                          playermove = MOVE_SWAP;
+                        swap = 1;
+                    }
+                    break;
+                case ACTION_UNDO:
+                    if(plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_UNDO))
+                    {
+                        if(level_undo(plevel))
+                            plevel->flags |= LEVELFLAG_UNDO;
+                        else
+                            plevel->flags &= ~LEVELFLAG_UNDO;
+                        playermove = MOVE_NONE;
+                        basetime = SDL_GetTicks();
+
+                        /* Refocus in case we've moved offscreen */
+                        redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+                    }
+                    break;
+                case ACTION_REDO:
+                    playermove = MOVE_REDO;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        if(mouse_button == 4 || mouse_button == 5)
+        {
+            action = ACTION_NONE;
+            mouse_button = 0;
+        }
+
+        /* Can't move if we've failed or solved the level */
+        if(plevel->flags & (LEVELFLAG_FAILED | LEVELFLAG_SOLVED))
+            playermove = MOVE_NONE;
+
+        /* If we can move, make the move */
+        if(playermove != MOVE_NONE && plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_PAUSED))
+        {
+            level_move(plevel, playermove);
+            basetime = SDL_GetTicks();
+            redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+
+            if(mouse_destination_x != 0 || mouse_destination_y != 0)
+            {
+                /* Have we reached our destination, or been blocked? */
+                if((plevel->player_x[plevel->player] == mouse_destination_x && plevel->player_y[plevel->player] == mouse_destination_y) || plevel->mover_first == NULL)
+                {
+                    action = ACTION_NONE;
+                    mouse_destination_x = 0;
+                    mouse_destination_y = 0;
+                }
+            }
+        }
+
+        //////// Display changes ////////
+
+        if(plevel->flags & LEVELFLAG_MOVES)
+        {
+            display_moves(plevel, plevelreplay);
+            plevel->flags &= ~LEVELFLAG_MOVES;
+        }
+
+        if(plevel->flags & LEVELFLAG_STARS)
+        {
+            display_stars(plevel);
+            plevel->flags &= ~LEVELFLAG_STARS;
+        }
+
+#ifdef XOR_COMPATIBILITY
+        if(plevel->flags & LEVELFLAG_SWITCH)
+        {
+            redraw |= SCREENREDRAW_ALL; 
+            plevel->flags &= ~LEVELFLAG_SWITCH;
+        }
+
+        if(plevel->flags & LEVELFLAG_MAP)
+        {
+            plevel->flags &= ~LEVELFLAG_MAP;
+            if(plevel->mode == MODE_XOR && options_xor_display)
+                display_map(plevel);
+        }
+#endif
+
+
+        if(!(plevel->flags & LEVELFLAG_SOLVED) && plevel->flags & LEVELFLAG_EXIT)
+        {
+            redraw |= SCREENREDRAW_BAR;
+            plevel->flags |= LEVELFLAG_SOLVED;
+        }
+
+        if(!(plevel->flags & LEVELFLAG_FAILED) && plevel->alive[0] == 0 && plevel->alive[1] ==0)
+        {
+            redraw |= SCREENREDRAW_BAR;
+            plevel->flags |= LEVELFLAG_FAILED;
+        }
+    }
+
+    screen_cursor(1);
+}
+
+void display_edit(struct level* plevel)
+{
+    int quit;
+
+    struct mover* pmover;
+    struct mover* pmovertmp;
+
+    static int editor_piece = 0;
+    int redraw = 0, predraw = 0, moved = 0, pmoved = 0;
+    int i, j;
+    int player;
+
+    int ex, ey, ep;
+    int x, y;
+    int bevel;
+    int piece_start = 0;
+    int piece_end = 0;
+    int piece_width = 0;
+    int piece_count = 0;
+    int action;
+    int effect;
+
+    SDL_Event event;
+    SDL_Rect drect;
+    SDL_Surface *psurface;
+
+    int mouse_x, mouse_y;
+    int dx, dy;
+
+    int mouse_button;
+    int mouse_time;
+
+    font_set_size(font_size_game);
+    graphics_reload();
+
+    display_bevellevel(plevel);
+
+    piece_count = 0;
+    while(editor_piece_maps[plevel->mode][piece_count] != PIECE_GONE)
+        piece_count ++;
+
+    if(editor_piece > piece_count)
+        editor_piece = 0;
+
+    /* The editor uses player 2, so store the player value */
+    player = plevel->player;
+
+    ex = plevel->player_x[2];
+    ey = plevel->player_y[2];
+    ep = editor_piece;
+
+    mouse_x = 0;
+    mouse_y = 0;
+    mouse_button = 0;
+    mouse_time = SDL_GetTicks();
+    action = 0;
+    effect = 0;
+
+    /* Force a complete redraw */
+    redraw = 2;
+
+    display_focus(plevel, 1);
+
+    quit = 0;
+    while(!quit)
+    {
+        plevel->player_x[2] = plevel->player_x[2];
+        plevel->player_y[2] = plevel->player_y[2];
+        plevel->player = 2;
+
+#ifdef XOR_COMPATIBILITY
+        if(plevel->mode == MODE_XOR)
+            xor_focus(plevel);
+#endif
+
+        /* Clear screen, and calculate size of bar */
+        if(redraw == 2)
+        {
+            screen_clear(pdisplaygraphics->background[0], pdisplaygraphics->background[1], pdisplaygraphics->background[2]);
+
+            piece_width = (screen_width - pdisplaygraphics->size_x - 2) / pdisplaygraphics->size_x;
+
+            display_bar_pixels = pdisplaygraphics->size_y + 1;
+
+            if(piece_width < piece_count)
+            {
+                if(display_bar_pixels < font_height)
+                    display_bar_pixels = font_height;
+                piece_width = (screen_width - pdisplaygraphics->size_x - 2) / pdisplaygraphics->size_x;
+            }
+            predraw = 2;
+        }
+
+        if(moved == 1)
+            redraw |= display_focus(plevel, 1);
+        
+        /* Draw level */
+        if(redraw)
+        {
+            display_level(plevel, SCREENREDRAW_ALL);
+
+            redraw = 0;
+            moved = 1;
+        }
+
+        /* Recalculate piece bar */
+        if(pmoved || predraw)
+        {
+            if(piece_width >= piece_count)
+            {
+                piece_start =0;
+                piece_end = piece_count;
+            }
+            else
+            {
+                while(editor_piece < piece_start + 3 && piece_start > 0)
+                {
+                    piece_start --;
+                    piece_end = piece_start + piece_width;
+
+                    predraw = 1;
+                }
+
+                while(editor_piece >= piece_start + piece_width - 3 && piece_end < piece_count)
+                {
+                    piece_start ++;
+                    piece_end = piece_start + piece_width;
+
+                    predraw = 1;
+                }
+
+                piece_end = piece_start + piece_width;
+                if(piece_end > piece_count)
+                    piece_end = piece_count;
+            }
+        }
+
+        /* Redraw all of piece bar */
+        if(predraw)
+        {
+            drect.x = 0;
+            drect.w = screen_width;
+            drect.y = screen_height - display_bar_pixels;
+            drect.h = display_bar_pixels;
+            SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+
+            drect.x = 0;
+            drect.w = screen_width - pdisplaygraphics->size_x - 2;
+            drect.y = screen_height - pdisplaygraphics->size_y;
+            drect.h = pdisplaygraphics->size_y;
+            SDL_SetClipRect(screen_surface, &drect);
+            for(i = piece_start; i < piece_end; i ++)
+            {
+                display_pieceabsolute(editor_piece_maps[plevel->mode][i], (i - piece_start) * pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, 0);
+            }
+            if(piece_start != 0)
+            {
+                psurface = font_render(ARROW_LEFT, COLOUR_WHITE | COLOUR_BOLD);
+                drect.x = 0;
+                drect.y = screen_height - (pdisplaygraphics->size_y + font_height) / 2;
+                SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+                SDL_FreeSurface(psurface);
+            }
+            if(piece_end != piece_count)
+            {
+                psurface = font_render(ARROW_RIGHT, COLOUR_WHITE | COLOUR_BOLD);
+                drect.x = piece_width * pdisplaygraphics->size_x - 2 - psurface->w;
+                drect.y = screen_height - (pdisplaygraphics->size_y + font_height) / 2;
+                SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+                SDL_FreeSurface(psurface);
+            }
+            SDL_SetClipRect(screen_surface, NULL);
+
+            drect.x = 0;
+            drect.w = screen_width;
+            drect.y = screen_height - pdisplaygraphics->size_y;
+            drect.h = pdisplaygraphics->size_y;
+            SDL_UpdateRects(screen_surface, 1, &drect);
+
+            pmoved = 1;
+        }
+
+        /* Redraw piece bar cursor */
+        if(pmoved)
+        {
+            pmoved = 0;
+
+            /* Remove cursor from previous piece */
+            if(!predraw)
+                display_pieceabsolute(editor_piece_maps[plevel->mode][ep], (ep - piece_start) * pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, 1);
+
+            /* Plot cursor */
+            display_pieceabsolute(PIECE_CURSOR, (editor_piece - piece_start) * pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, predraw ? 0 : 1);
+            
+            /* Plot current piece in far right corner */
+            display_pieceabsolute(editor_piece_maps[plevel->mode][editor_piece], screen_width - pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, predraw ? 0 : 1);
+        }
+
+        if(predraw)
+        {
+            drect.x = 0;
+            drect.w = screen_width;
+            drect.y = screen_height - display_bar_pixels;
+            drect.h = display_bar_pixels;
+            SDL_UpdateRects(screen_surface, 1, &drect);
+            predraw = 0;
+        }
+
+        /* Redraw level cursor */
+        if(moved)
+        {
+            moved = 0;
+
+            /* Create cosmetic movers */
+            level_setprevious(plevel, plevel->player_x[2], plevel->player_y[2], level_piece(plevel, plevel->player_x[2], plevel->player_y[2]));
+            if(pdisplaygraphics->shadows != NULL)
+                mover_newundo(plevel, ex, ey, MOVE_NONE, PIECE_SPACE, PIECE_SPACE, MOVER_UNDO); 
+            else
+                mover_newundo(plevel, ex, ey, MOVE_NONE, level_piece(plevel, ex, ey), PIECE_SPACE, MOVER_UNDO);
+            mover_newundo(plevel, plevel->player_x[2], plevel->player_y[2], MOVE_NONE, PIECE_CURSOR, PIECE_SPACE, MOVER_UNDO);
+            display_animation = 0.5;
+            display_movers(plevel, 1);
+            level_setmoving(plevel, ex, ey, MOVE_NONE);
+            level_setmoving(plevel, plevel->player_x[2], plevel->player_y[2], MOVE_NONE);
+            level_setprevious(plevel, plevel->player_x[2], plevel->player_y[2], PIECE_SPACE);
+
+            /* Delete cosmetic movers */
+            pmover = plevel->mover_first;
+            while(pmover != NULL)
+            {   
+#ifdef XOR_COMPATIBILITY
+                /* Update map if present */
+                if(options_xor_display && pmover->piece != PIECE_CURSOR)
+                    display_map_piece(plevel, pmover->piece, pmover->x, pmover->y, 1);
+#endif
+                pmovertmp = pmover;
+                pmover = pmover->next;
+                free(pmovertmp);
+            }
+            plevel->mover_first = NULL;
+            plevel->mover_last = NULL;
+        }
+
+        SDL_WaitEvent(&event);
+
+        /* Hide the mouse if not used whilst in full screen mode */
+        if(SDL_GetTicks() > mouse_time + MOUSE_TIMEOUT_MOVE)
+            screen_cursor(0);
+
+        /* Store previous cursor location for redrawing damaged areas */
+        ex = plevel->player_x[2];
+        ey = plevel->player_y[2];
+        ep = editor_piece;
+
+        switch(event.type)
+        {
+            case SDL_KEYDOWN:
+                switch(actions[event.key.keysym.sym])
+                {
+                    case ACTION_QUIT:
+                        quit = 1;
+                        break;
+
+                    case ACTION_HIDE:
+                           SDL_WM_IconifyWindow();
+                        break;
+        
+                    case ACTION_LEFT:
+                        if(plevel->player_x[2] > 0)
+                        {
+                            plevel->player_x[2] --; moved = 1;
+                        }
+                        break;
+
+                    case ACTION_RIGHT:
+                        if(plevel->player_x[2] < plevel->size_x - 1)
+                        {
+                            plevel->player_x[2] ++; moved = 1;
+                        }
+                        break;
+
+                    case ACTION_UP:
+                        if(plevel->player_y[2] > 0)
+                        {
+                            plevel->player_y[2] --; moved = 1;
+                        }
+                        break;
+
+                    case ACTION_DOWN:
+                        if(plevel->player_y[2] < plevel->size_y -1)
+                        {
+                            plevel->player_y[2] ++; moved = 1;
+                        }
+                        break;
+
+                    case ACTION_PIECE_LEFT:
+                        effect = ACTION_PIECE_LEFT;
+                        break;
+
+                    case ACTION_PIECE_RIGHT:
+                        effect = ACTION_PIECE_RIGHT;
+                        break;
+
+                    case ACTION_SWAP:
+                        effect = ACTION_SWAP;
+                        moved = 1;
+                        break;
+
+                    default:
+                        break;
+                }
+            break;
+
+
+                case SDL_MOUSEBUTTONDOWN:
+                    mouse_x = event.button.x;
+                    mouse_y = event.button.y;
+                    mouse_button = event.button.button;
+                    mouse_time = SDL_GetTicks();
+                    screen_cursor(1);
+
+                    if(mouse_button > 0 && mouse_button < MOUSE_BUTTONS_MAX)
+                        action = actions_mouse[ACTIONS_EDIT][mouse_button];
+
+                    switch(action)
+                    {
+                        case ACTION_HIDE:
+                            SDL_WM_IconifyWindow();
+                            action = ACTION_NONE;
+                            break;
+
+                        case ACTION_QUIT:
+                            quit = 1;
+                            break;
+
+                        case ACTION_PIECE_LEFT:
+                            effect = ACTION_PIECE_LEFT;
+                            break;
+
+                        case ACTION_PIECE_RIGHT:
+                            effect = ACTION_PIECE_RIGHT;
+                            break;
+
+                        default:
+                            break;
+                    }
+                    break;
+
+                case SDL_MOUSEBUTTONUP:
+                    if(action == ACTION_MOUSE_CLICK ||
+                            (action == ACTION_MOUSE_DRAG_OR_CLICK && ((SDL_GetTicks() < mouse_time + MOUSE_TIMEOUT_CLICK) || (mouse_y > (screen_height - display_bar_pixels)))))
+                    { 
+                        /* Have they clicked within the piece bar? */
+                        if(mouse_y > (screen_height - display_bar_pixels))
+                        {
+                            editor_piece = piece_start + ((mouse_x) / pdisplaygraphics->size_x);
+                            pmoved = 1;
+                        }
+                        else
+                        {
+                            plevel->player_x[2] = (mouse_x - display_offset_pixels_x) / pdisplaygraphics->size_x;
+                            plevel->player_y[2] = (mouse_y - display_offset_pixels_y) / pdisplaygraphics->size_y;
+                            moved = 2;
+                            effect = ACTION_SWAP;
+                        }
+                    }
+
+                    /* Buttons 4 and 5 can't be held down */
+                    if(mouse_button != 4 && mouse_button != 5)
+                    {  
+                        mouse_button = 0;
+                        action = ACTION_NONE;
+                    }
+                    break;
+
+                case SDL_MOUSEMOTION:
+                    mouse_time = SDL_GetTicks();
+                    screen_cursor(1);
+
+                    if(action == ACTION_MOUSE_DRAG || action == ACTION_MOUSE_DRAG_OR_CLICK)
+                    {  
+                        dx = mouse_x - event.motion.x;
+                        dy = mouse_y - event.motion.y;
+
+                        mouse_x = event.motion.x;
+                        mouse_y = event.motion.y;
+
+                        display_offset_pixels_y -= dy;
+                        display_offset_pixels_x -= dx;
+
+                        redraw |= 1;
+                    }
+                    else if(action == ACTION_MOUSE_CLICK)
+                    { 
+
+                        mouse_x = event.motion.x;
+                        mouse_y = event.motion.y;
+                        /* Have they clicked within the piece bar? */
+                        if(mouse_y > (screen_height - display_bar_pixels))
+                        {
+                            editor_piece = piece_start + ((mouse_x) / pdisplaygraphics->size_x);
+                            pmoved = 1;
+                        }
+                        else
+                        {
+                            plevel->player_x[2] = (mouse_x - display_offset_pixels_x) / pdisplaygraphics->size_x;
+                            plevel->player_y[2] = (mouse_y - display_offset_pixels_y) / pdisplaygraphics->size_y;
+                            moved = 2;
+                            effect = ACTION_SWAP;
+                        }
+                    }
+                    break;
+
+            case SDL_QUIT:
+                exit(0);
+
+            case SDL_VIDEORESIZE:
+                screen_resizeevent(&event);
+                redraw = 2;
+                break;
+
+            case SDL_ACTIVEEVENT:
+                if((event.active.state & SDL_APPACTIVE) && event.active.gain == 1)
+                    redraw = 2;
+                break;
+        }
+
+        switch(effect)
+        {
+            case ACTION_PIECE_LEFT:
+                editor_piece --;
+                if(editor_piece < 0)
+                    editor_piece = piece_count - 1;
+                pmoved = 1;
+                break;
+
+            case ACTION_PIECE_RIGHT:
+                editor_piece ++;
+                if(editor_piece >= piece_count)
+                    editor_piece = 0;
+                pmoved = 1;
+                break;
+
+            case ACTION_SWAP:
+                bevel = 0;
+                if(editor_piece_maps[plevel->mode][editor_piece] == PIECE_WALL || level_piece(plevel, plevel->player_x[2], plevel->player_y[2]) == PIECE_WALL)
+                    bevel = 1;
+                /* Don't allow the edges to be changed */
+                if(!(plevel->player_x[2] < 1 || plevel->player_x[2] > plevel->size_x - 2 || plevel->player_y[2] < 1 || plevel->player_y[2] > plevel->size_y - 2))
+                    level_setpiece(plevel, plevel->player_x[2], plevel->player_y[2], editor_piece_maps[plevel->mode][editor_piece]);
+
+                /* Rebevel if necessary */
+                if(bevel == 1)
+                {
+                    x = plevel->player_x[2]; y = plevel->player_y[2];
+                    for(i = -1; i < 2; i ++)
+                    {   
+                        for(j = - 1; j < 2; j ++)
+                        {   
+                                bevel = display_bevelsquare(plevel, x + i, y + j);
+                                if(bevel != (level_data(plevel, x + i, y + j) & BEVEL_ALL))
+                                {   
+                                    level_setdata(plevel, x + i, y + j, bevel | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
+                                    /* Redraw changed piece */
+                                    /* The mover will get deleted when next redrawn */
+                                    if(pdisplaygraphics->shadows != NULL)
+                                        mover_newundo(plevel, x + i, y + j, MOVE_NONE, PIECE_SPACE, PIECE_SPACE, MOVER_UNDO); 
+                                    else
+                                        mover_newundo(plevel, x + i, y + j, MOVE_NONE, level_piece(plevel, x + i, y + j), PIECE_SPACE, MOVER_UNDO);
+                                }
+                            }
+                        }
+                    }
+                break;
+
+            default:
+                break;
+        }
+        effect = ACTION_NONE;
+    }
+
+    /* Restore real player value */
+    plevel->player = player;
+    screen_cursor(1);
+}
+
+int display_type()
+{
+    return DISPLAY_SDL;
+}
+
+int scale_delay(int delay, int change)
+{
+    int tmp;
+    int magnitude;
+
+    if(delay < 1)
+        delay = 1;
+
+    if(change == 1)
+    {
+        tmp = delay;
+
+        while(tmp >= 100)
+            tmp = tmp / 10;
+
+        magnitude = delay / tmp;
+        
+        if(tmp < 20)
+            tmp = tmp + 1;
+        else if(tmp < 50)
+            tmp = tmp + 2;
+        else
+            tmp = tmp + 5;
+
+        delay = tmp * magnitude;
+
+        if(delay > 1000)
+            delay = 1;
+    }
+
+    if(change == -1)
+    {
+        tmp = delay;
+
+        while(tmp > 100)
+            tmp = tmp / 10;
+
+        magnitude = delay / tmp;
+
+        if(tmp > 50)
+            tmp = tmp - 5;
+        else if(tmp > 20)
+            tmp = tmp - 2;
+        else
+            tmp = tmp - 1;
+
+        delay = tmp * magnitude;
+
+        if(delay < 1)
+            delay = 1000;
+    }
+
+    return delay;
+}
+
+void display_options()
+{
+    struct menu* pmenu;
+    struct menu* pgraphicsmenu;
+    struct menu* pcoloursmenu;
+    struct menuentry* pentryscreensize;
+    struct menuentry* pentryfullscreen;
+    struct menuentry* pentrygraphics;
+    struct menuentry* pentrysize;
+    struct menuentry* pentrylevel;
+    struct menuentry* pentrycolours;
+    struct menuentry* pentryspeed;
+    struct menuentry* pentryfirstspeed;
+    struct menuentry* pentryreplayspeed;
+    struct menuentry* pentryundospeed;
+    int result;
+    int ok;
+
+    char buffer[4096];
+
+    pmenu = menu_new(gettext("Display Options"));
+
+    menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+    menuentry_new(pmenu, gettext("Save Options"), 'S', 0);
+
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+    pentrygraphics = menuentry_new(pmenu, gettext("Graphics Scheme"), 'G', 0);
+    pentrysize = menuentry_new(pmenu, gettext("Graphics Size"), 'I', 0);
+    pentrylevel = menuentry_new(pmenu, gettext("Graphics Level"), 'L', 0);
+    pentrycolours = menuentry_new(pmenu, gettext("Colour Scheme"), 'C', 0);
+
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+    pentryscreensize = menuentry_new(pmenu, gettext("Screen size"), 'Z', 0);
+    pentryfullscreen = menuentry_new(pmenu, gettext("Fullscreen"), 'F', 0);
+
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+    pentryfirstspeed = menuentry_new(pmenu, gettext("Player Speed"), 'P', MENU_SCROLLABLE);
+    pentryspeed = menuentry_new(pmenu, gettext("Move Speed"), 'M', MENU_SCROLLABLE);
+    pentryreplayspeed = menuentry_new(pmenu, gettext("Replay Speed"), 'R', MENU_SCROLLABLE);
+    pentryundospeed = menuentry_new(pmenu, gettext("Undo Speed"), 'U', MENU_SCROLLABLE);
+
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+    menuentry_new(pmenu, gettext("Change Keys"), 'K', 0);
+    menuentry_new(pmenu, gettext("Change Mouse"), 'O', 0);
+
+    /* XOR and Enigma options are only visible once an appropriate level has
+     * been seen so as not to confuse those simply playing Chroma levels */
+    if(0
+#ifdef XOR_COMPATIBILITY
+            || options_xor_options
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+            || options_enigma_options
+#endif
+      )
+    {
+        menuentry_new(pmenu, "", 0, MENU_SPACE);
+        menuentry_new(pmenu, gettext("Other Games Options"), 'X', 0);
+    }
+
+    if(options_debug & DEBUG_MENU)
+    {
+        menuentry_new(pmenu, "", 0, MENU_SPACE);
+        menuentry_new(pmenu, gettext("Debug Options"), 'D', 0);
+    }
+
+    ok = 0;
+    while(!ok)
+    {
+        if(options_sdl_width == 0 && options_sdl_height == 0)
+            sprintf(buffer, gettext("Auto (%d x %d)"), screen_width, screen_height);
+        else
+            sprintf(buffer, "%d x %d", screen_width, screen_height);
+        menuentry_extratext(pentryscreensize, buffer, NULL, NULL);
+
+        menuentry_extratext(pentryfullscreen, screen_fullscreen ? gettext("Yes") : gettext("No"), NULL, NULL);
+
+        sprintf(buffer, gettext("%d milliseconds"), options_sdl_delay);
+        menuentry_extratext(pentryspeed, buffer, NULL, NULL);
+
+        sprintf(buffer, gettext("%d milliseconds"), options_sdl_player_delay);
+        menuentry_extratext(pentryfirstspeed, buffer, NULL, NULL);
+
+        sprintf(buffer, gettext("%d milliseconds"), options_sdl_replay_delay);
+        menuentry_extratext(pentryreplayspeed, buffer, NULL, NULL);
+
+        sprintf(buffer, gettext("%d milliseconds"), options_sdl_undo_delay);
+        menuentry_extratext(pentryundospeed, buffer, NULL, NULL);
+
+        if(pdisplaygraphics != NULL)
+        {
+            if(options_sdl_size_x != pdisplaygraphics->size_x || options_sdl_size_y != pdisplaygraphics->size_y)
+                sprintf(buffer, gettext("Auto (%d x %d)"), pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+
+            else
+                sprintf(buffer, "%d x %d", pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+
+            menuentry_extratext(pentrysize, buffer, NULL, NULL);
+
+            menuentry_extratext(pentrygraphics, pdisplaygraphics->title != NULL ? pdisplaygraphics->title : gettext("[untitled graphics]"), NULL, NULL);
+
+            if(pdisplaygraphics->title == NULL)
+                menuentry_extratext(pentrygraphics, gettext("[untitled graphics]"), NULL, NULL);
+            else if(pdisplaygraphics->flags & GRAPHICS_TRANSLATE)
+                menuentry_extratext(pentrygraphics, gettext(pdisplaygraphics->title), NULL, NULL);
+            else
+                menuentry_extratext(pentrygraphics, pdisplaygraphics->title, NULL, NULL);
+
+            if(options_graphic_level != 0)
+                sprintf(buffer, "%d / %d", options_graphic_level, pdisplaygraphics->levels);
+            else
+                sprintf(buffer, gettext("Auto (%d)"), pdisplaygraphics->levels);
+
+            menuentry_extratext(pentrylevel, buffer, NULL, NULL);
+        }
+        else
+            menuentry_extratext(pentrygraphics, gettext("** NONE **"), NULL, NULL);
+
+        if(pdisplaygraphics != NULL && (pdisplaygraphics->flags & GRAPHICS_CURSES))
+            pentrycolours->flags = 0;
+        else
+            pentrycolours->flags = MENU_INVISIBLE | MENU_GREY;
+
+        if(pdisplaygraphics != NULL && pdisplaygraphics->sizes != NULL)
+            pentrysize->flags = 0;
+        else
+            pentrysize->flags = MENU_INVISIBLE | MENU_GREY;
+
+        if(pdisplaygraphics != NULL && pdisplaygraphics->levels > 0)
+            pentrylevel->flags = MENU_SCROLLABLE;
+        else
+            pentrylevel->flags = MENU_INVISIBLE | MENU_GREY;
+
+        if(pdisplaycolours == NULL)
+            menuentry_extratext(pentrycolours, gettext("** NONE **"), NULL, NULL);
+        else if(pdisplaycolours->title == NULL)
+            menuentry_extratext(pentrycolours, gettext("[untitled colours]"), NULL, NULL);
+        else if(pdisplaycolours->flags & COLOURS_TRANSLATE)
+            menuentry_extratext(pentrycolours, gettext(pdisplaycolours->title), NULL, NULL);
+        else
+            menuentry_extratext(pentrycolours, pdisplaycolours->title, NULL, NULL);
+
+        result = menu_process(pmenu);
+        if(result == MENU_QUIT)
+            ok = 1;
+
+        if(result == MENU_SELECT && pmenu->entry_selected != NULL)
+        {
+            switch(pmenu->entry_selected->key)
+            {
+                case 'Q':
+                    ok = 1;
+                    break;
+
+                case 'Z':
+                    display_screensizemenu();
+                    break;
+
+                case 'F':
+                    screen_fullscreen = 1 - screen_fullscreen;
+                    screen_resize(screen_width, screen_height, screen_fullscreen);
+                    options_sdl_fullscreen = screen_fullscreen;
+                    break;
+
+                case 'G':
+                    pgraphicsmenu = graphics_menu();
+                    if(menu_process(pgraphicsmenu) == MENU_SELECT)
+                    {
+                        if(pgraphicsmenu->entry_selected != NULL && pgraphicsmenu->entry_selected->value != NULL)
+                        {
+                            strcpy(options_graphics, pgraphicsmenu->entry_selected->value);
+                            graphics_init();
+                        }
+                    }
+                    menu_delete(pgraphicsmenu);
+                    break;
+
+                case 'C':
+                    pcoloursmenu = colours_menu();
+                    if(menu_process(pcoloursmenu) == MENU_SELECT)
+                    {
+                        if(pcoloursmenu->entry_selected != NULL && pcoloursmenu->entry_selected->value != NULL)
+                        {
+                            strcpy(options_colours, pcoloursmenu->entry_selected->value);
+                            colours_init();
+                            graphics_init();
+                        }
+                    }
+                    menu_delete(pcoloursmenu);
+                    break;
+
+                case 'I':
+                    display_options_size();
+                    break;
+
+                case 'S':
+                    display_options_save();
+                    ok = 1;
+                    break;
+
+                case 'K':
+                    display_options_keys();
+                    break;
+
+                case 'O':
+                    display_options_mouse();
+                    break;
+
+                case 'X':
+                    display_options_othergames();
+                    break;
+
+                case 'D':
+                    display_options_debug();
+                    break;
+            }
+        }
+
+        if(result == MENU_SCROLLLEFT && pmenu->entry_selected != NULL)
+        {
+            switch(pmenu->entry_selected->key)
+            {
+                case 'M':
+                    options_sdl_delay = scale_delay(options_sdl_delay, -1);
+                    break;
+                case 'P':
+                    options_sdl_player_delay = scale_delay(options_sdl_player_delay, -1);
+                    break;
+                case 'R':
+                    options_sdl_replay_delay = scale_delay(options_sdl_replay_delay, -1);
+                    break;
+                case 'U':
+                    options_sdl_undo_delay = scale_delay(options_sdl_undo_delay, -1);
+                    break;
+                case 'L':
+                    options_graphic_level --;
+                    if(options_graphic_level < 0)
+                        options_graphic_level = pdisplaygraphics->levels;
+                    graphics_reload();
+                    break;
+            }
+        }
+
+        if(result == MENU_SCROLLRIGHT && pmenu->entry_selected != NULL)
+        {
+            switch(pmenu->entry_selected->key)
+            {   
+                case 'M':
+                    options_sdl_delay = scale_delay(options_sdl_delay, 1);
+                    break;
+                case 'P':
+                    options_sdl_player_delay = scale_delay(options_sdl_player_delay, 1);
+                    break;
+                case 'R':
+                    options_sdl_replay_delay = scale_delay(options_sdl_replay_delay, 1);
+                    break;
+                case 'U':
+                    options_sdl_undo_delay = scale_delay(options_sdl_undo_delay, 1);
+                    break;
+                case 'L':
+                    options_graphic_level ++;
+                    if(options_graphic_level > pdisplaygraphics->levels)
+                        options_graphic_level = 0;
+                    graphics_reload();
+                    break;
+            }
+        }
+
+
+    }
+
+    menu_delete(pmenu);
+}
+
+void display_clip(struct level* plevel, int clip)
+{
+    SDL_Rect crect;
+
+    if(clip)
+    {
+        crect.x = 0;
+        crect.y = 0;
+        crect.w = screen_width;
+        crect.h = screen_height - display_bar_pixels;
+
+#ifdef XOR_COMPATIBILITY
+        if(plevel->mode == MODE_XOR && options_xor_display)
+        {
+            crect.w = pdisplaygraphics->size_x * 8;
+            crect.h = pdisplaygraphics->size_y * 8;
+            crect.x = (screen_width - crect.w) / 2;
+            crect.y = (screen_height - display_bar_pixels - crect.h) / 2;
+
+            /* Ensure bar is always visible */
+            if(crect.y + crect.h > screen_height - display_bar_pixels)
+                crect.h = screen_height - display_bar_pixels - crect.y;
+        }
+#endif
+
+        SDL_SetClipRect(screen_surface, &crect);
+    }
+    else
+    {
+        SDL_SetClipRect(screen_surface, NULL);
+    }
+}
+
+
+void display_screensizemenu()
+{
+    struct menu* pmenu;
+    struct menuentry *pentry;
+
+    int sizes[6][2] = { {640, 480}, {800, 600}, {1024, 768}, {1280, 1024}, {1600, 1200}, {0, 0} };
+    int i;
+    char buffer[256], tmp[256];
+    int custom;
+    SDL_Rect **modes;
+    int w, h;
+    int ok;
+
+    custom = 1;
+
+    pmenu = menu_new(gettext("Screen Size"));
+
+    menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+    pentry = menuentry_new(pmenu, gettext("Automatic sizing"), 0, 0);
+    menuentry_extratext(pentry, NULL, "0", "0");
+    if(options_sdl_width == 0 && options_sdl_height == 0)
+    {
+        pmenu->entry_selected = pentry;
+        custom = 0;
+    }
+
+    /* First, add modes that we know the screen can do */
+    modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
+    if(modes != (SDL_Rect **)0 && modes != (SDL_Rect **)-1)
+    {
+        for(i = 0; modes[i]; i ++)
+        {
+            w = modes[i]->w; h = modes[i]->h;
+            sprintf(buffer, "%d x %d", w, h);
+
+            pentry = pmenu->entry_first; ok = 1;
+            while(pentry != NULL)
+            {  
+                if(pentry->text != NULL && strcmp(pentry->text, buffer) == 0)
+                {  
+                    ok = 0;
+                }
+                pentry = pentry->next;
+            }
+
+            if(ok)
+            {
+                sprintf(tmp, "%04dx%04d", w, h);
+                pentry = menuentry_newwithvalue(pmenu, buffer, 0, MENU_SORT, tmp);
+                sprintf(buffer, "%d", w);
+                sprintf(tmp, "%d", h);
+                menuentry_extratext(pentry, NULL, buffer, tmp);
+                if(options_sdl_width == w && options_sdl_height == h)
+                    pmenu->entry_selected = pentry;
+                if(screen_width == w && screen_height == h)
+                    custom = 0;
+            }
+        }
+    }
+
+    /* Then add any of the default modes that haven't been already added */
+    i = 0;
+    while(sizes[i][0] != 0)
+    {
+        sprintf(buffer, "%d x %d", sizes[i][0], sizes[i][1]);
+        pentry = pmenu->entry_first; ok = 1;
+        while(pentry != NULL)
+        {
+            if(pentry->text != NULL && strcmp(pentry->text, buffer) == 0)
+            {
+                ok = 0;
+            }
+            pentry = pentry->next;
+        }
+
+        if(ok)
+        {
+            sprintf(tmp, "%04dx%04d", sizes[i][0], sizes[i][1]);
+            pentry = menuentry_newwithvalue(pmenu, buffer, 0, MENU_SORT, tmp);
+            sprintf(buffer, "%d", sizes[i][0]);
+            sprintf(tmp, "%d", sizes[i][1]);
+            menuentry_extratext(pentry, NULL, buffer, tmp);
+            if(options_sdl_width == sizes[i][0] && options_sdl_height == sizes[i][1])
+                pmenu->entry_selected = pentry;
+            if(screen_width == sizes[i][0] && screen_height == sizes[i][1])
+                custom = 0;
+        }
+
+        i ++;
+    }
+    if(custom)
+    {
+        sprintf(buffer, gettext("Custom (%d x %d)"), screen_width, screen_height);
+        pentry = menuentry_new(pmenu, buffer, 0, 0);
+        sprintf(buffer, "%d", screen_width);
+        sprintf(buffer + 16, "%d", screen_height);
+        menuentry_extratext(pentry, NULL, buffer, buffer + 16);
+        pmenu->entry_selected = pentry;
+    }
+
+    pentry = menuentry_new(pmenu, gettext("Current screen size:"), 0, MENU_NOTE);
+    sprintf(buffer, gettext("%d x %d (%d bits per pixel)"), screen_width, screen_height, screen_surface->format->BitsPerPixel);
+    pentry = menuentry_new(pmenu, buffer, 0, MENU_NOTE | MENU_RIGHT);
+
+    menu_assignletters(pmenu);
+
+    if(menu_process(pmenu) == MENU_SELECT)
+    {
+        if(pmenu->entry_selected->text3 != NULL)
+        {
+            options_sdl_width = atoi(pmenu->entry_selected->text3);
+            options_sdl_height = atoi(pmenu->entry_selected->text4);
+            screen_resize(options_sdl_width, options_sdl_height, screen_fullscreen);
+        }
+    }
+
+    menu_delete(pmenu);
+}
+
+int display_keyfixed(SDLKey key)
+{
+    if(key == SDLK_ESCAPE || key == SDLK_q || key == SDLK_RETURN || key == SDLK_UP || key == SDLK_DOWN || key == SDLK_LEFT || key == SDLK_RIGHT)
+        return 1;
+
+    return 0;
+}
+
+char *display_keyname(SDLKey key)
+{
+    return SDL_GetKeyName(key);
+}
+
+void display_addkeytomenu(struct menu* pmenu, int action, char *text)
+{
+    struct menuentry *pentry;
+    char buffer[256];
+    SDLKey key;
+
+    sprintf(buffer, "%d", action);
+    pentry = menuentry_newwithvalue(pmenu, text, 0, MENU_DOUBLE, buffer);
+
+    strcpy(buffer, "");
+    for(key = SDLK_FIRST; key < SDLK_LAST; key ++)
+    {
+        if(actions[key] == action)
+        {
+            if(strlen(buffer) != 0)
+                strcat(buffer,", ");
+            strcat(buffer, "[");
+            strcat(buffer, display_keyname(key));
+            strcat(buffer, "]");
+        }
+    }
+
+    if(strcmp(buffer, "") == 0)
+        strcpy(buffer, gettext("(none)"));
+
+    menuentry_extratext(pentry, NULL, NULL, buffer);
+}
+
+void display_options_keys()
+{
+    struct menu *pmenu;
+    struct menu *psubmenu;
+    struct menuentry *pentry;
+    int redraw;
+    int action;
+    int result;
+    int ok;
+    int subok;
+    char buffer[256];
+    SDLKey key;
+    SDL_Event event;
+
+    ok = 0;
+    while(!ok)
+    {
+        pmenu = menu_new(gettext("Keys"));
+
+        menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+        menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+        display_addkeytomenu(pmenu, ACTION_LEFT, gettext(action_name[ACTION_LEFT]));
+        display_addkeytomenu(pmenu, ACTION_RIGHT, gettext(action_name[ACTION_RIGHT]));
+        display_addkeytomenu(pmenu, ACTION_UP, gettext(action_name[ACTION_UP]));
+        display_addkeytomenu(pmenu, ACTION_DOWN, gettext(action_name[ACTION_DOWN]));
+        display_addkeytomenu(pmenu, ACTION_SWAP, gettext(action_name[ACTION_SWAP]));
+        display_addkeytomenu(pmenu, ACTION_UNDO, gettext(action_name[ACTION_UNDO]));
+        display_addkeytomenu(pmenu, ACTION_REDO, gettext(action_name[ACTION_REDO]));
+        display_addkeytomenu(pmenu, ACTION_FAST, gettext(action_name[ACTION_FAST]));
+        display_addkeytomenu(pmenu, ACTION_FASTER, gettext(action_name[ACTION_FASTER]));
+        display_addkeytomenu(pmenu, ACTION_SLOWER, gettext(action_name[ACTION_SLOWER]));
+        display_addkeytomenu(pmenu, ACTION_PAUSE, gettext(action_name[ACTION_PAUSE]));
+        display_addkeytomenu(pmenu, ACTION_QUIT, gettext(action_name[ACTION_QUIT]));
+        display_addkeytomenu(pmenu, ACTION_REDRAW, gettext(action_name[ACTION_REDRAW]));
+        display_addkeytomenu(pmenu, ACTION_HIDE, gettext(action_name[ACTION_HIDE]));
+        display_addkeytomenu(pmenu, ACTION_PIECE_LEFT, gettext(action_name[ACTION_PIECE_LEFT]));
+        display_addkeytomenu(pmenu, ACTION_PIECE_RIGHT, gettext(action_name[ACTION_PIECE_RIGHT]));
+
+        menu_assignletters(pmenu);
+
+        result = menu_process(pmenu);
+
+        if(result == MENU_QUIT)
+            ok = 1;
+
+        if(result == MENU_SELECT)
+        {   
+            if(pmenu->entry_selected->key == 'Q')
+                ok = 1;
+            else if(pmenu->entry_selected->value != NULL)
+            {
+                subok = 0;
+                redraw = MENUREDRAW_ALL;
+                while(!subok)
+                {
+                    action = atoi(pmenu->entry_selected->value);
+
+                    sprintf(buffer, gettext("Set keys for '%s'"), gettext(action_name[action]));
+                    psubmenu = menu_new(buffer);
+
+                    menuentry_new(psubmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+                    menuentry_new(psubmenu, "", 0, MENU_SPACE);
+
+                    for(key = SDLK_FIRST; key < SDLK_LAST; key ++)
+                    {   
+                        if(actions[key] == action)
+                        {   
+                            sprintf(buffer, "[%s]", display_keyname(key));
+                            pentry = menuentry_new(psubmenu, buffer, 0, MENU_GREY);
+                            if(display_keyfixed(key))
+                                menuentry_extratext(pentry, gettext("(fixed)"), NULL, NULL);
+                        }
+                    }
+
+                    menuentry_new(psubmenu, gettext("Press a key to add or remove it from this list."), 0, MENU_NOTE | MENU_CENTRE);
+
+                    menu_display(psubmenu, redraw);
+                    redraw = MENUREDRAW_ENTRIES;
+
+                    menu_delete(psubmenu);
+
+                    subok = 0;
+                    while(!subok)
+                    {
+                        SDL_WaitEvent(&event);
+
+                        switch(event.type)
+                        {
+                            case SDL_KEYDOWN:
+                                key = event.key.keysym.sym;
+                                if(key == SDLK_q || key == SDLK_ESCAPE)
+                                    subok = 1;
+                                else if(!display_keyfixed(key))
+                                {
+                                    if(actions[key] == action)
+                                        actions[key] = ACTION_NONE;
+                                    else
+                                        actions[key] = action;
+
+                                    subok = 2;
+                                }
+                                break;
+
+                            case SDL_QUIT:
+                                exit(0);
+
+                            case SDL_VIDEORESIZE:
+                                screen_resizeevent(&event);
+                                subok = 2;
+                                redraw = MENUREDRAW_ALL;
+                                break;
+
+                            case SDL_ACTIVEEVENT:
+                                if((event.active.state & SDL_APPACTIVE) && event.active.gain == 1)
+                                    redraw = MENUREDRAW_ALL;
+                                break;
+                        }
+                    }
+                    if(subok == 2)
+                        subok = 0;
+                }
+            }
+        }
+
+        menu_delete(pmenu);
+    }
+}
+
+void display_options_mouse()
+{
+    struct menu *pmenu;
+    struct menu *psubmenu;
+    struct menuentry *pentry;
+    int result;
+    int ok;
+    int subok;
+    int i, j, k;
+    int button, where;
+    char buffer[256];
+
+    char *locations[] = {"Game", "Editor", "Menu"};
+
+    int actions_game[] = {
+        ACTION_NONE,
+        ACTION_MOUSE_CLICK,
+        ACTION_MOUSE_DRAG,
+        ACTION_MOUSE_DRAG_OR_CLICK,
+        ACTION_SWAP,
+        ACTION_UNDO,
+        ACTION_REDO,
+        ACTION_HIDE,
+        ACTION_QUIT,
+        ACTION_MAX
+    };
+
+    int actions_edit[] = {
+        ACTION_NONE,
+        ACTION_MOUSE_CLICK,
+        ACTION_MOUSE_DRAG,
+        ACTION_MOUSE_DRAG_OR_CLICK,
+        ACTION_PIECE_LEFT,
+        ACTION_PIECE_RIGHT,
+        ACTION_HIDE,
+        ACTION_QUIT,
+        ACTION_MAX
+    };
+
+    int actions_menu[] = {
+        ACTION_NONE,
+        ACTION_UP,
+        ACTION_DOWN,
+        ACTION_PAGE_UP,
+        ACTION_PAGE_DOWN,
+        ACTION_HIDE,
+        ACTION_MOUSE_CLICK,
+        ACTION_QUIT,
+        ACTION_MAX
+    };
+
+    int *actions_available[] = {
+        actions_game,
+        actions_edit,
+        actions_menu,
+    };
+
+    char *actions_names_game[] = {
+        "Do nothing",
+        "Click to move player",
+        "Drag to scroll screen",
+        "Click to move player, drag to scroll screen",
+        "Click to swap player",
+        "Undo move",
+        "Redo move",
+        "Hide screen",
+        "Return to previous menu",
+        ""
+    };
+
+    char *actions_names_edit[] = {
+        "Do nothing",
+        "Click to set piece",
+        "Drag to scroll screen",
+        "Click to set piece, drag to scroll screen",
+        "Piece left",
+        "Piece right",
+        "Hide screen",
+        "Return to previous menu",
+        ""
+    };
+
+    char *actions_names_menu[] = {
+        "Do nothing",
+        "Move up",
+        "Move down",
+        "Page up",
+        "Page down",
+        "Hide screen",
+        "Select entry",
+        "Return to previous menu",
+        ""
+    };
+
+    char **actions_names[] = {
+        actions_names_game,
+        actions_names_edit,
+        actions_names_menu,
+    };
+
+    ok = 0;
+    pmenu = menu_new(gettext("Mouse"));
+
+    menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+
+    for(i = 0; i < 3; i ++)
+    {
+        menuentry_new(pmenu, "", 0, MENU_SPACE);
+        menuentry_new(pmenu, locations[i], 0, MENU_GREY);
+        for(j = 1; j < MOUSE_BUTTONS_MAX; j ++)
+        {
+            sprintf(buffer, gettext("Button %d"), j);
+            pentry = menuentry_new(pmenu, buffer, 0, 0);
+
+            sprintf(buffer, "%d%d", i, j);
+            menuentry_value(pentry, buffer);
+
+            /* Should be overwritten shortly - this is a fallback if not */
+            sprintf(buffer, "? %s", gettext(action_name[actions_mouse[i][j]]));
+            menuentry_extratext(pentry, buffer, NULL, NULL);
+
+            /* Perhaps not the neatest way of doing this here, but saves
+               having to have redundant actions merely to give them different
+               names. */
+            k = 0;
+            while(actions_available[i][k] != ACTION_MAX)
+            {
+                if(actions_available[i][k] == actions_mouse[i][j])
+                    menuentry_extratext(pentry, gettext(actions_names[i][k]), NULL, NULL);
+                k ++;
+            }
+        }
+    }
+
+    menu_assignletters(pmenu);
+
+    ok  = 0;
+    while(!ok)
+    {
+        result = menu_process(pmenu);
+
+        if(result == MENU_QUIT)
+            ok = 1;
+
+        if(result == MENU_SELECT)
+        {   
+            if(pmenu->entry_selected->key == 'Q')
+                ok = 1;
+            else if(pmenu->entry_selected->value != NULL)
+            {
+                where = pmenu->entry_selected->value[0] - '0';
+                button = pmenu->entry_selected->value[1] - '0';
+                sprintf(buffer, gettext("Set action for button %d in %s"), button, locations[where]);
+                psubmenu = menu_new(buffer);
+
+                menuentry_new(psubmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+                menuentry_new(psubmenu, "", 0, MENU_SPACE);
+
+                i = 0;
+                while(actions_available[where][i] != ACTION_MAX)
+                {
+                    pentry = menuentry_new(psubmenu, gettext(actions_names[where][i]), 0, 0);
+                    sprintf(buffer, "%d", i);
+                    menuentry_value(pentry, buffer);
+                    if(actions_mouse[where][button] == actions_available[where][i])
+                    {
+                        psubmenu->entry_selected = pentry;
+
+                        menuentry_new(psubmenu, gettext("Current action is:"), 0, MENU_NOTE);
+                        menuentry_new(psubmenu, gettext(actions_names[where][i]), 0, MENU_NOTE | MENU_RIGHT);
+                    }
+
+                    i ++;
+                }
+
+                menu_assignletters(psubmenu);
+
+                subok = 0;
+                while(!subok)
+                {
+                    result = menu_process(psubmenu);
+
+                    if(result == MENU_QUIT)
+                        subok = 1;
+
+                    if(result == MENU_SELECT)
+                    {
+                        if(psubmenu->entry_selected->key == 'Q')
+                            subok = 1;
+                        else if(psubmenu->entry_selected->value != NULL)
+                        {
+                            actions_mouse[where][button] = actions_available[where][atoi(psubmenu->entry_selected->value)];
+                            menuentry_extratext(pmenu->entry_selected, gettext(actions_names[where][atoi(psubmenu->entry_selected->value)]), NULL, NULL);
+                            subok = 1;
+                        }
+                    }
+
+                }
+                menu_delete(psubmenu);
+            }
+        }
+
+    }
+
+    menu_delete(pmenu);
+}
+
+void display_options_size()
+{
+    struct menu* pmenu;
+    struct menuentry* pentry;
+    struct graphicssize* psize;
+    char buffer[4096];
+
+    pmenu = menu_new(gettext("Graphics Size"));
+
+    menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+    menuentry_new(pmenu, gettext("Current graphics size:"), 0, MENU_NOTE);
+    if(pdisplaygraphics != NULL)
+    {
+        if(options_sdl_size_x != pdisplaygraphics->size_x || options_sdl_size_y != pdisplaygraphics->size_y)
+            sprintf(buffer, gettext("Automatic (%d x %d)"), pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+        else
+            sprintf(buffer, "%d x %d", pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+        menuentry_new(pmenu, buffer, 0, MENU_NOTE | MENU_RIGHT);
+    }
+    else
+        menuentry_new(pmenu, gettext("** NONE **"), 0, MENU_NOTE | MENU_RIGHT);
+
+    pentry = menuentry_new(pmenu, gettext("Automatic sizing"), 0, 0);
+    menuentry_extratext(pentry, NULL, "0", "0");
+    if(options_sdl_size_x != pdisplaygraphics->size_x || options_sdl_size_y != pdisplaygraphics->size_y)
+        pmenu->entry_selected = pentry;
+
+    psize = pdisplaygraphics->sizes;
+    while(psize != NULL)
+    {
+        if(psize->flags & SIZE_PIECES)
+        {
+            sprintf(buffer, "%d x %d", psize->x, psize->y);
+            pentry = menuentry_new(pmenu, buffer, 0, 0);
+            if(psize->x == options_sdl_size_x && psize->y == options_sdl_size_y)
+                pmenu->entry_selected = pentry;
+            sprintf(buffer, "%d", psize->x);
+            sprintf(buffer + 16, "%d", psize->y);
+            menuentry_extratext(pentry, NULL, buffer, buffer + 16);
+        }
+        psize = psize->next;
+    }
+    menu_assignletters(pmenu);
+
+    if(menu_process(pmenu) == MENU_SELECT)
+    {
+        if(pmenu->entry_selected->text3 != NULL)
+        {
+            options_sdl_size_x = atoi(pmenu->entry_selected->text3);
+            options_sdl_size_y = atoi(pmenu->entry_selected->text4);
+
+            graphics_init();
+        }
+    }
+
+    menu_delete(pmenu);
+}
+
+void display_options_debug()
+{
+    struct menu* pmenu;
+    struct menuentry* pentryorder;
+    struct menuentry* pentrymovers;
+    struct menuentry* pentryspeed;
+    struct menuentry* pentryhidden;
+
+    int ok;
+    int result;
+
+    pmenu = menu_new(gettext("Debug Options"));
+
+    menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+  
+    pentryorder = menuentry_new(pmenu, gettext("Display order of movers"), 'O', 0);
+    pentrymovers = menuentry_new(pmenu, gettext("List movers on stderr"), 'M', 0);
+    pentryspeed = menuentry_new(pmenu, gettext("Show frames per second"), 'F', 0);
+    pentryhidden = menuentry_new(pmenu, gettext("Show hidden items"), 'H', 0);
+
+    ok = 0;
+    while(!ok)
+    {
+        menuentry_extratext(pentryorder, options_debug & DEBUG_ORDER ? gettext("yes") : gettext("no"), NULL, NULL);
+        menuentry_extratext(pentrymovers, options_debug & DEBUG_MOVERS ? gettext("yes") : gettext("no"), NULL, NULL);
+        menuentry_extratext(pentryspeed, options_debug & DEBUG_SPEED ? gettext("yes") : gettext("no"), NULL, NULL);
+        menuentry_extratext(pentryhidden, options_debug & DEBUG_HIDDEN ? gettext("yes") : gettext("no"), NULL, NULL);
+
+        result = menu_process(pmenu);
+        if(result == MENU_QUIT)
+            ok = 1;
+
+        if(result == MENU_SELECT && pmenu->entry_selected != NULL)
+        {   
+            switch(pmenu->entry_selected->key)
+            {   
+                case 'Q':
+                    ok = 1;
+                    break;
+
+                case 'O':
+                    options_debug ^= DEBUG_ORDER;
+                    break;
+
+                case 'M':
+                    options_debug ^= DEBUG_MOVERS;
+                    break;
+
+                case 'F':
+                    options_debug ^= DEBUG_SPEED;
+                    break;
+
+                case 'H':
+                    options_debug ^= DEBUG_HIDDEN;
+                    break;
+            }
+
+            pmenu->redraw = MENUREDRAW_CHANGED;
+            pmenu->entry_selected->redraw = 1;
+        }
+
+    }
+
+    menu_delete(pmenu);
+}
+
+void display_options_othergames()
+{
+    struct menu* pmenu;
+#ifdef XOR_COMPATIBILITY
+    struct menuentry* pentryxormode;
+    struct menuentry* pentryxordisplay;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+    struct menuentry* pentryenigmamode;
+#endif
+
+    int ok;
+    int result;
+
+    pmenu = menu_new(gettext("Other Games Options"));
+
+    menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+
+    menuentry_new(pmenu, "", 0, MENU_SPACE);
+#ifdef XOR_COMPATIBILITY 
+    pentryxormode = menuentry_new(pmenu, gettext("XOR Engine"), 'X', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
+    pentryxordisplay = menuentry_new(pmenu, gettext("XOR Display"), 'D', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
+    if(options_xor_options)
+        menuentry_new(pmenu, "", 0, MENU_SPACE);
+#endif
+
+#ifdef ENIGMA_COMPATIBILITY
+    pentryenigmamode = menuentry_new(pmenu, gettext("Enigma Engine"), 'E', options_enigma_options ? 0 : MENU_INVISIBLE | MENU_GREY);
+#endif
+
+    ok = 0;
+    while(!ok)
+    {
+#ifdef XOR_COMPATIBILITY
+        menuentry_extratext(pentryxormode, options_xor_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
+        menuentry_extratext(pentryxordisplay, options_xor_display ? gettext("partial") : gettext("full"), NULL, NULL);
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+        menuentry_extratext(pentryenigmamode, options_enigma_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
+#endif
+
+        result = menu_process(pmenu);
+        if(result == MENU_QUIT)
+            ok = 1;
+
+        if(result == MENU_SELECT && pmenu->entry_selected != NULL)
+        {   
+            switch(pmenu->entry_selected->key)
+            {   
+                case 'Q':
+                    ok = 1;
+                    break;
+
+#ifdef XOR_COMPATIBILITY
+                case 'X':
+                    options_xor_mode = 1 - options_xor_mode;
+                    break;
+
+                case 'D':
+                    options_xor_display = 1 - options_xor_display;
+                    break;
+#endif
+
+#ifdef ENIGMA_COMPATIBILITY
+                case 'E':
+                    options_enigma_mode = 1 - options_enigma_mode;
+                    break;
+#endif
+            }
+
+            pmenu->redraw = MENUREDRAW_CHANGED;
+            pmenu->entry_selected->redraw = 1;
+        }
+
+    }
+
+    menu_delete(pmenu);
+}
+
+void display_options_save()
+{
+    FILE *file;
+    char filename[FILENAME_MAX];
+    SDLKey key;
+    int i, j;
+    char *locations[] = {"game", "editor", "menu"};
+
+    getfilename("sdl.chroma", filename, 1, LOCATION_LOCAL);
+
+    file = fopen(filename, "w");
+    if(file == NULL)
+    {   
+        warning("Unable to save options");
+        return;
+    }
+
+    fprintf(file, "<!-- Chroma SDL options \n"
+                  "     This file is automatically generated. -->\n"
+                  "\n"
+                  "<chroma type=\"options\">\n");
+
+    fprintf(file, "    <screen ");
+    if(options_sdl_width == 0 && options_sdl_height == 0)
+        fprintf(file, "width=\"auto\" height=\"auto\" ");
+    else
+        fprintf(file, "width=\"%d\" height=\"%d\" ", options_sdl_width, options_sdl_height);
+    fprintf(file, "fullscreen=\"%s\" />\n", options_sdl_fullscreen == 1 ? "yes" : "no");
+
+    fprintf(file, "    <graphics scheme=\"%s\" ", options_graphics);
+    if(options_graphic_level != 0)
+        fprintf(file, "level=\"%d\" ", options_graphic_level);
+    if(options_sdl_size_x == 0)
+        fprintf(file, "width=\"auto\" height=\"auto\" />\n");
+    else
+        fprintf(file, "width=\"%d\" height=\"%d\" />\n", options_sdl_size_x, options_sdl_size_y);
+
+    fprintf(file, "    <colour scheme=\"%s\" />\n", options_colours);
+
+    fprintf(file, "    <move speed=\"%d\" />\n", options_sdl_delay);
+    fprintf(file, "    <player speed=\"%d\" />\n", options_sdl_player_delay);
+    fprintf(file, "    <replay speed=\"%d\" />\n", options_sdl_replay_delay);
+    fprintf(file, "    <undo speed=\"%d\" />\n", options_sdl_undo_delay);
+
+#ifdef XOR_COMPATIBILITY
+    if(options_xor_options)
+        fprintf(file, "    <xor mode=\"%s\" display=\"%s\" />\n", options_xor_mode ? "exact" : "approximate", options_xor_display ? "partial" : "full");
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+    if(options_enigma_options)
+        fprintf(file, "    <enigma mode=\"%s\" />\n", options_enigma_mode ? "exact" : "approximate");
+#endif
+
+    fprintf(file, "    <!-- Set <debug menu=\"yes\" /> to change debug options within Chroma -->\n");
+    fprintf(file, "    <debug ");
+    fprintf(file, "menu=\"%s\" ", options_debug & DEBUG_MENU ? "yes" : "no");
+    fprintf(file, "order=\"%s\" ", options_debug & DEBUG_ORDER ? "yes" : "no");
+    fprintf(file, "speed=\"%s\" ", options_debug & DEBUG_SPEED ? "yes" : "no");
+    fprintf(file, "movers=\"%s\" ", options_debug & DEBUG_MOVERS ? "yes" : "no");
+    fprintf(file, "hidden=\"%s\" ", options_debug & DEBUG_HIDDEN ? "yes" : "no");
+    fprintf(file, "/>\n");
+
+    fprintf(file, "    <keys>\n");
+
+    for(key = SDLK_FIRST; key < SDLK_LAST; key ++)
+    {
+        if(actions[key] != ACTION_NONE)
+            fprintf(file, "        <key name=\"%s\" action=\"%s\" />\n", display_keyname(key), action_shortname[actions[key]]);
+    }
+
+    fprintf(file, "    </keys>\n");
+
+    for(i = 0; i < 3; i ++)
+    {
+        fprintf(file, "    <mouse location=\"%s\">\n", locations[i]);
+        for(j = 1; j < MOUSE_BUTTONS_MAX; j ++)
+        {
+            fprintf(file, "        <button type=\"%d\" action=\"%s\" />\n", j, action_shortname[actions_mouse[i][j]]);
+        }
+        fprintf(file, "    </mouse>\n");
+    }
+
+    fprintf(file, "</chroma>\n");
+
+    fclose(file);
+}
+
+void display_options_load()
+{
+    struct parser* pparser;
+    char filename[FILENAME_MAX];
+    int state;
+    SDLKey k;
+    int i, key, action, location, button;
+
+    /* Sensible defaults */
+    options_sdl_fullscreen = 1;
+    options_sdl_width = 0;
+    options_sdl_height = 0;
+    options_sdl_delay = 100;
+    options_sdl_player_delay = 200;
+    options_sdl_replay_delay = 200;
+    options_sdl_undo_delay = 200;
+    options_sdl_size_x = 0;
+    options_sdl_size_y = 0;
+    options_graphic_level = 0;
+#ifdef XOR_COMPATIBILITY
+    options_xor_options = 0;
+    options_xor_mode = 1;
+    options_xor_display = 0;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+    options_enigma_options = 0;
+    options_enigma_mode = 1;
+#endif
+    options_debug = 0;
+
+    getfilename("colours", filename, 0, LOCATION_SYSTEM);
+    sprintf(options_colours, "%s/%s", filename, COLOURS_DEFAULT);
+    getfilename("graphics", filename, 0, LOCATION_SYSTEM);
+    sprintf(options_graphics, "%s/%s", filename, GRAPHICS_DEFAULT);
+
+    getfilename("sdl.chroma", filename, 0, LOCATION_LOCAL);
+
+    for(k = SDLK_FIRST; k < SDLK_LAST; k ++)
+    {
+        actions[k] = ACTION_NONE;
+    }
+
+    for(action = 0; action < MOUSE_BUTTONS_MAX; action ++)
+    {
+        actions_mouse[ACTIONS_GAME][action] = ACTION_NONE;
+        actions_mouse[ACTIONS_MENU][action] = ACTION_NONE;
+        actions_mouse[ACTIONS_EDIT][action] = ACTION_NONE;
+    }
+
+    /* Fixed keys */
+    actions[SDLK_UP] = ACTION_UP;
+    actions[SDLK_DOWN] = ACTION_DOWN;
+    actions[SDLK_LEFT] = ACTION_LEFT;
+    actions[SDLK_RIGHT] = ACTION_RIGHT;
+    actions[SDLK_RETURN] = ACTION_SWAP;
+    actions[SDLK_q] = ACTION_QUIT;
+    actions[SDLK_ESCAPE] = ACTION_QUIT;
+
+    actions_mouse[ACTIONS_GAME][1] = ACTION_MOUSE_DRAG_OR_CLICK;
+    actions_mouse[ACTIONS_GAME][2] = ACTION_SWAP;
+    actions_mouse[ACTIONS_GAME][3] = ACTION_UNDO;
+    actions_mouse[ACTIONS_GAME][4] = ACTION_UNDO;
+    actions_mouse[ACTIONS_GAME][5] = ACTION_REDO;
+
+    actions_mouse[ACTIONS_MENU][1] = ACTION_MOUSE_CLICK;
+    actions_mouse[ACTIONS_MENU][2] = ACTION_MOUSE_CLICK;
+    actions_mouse[ACTIONS_MENU][3] = ACTION_QUIT;
+    actions_mouse[ACTIONS_MENU][4] = ACTION_PAGE_UP;
+    actions_mouse[ACTIONS_MENU][5] = ACTION_PAGE_DOWN;
+
+    actions_mouse[ACTIONS_EDIT][1] = ACTION_MOUSE_CLICK;
+    actions_mouse[ACTIONS_EDIT][2] = ACTION_MOUSE_DRAG;
+    actions_mouse[ACTIONS_EDIT][3] = ACTION_QUIT;
+    actions_mouse[ACTIONS_EDIT][4] = ACTION_PIECE_LEFT;
+    actions_mouse[ACTIONS_EDIT][5] = ACTION_PIECE_RIGHT;
+
+    /* Sensible default keys */
+    if(!isfile(filename))
+    {
+        actions[SDLK_SPACE] = ACTION_SWAP;
+        actions[SDLK_f] = ACTION_FAST;
+        actions[SDLK_BACKSPACE] = ACTION_UNDO;
+        actions[SDLK_DELETE] = ACTION_UNDO;
+        actions[SDLK_u] = ACTION_UNDO;
+        actions[SDLK_INSERT] = ACTION_REDO;
+        actions[SDLK_y] = ACTION_REDO;
+        actions[SDLK_p] = ACTION_PAUSE;
+        actions[SDLK_LSHIFT] = ACTION_FASTER;
+        actions[SDLK_RSHIFT] = ACTION_FASTER;
+        actions[SDLK_LCTRL] = ACTION_SLOWER;
+        actions[SDLK_RCTRL] = ACTION_SLOWER;
+        actions[SDLK_z] = ACTION_PIECE_LEFT;
+        actions[SDLK_x] = ACTION_PIECE_RIGHT;
+        actions[SDLK_PAGEUP] = ACTION_PIECE_LEFT;;
+        actions[SDLK_PAGEDOWN] = ACTION_PIECE_RIGHT;
+        actions[SDLK_KP8] = ACTION_UP;
+        actions[SDLK_KP2] = ACTION_DOWN;
+        actions[SDLK_KP4] = ACTION_LEFT;
+        actions[SDLK_KP6] = ACTION_RIGHT;
+        actions[SDLK_KP_ENTER] = ACTION_SWAP;
+        actions[SDLK_KP_MINUS] = ACTION_UNDO;
+        actions[SDLK_KP_PLUS] = ACTION_REDO;
+        actions[SDLK_KP_DIVIDE] = ACTION_PIECE_LEFT;
+        actions[SDLK_KP_MULTIPLY] = ACTION_PIECE_RIGHT;
+
+        return;
+    }
+
+    /* Parse XML file */
+    /*
+       <chroma type="options">
+           <colour scheme="filename" />
+           <move speed="speed" />
+           <replay speed="speed" />
+           <xor mode="mode" />
+           <debug movers="yes/no" />
+           <keys>
+               <key name="name" action="action" />
+           </keys>
+       </chroma>
+    */
+
+    pparser = parser_new(filename);
+
+    enum {
+        OPTIONSPARSER_END,       /* End of file */
+        OPTIONSPARSER_OUTSIDE,   /* Outside of <chroma> */
+        OPTIONSPARSER_CHROMA,    /* Inside <chroma> */
+        OPTIONSPARSER_KEYS,      /* Inside <keys> */
+        OPTIONSPARSER_MOUSE      /* Inside <mouse> */
+    };
+
+    state = OPTIONSPARSER_OUTSIDE;
+    key = 0;
+    action = 0;
+    location = -1;
+    button = -1;
+
+    while(state != OPTIONSPARSER_END)
+    {
+        switch(parser_parse(pparser))
+        {
+            case PARSER_END:
+                state = OPTIONSPARSER_END;
+                break;
+
+            case PARSER_ELEMENT_START:
+                switch(state)
+                {
+                    case OPTIONSPARSER_CHROMA:
+                        if(parser_match(pparser, 0, "keys"))
+                            state = OPTIONSPARSER_KEYS;
+                        if(parser_match(pparser, 0, "mouse"))
+                        {
+                            location = -1;
+                            state = OPTIONSPARSER_MOUSE;
+                        }
+                        break;
+
+                    case OPTIONSPARSER_KEYS:
+                        if(parser_match(pparser, 0, "key"))
+                        {
+                            key = 0;
+                            action = ACTION_NONE;
+                        }
+                        break;
+
+                    case OPTIONSPARSER_MOUSE:
+                        if(parser_match(pparser, 0, "button"))
+                        {
+                            button = -1;
+                            action = ACTION_NONE;
+                        }
+                        break;
+                }
+                break;
+
+            case PARSER_ELEMENT_END:
+                switch(state)
+                {
+                    case OPTIONSPARSER_KEYS:
+                        if(parser_match(pparser, 0, "keys"))
+                            state = OPTIONSPARSER_CHROMA;
+                        if(parser_match(pparser, 0, "key"))
+                        {
+                            if(key != 0 && !display_keyfixed(key))
+                                actions[key] = action;
+                        }
+                        break;
+
+                    case OPTIONSPARSER_MOUSE:
+                        if(parser_match(pparser, 0, "mouse"))
+                            state = OPTIONSPARSER_CHROMA;
+                        if(parser_match(pparser, 0, "button"))
+                        {
+                            if(location != -1 && button != -1)
+                                actions_mouse[location][button] = action;
+                        }
+                        break;
+                }
+                break;
+
+            case PARSER_CONTENT:
+                break;
+
+            case PARSER_ATTRIBUTE:
+                switch(state)
+                {
+                    case OPTIONSPARSER_OUTSIDE:
+                        if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "type"))
+                        {   
+                            if(parser_match(pparser, 0, "options"))
+                                state = OPTIONSPARSER_CHROMA;
+                        }
+                        break;
+
+                    case OPTIONSPARSER_CHROMA:
+                        if(parser_match(pparser, 2, "screen") && parser_match(pparser, 1, "width"))
+                        {
+                            options_sdl_width = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "screen") && parser_match(pparser, 1, "height"))
+                        {
+                            options_sdl_height = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "screen") && parser_match(pparser, 1, "fullscreen"))
+                        {
+                            if(parser_match(pparser, 0, "yes"))
+                                options_sdl_fullscreen = 1;
+                            if(parser_match(pparser, 0, "no"))
+                                options_sdl_fullscreen = 0;
+                        }
+                        if(parser_match(pparser, 2, "colour") && parser_match(pparser, 1, "scheme"))
+                        {
+                            strncpy(options_colours, parser_text(pparser, 0), FILENAME_MAX);
+                        }
+                        if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "scheme"))
+                        {
+                            strncpy(options_graphics, parser_text(pparser, 0), FILENAME_MAX);
+                        }
+                        if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "width"))
+                        {
+                            options_sdl_size_x = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "height"))
+                        {
+                            options_sdl_size_y = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "level"))
+                        {
+                            options_graphic_level = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "move") && parser_match(pparser, 1, "speed"))
+                        {
+                            options_sdl_delay = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "player") && parser_match(pparser, 1, "speed"))
+                        {
+                            options_sdl_player_delay = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "replay") && parser_match(pparser, 1, "speed"))
+                        {
+                            options_sdl_replay_delay = atoi(parser_text(pparser, 0));
+                        }
+                        if(parser_match(pparser, 2, "undo") && parser_match(pparser, 1, "speed"))
+                        {
+                            options_sdl_undo_delay = atoi(parser_text(pparser, 0));
+                        }
+#ifdef XOR_COMPATIBILITY
+                        if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "mode"))
+                        {
+                            options_xor_options = 1;
+
+                            if(parser_match(pparser, 0, "approximate"))
+                                options_xor_mode = 0;
+                            if(parser_match(pparser, 0, "exact"))
+                                options_xor_mode = 1;
+                        }
+                        if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "display"))
+                        {
+                            options_xor_options = 1;
+
+                            if(parser_match(pparser, 0, "full"))
+                                options_xor_display = 0;
+                            if(parser_match(pparser, 0, "partial"))
+                                options_xor_display = 1;
+                        }
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+                        if(parser_match(pparser, 2, "enigma") && parser_match(pparser, 1, "mode"))
+                        {
+                            options_enigma_options = 1;
+
+                            if(parser_match(pparser, 0, "approximate"))
+                                options_enigma_mode = 0;
+                            if(parser_match(pparser, 0, "exact"))
+                                options_enigma_mode = 1;
+                        }
+#endif
+                        if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "menu"))
+                        {
+                            if(parser_match(pparser, 0, "yes"))
+                                options_debug |= DEBUG_MENU;
+                        }
+                        if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "order"))
+                        {
+                            if(parser_match(pparser, 0, "yes"))
+                                options_debug |= DEBUG_ORDER;
+                        }
+                        if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "speed"))
+                        {
+                            if(parser_match(pparser, 0, "yes"))
+                                options_debug |= DEBUG_SPEED;
+                        }
+                        if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "movers"))
+                        {
+                            if(parser_match(pparser, 0, "yes"))
+                                options_debug |= DEBUG_MOVERS;
+                        }
+                        if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "hidden"))
+                        {
+                            if(parser_match(pparser, 0, "yes"))
+                                options_debug |= DEBUG_HIDDEN;
+                        }
+                        break;
+
+                    case OPTIONSPARSER_KEYS:
+                        if(parser_match(pparser, 2, "key") &&  parser_match(pparser, 1, "name"))
+                        {
+                            for(i = SDLK_FIRST; i < SDLK_LAST; i ++)
+                            {
+                                if(parser_match(pparser, 0, display_keyname(i)))
+                                {
+                                    key = i;
+                                    i = SDLK_LAST;
+                                }
+                            }
+                        }
+                        if(parser_match(pparser, 2, "key") &&  parser_match(pparser, 1, "action"))
+                        {
+                            for(i = ACTION_KEY_MIN; i < ACTION_KEY_MAX; i ++)
+                            {
+                                if(parser_match(pparser, 0, action_shortname[i]))
+                                {
+                                    action = i;
+                                    i = ACTION_KEY_MAX;
+                                }
+                            }
+                        }
+                        break;
+
+                    case OPTIONSPARSER_MOUSE:
+                        if(parser_match(pparser, 2, "button") &&  parser_match(pparser, 1, "type"))
+                        {
+                            button = atoi(parser_text(pparser, 0));
+                            if(button < 1 || button >= MOUSE_BUTTONS_MAX)
+                                button = -1;
+                        }
+                        if(parser_match(pparser, 2, "mouse") &&  parser_match(pparser, 1, "location"))
+                        {
+                            if(parser_match(pparser, 0, "game"))
+                                location = ACTIONS_GAME;
+                            if(parser_match(pparser, 0, "menu"))
+                                location = ACTIONS_MENU;
+                            if(parser_match(pparser, 0, "editor"))
+                                location = ACTIONS_EDIT;
+                        }
+                        if(parser_match(pparser, 2, "button") &&  parser_match(pparser, 1, "action"))
+                        {
+                            for(i = 0; i < ACTION_MAX; i ++)
+                            {
+                                if(parser_match(pparser, 0, action_shortname[i]))
+                                {
+                                    action = i;
+                                    i = ACTION_MAX;
+                                }
+                            }
+                        }
+                        break;
+                }
+                break;
+
+            case PARSER_ERROR:
+                state = OPTIONSPARSER_END;
+                break;
+        }
+    }
+
+    parser_delete(pparser);
+}