--- /dev/null
+/*
+ cursesdisplay.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 <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <locale.h>
+
+#ifdef CHROMA_CURSES_HEADER
+#include CHROMA_CURSES_HEADER
+#else
+#ifdef __WIN32__
+#include <curses.h>
+#else
+#include <ncurses.h>
+#endif
+#endif
+
+#include "chroma.h"
+#include "menu.h"
+#include "level.h"
+#include "display.h"
+#include "colours.h"
+#include "actions.h"
+#include "util.h"
+#include "xmlparser.h"
+
+char options_colours[FILENAME_MAX] = COLOURS_DEFAULT;
+int options_curses_delay = 1;
+int options_curses_replay_delay = 1;
+int options_debug = 0;
+#ifdef XOR_COMPATIBILITY
+int options_xor_options = 0;
+int options_xor_mode = 1;
+int options_xor_display = 0;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+int options_enigma_options = 0;
+int options_enigma_mode = 1;
+#endif
+
+
+extern struct colours* pdisplaycolours;
+extern int *editor_piece_maps[];
+extern char *action_name[];
+extern char *action_shortname[];
+
+/* Translation table for colours.
+ This is necessary as some versions of curses interchange red and blue.
+ */
+short colourtrans[] = {COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE};
+
+int dp_attr[256], dp_col[256];
+char dp_char[256];
+
+int actions[KEY_MAX];
+
+int display_size_x, display_size_y;
+int display_offset_x, display_offset_y;
+int display_focus_x, display_focus_y;
+int display_start_x, display_start_y;
+int display_end_x, display_end_y;
+int display_border = 7;
+
+void display_piece(struct level* plevel, int piece);
+
+int display_colourpairs = 0;
+short display_cpfore[64];
+short display_cpback[64];
+
+short colourpair_red;
+short colourpair_green;
+short colourpair_yellow;
+short colourpair_blue;
+short colourpair_cyan;
+short colourpair_magenta;
+short colourpair_cyan;
+short colourpair_white;
+short colourpair_menu;
+short colourpair_menugrey;
+
+char *display_keyname(int i);
+void display_addkeytomenu(struct menu* pmenu, int action, char *text);
+void display_keys();
+void display_debug();
+void display_initcolours();
+
+void display_options_othergames();
+
+short display_newcolourpair(short foreground, short background)
+{
+ short i;
+
+ for(i = 1; i <= display_colourpairs; i ++)
+ {
+ if(foreground == display_cpfore[i] && background == display_cpback[i])
+ return i;
+ }
+ display_colourpairs ++;
+ display_cpfore[display_colourpairs] = foreground;
+ display_cpback[display_colourpairs] = background;
+
+ init_pair(display_colourpairs, foreground, background);
+
+ return display_colourpairs;
+}
+
+void display_init()
+{
+ setlocale(LC_CTYPE, "");
+ atexit(display_quit);
+ initscr();
+ raw();
+ noecho();
+ keypad(stdscr, TRUE);
+ curs_set(0);
+ start_color();
+
+ getmaxyx(stdscr, display_size_y, display_size_x);
+
+ colourpair_red = display_newcolourpair(COLOR_RED, COLOR_BLACK);
+ colourpair_green = display_newcolourpair(COLOR_GREEN, COLOR_BLACK);
+ colourpair_yellow = display_newcolourpair(COLOR_YELLOW, COLOR_BLACK);
+ colourpair_blue = display_newcolourpair(COLOR_BLUE, COLOR_BLACK);
+ colourpair_magenta = display_newcolourpair(COLOR_MAGENTA, COLOR_BLACK);
+ colourpair_cyan = display_newcolourpair(COLOR_CYAN, COLOR_BLACK);
+ colourpair_white = display_newcolourpair(COLOR_WHITE, COLOR_BLACK);
+ colourpair_menu = display_newcolourpair(COLOR_CYAN, COLOR_BLUE);
+ colourpair_menugrey = display_newcolourpair(COLOR_CYAN, COLOR_BLACK);
+
+ display_options_load();
+ colours_init();
+ display_initcolours();
+}
+
+void display_initcolours()
+{
+ int i;
+ short fg, bg;
+
+#ifdef PDCURSES
+ short tg;
+#endif
+
+ for(i = 0; i < PIECE_MAX; i ++)
+ {
+ fg = pdisplaycolours->foreground[i];
+ if(fg < 0 || fg > 7)
+ fg = 7;
+
+ bg = pdisplaycolours->background[i];
+ if(bg < 0 || bg > 7)
+ bg = 0;
+
+#ifdef PDCURSES
+ /* PDCurses doesn't handle reverse colours well; we swap them manually */
+ if(pdisplaycolours->reverse[i])
+ {
+ tg = fg; fg = bg; bg = tg;
+ }
+#endif
+
+ dp_attr[i] = COLOR_PAIR(display_newcolourpair(colourtrans[fg], colourtrans[bg]));
+
+ if(pdisplaycolours->bold[i])
+ dp_attr[i] |= A_BOLD;
+
+#ifndef PDCURSES
+ if(pdisplaycolours->reverse[i])
+ dp_attr[i] |= A_REVERSE;
+#endif
+ }
+
+}
+
+void display_quit()
+{
+ clear();
+ refresh();
+ endwin();
+}
+
+void display_hide()
+{
+ clear();
+ refresh();
+ getch();
+}
+
+void display_piece(struct level* plevel, int piece)
+{
+ int p;
+
+ if(piece < 0 || piece >= PIECE_MAX)
+ return;
+
+ /* Use the colours of PIECE_PLAYER_ONE for the active player,
+ and the colours of PIECE_PLAYER_TWO for the inactive one. */
+ p = piece;
+ if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
+ {
+ if(plevel->player != 2)
+ {
+ if(plevel->player != (p & 1))
+ p = PIECE_PLAYER_TWO;
+ else
+ p = PIECE_PLAYER_ONE;
+ }
+ else
+ p = PIECE_PLAYER_ONE;
+ }
+
+ addch(pdisplaycolours->character[piece] | dp_attr[p]);
+}
+
+void display_moves(struct level* plevel, struct level* plevelreplay)
+{
+ static int length = 0;
+ int i;
+ char buffer[256];
+ int moves, moves2;
+
+ moves = 0;
+ if(plevel->move_current != NULL)
+ {
+ /* If move_current->mover_first == NULL, we've actually undone all of
+ the current move, and are just about to move back to the previous
+ one; we treat it as the previous one for counting purposes */
+ if(plevel->move_current->mover_first != NULL)
+ moves = plevel->move_current->count;
+ else
+ moves = plevel->move_current->count - 1;
+ }
+
+ moves2 = -1;
+ if(plevelreplay != NULL)
+ {
+ moves2 = 0;
+ if(plevelreplay->move_last != NULL)
+ moves2 = plevelreplay->move_last->count;
+ }
+ /* Similarly, move_current->mover_first == NULL complicates things here */
+ else if(plevel->move_current != plevel->move_last
+ || (plevel->move_current != NULL && plevel->move_current->mover_first == NULL))
+ {
+ 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"));
+
+ attron(COLOR_PAIR(colourpair_cyan));
+
+ /* Blank previous display only if necessary */
+ if(utf8strlen(buffer) < length)
+ {
+ for(i = 0; i < length; i ++)
+ mvprintw(display_size_y - 1, display_size_x - 2 - length + i, " ");
+ }
+
+ length = utf8strlen(buffer);
+
+ mvprintw(display_size_y - 1, display_size_x - utf8strlen(buffer) - 2, "%s", buffer);
+ attroff(COLOR_PAIR(colourpair_cyan));
+
+ move(display_size_y - 1, display_size_x - 1);
+ display_piece(plevel, PIECE_PLAYER_ONE + plevel->player);
+ refresh();
+}
+
+void display_stars(struct level* plevel)
+{
+ static int length = 0;
+ char buffer[256];
+ int i;
+
+ 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"));
+
+ attron(COLOR_PAIR(colourpair_yellow));
+
+ /* Blank previous display only if necessary */
+ if(utf8strlen(buffer) < length)
+ {
+ for(i = 0; i < length; i ++)
+ mvprintw(display_size_y - 1, i + 2, " ");
+ }
+
+ length = utf8strlen(buffer);
+
+ mvprintw(display_size_y - 1, 2, "%s", buffer);
+ attroff(COLOR_PAIR(colourpair_yellow));
+
+ move(display_size_y - 1, 0);
+ display_piece(plevel, PIECE_STAR);
+}
+
+int display_focus(struct level* plevel)
+{
+ int px, py;
+ int ox, oy;
+#ifdef XOR_COMPATIBILITY
+ int redraw;
+#endif
+
+ getmaxyx(stdscr, display_size_y, display_size_x);
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ {
+ if(display_start_x != plevel->view_x[plevel->player] || display_start_y != plevel->view_y[plevel->player])
+ redraw = 1;
+ else
+ redraw = 0;
+
+ 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;
+
+ return redraw;
+ }
+#endif
+
+ ox = display_start_x;
+ oy = display_start_y;
+ px = plevel->player_x[plevel->player];
+ py = plevel->player_y[plevel->player];
+
+ if(plevel->size_x < display_size_x)
+ {
+ display_start_x = 0;
+ display_end_x = plevel->size_x;
+ }
+ else
+ {
+ if(px < display_start_x + display_border)
+ display_start_x = px - display_border;
+ if(px >= display_start_x + display_size_x - display_border)
+ display_start_x = px - display_size_x + display_border;
+ if(display_start_x < 0)
+ display_start_x = 0;
+ if(display_start_x + display_size_x > plevel->size_x)
+ display_start_x = plevel->size_x - display_size_x;
+ display_end_x = display_start_x + display_size_x;
+
+ }
+ if(plevel->size_y < display_size_y - 1)
+ {
+ display_start_y = 0;
+ display_end_y = plevel->size_y;
+ }
+ else
+ {
+ if(py < display_start_y + display_border)
+ display_start_y = py - display_border;
+ if(py >= display_start_y + display_size_y - 1 - display_border)
+ display_start_y = py - display_size_y + 1 + display_border;
+ if(display_start_y < 0)
+ display_start_y = 0;
+ if(display_start_y + display_size_y - 1 > plevel->size_y)
+ display_start_y = plevel->size_y - display_size_y + 1;
+ display_end_y = display_start_y + display_size_y - 1;
+
+ }
+
+ if(ox != display_start_x || oy != display_start_y)
+ return 1;
+ else
+ return 0;
+}
+
+void display_level(struct level* plevel)
+{
+ int x, y;
+ int p;
+
+ clear();
+
+ getmaxyx(stdscr, display_size_y, display_size_x);
+
+ if(display_start_x < 0)
+ display_start_x = 0;
+ if(display_start_y < 0)
+ display_start_y = 0;
+
+ if(display_end_x > plevel->size_x)
+ display_end_x = plevel->size_x;
+ if(display_end_y > plevel->size_y)
+ display_end_y = plevel->size_y;
+
+ display_offset_x = (display_size_x - (display_end_x - display_start_x))/2;
+ display_offset_y = (display_size_y - (display_end_y - display_start_y))/2;
+
+ for(y = display_start_y; y < display_end_y; y++)
+ {
+ move(y + display_offset_y - display_start_y, display_offset_x);
+ for(x = display_start_x; x < display_end_x; x++)
+ {
+ p = level_piece(plevel, x, y);
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched && (p == PIECE_SPACE || p == PIECE_WALL))
+ p = PIECE_DARKNESS;
+#endif
+ display_piece(plevel, p);
+ }
+ }
+}
+
+void display_play(struct level* plevel, struct level* plevelreplay)
+{
+ int key;
+ int quit;
+ struct mover* pmover;
+ int redraw;
+ int x, y;
+ int p;
+ int playermove;
+ int delay;
+ int fast;
+ int pass;
+ int c;
+ short cp;
+ char font_logo_colours[] = "1326454646644";
+ char buffer[256];
+
+ quit = 0;
+ redraw = 1;
+ fast = 0;
+
+ while(!quit)
+ {
+ redraw += display_focus(plevel);
+
+ if(redraw)
+ {
+ display_level(plevel);
+
+ if(plevel->title != NULL)
+ {
+ y = display_size_y - 1;
+ x = (display_size_x - utf8strlen(plevel->title) - (plevel->flags & LEVELFLAG_TESTING ? utf8strlen(gettext("testing: ")) : 0) ) / 2;
+ if(x < 0)
+ x = 0;
+ move(y, x);
+
+ if(plevel->flags & LEVELFLAG_TESTING)
+ {
+ attron(COLOR_PAIR(colourpair_cyan));
+ printw(gettext("testing: "));
+ attroff(COLOR_PAIR(colourpair_cyan));
+ }
+ if((strncmp(gettext(plevel->title), "chroma", 6) == 0))
+ {
+ strcpy(buffer, gettext(plevel->title));
+
+ for(x = 0; x < strlen(buffer); x ++)
+ {
+ cp = colourpair_white;
+ if(x < strlen(font_logo_colours))
+ c = font_logo_colours[x] - '0';
+ else
+ {
+ printw(buffer + x);
+ x = strlen(buffer);
+ break;
+ }
+ switch(c)
+ {
+ case 1:
+ cp = colourpair_red;
+ break;
+ case 2:
+ cp = colourpair_green;
+ break;
+ case 3:
+ cp = colourpair_yellow;
+ break;
+ case 4:
+ cp = colourpair_blue;
+ break;
+ case 5:
+ cp = colourpair_magenta;
+ break;
+ case 6:
+ cp = colourpair_cyan;
+ break;
+ default:
+ cp = colourpair_white;
+ break;
+ }
+ addch((*(buffer + x)) | COLOR_PAIR(cp));
+ }
+ }
+ else
+ printw("%s", plevel->title);
+ }
+
+ display_moves(plevel, plevelreplay);
+ display_stars(plevel);
+
+ refresh();
+ curs_set(0);
+ redraw = 0;
+ }
+
+ /* If there are movers, plot and then evolve them */
+ if(plevel->mover_first != NULL && !(plevel->flags & LEVELFLAG_PAUSED))
+ {
+ /* Plot movers in two passes - first spaces, then non-spaces.
+ This is counter-intuitive, but makes undoing the player work. */
+ for(pass = 0; pass < 2; pass ++)
+ {
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ if((pass == 0 && pmover->piece != PIECE_SPACE) ||
+ (pass == 1 && pmover->piece == PIECE_SPACE))
+ {
+ pmover = pmover->next;
+ continue;
+ }
+ x = pmover->x;
+ y = pmover->y;;
+ if(x >= display_start_x && x < display_end_x && y>= display_start_y && y < display_end_y)
+ {
+ move(display_offset_y - display_start_y + y, display_offset_x - display_start_x + x);
+ p = pmover->piece;
+ #ifdef XOR_COMPATIBILITY
+ if(plevel->switched && (p == PIECE_SPACE || p == PIECE_WALL))
+ p = PIECE_DARKNESS;
+ #endif
+ if(p != PIECE_GONE)
+ display_piece(plevel, p);
+ }
+ pmover = pmover->next;
+ }
+ }
+
+ /* Debug movers */
+ if(options_debug & DEBUG_ORDER)
+ {
+ /* Display the movers */
+ pmover = plevel->mover_first;
+ y = 0;
+ while(pmover != NULL && y < display_size_y - 1)
+ {
+ if(pmover->piece != PIECE_GONE)
+ {
+ move(y ++, 0);
+ display_piece(plevel, pmover->piece);
+ printw(" %2d,%2d ", pmover->x, pmover->y);
+
+ }
+ pmover = pmover->next;
+ }
+ while(y < display_size_y - 1)
+ mvprintw(y++, 0, " ");
+
+ /* Display the stack if our game engine uses it */
+ if(0
+ #ifdef XOR_COMPATIBILITY
+ || (plevel->mode == MODE_XOR && options_xor_mode)
+ #endif
+ #ifdef ENIGMA_COMPATIBILITY
+ || (plevel->mode == MODE_ENIGMA && options_enigma_mode)
+ #endif
+ )
+ {
+ pmover = plevel->stack_first;
+ y = 0;
+ while(pmover != NULL && y < display_size_y - 1)
+ {
+ if(pmover->piece != PIECE_GONE)
+ {
+ move(y ++, display_size_x - 8);
+ display_piece(plevel, pmover->piece);
+ printw(" %2d,%2d ", pmover->x, pmover->y);
+
+ }
+ pmover = pmover->next;
+ }
+ while(y < display_size_y - 1)
+ mvprintw(y++, display_size_x - 8, " ");
+ }
+ }
+
+ refresh();
+
+ /* Evolve movers */
+ if(!(plevel->flags & LEVELFLAG_UNDO))
+ {
+ if(level_evolve(plevel))
+ redraw += display_focus(plevel);
+ level_storemovers(plevel);
+ }
+ else
+ {
+ if(level_undo(plevel))
+ plevel->flags |= LEVELFLAG_UNDO;
+ else
+ plevel->flags &= ~LEVELFLAG_UNDO;
+ }
+ }
+
+ /* Determine which delay to use */
+ delay = options_curses_delay;
+ if(plevelreplay != NULL)
+ {
+ if(plevel->mover_first == NULL && plevelreplay->move_current != NULL)
+ delay = options_curses_replay_delay;
+
+ if(fast)
+ delay = 0;
+
+ if(plevelreplay->flags & LEVELFLAG_UNDO)
+ {
+ if(plevel->move_current == NULL && plevel->mover_first == NULL)
+ {
+ if(options_curses_replay_delay != 0)
+ delay = options_curses_replay_delay;
+ else
+ delay = 1;
+ }
+ }
+ else
+ {
+ if(plevel->mover_first == NULL && plevelreplay->move_current == NULL)
+ delay = -1;
+ }
+ }
+ else
+ {
+ if(fast)
+ delay = 0;
+
+ if(plevel->mover_first == NULL)
+ {
+ delay = -1;
+ fast = 0;
+ }
+ }
+
+ if(delay > 0)
+ halfdelay(delay);
+
+ if(delay != 0)
+ {
+ key = getch();
+ if(key < 0 || key >= KEY_MAX)
+ key = 0;
+ if(key >= 'a' && key <='z')
+ key -= 32;
+ }
+ else
+ key = 0;
+
+ if(delay > 0)
+ cbreak();
+
+ playermove = MOVE_NONE;
+
+ switch(actions[key])
+ {
+ case ACTION_REDRAW:
+ redraw = 1;
+ break;
+
+ case ACTION_HIDE:
+ display_hide();
+ redraw = 1;
+ break;
+
+ case ACTION_QUIT:
+ quit = 1;
+ break;
+
+ case ACTION_FAST:
+ fast = 1 - fast;
+ break;
+
+ case ACTION_LEFT:
+ if(plevelreplay != NULL)
+ {
+ plevelreplay->flags |= LEVELFLAG_UNDO;
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ }
+ else
+ playermove = MOVE_LEFT;
+ break;
+
+ case ACTION_RIGHT:
+ if(plevelreplay != NULL)
+ {
+ plevelreplay->flags &= ~LEVELFLAG_UNDO;
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ }
+ else
+ playermove = MOVE_RIGHT;
+ break;
+
+ case ACTION_UP:
+ if(plevelreplay != NULL)
+ plevelreplay->flags |= LEVELFLAG_PAUSED;
+ else
+ playermove = MOVE_UP;
+ break;
+
+ case ACTION_DOWN:
+ if(plevelreplay != NULL)
+ plevelreplay->flags |= LEVELFLAG_PAUSED;
+ else
+ playermove = MOVE_DOWN;
+ break;
+
+ case ACTION_PAUSE:
+ if(plevelreplay != NULL)
+ {
+ if(plevelreplay->flags & LEVELFLAG_PAUSED)
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ else
+ plevelreplay->flags |= LEVELFLAG_PAUSED;
+ }
+ else if(plevel->mover_first != NULL)
+ {
+ if(plevel->flags & LEVELFLAG_PAUSED)
+ plevel->flags &= ~LEVELFLAG_PAUSED;
+ else
+ plevel->flags |= LEVELFLAG_PAUSED;
+ plevel->flags |= LEVELFLAG_MOVES;
+ }
+ break;
+
+ case ACTION_SWAP:
+ if(plevelreplay == NULL)
+ playermove = MOVE_SWAP;
+ break;
+
+ case ACTION_UNDO:
+ if(plevelreplay == NULL)
+ {
+ 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;
+ }
+ }
+ break;
+
+ case ACTION_REDO:
+ playermove = MOVE_REDO;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Are we replaying the level? */
+ if(plevelreplay != NULL)
+ {
+ /* 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;
+ }
+ }
+ }
+ }
+
+ /* 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)
+ level_move(plevel, playermove);
+
+ /* Display things changed by the move */
+ 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;
+ }
+
+ if(plevel->flags & LEVELFLAG_SWITCH)
+ {
+ redraw = 1;
+ plevel->flags ^= LEVELFLAG_SWITCH;
+ }
+
+ #ifdef XOR_COMPATIBILITY
+ if(plevel->flags & LEVELFLAG_MAP)
+ {
+ /* No sensible way to handle this in curses */
+ plevel->flags ^= LEVELFLAG_MAP;
+ }
+ #endif
+
+ if(!(plevel->flags & LEVELFLAG_SOLVED) && plevel->flags & LEVELFLAG_EXIT)
+ {
+ plevel->flags |= LEVELFLAG_SOLVED;
+ display_stars(plevel);
+ }
+
+ if(!(plevel->flags & LEVELFLAG_FAILED) && plevel->alive[0] == 0 && plevel->alive[1] ==0)
+ {
+ plevel->flags |= LEVELFLAG_FAILED;
+ display_moves(plevel, plevelreplay);
+ }
+ }
+}
+
+void display_edit(struct level* plevel)
+{
+ int key;
+ int quit;
+ static int editor_piece = PIECE_SPACE;
+ int redraw, moved, pmoved;
+ int i;
+ int player;
+ int piece_count;
+
+ redraw = 1;
+ moved = 1;
+ pmoved = 1;
+
+ /* Store player */
+ player = plevel->player;
+ plevel->player = 2;
+
+ piece_count = 0;
+ while(editor_piece_maps[plevel->mode][piece_count] != PIECE_GONE)
+ piece_count ++;
+
+ if(editor_piece > piece_count)
+ editor_piece = 0;
+
+ quit = 0;
+ while(!quit)
+ {
+ redraw += display_focus(plevel);
+
+ if(redraw)
+ {
+ redraw = 0;
+
+ display_level(plevel);
+
+ for(i = 0; i < piece_count; i ++)
+ {
+ move(display_size_y - 1, 1 + i * 2);
+ display_piece(plevel, editor_piece_maps[plevel->mode][i]);
+ }
+
+ move(display_size_y - 1, display_size_x - 4);
+ printw("[ ]");
+
+ curs_set(1);
+
+ pmoved = 1;
+ }
+
+ if(pmoved)
+ {
+ pmoved = 0;
+
+ move(display_size_y - 1, editor_piece * 2);
+ printw(">");
+ move(display_size_y - 1, 2 + editor_piece * 2);
+ printw("<");
+ move(display_size_y - 1, display_size_x - 3);
+ display_piece(plevel, editor_piece_maps[plevel->mode][editor_piece]);
+ moved = 1;
+ }
+
+ if(moved)
+ {
+ moved = 0;
+
+ move(display_offset_y - display_start_y + plevel->player_y[2], display_offset_x - display_start_x + plevel->player_x[2]);
+ refresh();
+ }
+
+ key = getch();
+ if(key < 0 || key >= KEY_MAX)
+ key = 0;
+ if(key >= 'a' && key <='z')
+ key -= 32;
+
+ switch(actions[key])
+ {
+ case ACTION_REDRAW:
+ redraw = 1;
+ break;
+
+ case ACTION_HIDE:
+ display_hide();
+ redraw = 1;
+ break;
+
+ case ACTION_QUIT:
+ quit = 1;
+ 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_SWAP:
+ level_setpiece(plevel, plevel->player_x[2], plevel->player_y[2], editor_piece_maps[plevel->mode][editor_piece]);
+ display_piece(plevel, editor_piece_maps[plevel->mode][editor_piece]);
+ moved = 1;
+ break;
+
+ case ACTION_PIECE_LEFT:
+ move(display_size_y - 1, editor_piece * 2);
+ printw(" ");
+ move(display_size_y - 1, 2 + editor_piece * 2);
+ printw(" ");
+ editor_piece --;
+ if(editor_piece < 0)
+ editor_piece = piece_count - 1;
+ pmoved = 1;
+ break;
+
+ case ACTION_PIECE_RIGHT:
+ move(display_size_y - 1, editor_piece * 2);
+ printw(" ");
+ move(display_size_y - 1, 2 + editor_piece * 2);
+ printw(" ");
+ editor_piece ++;
+ if(editor_piece >= piece_count)
+ editor_piece = 0;
+ pmoved = 1;
+ break;
+ }
+ }
+
+ /* Restore real player */
+ plevel->player = player;
+}
+
+int display_type()
+{
+ return DISPLAY_CURSES;
+}
+
+void display_options()
+{
+ struct menu* pmenu;
+ struct menu* pcoloursmenu;
+ struct menuentry* pentrycolours;
+ struct menuentry* pentryspeed;
+ struct menuentry* pentryreplayspeed;
+ char buffer[256];
+ int ok;
+ int result;
+
+ pmenu = menu_new(gettext("Display Options"));
+
+ menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ menuentry_new(pmenu, gettext("Save Options"), 'S', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentrycolours = menuentry_new(pmenu, gettext("Colour Scheme"), 'C', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentryspeed = menuentry_new(pmenu, gettext("Move Speed"), 'M', MENU_SCROLLABLE);
+ pentryreplayspeed = menuentry_new(pmenu, gettext("Replay Speed"), 'R', MENU_SCROLLABLE);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ menuentry_new(pmenu, gettext("Change Keys"), 'K', 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(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);
+
+ switch(options_curses_delay)
+ {
+ case -1:
+ sprintf(buffer, gettext("after a key is pressed"));
+ break;
+ case 0:
+ sprintf(buffer, gettext("instantaneous"));
+ break;
+ default:
+ sprintf(buffer, gettext("%d00 milliseconds"), options_curses_delay);
+ break;
+ }
+ menuentry_extratext(pentryspeed, buffer, NULL, NULL);
+
+ switch(options_curses_replay_delay)
+ {
+ case -1:
+ sprintf(buffer, gettext("after a key is pressed"));
+ break;
+ case 0:
+ sprintf(buffer, gettext("instantaneous"));
+ break;
+ default:
+ sprintf(buffer, gettext("%d00 milliseconds"), options_curses_replay_delay);
+ break;
+ }
+ menuentry_extratext(pentryreplayspeed, buffer, 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 '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();
+ display_initcolours();
+ }
+ }
+ menu_delete(pcoloursmenu);
+ break;
+
+ case 'S':
+ display_options_save();
+ ok = 1;
+ break;
+
+ case 'K':
+ display_keys();
+ break;
+
+ case 'X':
+ display_options_othergames();
+ break;
+
+ case 'D':
+ display_debug();
+ break;
+ }
+ }
+
+ if(result == MENU_SCROLLLEFT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'M':
+ options_curses_delay --;
+ if(options_curses_delay < -1)
+ options_curses_delay = 10;
+#ifdef PDCURSES
+ if(options_curses_delay > 0)
+ options_curses_delay = 0;
+#endif
+ break;
+ case 'R':
+ options_curses_replay_delay --;
+ if(options_curses_replay_delay < -1)
+ options_curses_replay_delay = 10;
+
+#ifdef PDCURSES
+ if(options_curses_replay_delay > 0)
+ options_curses_replay_delay = 0;
+#endif
+ break;
+ }
+ }
+
+ if(result == MENU_SCROLLRIGHT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'M':
+ options_curses_delay ++;
+ if(options_curses_delay > 10)
+ options_curses_delay = -1;
+#ifdef PDCURSES
+ if(options_curses_delay > 0)
+ options_curses_delay = 0;
+#endif
+ break;
+ case 'R':
+ options_curses_replay_delay ++;
+ if(options_curses_replay_delay > 10)
+ options_curses_replay_delay = -1;
+#ifdef PDCURSES
+ if(options_curses_replay_delay > 0)
+ options_curses_replay_delay = 0;
+#endif
+ break;
+ }
+ }
+ }
+
+ menu_delete(pmenu);
+}
+
+int display_keyfixed(int i)
+{
+ if(i == 0 || i == KEY_RESIZE || i == 27 || i == 'Q' || i == '\n' || i == '\r' || i == KEY_UP || i == KEY_DOWN || i == KEY_LEFT || i == KEY_RIGHT)
+ return 1;
+
+ return 0;
+}
+
+char *display_keyname(int i)
+{
+ static char buffer[4];
+
+ if(i == '\t')
+ return "TAB";
+ if(i == '\n')
+ return "ENTER";
+ if(i == 27)
+ return "ESCAPE";
+ if(i == 32)
+ return "SPACE";
+ if(i == KEY_DC)
+ return "DELETE";
+ if(i == KEY_IC)
+ return "INSERT";
+
+ if(keyname(i) == NULL)
+ return "UNKNOWN";
+
+ if(strcmp(keyname(i), "NO KEY NAME") == 0)
+ {
+ if(i >= 0 && i < 32)
+ {
+ sprintf(buffer, "^%c", i + '@');
+ return buffer;
+ }
+ if(i > 32 && i < 127)
+ {
+ sprintf(buffer, "%c", i);
+ return buffer;
+ }
+
+ return "UNKNOWN";
+ }
+
+ if(strncmp(keyname(i), "KEY_", 4) == 0)
+ return (char *)(keyname(i) + 4);
+
+ return (char *)keyname(i);
+}
+
+void display_addkeytomenu(struct menu* pmenu, int action, char *text)
+{
+ struct menuentry *pentry;
+ char buffer[256];
+ int i;
+
+ sprintf(buffer, "%d", action);
+ pentry = menuentry_newwithvalue(pmenu, text, 0, MENU_DOUBLE, buffer);
+
+ strcpy(buffer, "");
+ for(i = 0; i < KEY_MAX; i ++)
+ {
+ if(actions[i] == action && i != KEY_RESIZE)
+ {
+ if(strlen(buffer) != 0)
+ strcat(buffer,", ");
+ strcat(buffer, "[");
+ strcat(buffer, display_keyname(i));
+ strcat(buffer, "]");
+ }
+ }
+
+ if(strcmp(buffer, "") == 0)
+ strcpy(buffer, "(none)");
+
+ menuentry_extratext(pentry, NULL, NULL, buffer);
+
+}
+
+void display_keys()
+{
+ struct menu *pmenu;
+ struct menu *psubmenu;
+ struct menuentry *pentry;
+ int action;
+ int result;
+ int redraw;
+ int ok;
+ int subok;
+ char buffer[256];
+ int i;
+ int key;
+
+ 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_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)
+ {
+ redraw = MENUREDRAW_ALL;
+ subok = 0;
+ 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(i = 0; i < KEY_MAX; i ++)
+ {
+ if(actions[i] == action && i != KEY_RESIZE)
+ {
+ sprintf(buffer, "[%s]", display_keyname(i));
+ pentry = menuentry_new(psubmenu, buffer, 0, MENU_GREY);
+ if(display_keyfixed(i))
+ menuentry_extratext(pentry, gettext("(fixed)"), NULL, NULL);
+ }
+ }
+ menuentry_new(psubmenu, "", 0, MENU_SPACE);
+
+ 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);
+
+ key = getch();
+ if(key == KEY_RESIZE)
+ {
+ getmaxyx(stdscr, display_size_y, display_size_x);
+ redraw = MENUREDRAW_ALL;
+ }
+
+ if(key >= 'a' && key <='z')
+ key -=32;
+
+ if(key == 27 || key == 'q' || key == 'Q' || key== '\n')
+ subok = 1;
+ else if(!display_keyfixed(key))
+ {
+ if(actions[key] == action)
+ actions[key] = ACTION_NONE;
+ else
+ actions[key] = action;
+ }
+ }
+ }
+ }
+
+ menu_delete(pmenu);
+ }
+}
+
+void display_debug()
+{
+ struct menu* pmenu;
+ struct menuentry* pentrymovers;
+ 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);
+
+ pentrymovers = menuentry_new(pmenu, gettext("Display order of movers"), 'O', MENU_SCROLLABLE);
+ pentryhidden = menuentry_new(pmenu, gettext("Show hidden items"), 'H', MENU_SCROLLABLE);
+
+ ok = 0;
+ while(!ok)
+ {
+ menuentry_extratext(pentrymovers, options_debug & DEBUG_ORDER ? 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 || result == MENU_SCROLLLEFT || result == MENU_SCROLLRIGHT) && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'Q':
+ ok = 1;
+ break;
+
+ case 'O':
+ options_debug ^= DEBUG_ORDER;
+ 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];
+ int i;
+
+ getfilename("curses.chroma", filename, 1, 0);
+
+ file = fopen(filename, "w");
+ if(file == NULL)
+ {
+ warning("Unable to save options");
+ return;
+ }
+
+ fprintf(file, "<!-- Chroma curses options \n"
+ " This file is automatically generated. -->\n"
+ "\n"
+ "<chroma type=\"options\">\n");
+
+ fprintf(file, " <colour scheme=\"%s\" />\n", options_colours);
+
+ if(options_curses_delay == -1)
+ fprintf(file, " <move speed=\"key\" />\n");
+ else
+ fprintf(file, " <move speed=\"%d\" />\n", options_curses_delay * 100);
+
+ if(options_curses_replay_delay == -1)
+ fprintf(file, " <replay speed=\"key\" />\n");
+ else
+ fprintf(file, " <replay speed=\"%d\" />\n", options_curses_replay_delay * 100);
+
+#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, "hidden=\"%s\" ", options_debug & DEBUG_HIDDEN ? "yes" : "no");
+ fprintf(file,"/>\n");
+
+ fprintf(file, " <keys>\n");
+
+ for(i = 0; i < KEY_MAX; i ++)
+ {
+ if(actions[i] != ACTION_NONE && i != KEY_RESIZE)
+ fprintf(file, " <key name=\"%s\" action=\"%s\" />\n", display_keyname(i), action_shortname[actions[i]]);
+ }
+
+ fprintf(file, " </keys>\n");
+
+ fprintf(file, "</chroma>\n");
+
+ fclose(file);
+}
+
+void display_options_load()
+{
+ struct parser* pparser;
+ char filename[FILENAME_MAX];
+ int state;
+ int i;
+ int key, action;
+
+ /* Sensible defaults */
+#ifdef PDCURSES
+ /* halfdelay() is broken in PDCurses */
+ options_curses_delay = 0;
+ options_curses_replay_delay = 0;
+#else
+ options_curses_delay = 1;
+ options_curses_replay_delay = 1;
+#endif
+#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, 1);
+ sprintf(options_colours, "%s/%s", filename, COLOURS_DEFAULT);
+
+ getfilename("curses.chroma", filename, 0, 0);
+
+ for(i = 0; i < KEY_MAX; i ++)
+ {
+ actions[i] = ACTION_NONE;
+ }
+
+ /* Fixed keys */
+ actions[KEY_RESIZE] = ACTION_REDRAW;
+ actions[KEY_UP] = ACTION_UP;
+ actions[KEY_DOWN] = ACTION_DOWN;
+ actions[KEY_LEFT] = ACTION_LEFT;
+ actions[KEY_RIGHT] = ACTION_RIGHT;
+ actions['\r'] = ACTION_SWAP;
+ actions['\n'] = ACTION_SWAP;
+ actions['Q'] = ACTION_QUIT;
+ actions[27] = ACTION_QUIT;
+
+ /* Sensible default keys */
+ if(!isfile(filename))
+ {
+ actions[12] = ACTION_REDRAW;
+ actions[' '] = ACTION_SWAP;
+ actions['F'] = ACTION_FAST;
+ actions[KEY_BACKSPACE] = ACTION_UNDO;
+ actions[KEY_DC] = ACTION_UNDO;
+ actions['U'] = ACTION_UNDO;
+ actions[KEY_IC] = ACTION_REDO;
+ actions['Y'] = ACTION_REDO;
+ actions['Z'] = ACTION_PIECE_LEFT;
+ actions['X'] = ACTION_PIECE_RIGHT;
+ actions[KEY_PPAGE] = ACTION_PIECE_LEFT;;
+ actions[KEY_NPAGE] = ACTION_PIECE_RIGHT;
+ actions['P'] = ACTION_PAUSE;
+ return;
+ }
+
+ /* Parse XML file */
+ /*
+ <chroma type="options">
+ <colour scheme="filename" />
+ <move speed="speed" />
+ <replay speed="speed" />
+ <xor mode="mode" />
+ <debug menu="yes/no" 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> */
+ };
+
+ state = OPTIONSPARSER_OUTSIDE;
+ key = 0;
+ action = 0;
+
+ 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;
+ break;
+
+ case OPTIONSPARSER_KEYS:
+ if(parser_match(pparser, 0, "key"))
+ {
+ key = 0;
+ action = ACTION_NONE;
+ }
+ break;
+
+ default:
+ 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;
+
+ default:
+ 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, "colour") && parser_match(pparser, 1, "scheme"))
+ {
+ strncpy(options_colours, parser_text(pparser, 0), FILENAME_MAX);
+ }
+ if(parser_match(pparser, 2, "move") && parser_match(pparser, 1, "speed"))
+ {
+ if(parser_match(pparser, 0, "key"))
+ options_curses_delay = -1;
+ else
+ options_curses_delay = atoi(parser_text(pparser, 0)) / 100;
+#ifdef PDCURSES
+ if(options_curses_delay > 0)
+ options_curses_delay = 0;
+#endif
+ }
+ if(parser_match(pparser, 2, "replay") && parser_match(pparser, 1, "speed"))
+ {
+ if(parser_match(pparser, 0, "key"))
+ options_curses_replay_delay = -1;
+ else
+ options_curses_replay_delay = atoi(parser_text(pparser, 0)) / 100;
+#ifdef PDCURSES
+ if(options_curses_replay_delay > 0)
+ options_curses_replay_delay = 0;
+#endif
+ }
+#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, "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 = 0; i < KEY_MAX; i ++)
+ {
+ if(parser_match(pparser, 0, display_keyname(i)))
+ {
+ key = i;
+ i = KEY_MAX;
+ }
+ }
+ }
+ 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;
+ }
+ break;
+
+ case PARSER_ERROR:
+ state = OPTIONSPARSER_END;
+ break;
+ }
+ }
+
+ parser_delete(pparser);
+}