chessbox:  Fixes and enhancements

- Unfinished game is now saved along with current position.
- All savings are automatically done on shutdown.
- Implemented facility to view played games.
- Fixed bug that prevented program from the very first move.

Patch by Igor Poretsky

Change-Id: I997b97752e4362ed953309bea985d071f9db229b
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 8aed53f..fd2e451 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -15259,3 +15259,20 @@
     recording: "Recording Directory"
   </voice>
 </phrase>
+<phrase>
+  id: LANG_CHESSBOX_MENU_VIEW_GAMES
+  desc: in the chessbox menu
+  user: core
+  <source>
+    *: none
+    lcd_bitmap: "View Played Games"
+  </source>
+  <dest>
+    *: none
+    lcd_bitmap: "View Played Games"
+  </dest>
+  <voice>
+    *: none
+    lcd_bitmap: "View Played Games"
+  </voice>
+</phrase>
diff --git a/apps/plugins/chessbox/chessbox.c b/apps/plugins/chessbox/chessbox.c
index 52a1f27..397fd0e 100644
--- a/apps/plugins/chessbox/chessbox.c
+++ b/apps/plugins/chessbox/chessbox.c
@@ -74,6 +74,8 @@
 #define COMMAND_SELECT    10
 #define COMMAND_NEXT      11
 #define COMMAND_PREV      12
+#define COMMAND_VIEW      13
+#define COMMAND_RETURN    14
 
 short plugin_mode;
 
@@ -360,7 +362,7 @@
                                                       sizeof(temp));
         rb->write(fd, buf, ch_ct);
     }
