1 /*
2     cursesdisplay.c
3 
4     Copyright (C) 2010-2019 Amf
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <libintl.h>
24 #include <locale.h>
25 
26 #ifdef CHROMA_CURSES_HEADER
27 #include CHROMA_CURSES_HEADER
28 #else
29 #ifdef __WIN32__
30 #include <curses.h>
31 #else
32 #include <ncurses.h>
33 #endif
34 #endif
35 
36 #include "chroma.h"
37 #include "menu.h"
38 #include "level.h"
39 #include "display.h"
40 #include "colours.h"
41 #include "actions.h"
42 #include "util.h"
43 #include "xmlparser.h"
44 
45 char options_colours[FILENAME_MAX] = COLOURS_DEFAULT;
46 int options_curses_delay = 1;
47 int options_curses_replay_delay = 1;
48 int options_debug = 0;
49 #ifdef XOR_COMPATIBILITY
50 int options_xor_options = 0;
51 int options_xor_mode = 1;
52 int options_xor_display = 0;
53 #endif
54 #ifdef ENIGMA_COMPATIBILITY
55 int options_enigma_options = 0;
56 int options_enigma_mode = 1;
57 #endif
58 
59 
60 extern struct colours* pdisplaycolours;
61 extern int *editor_piece_maps[];
62 extern char *action_name[];
63 extern char *action_shortname[];
64 
65 /* Translation table for colours.
66    This is necessary as some versions of curses interchange red and blue.
67  */
68 short colourtrans[] = {COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE};
69 
70 int dp_attr[256], dp_col[256];
71 char dp_char[256];
72 
73 int actions[KEY_MAX];
74 
75 int display_size_x, display_size_y;
76 int display_offset_x, display_offset_y;
77 int display_focus_x, display_focus_y;
78 int display_start_x, display_start_y;
79 int display_end_x, display_end_y;
80 int display_border = 7;
81 
82 void display_piece(struct level* plevel, int piece);
83 
84 int display_colourpairs = 0;
85 short display_cpfore[64];
86 short display_cpback[64];
87 
88 short colourpair_red;
89 short colourpair_green;
90 short colourpair_yellow;
91 short colourpair_blue;
92 short colourpair_cyan;
93 short colourpair_magenta;
94 short colourpair_cyan;
95 short colourpair_white;
96 short colourpair_menu;
97 short colourpair_menugrey;
98 
99 char *display_keyname(int i);
100 void display_addkeytomenu(struct menu* pmenu, int action, char *text);
101 void display_keys();
102 void display_debug();
103 void display_initcolours();
104 
105 void display_options_othergames();
106 
display_newcolourpair(short foreground,short background)107 short display_newcolourpair(short foreground, short background)
108 {
109     short i;
110 
111     for(i = 1; i <= display_colourpairs; i ++)
112     {
113         if(foreground == display_cpfore[i] && background == display_cpback[i])
114             return i;
115     }
116     display_colourpairs ++;
117     display_cpfore[display_colourpairs] = foreground;
118     display_cpback[display_colourpairs] = background;
119 
120     init_pair(display_colourpairs, foreground, background);
121 
122     return display_colourpairs;
123 }
124 
display_init(int argc,char ** argv)125 void display_init(int argc, char **argv)
126 {
127     setlocale(LC_CTYPE, "");
128     atexit(display_quit);
129     initscr();
130     raw();
131     noecho();
132     keypad(stdscr, TRUE);
133     curs_set(0);
134     start_color();
135 
136     getmaxyx(stdscr, display_size_y, display_size_x);
137 
138     colourpair_red = display_newcolourpair(COLOR_RED, COLOR_BLACK);
139     colourpair_green = display_newcolourpair(COLOR_GREEN, COLOR_BLACK);
140     colourpair_yellow = display_newcolourpair(COLOR_YELLOW, COLOR_BLACK);
141     colourpair_blue = display_newcolourpair(COLOR_BLUE, COLOR_BLACK);
142     colourpair_magenta = display_newcolourpair(COLOR_MAGENTA, COLOR_BLACK);
143     colourpair_cyan = display_newcolourpair(COLOR_CYAN, COLOR_BLACK);
144     colourpair_white = display_newcolourpair(COLOR_WHITE, COLOR_BLACK);
145     colourpair_menu = display_newcolourpair(COLOR_CYAN, COLOR_BLUE);
146     colourpair_menugrey = display_newcolourpair(COLOR_CYAN, COLOR_BLACK);
147 
148     display_options_load();
149     colours_init();
150     display_initcolours();
151 }
152 
display_initcolours()153 void display_initcolours()
154 {
155     int i;
156     short fg, bg;
157 
158 #ifdef PDCURSES
159     short tg;
160 #endif
161 
162     for(i = 0; i < PIECE_MAX; i ++)
163     {
164         fg = pdisplaycolours->foreground[i];
165         if(fg < 0 || fg > 7)
166         fg = 7;
167 
168         bg = pdisplaycolours->background[i];
169         if(bg < 0 || bg > 7)
170         bg = 0;
171 
172 #ifdef PDCURSES
173         /* PDCurses doesn't handle reverse colours well; we swap them manually */
174         if(pdisplaycolours->reverse[i])
175         {
176             tg = fg; fg = bg; bg = tg;
177         }
178 #endif
179 
180         dp_attr[i] = COLOR_PAIR(display_newcolourpair(colourtrans[fg], colourtrans[bg]));
181 
182         if(pdisplaycolours->bold[i])
183             dp_attr[i] |= A_BOLD;
184 
185 #ifndef PDCURSES
186         if(pdisplaycolours->reverse[i])
187             dp_attr[i] |= A_REVERSE;
188 #endif
189     }
190 
191 }
192 
display_quit()193 void display_quit()
194 {
195     clear();
196     refresh();
197     endwin();
198 }
199 
display_hide()200 void display_hide()
201 {
202     clear();
203     refresh();
204     getch();
205 }
206 
display_piece(struct level * plevel,int piece)207 void display_piece(struct level* plevel, int piece)
208 {
209     int p;
210 
211     if(piece < 0 || piece >= PIECE_MAX)
212     return;
213 
214     /* Use the colours of PIECE_PLAYER_ONE for the active player,
215        and the colours of PIECE_PLAYER_TWO for the inactive one. */
216     p = piece;
217     if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
218     {
219         if(plevel->player != 2)
220         {
221             if(plevel->player != (p & 1))
222             p = PIECE_PLAYER_TWO;
223             else
224             p = PIECE_PLAYER_ONE;
225         }
226         else
227             p = PIECE_PLAYER_ONE;
228     }
229 
230     addch(pdisplaycolours->character[piece] | dp_attr[p]);
231 }
232 
display_moves(struct level * plevel,struct level * plevelreplay)233 void display_moves(struct level* plevel, struct level* plevelreplay)
234 {
235     static int length = 0;
236     int i;
237     char buffer[256];
238     int moves, moves2;
239 
240     moves = 0;
241     if(plevel->move_current != NULL)
242     {
243         /* If move_current->mover_first == NULL, we've actually undone all of
244            the current move, and are just about to move back to the previous
245            one; we treat it as the previous one for counting purposes */
246         if(plevel->move_current->mover_first != NULL)
247             moves = plevel->move_current->count;
248         else
249             moves = plevel->move_current->count - 1;
250     }
251 
252     moves2 = -1;
253     if(plevelreplay != NULL)
254     {
255         moves2 = 0;
256         if(plevelreplay->move_last != NULL)
257             moves2 = plevelreplay->move_last->count;
258     }
259     /* Similarly, move_current->mover_first == NULL complicates things here */
260     else if(plevel->move_current != plevel->move_last
261             || (plevel->move_current != NULL && plevel->move_current->mover_first == NULL))
262     {
263         if(plevel->move_last != NULL)
264             moves2 = plevel->move_last->count;
265     }
266 
267     if(moves2 != -1)
268         sprintf(buffer, "%s%d/%d",
269                 plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") :
270                 plevelreplay != NULL ? gettext("replay ") : "",
271                 moves, moves2);
272     else
273         sprintf(buffer, "%s%d",
274                 plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") : "",
275                 moves);
276 
277     if(plevel->flags & LEVELFLAG_FAILED)
278         sprintf(buffer, gettext("failed"));
279 
280     attron(COLOR_PAIR(colourpair_cyan));
281 
282     /* Blank previous display only if necessary */
283     if(utf8strlen(buffer) < length)
284     {
285         for(i = 0; i < length; i ++)
286             mvprintw(display_size_y - 1, display_size_x - 2 - length + i, " ");
287     }
288 
289     length = utf8strlen(buffer);
290 
291     mvprintw(display_size_y - 1, display_size_x - utf8strlen(buffer) - 2, "%s", buffer);
292     attroff(COLOR_PAIR(colourpair_cyan));
293 
294     move(display_size_y - 1, display_size_x - 1);
295     display_piece(plevel, PIECE_PLAYER_ONE + plevel->player);
296     refresh();
297 }
298 
display_stars(struct level * plevel)299 void display_stars(struct level* plevel)
300 {
301     static int length = 0;
302     char buffer[256];
303     int i;
304 
305     sprintf(buffer, "%d/%d", plevel->stars_caught, plevel->stars_total);
306 
307     if(plevel->stars_exploded != 0)
308         sprintf(buffer, gettext("%d lost"), plevel->stars_exploded);
309 
310     if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED))
311         sprintf(buffer, gettext("solved"));
312 
313     attron(COLOR_PAIR(colourpair_yellow));
314 
315     /* Blank previous display only if necessary */
316     if(utf8strlen(buffer) < length)
317     {
318         for(i = 0; i < length; i ++)
319             mvprintw(display_size_y - 1, i + 2, " ");
320     }
321 
322     length = utf8strlen(buffer);
323 
324     mvprintw(display_size_y - 1, 2, "%s", buffer);
325     attroff(COLOR_PAIR(colourpair_yellow));
326 
327     move(display_size_y - 1, 0);
328     display_piece(plevel, PIECE_STAR);
329 }
330 
display_focus(struct level * plevel)331 int display_focus(struct level* plevel)
332 {
333     int px, py;
334     int ox, oy;
335 #ifdef XOR_COMPATIBILITY
336     int redraw;
337 #endif
338 
339     getmaxyx(stdscr, display_size_y, display_size_x);
340 
341 #ifdef XOR_COMPATIBILITY
342     if(plevel->mode == MODE_XOR && options_xor_display)
343     {
344         if(display_start_x != plevel->view_x[plevel->player] || display_start_y != plevel->view_y[plevel->player])
345             redraw = 1;
346         else
347             redraw = 0;
348 
349         display_start_x = plevel->view_x[plevel->player];
350         display_start_y = plevel->view_y[plevel->player];
351         display_end_x = display_start_x + 8;
352         display_end_y = display_start_y + 8;
353 
354         return redraw;
355     }
356 #endif
357 
358     ox = display_start_x;
359     oy = display_start_y;
360     px = plevel->player_x[plevel->player];
361     py = plevel->player_y[plevel->player];
362 
363     if(plevel->size_x < display_size_x)
364     {
365         display_start_x = 0;
366         display_end_x = plevel->size_x;
367     }
368     else
369     {
370         if(px < display_start_x + display_border)
371            display_start_x = px - display_border;
372         if(px >= display_start_x + display_size_x - display_border)
373             display_start_x = px - display_size_x + display_border;
374         if(display_start_x < 0)
375             display_start_x = 0;
376         if(display_start_x + display_size_x > plevel->size_x)
377             display_start_x = plevel->size_x - display_size_x;
378         display_end_x = display_start_x + display_size_x;
379 
380     }
381     if(plevel->size_y < display_size_y - 1)
382     {
383         display_start_y = 0;
384         display_end_y = plevel->size_y;
385     }
386     else
387     {
388         if(py < display_start_y + display_border)
389            display_start_y = py - display_border;
390         if(py >= display_start_y + display_size_y - 1 - display_border)
391             display_start_y = py - display_size_y + 1 + display_border;
392         if(display_start_y < 0)
393             display_start_y = 0;
394         if(display_start_y + display_size_y - 1 > plevel->size_y)
395             display_start_y = plevel->size_y - display_size_y + 1;
396         display_end_y = display_start_y + display_size_y - 1;
397 
398     }
399 
400     if(ox != display_start_x || oy != display_start_y)
401         return 1;
402     else
403         return 0;
404 }
405 
display_level(struct level * plevel)406 void display_level(struct level* plevel)
407 {
408     int x, y;
409     int p;
410 
411     clear();
412 
413     getmaxyx(stdscr, display_size_y, display_size_x);
414 
415     if(display_start_x < 0)
416         display_start_x = 0;
417     if(display_start_y < 0)
418         display_start_y = 0;
419 
420     if(display_end_x > plevel->size_x)
421         display_end_x = plevel->size_x;
422     if(display_end_y > plevel->size_y)
423         display_end_y = plevel->size_y;
424 
425     display_offset_x = (display_size_x - (display_end_x - display_start_x))/2;
426     display_offset_y = (display_size_y - (display_end_y - display_start_y))/2;
427 
428     for(y = display_start_y; y < display_end_y; y++)
429     {
430         move(y + display_offset_y - display_start_y, display_offset_x);
431         for(x = display_start_x; x < display_end_x; x++)
432         {
433             p = level_piece(plevel, x, y);
434 #ifdef XOR_COMPATIBILITY
435             if(plevel->switched && (p == PIECE_SPACE || p == PIECE_WALL))
436                 p = PIECE_DARKNESS;
437 #endif
438             display_piece(plevel, p);
439         }
440     }
441 }
442 
display_play(struct level * plevel,struct level * plevelreplay)443 void display_play(struct level* plevel, struct level* plevelreplay)
444 {
445     int key;
446     int quit;
447     struct mover* pmover;
448     int redraw;
449     int x, y;
450     int p;
451     int playermove;
452     int delay;
453     int fast;
454     int pass;
455     int c;
456     short cp;
457     char font_logo_colours[] = "1326454646644";
458     char buffer[256];
459 
460     quit = 0;
461     redraw = 1;
462     fast = 0;
463 
464     while(!quit)
465     {
466         redraw += display_focus(plevel);
467 
468         if(redraw)
469         {
470             display_level(plevel);
471 
472             if(plevel->title != NULL)
473             {
474                 y = display_size_y - 1;
475                 x = (display_size_x - utf8strlen(plevel->title) - (plevel->flags & LEVELFLAG_TESTING ? utf8strlen(gettext("testing: ")) : 0) ) / 2;
476                 if(x < 0)
477                     x = 0;
478                 move(y, x);
479 
480                 if(plevel->flags & LEVELFLAG_TESTING)
481                 {
482                     attron(COLOR_PAIR(colourpair_cyan));
483                     printw(gettext("testing: "));
484                     attroff(COLOR_PAIR(colourpair_cyan));
485                 }
486                 if((strncmp(gettext(plevel->title), "chroma", 6) == 0))
487                 {
488                     strcpy(buffer, gettext(plevel->title));
489 
490                     for(x = 0; x < strlen(buffer); x ++)
491                     {
492                         cp = colourpair_white;
493                         if(x < strlen(font_logo_colours))
494                             c = font_logo_colours[x] - '0';
495                         else
496                         {
497                             printw("%s", buffer + x);
498                             x = strlen(buffer);
499                             break;
500                         }
501                         switch(c)
502                         {
503                             case 1:
504                                 cp = colourpair_red;
505                                 break;
506                             case 2:
507                                 cp = colourpair_green;
508                                 break;
509                             case 3:
510                                 cp = colourpair_yellow;
511                                 break;
512                             case 4:
513                                 cp = colourpair_blue;
514                                 break;
515                             case 5:
516                                 cp = colourpair_magenta;
517                                 break;
518                             case 6:
519                                 cp = colourpair_cyan;
520                                 break;
521                             default:
522                                 cp = colourpair_white;
523                                 break;
524                         }
525                         addch((*(buffer + x)) | COLOR_PAIR(cp));
526                     }
527                 }
528                 else
529                     printw("%s", plevel->title);
530             }
531 
532             display_moves(plevel, plevelreplay);
533             display_stars(plevel);
534 
535             refresh();
536             curs_set(0);
537             redraw = 0;
538         }
539 
540         /* If there are movers, plot and then evolve them */
541         if(plevel->mover_first != NULL && !(plevel->flags & LEVELFLAG_PAUSED))
542         {
543             /* Plot movers in two passes - first spaces, then non-spaces.
544                This is counter-intuitive, but makes undoing the player work. */
545             for(pass = 0; pass < 2; pass ++)
546             {
547                 pmover = plevel->mover_first;
548                 while(pmover != NULL)
549                 {
550                     if((pass == 0 && pmover->piece != PIECE_SPACE) ||
551                        (pass == 1 && pmover->piece == PIECE_SPACE))
552                     {
553                         pmover = pmover->next;
554                         continue;
555                     }
556                     x = pmover->x;
557                     y = pmover->y;;
558                     if(x >= display_start_x && x < display_end_x && y>= display_start_y && y < display_end_y)
559                     {
560                         move(display_offset_y - display_start_y + y, display_offset_x - display_start_x + x);
561                         p = pmover->piece;
562     #ifdef XOR_COMPATIBILITY
563                         if(plevel->switched && (p == PIECE_SPACE || p == PIECE_WALL))
564                             p = PIECE_DARKNESS;
565     #endif
566                         if(p != PIECE_GONE)
567                             display_piece(plevel, p);
568                     }
569                     pmover = pmover->next;
570                 }
571             }
572 
573             /* Debug movers */
574             if(options_debug & DEBUG_ORDER)
575             {
576                 /* Display the movers */
577                 pmover = plevel->mover_first;
578                 y = 0;
579                 while(pmover != NULL && y < display_size_y - 1)
580                 {
581                     if(pmover->piece != PIECE_GONE)
582                     {
583                         move(y ++, 0);
584                         display_piece(plevel, pmover->piece);
585                         printw(" %2d,%2d ", pmover->x, pmover->y);
586 
587                     }
588                     pmover = pmover->next;
589                 }
590                 while(y < display_size_y - 1)
591                     mvprintw(y++, 0, "        ");
592 
593                 /* Display the stack if our game engine uses it */
594                 if(0
595     #ifdef XOR_COMPATIBILITY
596                     || (plevel->mode == MODE_XOR && options_xor_mode)
597     #endif
598     #ifdef ENIGMA_COMPATIBILITY
599                     || (plevel->mode == MODE_ENIGMA && options_enigma_mode)
600     #endif
601                   )
602                 {
603                     pmover = plevel->stack_first;
604                     y = 0;
605                     while(pmover != NULL && y < display_size_y - 1)
606                     {
607                         if(pmover->piece != PIECE_GONE)
608                         {
609                             move(y ++, display_size_x - 8);
610                             display_piece(plevel, pmover->piece);
611                             printw(" %2d,%2d ", pmover->x, pmover->y);
612 
613                         }
614                         pmover = pmover->next;
615                     }
616                     while(y < display_size_y - 1)
617                         mvprintw(y++, display_size_x - 8, "        ");
618                 }
619             }
620 
621             refresh();
622 
623             /* Evolve movers */
624             if(!(plevel->flags & LEVELFLAG_UNDO))
625             {
626                 if(level_evolve(plevel))
627                     redraw += display_focus(plevel);
628                 level_storemovers(plevel);
629             }
630             else
631             {
632                 if(level_undo(plevel))
633                     plevel->flags |= LEVELFLAG_UNDO;
634                 else
635                     plevel->flags &= ~LEVELFLAG_UNDO;
636             }
637         }
638 
639         /* Determine which delay to use */
640         delay = options_curses_delay;
641         if(plevelreplay != NULL)
642         {
643            if(plevel->mover_first == NULL && plevelreplay->move_current != NULL)
644               delay = options_curses_replay_delay;
645 
646             if(fast)
647             delay = 0;
648 
649             if(plevelreplay->flags & LEVELFLAG_UNDO)
650             {
651                 if(plevel->move_current == NULL && plevel->mover_first == NULL)
652                 {
653                     if(options_curses_replay_delay != 0)
654                         delay = options_curses_replay_delay;
655                     else
656                         delay = 1;
657                 }
658             }
659             else
660             {
661                 if(plevel->mover_first == NULL && plevelreplay->move_current == NULL)
662                     delay = -1;
663             }
664         }
665         else
666         {
667             if(fast)
668                 delay = 0;
669 
670             if(plevel->mover_first == NULL)
671             {
672                 delay = -1;
673                 fast = 0;
674             }
675         }
676 
677         if(delay > 0)
678             halfdelay(delay);
679 
680         if(delay != 0)
681         {
682             key = getch();
683             if(key < 0 || key >= KEY_MAX)
684                 key = 0;
685             if(key >= 'a' && key <='z')
686                 key -= 32;
687         }
688         else
689             key = 0;
690 
691         if(delay > 0)
692             cbreak();
693 
694         playermove = MOVE_NONE;
695 
696         switch(actions[key])
697         {
698             case ACTION_REDRAW:
699                 redraw = 1;
700                 break;
701 
702             case ACTION_HIDE:
703                 display_hide();
704                 redraw = 1;
705                 break;
706 
707             case ACTION_QUIT:
708                 quit = 1;
709                 break;
710 
711             case ACTION_FAST:
712                 fast = 1 - fast;
713                 break;
714 
715             case ACTION_LEFT:
716                  if(plevelreplay != NULL)
717                  {
718                      plevelreplay->flags |= LEVELFLAG_UNDO;
719                      plevelreplay->flags &= ~LEVELFLAG_PAUSED;
720                  }
721                  else
722                      playermove = MOVE_LEFT;
723                 break;
724 
725             case ACTION_RIGHT:
726                 if(plevelreplay != NULL)
727                 {
728                     plevelreplay->flags &= ~LEVELFLAG_UNDO;
729                     plevelreplay->flags &= ~LEVELFLAG_PAUSED;
730                 }
731                 else
732                     playermove = MOVE_RIGHT;
733                 break;
734 
735             case ACTION_UP:
736                 if(plevelreplay != NULL)
737                     plevelreplay->flags |= LEVELFLAG_PAUSED;
738                 else
739                     playermove = MOVE_UP;
740                 break;
741 
742             case ACTION_DOWN:
743                 if(plevelreplay != NULL)
744                     plevelreplay->flags |= LEVELFLAG_PAUSED;
745                 else
746                     playermove = MOVE_DOWN;
747                 break;
748 
749             case ACTION_PAUSE:
750                 if(plevelreplay != NULL)
751                 {
752                    if(plevelreplay->flags & LEVELFLAG_PAUSED)
753                        plevelreplay->flags &= ~LEVELFLAG_PAUSED;
754                    else
755                        plevelreplay->flags |= LEVELFLAG_PAUSED;
756                 }
757                 else if(plevel->mover_first != NULL)
758                 {
759                    if(plevel->flags & LEVELFLAG_PAUSED)
760                        plevel->flags &= ~LEVELFLAG_PAUSED;
761                    else
762                        plevel->flags |= LEVELFLAG_PAUSED;
763                    plevel->flags |= LEVELFLAG_MOVES;
764                 }
765                 break;
766 
767             case ACTION_SWAP:
768                 if(plevelreplay == NULL)
769                 playermove = MOVE_SWAP;
770                 break;
771 
772             case ACTION_UNDO:
773                 if(plevelreplay == NULL)
774                 {
775                     if(plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_UNDO))
776                     {
777                         if(level_undo(plevel))
778                         plevel->flags |= LEVELFLAG_UNDO;
779                         else
780                         plevel->flags &= ~LEVELFLAG_UNDO;
781                     playermove = MOVE_NONE;
782                     }
783                 }
784                 break;
785 
786             case ACTION_REDO:
787                 playermove = MOVE_REDO;
788                 break;
789 
790             default:
791                 break;
792         }
793 
794         /* Are we replaying the level? */
795         if(plevelreplay != NULL)
796         {
797             /* Is it time for another move? */
798             if(plevel->mover_first == NULL && !(plevelreplay->flags & LEVELFLAG_PAUSED))
799             {
800                 /* Moving backwards through replay */
801                 if(plevelreplay->flags & LEVELFLAG_UNDO)
802                 {
803                     if(level_undo(plevel))
804                     {
805                         plevel->flags |= LEVELFLAG_UNDO;
806                         if(plevelreplay->move_current != NULL)
807                             plevelreplay->move_current = plevelreplay->move_current->previous;
808                         else
809                             plevelreplay->move_current = plevelreplay->move_last;
810                     }
811                     else
812                         plevel->flags &= ~LEVELFLAG_UNDO;
813                 }
814                 /* Moving forwards through replay */
815                 else
816                 {
817                     if(plevelreplay->move_current != NULL)
818                     {
819                         playermove = plevelreplay->move_current->direction;
820                         plevelreplay->move_current = plevelreplay->move_current->next;
821                     }
822                 }
823             }
824         }
825 
826         /* Can't move if we've failed or solved the level */
827         if(plevel->flags & (LEVELFLAG_FAILED | LEVELFLAG_SOLVED))
828             playermove = MOVE_NONE;
829 
830         /* If we can move, make the move */
831         if(playermove != MOVE_NONE && plevel->mover_first == NULL)
832             level_move(plevel, playermove);
833 
834         /* Display things changed by the move */
835         if(plevel->flags & LEVELFLAG_MOVES)
836         {
837             display_moves(plevel, plevelreplay);
838                 plevel->flags ^= LEVELFLAG_MOVES;
839         }
840 
841         if(plevel->flags & LEVELFLAG_STARS)
842         {
843             display_stars(plevel);
844             plevel->flags ^= LEVELFLAG_STARS;
845         }
846 
847         if(plevel->flags & LEVELFLAG_SWITCH)
848         {
849             redraw = 1;
850             plevel->flags ^= LEVELFLAG_SWITCH;
851         }
852 
853     #ifdef XOR_COMPATIBILITY
854         if(plevel->flags & LEVELFLAG_MAP)
855         {
856             /* No sensible way to handle this in curses */
857             plevel->flags ^= LEVELFLAG_MAP;
858         }
859     #endif
860 
861         if(!(plevel->flags & LEVELFLAG_SOLVED) && plevel->flags & LEVELFLAG_EXIT)
862         {
863             plevel->flags |= LEVELFLAG_SOLVED;
864             display_stars(plevel);
865         }
866 
867         if(!(plevel->flags & LEVELFLAG_FAILED) && plevel->alive[0] == 0 && plevel->alive[1] ==0)
868         {
869             plevel->flags |= LEVELFLAG_FAILED;
870             display_moves(plevel, plevelreplay);
871         }
872     }
873 }
874 
display_edit(struct level * plevel)875 void display_edit(struct level* plevel)
876 {
877     int key;
878     int quit;
879     static int editor_piece = PIECE_SPACE;
880     int redraw, moved, pmoved;
881     int i;
882     int player;
883     int piece_count;
884 
885     redraw = 1;
886     moved = 1;
887     pmoved = 1;
888 
889     /* Store player */
890     player = plevel->player;
891     plevel->player = 2;
892 
893     piece_count = 0;
894     while(editor_piece_maps[plevel->mode][piece_count] != PIECE_GONE)
895         piece_count ++;
896 
897     if(editor_piece > piece_count)
898         editor_piece = 0;
899 
900     quit = 0;
901     while(!quit)
902     {
903         redraw += display_focus(plevel);
904 
905         if(redraw)
906         {
907             redraw = 0;
908 
909             display_level(plevel);
910 
911             for(i = 0; i < piece_count; i ++)
912             {
913                 move(display_size_y - 1, 1 + i * 2);
914                 display_piece(plevel, editor_piece_maps[plevel->mode][i]);
915             }
916 
917             move(display_size_y - 1, display_size_x - 4);
918             printw("[ ]");
919 
920             curs_set(1);
921 
922             pmoved = 1;
923         }
924 
925         if(pmoved)
926         {
927             pmoved = 0;
928 
929             move(display_size_y - 1, editor_piece * 2);
930             printw(">");
931             move(display_size_y - 1, 2 + editor_piece * 2);
932             printw("<");
933             move(display_size_y - 1, display_size_x - 3);
934             display_piece(plevel, editor_piece_maps[plevel->mode][editor_piece]);
935             moved = 1;
936         }
937 
938         if(moved)
939         {
940             moved = 0;
941 
942             move(display_offset_y - display_start_y + plevel->player_y[2], display_offset_x - display_start_x + plevel->player_x[2]);
943             refresh();
944         }
945 
946         key = getch();
947         if(key < 0 || key >= KEY_MAX)
948             key = 0;
949         if(key >= 'a' && key <='z')
950             key -= 32;
951 
952         switch(actions[key])
953         {
954             case ACTION_REDRAW:
955                 redraw = 1;
956                 break;
957 
958             case ACTION_HIDE:
959                 display_hide();
960                 redraw = 1;
961                 break;
962 
963             case ACTION_QUIT:
964                 quit = 1;
965                 break;
966 
967             case ACTION_LEFT:
968                 if(plevel->player_x[2] > 0)
969                 {
970                     plevel->player_x[2] --; moved = 1;
971                 }
972                 break;
973 
974             case ACTION_RIGHT:
975                 if(plevel->player_x[2] < plevel->size_x - 1)
976                 {
977                     plevel->player_x[2] ++; moved = 1;
978                 }
979                 break;
980 
981             case ACTION_UP:
982                 if(plevel->player_y[2] > 0)
983                 {
984                     plevel->player_y[2] --; moved = 1;
985                 }
986                 break;
987 
988             case ACTION_DOWN:
989                 if(plevel->player_y[2] < plevel->size_y -1)
990                 {
991                     plevel->player_y[2] ++; moved = 1;
992                 }
993                 break;
994 
995             case ACTION_SWAP:
996                 level_setpiece(plevel, plevel->player_x[2], plevel->player_y[2], editor_piece_maps[plevel->mode][editor_piece]);
997                 display_piece(plevel, editor_piece_maps[plevel->mode][editor_piece]);
998                 moved = 1;
999                 break;
1000 
1001             case ACTION_PIECE_LEFT:
1002                 move(display_size_y - 1, editor_piece * 2);
1003                 printw(" ");
1004                 move(display_size_y - 1, 2 + editor_piece * 2);
1005                 printw(" ");
1006                 editor_piece --;
1007                 if(editor_piece < 0)
1008                     editor_piece = piece_count - 1;
1009                 pmoved = 1;
1010                 break;
1011 
1012             case ACTION_PIECE_RIGHT:
1013                 move(display_size_y - 1, editor_piece * 2);
1014                 printw(" ");
1015                 move(display_size_y - 1, 2 + editor_piece * 2);
1016                 printw(" ");
1017                 editor_piece ++;
1018                 if(editor_piece >= piece_count)
1019                     editor_piece = 0;
1020                 pmoved = 1;
1021                 break;
1022         }
1023     }
1024 
1025     /* Restore real player */
1026     plevel->player = player;
1027 }
1028 
display_type()1029 int display_type()
1030 {
1031     return DISPLAY_CURSES;
1032 }
1033 
display_options()1034 void display_options()
1035 {
1036     struct menu* pmenu;
1037     struct menu* pcoloursmenu;
1038     struct menuentry* pentrycolours;
1039     struct menuentry* pentryspeed;
1040     struct menuentry* pentryreplayspeed;
1041     char buffer[256];
1042     int ok;
1043     int result;
1044 
1045     pmenu = menu_new(gettext("Display Options"));
1046 
1047     menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
1048     menuentry_new(pmenu, "", 0, MENU_SPACE);
1049 
1050     menuentry_new(pmenu, gettext("Save Options"), 'S', 0);
1051     menuentry_new(pmenu, "", 0, MENU_SPACE);
1052 
1053     pentrycolours = menuentry_new(pmenu, gettext("Colour Scheme"), 'C', 0);
1054     menuentry_new(pmenu, "", 0, MENU_SPACE);
1055 
1056     pentryspeed = menuentry_new(pmenu, gettext("Move Speed"), 'M', MENU_SCROLLABLE);
1057     pentryreplayspeed = menuentry_new(pmenu, gettext("Replay Speed"), 'R', MENU_SCROLLABLE);
1058     menuentry_new(pmenu, "", 0, MENU_SPACE);
1059 
1060     menuentry_new(pmenu, gettext("Change Keys"), 'K', 0);
1061 
1062     /* XOR and Enigma options are only visible once an appropriate level has
1063      * been seen so as not to confuse those simply playing Chroma levels */
1064     if(0
1065 #ifdef XOR_COMPATIBILITY
1066         || options_xor_options
1067 #endif
1068 #ifdef ENIGMA_COMPATIBILITY
1069         || options_enigma_options
1070 #endif
1071       )
1072     {
1073         menuentry_new(pmenu, "", 0, MENU_SPACE);
1074         menuentry_new(pmenu, gettext("Other Games Options"), 'X', 0);
1075     }
1076 
1077     if(options_debug & DEBUG_MENU)
1078     {
1079         menuentry_new(pmenu, "", 0, MENU_SPACE);
1080         menuentry_new(pmenu, gettext("Debug Options"), 'D', 0);
1081     }
1082 
1083     ok = 0;
1084     while(!ok)
1085     {
1086         if(pdisplaycolours == NULL)
1087             menuentry_extratext(pentrycolours, gettext("** NONE **"), NULL, NULL);
1088         else if(pdisplaycolours->title == NULL)
1089             menuentry_extratext(pentrycolours, gettext("[untitled colours]"), NULL, NULL);
1090         else if(pdisplaycolours->flags & COLOURS_TRANSLATE)
1091             menuentry_extratext(pentrycolours, gettext(pdisplaycolours->title), NULL, NULL);
1092         else
1093             menuentry_extratext(pentrycolours, pdisplaycolours->title, NULL, NULL);
1094 
1095         switch(options_curses_delay)
1096         {
1097             case -1:
1098                 sprintf(buffer, gettext("after a key is pressed"));
1099                 break;
1100             case 0:
1101                 sprintf(buffer, gettext("instantaneous"));
1102                 break;
1103             default:
1104                 sprintf(buffer, gettext("%d00 milliseconds"), options_curses_delay);
1105                 break;
1106         }
1107         menuentry_extratext(pentryspeed, buffer, NULL, NULL);
1108 
1109         switch(options_curses_replay_delay)
1110         {
1111             case -1:
1112                 sprintf(buffer, gettext("after a key is pressed"));
1113                 break;
1114             case 0:
1115                 sprintf(buffer, gettext("instantaneous"));
1116                 break;
1117             default:
1118                 sprintf(buffer, gettext("%d00 milliseconds"), options_curses_replay_delay);
1119                 break;
1120         }
1121         menuentry_extratext(pentryreplayspeed, buffer, NULL, NULL);
1122 
1123         result = menu_process(pmenu);
1124         if(result == MENU_QUIT)
1125             ok = 1;
1126 
1127         if(result == MENU_SELECT && pmenu->entry_selected != NULL)
1128         {
1129             switch(pmenu->entry_selected->key)
1130             {
1131                 case 'Q':
1132                     ok = 1;
1133                     break;
1134 
1135                 case 'C':
1136                     pcoloursmenu = colours_menu();
1137                     if(menu_process(pcoloursmenu) == MENU_SELECT)
1138                     {
1139                         if(pcoloursmenu->entry_selected != NULL && pcoloursmenu->entry_selected->value != NULL)
1140                         {
1141                             strcpy(options_colours, pcoloursmenu->entry_selected->value);
1142                             colours_init();
1143                             display_initcolours();
1144                         }
1145                     }
1146                     menu_delete(pcoloursmenu);
1147                     break;
1148 
1149                 case 'S':
1150                     display_options_save();
1151                     ok = 1;
1152                     break;
1153 
1154                 case 'K':
1155                     display_keys();
1156                     break;
1157 
1158                 case 'X':
1159                     display_options_othergames();
1160                     break;
1161 
1162                 case 'D':
1163                     display_debug();
1164                     break;
1165             }
1166         }
1167 
1168         if(result == MENU_SCROLLLEFT && pmenu->entry_selected != NULL)
1169         {
1170             switch(pmenu->entry_selected->key)
1171             {
1172                 case 'M':
1173                     options_curses_delay --;
1174                     if(options_curses_delay < -1)
1175                         options_curses_delay = 10;
1176 #ifdef PDCURSES
1177                     if(options_curses_delay > 0)
1178                         options_curses_delay = 0;
1179 #endif
1180                     break;
1181                 case 'R':
1182                     options_curses_replay_delay --;
1183                     if(options_curses_replay_delay < -1)
1184                         options_curses_replay_delay = 10;
1185 
1186 #ifdef PDCURSES
1187                     if(options_curses_replay_delay > 0)
1188                         options_curses_replay_delay = 0;
1189 #endif
1190                     break;
1191             }
1192         }
1193 
1194         if(result == MENU_SCROLLRIGHT && pmenu->entry_selected != NULL)
1195         {
1196             switch(pmenu->entry_selected->key)
1197             {
1198                 case 'M':
1199                     options_curses_delay ++;
1200                     if(options_curses_delay > 10)
1201                         options_curses_delay = -1;
1202 #ifdef PDCURSES
1203                     if(options_curses_delay > 0)
1204                         options_curses_delay = 0;
1205 #endif
1206                     break;
1207                 case 'R':
1208                     options_curses_replay_delay ++;
1209                     if(options_curses_replay_delay > 10)
1210                         options_curses_replay_delay = -1;
1211 #ifdef PDCURSES
1212                     if(options_curses_replay_delay > 0)
1213                         options_curses_replay_delay = 0;
1214 #endif
1215                     break;
1216             }
1217         }
1218     }
1219 
1220     menu_delete(pmenu);
1221 }
1222 
display_keyfixed(int i)1223 int display_keyfixed(int i)
1224 {
1225     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)
1226         return 1;
1227 
1228     return 0;
1229 }
1230 
display_keyname(int i)1231 char *display_keyname(int i)
1232 {
1233     static char buffer[4];
1234 
1235     if(i == '\t')
1236         return "TAB";
1237     if(i == '\n')
1238         return "ENTER";
1239     if(i == 27)
1240         return "ESCAPE";
1241     if(i == 32)
1242         return "SPACE";
1243     if(i == KEY_DC)
1244         return "DELETE";
1245     if(i == KEY_IC)
1246         return "INSERT";
1247 
1248     if(keyname(i) == NULL)
1249         return "UNKNOWN";
1250 
1251     if(strcmp(keyname(i), "NO KEY NAME") == 0)
1252     {
1253         if(i >= 0 && i < 32)
1254         {
1255             sprintf(buffer, "^%c", i + '@');
1256             return buffer;
1257         }
1258         if(i > 32 && i < 127)
1259         {
1260             sprintf(buffer, "%c", i);
1261             return buffer;
1262         }
1263 
1264         return "UNKNOWN";
1265     }
1266 
1267     if(strncmp(keyname(i), "KEY_", 4) == 0)
1268         return (char *)(keyname(i) + 4);
1269 
1270     return (char *)keyname(i);
1271 }
1272 
display_addkeytomenu(struct menu * pmenu,int action,char * text)1273 void display_addkeytomenu(struct menu* pmenu, int action, char *text)
1274 {
1275     struct menuentry *pentry;
1276     char buffer[256];
1277     int i;
1278 
1279     sprintf(buffer, "%d", action);
1280     pentry = menuentry_newwithvalue(pmenu, text, 0, MENU_DOUBLE, buffer);
1281 
1282     strcpy(buffer, "");
1283     for(i = 0; i < KEY_MAX; i ++)
1284     {
1285         if(actions[i] == action && i != KEY_RESIZE)
1286         {
1287             if(strlen(buffer) != 0)
1288                 strcat(buffer,", ");
1289             strcat(buffer, "[");
1290             strcat(buffer, display_keyname(i));
1291             strcat(buffer, "]");
1292         }
1293     }
1294 
1295     if(strcmp(buffer, "") == 0)
1296         strcpy(buffer, "(none)");
1297 
1298     menuentry_extratext(pentry, NULL, NULL, buffer);
1299 
1300 }
1301 
display_keys()1302 void display_keys()
1303 {
1304     struct menu *pmenu;
1305     struct menu *psubmenu;
1306     struct menuentry *pentry;
1307     int action;
1308     int result;
1309     int redraw;
1310     int ok;
1311     int subok;
1312     char buffer[256];
1313     int i;
1314     int key;
1315 
1316     ok = 0;
1317     while(!ok)
1318     {
1319         pmenu = menu_new(gettext("Keys"));
1320 
1321         menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
1322         menuentry_new(pmenu, "", 0, MENU_SPACE);
1323 
1324         display_addkeytomenu(pmenu, ACTION_LEFT, gettext(action_name[ACTION_LEFT]));
1325         display_addkeytomenu(pmenu, ACTION_RIGHT, gettext(action_name[ACTION_RIGHT]));
1326         display_addkeytomenu(pmenu, ACTION_UP, gettext(action_name[ACTION_UP]));
1327         display_addkeytomenu(pmenu, ACTION_DOWN, gettext(action_name[ACTION_DOWN]));
1328         display_addkeytomenu(pmenu, ACTION_SWAP, gettext(action_name[ACTION_SWAP]));
1329         display_addkeytomenu(pmenu, ACTION_UNDO, gettext(action_name[ACTION_UNDO]));
1330         display_addkeytomenu(pmenu, ACTION_REDO, gettext(action_name[ACTION_REDO]));
1331         display_addkeytomenu(pmenu, ACTION_FAST, gettext(action_name[ACTION_FAST]));
1332         display_addkeytomenu(pmenu, ACTION_PAUSE, gettext(action_name[ACTION_PAUSE]));
1333         display_addkeytomenu(pmenu, ACTION_QUIT, gettext(action_name[ACTION_QUIT]));
1334         display_addkeytomenu(pmenu, ACTION_REDRAW, gettext(action_name[ACTION_REDRAW]));
1335         display_addkeytomenu(pmenu, ACTION_HIDE, gettext(action_name[ACTION_HIDE]));
1336         display_addkeytomenu(pmenu, ACTION_PIECE_LEFT, gettext(action_name[ACTION_PIECE_LEFT]));
1337         display_addkeytomenu(pmenu, ACTION_PIECE_RIGHT, gettext(action_name[ACTION_PIECE_RIGHT]));
1338 
1339         menu_assignletters(pmenu);
1340 
1341         result = menu_process(pmenu);
1342 
1343         if(result == MENU_QUIT)
1344             ok = 1;
1345 
1346         if(result == MENU_SELECT)
1347         {
1348             if(pmenu->entry_selected->key == 'Q')
1349                 ok = 1;
1350             else if(pmenu->entry_selected->value != NULL)
1351             {
1352                 redraw = MENUREDRAW_ALL;
1353                 subok = 0;
1354                 while(!subok)
1355                 {
1356                     action = atoi(pmenu->entry_selected->value);
1357 
1358                     sprintf(buffer, gettext("Set keys for '%s'"), gettext(action_name[action]));
1359                     psubmenu = menu_new(buffer);
1360 
1361                     menuentry_new(psubmenu, gettext("Quit and return to previous menu"), 'Q', 0);
1362                     menuentry_new(psubmenu, "", 0, MENU_SPACE);
1363 
1364                     for(i = 0; i < KEY_MAX; i ++)
1365                     {
1366                         if(actions[i] == action && i != KEY_RESIZE)
1367                         {
1368                             sprintf(buffer, "[%s]", display_keyname(i));
1369                             pentry = menuentry_new(psubmenu, buffer, 0, MENU_GREY);
1370                             if(display_keyfixed(i))
1371                                 menuentry_extratext(pentry, gettext("(fixed)"), NULL, NULL);
1372                         }
1373                     }
1374                     menuentry_new(psubmenu, "", 0, MENU_SPACE);
1375 
1376                     menuentry_new(psubmenu, gettext("Press a key to add or remove it from this list."), 0, MENU_NOTE | MENU_CENTRE);
1377 
1378                     menu_display(psubmenu, redraw);
1379                     redraw = MENUREDRAW_ENTRIES;
1380                     menu_delete(psubmenu);
1381 
1382                     key = getch();
1383                     if(key == KEY_RESIZE)
1384                     {
1385                         getmaxyx(stdscr, display_size_y, display_size_x);
1386                         redraw = MENUREDRAW_ALL;
1387                     }
1388 
1389                     if(key >= 'a' && key <='z')
1390                         key -=32;
1391 
1392                     if(key == 27 || key == 'q' || key == 'Q' || key== '\n')
1393                         subok = 1;
1394                     else if(!display_keyfixed(key))
1395                     {
1396                         if(actions[key] == action)
1397                             actions[key] = ACTION_NONE;
1398                         else
1399                             actions[key] = action;
1400                     }
1401                 }
1402             }
1403         }
1404 
1405         menu_delete(pmenu);
1406     }
1407 }
1408 
display_debug()1409 void display_debug()
1410 {
1411     struct menu* pmenu;
1412     struct menuentry* pentrymovers;
1413     struct menuentry* pentryhidden;
1414     int ok;
1415     int result;
1416 
1417     pmenu = menu_new(gettext("Debug Options"));
1418 
1419     menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
1420 
1421     menuentry_new(pmenu, "", 0, MENU_SPACE);
1422 
1423     pentrymovers = menuentry_new(pmenu, gettext("Display order of movers"), 'O', MENU_SCROLLABLE);
1424     pentryhidden = menuentry_new(pmenu, gettext("Show hidden items"), 'H', MENU_SCROLLABLE);
1425 
1426     ok = 0;
1427     while(!ok)
1428     {
1429         menuentry_extratext(pentrymovers, options_debug & DEBUG_ORDER ? gettext("yes") : gettext("no"), NULL, NULL);
1430         menuentry_extratext(pentryhidden, options_debug & DEBUG_HIDDEN ? gettext("yes") : gettext("no"), NULL, NULL);
1431 
1432         result = menu_process(pmenu);
1433         if(result == MENU_QUIT)
1434             ok = 1;
1435 
1436         if((result == MENU_SELECT || result == MENU_SCROLLLEFT || result == MENU_SCROLLRIGHT) && pmenu->entry_selected != NULL)
1437         {
1438             switch(pmenu->entry_selected->key)
1439             {
1440                 case 'Q':
1441                     ok = 1;
1442                     break;
1443 
1444                 case 'O':
1445                     options_debug ^= DEBUG_ORDER;
1446                     break;
1447 
1448                 case 'H':
1449                     options_debug ^= DEBUG_HIDDEN;
1450                     break;
1451             }
1452 
1453             pmenu->redraw = MENUREDRAW_CHANGED;
1454             pmenu->entry_selected->redraw = 1;
1455         }
1456 
1457     }
1458 
1459     menu_delete(pmenu);
1460 }
1461 
display_options_othergames()1462 void display_options_othergames()
1463 {
1464     struct menu* pmenu;
1465 #ifdef XOR_COMPATIBILITY
1466     struct menuentry* pentryxormode;
1467     struct menuentry* pentryxordisplay;
1468 #endif
1469 #ifdef ENIGMA_COMPATIBILITY
1470     struct menuentry* pentryenigmamode;
1471 #endif
1472 
1473     int ok;
1474     int result;
1475 
1476     pmenu = menu_new(gettext("Other Games Options"));
1477 
1478     menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
1479 
1480     menuentry_new(pmenu, "", 0, MENU_SPACE);
1481 
1482 #ifdef XOR_COMPATIBILITY
1483     pentryxormode = menuentry_new(pmenu, gettext("XOR Engine"), 'X', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
1484     pentryxordisplay = menuentry_new(pmenu, gettext("XOR Display"), 'D', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
1485     if(options_xor_options)
1486         menuentry_new(pmenu, "", 0, MENU_SPACE);
1487 #endif
1488 
1489 #ifdef ENIGMA_COMPATIBILITY
1490     pentryenigmamode = menuentry_new(pmenu, gettext("Enigma Engine"), 'E', options_enigma_options ? 0 : MENU_INVISIBLE | MENU_GREY);
1491 #endif
1492 
1493     ok = 0;
1494     while(!ok)
1495     {
1496 #ifdef XOR_COMPATIBILITY
1497         menuentry_extratext(pentryxormode, options_xor_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
1498         menuentry_extratext(pentryxordisplay, options_xor_display ? gettext("partial") : gettext("full"), NULL, NULL);
1499 #endif
1500 #ifdef ENIGMA_COMPATIBILITY
1501         menuentry_extratext(pentryenigmamode, options_enigma_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
1502 #endif
1503 
1504         result = menu_process(pmenu);
1505         if(result == MENU_QUIT)
1506             ok = 1;
1507 
1508         if(result == MENU_SELECT && pmenu->entry_selected != NULL)
1509         {
1510             switch(pmenu->entry_selected->key)
1511             {
1512                 case 'Q':
1513                     ok = 1;
1514                     break;
1515 
1516 #ifdef XOR_COMPATIBILITY
1517                 case 'X':
1518                     options_xor_mode = 1 - options_xor_mode;
1519                     break;
1520 
1521                 case 'D':
1522                     options_xor_display = 1 - options_xor_display;
1523                     break;
1524 #endif
1525 
1526 #ifdef ENIGMA_COMPATIBILITY
1527                 case 'E':
1528                     options_enigma_mode = 1 - options_enigma_mode;
1529                     break;
1530 #endif
1531             }
1532 
1533             pmenu->redraw = MENUREDRAW_CHANGED;
1534             pmenu->entry_selected->redraw = 1;
1535         }
1536 
1537     }
1538 
1539     menu_delete(pmenu);
1540 }
1541 
display_options_save()1542 void display_options_save()
1543 {
1544     FILE *file;
1545     char filename[FILENAME_MAX];
1546     int i;
1547 
1548     getfilename("curses.chroma", filename, 1, 0);
1549 
1550     file = fopen(filename, "w");
1551     if(file == NULL)
1552     {
1553         warning("Unable to save options");
1554         return;
1555     }
1556 
1557     fprintf(file, "<!-- Chroma curses options \n"
1558                   "     This file is automatically generated. -->\n"
1559                   "\n"
1560                   "<chroma type=\"options\">\n");
1561 
1562     fprintf(file, "    <colour scheme=\"%s\" />\n", options_colours);
1563 
1564     if(options_curses_delay == -1)
1565         fprintf(file, "    <move speed=\"key\" />\n");
1566     else
1567         fprintf(file, "    <move speed=\"%d\" />\n", options_curses_delay * 100);
1568 
1569     if(options_curses_replay_delay == -1)
1570         fprintf(file, "    <replay speed=\"key\" />\n");
1571     else
1572         fprintf(file, "    <replay speed=\"%d\" />\n", options_curses_replay_delay * 100);
1573 
1574 #ifdef XOR_COMPATIBILITY
1575     if(options_xor_options)
1576         fprintf(file, "    <xor mode=\"%s\" display=\"%s\" />\n", options_xor_mode ? "exact" : "approximate", options_xor_display ? "partial" : "full");
1577 #endif
1578 #ifdef ENIGMA_COMPATIBILITY
1579     if(options_enigma_options)
1580         fprintf(file, "    <enigma mode=\"%s\" />\n", options_enigma_mode ? "exact" : "approximate");
1581 #endif
1582 
1583     fprintf(file, "    <!-- Set <debug menu=\"yes\" /> to change debug options within Chroma -->\n");
1584     fprintf(file, "    <debug ");
1585     fprintf(file, "menu=\"%s\" ", options_debug & DEBUG_MENU ? "yes" : "no");
1586     fprintf(file, "order=\"%s\" ", options_debug & DEBUG_ORDER ? "yes" : "no");
1587     fprintf(file, "hidden=\"%s\" ", options_debug & DEBUG_HIDDEN ? "yes" : "no");
1588     fprintf(file,"/>\n");
1589 
1590     fprintf(file, "    <keys>\n");
1591 
1592     for(i = 0; i < KEY_MAX; i ++)
1593     {
1594         if(actions[i] != ACTION_NONE && i != KEY_RESIZE)
1595             fprintf(file, "        <key name=\"%s\" action=\"%s\" />\n", display_keyname(i), action_shortname[actions[i]]);
1596     }
1597 
1598     fprintf(file, "    </keys>\n");
1599 
1600     fprintf(file, "</chroma>\n");
1601 
1602     fclose(file);
1603 }
1604 
display_options_load()1605 void display_options_load()
1606 {
1607     struct parser* pparser;
1608     char filename[FILENAME_MAX];
1609     int state;
1610     int i;
1611     int key, action;
1612 
1613     /* Sensible defaults */
1614 #ifdef PDCURSES
1615     /* halfdelay() is broken in PDCurses */
1616     options_curses_delay = 0;
1617     options_curses_replay_delay = 0;
1618 #else
1619     options_curses_delay = 1;
1620     options_curses_replay_delay = 1;
1621 #endif
1622 #ifdef XOR_COMPATIBILITY
1623     options_xor_options = 0;
1624     options_xor_mode = 1;
1625     options_xor_display = 0;
1626 #endif
1627 #ifdef ENIGMA_COMPATIBILITY
1628     options_enigma_options = 0;
1629     options_enigma_mode = 1;
1630 #endif
1631     options_debug = 0;
1632 
1633     getfilename("colours", filename, 0, 1);
1634     snprintf(options_colours, sizeof(options_colours), "%s/%s", filename, COLOURS_DEFAULT);
1635 
1636     getfilename("curses.chroma", filename, 0, 0);
1637 
1638     for(i = 0; i < KEY_MAX; i ++)
1639     {
1640         actions[i] = ACTION_NONE;
1641     }
1642 
1643     /* Fixed keys */
1644     actions[KEY_RESIZE] = ACTION_REDRAW;
1645     actions[KEY_UP] = ACTION_UP;
1646     actions[KEY_DOWN] = ACTION_DOWN;
1647     actions[KEY_LEFT] = ACTION_LEFT;
1648     actions[KEY_RIGHT] = ACTION_RIGHT;
1649     actions['\r'] = ACTION_SWAP;
1650     actions['\n'] = ACTION_SWAP;
1651     actions['Q'] = ACTION_QUIT;
1652     actions[27] = ACTION_QUIT;
1653 
1654     /* Sensible default keys */
1655     if(!isfile(filename))
1656     {
1657         actions[12] = ACTION_REDRAW;
1658         actions[' '] = ACTION_SWAP;
1659         actions['F'] = ACTION_FAST;
1660         actions[KEY_BACKSPACE] = ACTION_UNDO;
1661         actions[KEY_DC] = ACTION_UNDO;
1662         actions['U'] = ACTION_UNDO;
1663         actions[KEY_IC] = ACTION_REDO;
1664         actions['Y'] = ACTION_REDO;
1665         actions['Z'] = ACTION_PIECE_LEFT;
1666         actions['X'] = ACTION_PIECE_RIGHT;
1667         actions[KEY_PPAGE] = ACTION_PIECE_LEFT;;
1668         actions[KEY_NPAGE] = ACTION_PIECE_RIGHT;
1669         actions['P'] = ACTION_PAUSE;
1670         return;
1671     }
1672 
1673     /* Parse XML file */
1674     /*
1675        <chroma type="options">
1676            <colour scheme="filename" />
1677            <move speed="speed" />
1678            <replay speed="speed" />
1679            <xor mode="mode" />
1680            <debug menu="yes/no" movers="yes/no" />
1681            <keys>
1682                <key name="name" action="action" />
1683            </keys>
1684        </chroma>
1685     */
1686 
1687     pparser = parser_new(filename);
1688 
1689     enum {
1690         OPTIONSPARSER_END,       /* End of file */
1691         OPTIONSPARSER_OUTSIDE,   /* Outside of <chroma> */
1692         OPTIONSPARSER_CHROMA,    /* Inside <chroma> */
1693         OPTIONSPARSER_KEYS       /* Inside <keys> */
1694     };
1695 
1696     state = OPTIONSPARSER_OUTSIDE;
1697     key = 0;
1698     action = 0;
1699 
1700     while(state != OPTIONSPARSER_END)
1701     {
1702         switch(parser_parse(pparser))
1703         {
1704             case PARSER_END:
1705                 state = OPTIONSPARSER_END;
1706                 break;
1707 
1708             case PARSER_ELEMENT_START:
1709                 switch(state)
1710                 {
1711                     case OPTIONSPARSER_CHROMA:
1712                         if(parser_match(pparser, 0, "keys"))
1713                             state = OPTIONSPARSER_KEYS;
1714                         break;
1715 
1716                     case OPTIONSPARSER_KEYS:
1717                         if(parser_match(pparser, 0, "key"))
1718                         {
1719                             key = 0;
1720                             action = ACTION_NONE;
1721                         }
1722                         break;
1723 
1724                     default:
1725                         break;
1726                 }
1727                 break;
1728 
1729             case PARSER_ELEMENT_END:
1730                 switch(state)
1731                 {
1732                     case OPTIONSPARSER_KEYS:
1733                         if(parser_match(pparser, 0, "keys"))
1734                         {
1735                             state = OPTIONSPARSER_CHROMA;
1736                         }
1737                         if(parser_match(pparser, 0, "key"))
1738                         {
1739                             if(key != 0 && !display_keyfixed(key))
1740                                 actions[key] = action;
1741                         }
1742                         break;
1743 
1744                     default:
1745                         break;
1746                 }
1747                 break;
1748 
1749             case PARSER_CONTENT:
1750                 break;
1751 
1752             case PARSER_ATTRIBUTE:
1753                 switch(state)
1754                 {
1755                     case OPTIONSPARSER_OUTSIDE:
1756                         if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "type"))
1757                         {
1758                             if(parser_match(pparser, 0, "options"))
1759                                 state = OPTIONSPARSER_CHROMA;
1760                         }
1761                         break;
1762 
1763                     case OPTIONSPARSER_CHROMA:
1764                         if(parser_match(pparser, 2, "colour") && parser_match(pparser, 1, "scheme"))
1765                         {
1766                             strncpy(options_colours, parser_text(pparser, 0), FILENAME_MAX);
1767                         }
1768                         if(parser_match(pparser, 2, "move") && parser_match(pparser, 1, "speed"))
1769                         {
1770                             if(parser_match(pparser, 0, "key"))
1771                                 options_curses_delay = -1;
1772                             else
1773                                 options_curses_delay = atoi(parser_text(pparser, 0)) / 100;
1774 #ifdef PDCURSES
1775                 if(options_curses_delay > 0)
1776                     options_curses_delay = 0;
1777 #endif
1778                         }
1779                         if(parser_match(pparser, 2, "replay") && parser_match(pparser, 1, "speed"))
1780                         {
1781                             if(parser_match(pparser, 0, "key"))
1782                                 options_curses_replay_delay = -1;
1783                             else
1784                                 options_curses_replay_delay = atoi(parser_text(pparser, 0)) / 100;
1785 #ifdef PDCURSES
1786                 if(options_curses_replay_delay > 0)
1787                     options_curses_replay_delay = 0;
1788 #endif
1789                         }
1790 #ifdef XOR_COMPATIBILITY
1791                         if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "mode"))
1792                         {
1793                             options_xor_options = 1;
1794 
1795                             if(parser_match(pparser, 0, "approximate"))
1796                                 options_xor_mode = 0;
1797                             if(parser_match(pparser, 0, "exact"))
1798                                 options_xor_mode = 1;
1799                         }
1800                         if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "display"))
1801                         {
1802                             options_xor_options = 1;
1803 
1804                             if(parser_match(pparser, 0, "full"))
1805                                 options_xor_display = 0;
1806                             if(parser_match(pparser, 0, "partial"))
1807                                 options_xor_display = 1;
1808                         }
1809 #endif
1810 #ifdef ENIGMA_COMPATIBILITY
1811                         if(parser_match(pparser, 2, "enigma") && parser_match(pparser, 1, "mode"))
1812                         {
1813                             options_enigma_options = 1;
1814 
1815                             if(parser_match(pparser, 0, "approximate"))
1816                                 options_enigma_mode = 0;
1817                             if(parser_match(pparser, 0, "exact"))
1818                                 options_enigma_mode = 1;
1819                         }
1820 #endif
1821                         if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "menu"))
1822                         {
1823                             if(parser_match(pparser, 0, "yes"))
1824                                 options_debug |= DEBUG_MENU;
1825                         }
1826                         if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "order"))
1827                         {
1828                             if(parser_match(pparser, 0, "yes"))
1829                                 options_debug |= DEBUG_ORDER;
1830                         }
1831                         if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "hidden"))
1832                         {
1833                             if(parser_match(pparser, 0, "yes"))
1834                                 options_debug |= DEBUG_HIDDEN;
1835                         }
1836                         break;
1837 
1838                     case OPTIONSPARSER_KEYS:
1839                         if(parser_match(pparser, 2, "key") &&  parser_match(pparser, 1, "name"))
1840                         {
1841                             for(i = 0; i < KEY_MAX; i ++)
1842                             {
1843                                 if(parser_match(pparser, 0, display_keyname(i)))
1844                                 {
1845                                     key = i;
1846                                     i = KEY_MAX;
1847                                 }
1848                             }
1849                         }
1850                         if(parser_match(pparser, 2, "key") &&  parser_match(pparser, 1, "action"))
1851                         {
1852                             for(i = ACTION_KEY_MIN; i < ACTION_KEY_MAX; i ++)
1853                             {
1854                                 if(parser_match(pparser, 0, action_shortname[i]))
1855                                 {
1856                                     action = i;
1857                                     i = ACTION_KEY_MAX;
1858                                 }
1859                             }
1860                         }
1861                         break;
1862                 }
1863                 break;
1864 
1865             case PARSER_ERROR:
1866                 state = OPTIONSPARSER_END;
1867                 break;
1868         }
1869     }
1870 
1871     parser_delete(pparser);
1872 }
1873