-    for (i = 0; i <= GameCnt; i++) {
+    for (i = 0; i < ((GameCnt + 1) & 0xFF); i++) {
         ch_ct = rb->snprintf(buf,31,"GameCt %d, %d bytes\n",i,
                                                       sizeof(GameCnt));
         rb->write(fd, buf, ch_ct);
@@ -399,17 +401,21 @@
 }
 #endif
 
-/* ---- Save current position ---- */
-static void cb_saveposition ( void ) {
+/* ---- Save current position and game history ---- */
+static void cb_saveposition ( struct pgn_game_node* game ) {
     int fd;
     short sq,i,c;
     unsigned short temp;
+    struct pgn_ply_node *ply;
+    char buf[4];
+
 #ifdef CHESSBOX_SAVE_FILE_DBG
     cb_saveposition_dbg();
 #endif
+
     rb->splash ( 0 , ID2P(LANG_CHESSBOX_SAVING_POSITION) );
 
-    fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
+    fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
 
     computer++; rb->write(fd, &(computer), sizeof(computer)); computer--;
     opponent++; rb->write(fd, &(opponent), sizeof(opponent)); opponent--;
@@ -438,7 +444,9 @@
         temp = 256*board[sq] + c ;
         rb->write(fd, &(temp), sizeof(temp));
     }
-    for (i = 0; i <= GameCnt; i++) {
+    c = GameCnt;
+    rb->write(fd, &(c), sizeof(c));
+    for (i = 0; i < ((GameCnt + 1) & 0xFF); i++) {
         if (GameList[i].color == neutral)
             c = 0;
         else
@@ -451,14 +459,24 @@
         rb->write(fd, &(GameList[i].piece), sizeof(GameList[i].piece));
         rb->write(fd, &(c), sizeof(c));
     }
+    for (ply=game->first_ply; ply!=NULL; ply=ply->next_node) {
+        buf[0] = ply->column_from + 'a';
+        buf[1] = ply->row_from + '1';
+        buf[2] = ply->column_to + 'a';
+        buf[3] = ply->row_to + '1';
+        rb->write(fd, buf, 4);
+    }
     rb->close(fd);
 }
 
-/* ---- Restore saved position ---- */
-static void cb_restoreposition ( void ) {
+/* ---- Restore saved position and game history ---- */
+static struct pgn_game_node* cb_restoreposition ( void ) {
     int fd;
     short sq;
     unsigned short m;
+    short n;
+    char buf[4];
+    struct pgn_game_node* game = pgn_init_game();
 
     if ( (fd = rb->open(SAVE_FILE, O_RDONLY)) >= 0 ) {
         rb->splash ( 0 , ID2P(LANG_CHESSBOX_LOADING_POSITION) );
@@ -492,9 +510,12 @@
             else
                 --color[sq];
         }
-        GameCnt = MAX_GAME_CNT - 1; /*uchar rollsover to 0 after 255*/
-        while (rb->read(fd, &(GameList[++GameCnt].gmove),
-                        sizeof(GameList[GameCnt].gmove)) > 0) {
+        rb->read(fd, &(n), sizeof(n));
+        n++;
+        n &= 0xFF;
+        for (GameCnt = 0; GameCnt < n; GameCnt++) {
+            rb->read(fd, &(GameList[GameCnt].gmove),
+                     sizeof(GameList[GameCnt].gmove));
             rb->read(fd, &(GameList[GameCnt].score),
                      sizeof(GameList[GameCnt].score));
             rb->read(fd, &(GameList[GameCnt].depth),
@@ -506,7 +527,7 @@
             rb->read(fd, &(GameList[GameCnt].piece),
                      sizeof(GameList[GameCnt].piece));
             rb->read(fd, &(GameList[GameCnt].color),
-                         sizeof(GameList[GameCnt].color));
+                     sizeof(GameList[GameCnt].color));
             if (GameList[GameCnt].color == 0)
                 GameList[GameCnt].color = neutral;
             else
@@ -516,47 +537,45 @@
         if (TimeControl.clock[white] > 0)
             TCflag = true;
         computer--; opponent--;
+        n = 0;
+        while (rb->read(fd, buf, 4) > 0)
+            pgn_append_ply(game, ((n++) & 1) ? black : white, buf, false);
+        rb->close(fd);
     }
-    rb->close(fd);
     cb_setlevel(Level);
     InitializeStats();
     Sdepth = 0;
+
+    return game;
 }
 
 /* ---- show menu in viewer mode---- */
 static int cb_menu_viewer(void)
 {
     int selection;
-    int result = 0;
-    bool menu_quit = false;
 
     MENUITEM_STRINGLIST(menu,"Chessbox Menu",NULL,
                         ID2P(LANG_CHESSBOX_MENU_RESTART_GAME),
                         ID2P(LANG_CHESSBOX_MENU_SELECT_OTHER_GAME),
+                        ID2P(LANG_CHESSBOX_MENU_RESUME_GAME),
+                        ID2P(LANG_RETURN),
                         ID2P(LANG_MENU_QUIT));
 
-    while(!menu_quit)
+    switch(rb->do_menu(&menu, &selection, NULL, false))
     {
-        switch(rb->do_menu(&menu, &selection, NULL, false))
-        {
-            case 0:
-                menu_quit = true;
-                result = COMMAND_RESTART;
-                break;
-            case 1:
-                result = COMMAND_SELECT;
-                menu_quit = true;
-                break;
-            case 2:
-                result = COMMAND_QUIT;
-                menu_quit = true;
-                break;
-        }
+        case 0:
+            return COMMAND_RESTART;
+        case 1:
+            return COMMAND_SELECT;
+        case 3:
+            return COMMAND_RETURN;
+        case 4:
+            return COMMAND_QUIT;
     }
-    return result;
+    return COMMAND_RESUME;
 }
 
-/* ---- get a command in game mode ---- */
+/* ---- get a command in viewer mode ---- */
 static struct cb_command cb_get_viewer_command (void) {
     int button;
     struct cb_command result = { 0, {0,0,0,0,0}, 0 };
@@ -592,17 +611,18 @@
 }
 
 /* ---- viewer main loop ---- */
-static void cb_start_viewer(char* filename){
+static bool cb_start_viewer(const char* filename){
     struct pgn_game_node *first_game, *selected_game;
     struct pgn_ply_node *curr_ply;
     bool exit_game = false;
     bool exit_viewer = false;
+    bool exit_app = false;
     struct cb_command command;
 
     first_game = pgn_list_games(filename);
     if (first_game == NULL){
         rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_NO_GAMES) );
-        return;
+        return exit_app;
     }
 
     do {
@@ -759,6 +779,8 @@
                         exit_game = true;
                         break;
                     case COMMAND_QUIT:
+                        exit_app = true;
+                    case COMMAND_RETURN:
                         exit_viewer = true;
                         break;
                 }
@@ -767,57 +789,44 @@
             rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_PGN_PARSE_ERROR));
         }
     } while (!exit_viewer);
+    return exit_app;
 }
 
 /* ---- show menu ---- */
 static int cb_menu(void)
 {
     int selection;
-    int result = 0;
-    bool menu_quit = false;
 
     MENUITEM_STRINGLIST(menu,"Chessbox Menu",NULL,
                         ID2P(LANG_CHESSBOX_MENU_NEW_GAME),
                         ID2P(LANG_CHESSBOX_MENU_RESUME_GAME),
                         ID2P(LANG_CHESSBOX_MENU_SAVE_GAME),
                         ID2P(LANG_CHESSBOX_MENU_RESTORE_GAME),
+                        ID2P(LANG_CHESSBOX_MENU_VIEW_GAMES),
 #ifdef HAVE_PLAYBACK_CONTROL
                         ID2P(LANG_PLAYBACK_CONTROL),
 #endif
                         ID2P(LANG_MENU_QUIT));
 
-    while(!menu_quit)
+    switch(rb->do_menu(&menu, &selection, NULL, false))
     {
-        switch(rb->do_menu(&menu, &selection, NULL, false))
-        {
-            case 0:
-                menu_quit = true;
-                result = COMMAND_RESTART;
-                break;
-            case 1:
-                result = COMMAND_RESUME;
-                menu_quit = true;
-                break;
-            case 2:
-                result = COMMAND_SAVE;
-                menu_quit = true;
-                break;
-            case 3:
-                result = COMMAND_RESTORE;
-                menu_quit = true;
-                break;
-            case 4:
+        case 0:
+            return COMMAND_RESTART;
+        case 2:
+            return COMMAND_SAVE;
+        case 3:
+            return COMMAND_RESTORE;
+        case 4:
+            return COMMAND_VIEW;
+        case 5:
 #ifdef HAVE_PLAYBACK_CONTROL
-                playback_control(NULL);
-                break;
-            case 5:
+            playback_control(NULL);
+            break;
+        case 6:
 #endif
-                result = COMMAND_QUIT;
-                menu_quit = true;
-                break;
-        }
+            return COMMAND_QUIT;
     }
-    return result;
+    return COMMAND_RESUME;
 }
 
 /* ---- get a command in game mode ---- */
@@ -1029,12 +1038,8 @@
     /* init board */
     GNUChess_Initialize();
 
-    /* init PGN history data structures */
-    game = pgn_init_game();
-
     /* restore saved position, if saved */
-    cb_restoreposition();
-    /* TODO: save/restore the PGN history of unfinished games */
+    game = cb_restoreposition();
 
     /* draw the board */
     /* I don't like configscreens, start game inmediatly */
@@ -1097,7 +1102,7 @@
                 cb_drawboard();
                 break;
             case COMMAND_SAVE:
-                cb_saveposition();
+                cb_saveposition(game);
                 cb_drawboard();
                 break;
             case COMMAND_RESTORE:
@@ -1106,14 +1111,23 @@
                 /* init board */
                 GNUChess_Initialize();
 
-                /* init PGN history data structures */
-                game = pgn_init_game();
-
                 /* restore saved position, if saved */
-                cb_restoreposition();
+                game = cb_restoreposition();
 
                 cb_drawboard();
                 break;
+            case COMMAND_VIEW:
+                if (rb->file_exists(pgn_file)) {
+                    cb_saveposition(game);
+                    if (cb_start_viewer(pgn_file))
+                        return;
+                    GNUChess_Initialize();
+                    game = cb_restoreposition();
+                }else{
+                    rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_NO_GAMES) );
+                }
+                cb_drawboard();
+                break;
             case COMMAND_PLAY:
                 if (opponent == white) {
                     opponent = black;
@@ -1158,9 +1172,7 @@
         }
     }
 
-    cb_saveposition();
-    /* TODO: save/restore the PGN history of unfinished games */
-    rb->lcd_setfont(FONT_UI);
+    cb_saveposition(game);
 
 }
 
@@ -1187,6 +1199,8 @@
         cb_play_game();
     }
 
+    rb->lcd_setfont(FONT_UI);
+
     if (cb_sysevent)
         rb->default_event_handler(cb_sysevent);
 
diff --git a/apps/plugins/chessbox/chessbox_pgn.c b/apps/plugins/chessbox/chessbox_pgn.c
index 39a24d6..40e88e5 100644
--- a/apps/plugins/chessbox/chessbox_pgn.c
+++ b/apps/plugins/chessbox/chessbox_pgn.c
@@ -22,9 +22,9 @@
 #include "plugin.h"
 #include "chessbox_pgn.h"
 
-#define PGN_FILE  PLUGIN_GAMES_DATA_DIR  "/chessbox.pgn"
 #define LOG_FILE  PLUGIN_GAMES_DATA_DIR  "/chessbox.log"
 int loghandler;
+const char *pgn_file = PLUGIN_GAMES_DATA_DIR  "/chessbox.pgn";
 
 short kn_offs[8][2] = {{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
 short rk_offs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
@@ -889,7 +889,7 @@
         ply_count++;
     }
 
-    fhandler = rb->open(PGN_FILE, O_WRONLY|O_CREAT|O_APPEND, 0666);
+    fhandler = rb->open(pgn_file, O_WRONLY|O_CREAT|O_APPEND, 0666);
 
 
     /* the first 7 tags are mandatory according to the PGN specification so we
diff --git a/apps/plugins/chessbox/chessbox_pgn.h b/apps/plugins/chessbox/chessbox_pgn.h
index 1159d0c..3c4d535 100644
--- a/apps/plugins/chessbox/chessbox_pgn.h
+++ b/apps/plugins/chessbox/chessbox_pgn.h
@@ -649,7 +649,7 @@
 /* structure to represent the plies */
 struct pgn_ply_node {
     unsigned short player;
-    char pgn_text[9];
+    char pgn_text[11];
     unsigned short row_from;
     unsigned short column_from;
     unsigned short row_to;
@@ -677,6 +677,9 @@
     struct pgn_game_node* next_node;
 };
 
+/* File for saving games */
+extern const char* pgn_file;
+
 /* Return the list of games in a PGN file.
  * Parsing of the games themselves is postponed until
  * the user selects a game, that obviously saves processing
diff --git a/apps/plugins/chessbox/gnuchess.c b/apps/plugins/chessbox/gnuchess.c
index ff9bb99..f0600e3 100644
--- a/apps/plugins/chessbox/gnuchess.c
+++ b/apps/plugins/chessbox/gnuchess.c
@@ -1167,12 +1167,12 @@
   m = 0;
   while ( o_c < MAX_OPENING )  {
       m_c = 0 ;
-      for (j = 0; j <= GameCnt; j++) {
+      for (j = 0; j < ((GameCnt + 1) & 0xFF); j++) {
         if ( GameList[j].gmove != OBook[o_c][m_c] ) break;
         m_c++;
       }
       /* I added ( m != OBook[o_c][m_c] ) trying to get more random games */
-      if ( ( j > GameCnt ) && ( m != OBook[o_c][m_c] ) ) {
+      if ( ( j >= ((GameCnt + 1) & 0xFF) ) && ( m != OBook[o_c][m_c] ) ) {
           r=rb->rand();
           if ( r > r0 ) {
                   r0 = r; m = OBook[o_c][m_c];
@@ -2066,7 +2066,7 @@
 short b[64];
 unsigned short m;
   *cnt = c = 0;
-  if (GameCnt > Game50+3)
+  if (((GameCnt + 1) & 0xFF) > Game50+4)
     {
       for (i = 0; i < 64; b[i++] = 0);
       for (i = GameCnt; i > Game50; i--)