1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Mauricio Peccorini
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "chessbox_pgn.h"
25 #define PGN_FILE PLUGIN_GAMES_DIR "/chessbox.pgn"
26 #define LOG_FILE PLUGIN_GAMES_DIR "/chessbox.log"
29 short kn_offs
[8][2] = {{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
30 short rk_offs
[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
31 short bp_offs
[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
33 /* global vars for pl_malloc() */
37 /* simple function to "allocate" memory in pluginbuffer.
38 * (borrowed from dict.c)
40 void *pl_malloc(ssize_t size
)
57 /* init function for pl_malloc() */
58 void pl_malloc_init(void)
60 bufptr
= rb
->plugin_get_buffer((size_t *)&bufleft
);
63 void process_tag(struct pgn_game_node
* game
, char* buffer
){
67 while (buffer
[pos
+1] != ' '){
68 tag_type
[pos
] = buffer
[pos
+1];
73 while (buffer
[pos
] != '"'){
74 tag_value
[pos2
] = buffer
[pos
];
78 /* truncate tag values that are too large */
82 tag_value
[pos2
] = '\0';
84 if (rb
->strcmp(tag_type
,"White") == 0){
85 rb
->strcpy(game
->white_player
, tag_value
);
87 if (rb
->strcmp(tag_type
,"Black") == 0){
88 rb
->strcpy(game
->black_player
, tag_value
);
90 if (rb
->strcmp(tag_type
,"Result") == 0){
91 rb
->strcpy(game
->result
, tag_value
);
93 if (rb
->strcmp(tag_type
,"Date") == 0){
94 rb
->strcpy(game
->game_date
, tag_value
);
98 unsigned short get_next_token(const char* line_buffer
, unsigned short initial_pos
,
100 unsigned short pos
, token_pos
=0;
101 for (pos
= initial_pos
;line_buffer
[pos
] == ' ' || line_buffer
[pos
] == '.';pos
++);
103 token_buffer
[token_pos
] = line_buffer
[pos
];
105 } while (line_buffer
[pos
] != ' ' && line_buffer
[pos
] != '.'
106 && line_buffer
[pos
] != '\0');
107 /* ignore annotations */
108 while (token_buffer
[token_pos
-1] == '!' || token_buffer
[token_pos
-1] == '?'){
111 token_buffer
[token_pos
] = '\0';
115 unsigned short piece_from_pgn(char pgn_piece
){
131 char pgn_from_piece(unsigned short piece
, unsigned short color
){
132 char pgn_piece
= ' ';
156 if (color
== black
&& pgn_piece
!= ' '){
162 void pgn_to_coords(struct pgn_ply_node
* ply
){
163 unsigned short str_length
= rb
->strlen(ply
->pgn_text
);
165 rb
->strcpy(str
,ply
->pgn_text
);
166 ply
->column_from
= 0xFF;
167 ply
->row_from
= 0xFF;
168 ply
->column_to
= 0xFF;
170 ply
->taken_piece
= no_piece
;
171 ply
->promotion_piece
= no_piece
;
172 ply
->enpassant
= false;
174 ply
->promotion
= false;
175 unsigned short i
, j
, piece
;
178 if (str_length
>= 3 && (str
[0] == 'O' || str
[0] == '0') && str
[1] == '-'
179 && (str
[2] == 'O' || str
[2] == '0')) {
182 if (str_length
>= 5 && str
[3] == '-' && (str
[4] == 'O' || str
[4] == '0')){
183 /* castle queenside */
184 if (ply
->player
== white
){
185 ply
->row_from
= 0; ply
->column_from
= 4;
186 ply
->row_to
= 0; ply
->column_to
= 2;
187 /* update the rook's position, the king's position will be updated later */
188 board
[locn
[0][3]] = rook
; color
[locn
[0][3]] = white
;
189 board
[locn
[0][0]] = no_piece
; color
[locn
[0][0]] = neutral
;
191 ply
->row_from
= 7; ply
->column_from
= 4;
192 ply
->row_to
= 7; ply
->column_to
= 2;
193 board
[locn
[7][3]] = rook
; color
[locn
[7][3]] = black
;
194 board
[locn
[7][0]] = no_piece
; color
[locn
[7][0]] = neutral
;
197 /* castle kingside */
198 if (ply
->player
== white
){
199 ply
->row_from
= 0; ply
->column_from
= 4;
200 ply
->row_to
= 0; ply
->column_to
= 6;
201 board
[locn
[0][5]] = rook
; color
[locn
[0][5]] = white
;
202 board
[locn
[0][7]] = no_piece
; color
[locn
[0][7]] = neutral
;
204 ply
->row_from
= 7; ply
->column_from
= 4;
205 ply
->row_to
= 7; ply
->column_to
= 6;
206 board
[locn
[7][5]] = rook
; color
[locn
[7][5]] = black
;
207 board
[locn
[7][7]] = no_piece
; color
[locn
[7][7]] = neutral
;
210 } else if (str
[0] >= 'a' && str
[0] <= 'h'){
212 ply
->column_from
= str
[0] - 'a';
214 ply
->row_from
= str
[3] - '1' + ((ply
->player
==white
)?-1:1);
215 ply
->row_to
= str
[3] - '1';
216 ply
->column_to
= str
[2] - 'a';
217 if (board
[locn
[ply
->row_to
][ply
->column_to
]] == no_piece
){
218 /* en-passant, remove the pawn */
219 ply
->enpassant
= true;
220 board
[locn
[ply
->row_from
][ply
->column_to
]] = no_piece
;
221 color
[locn
[ply
->row_from
][ply
->column_to
]] = neutral
;
222 ply
->taken_piece
= pawn
;
224 ply
->taken_piece
= board
[locn
[ply
->row_to
][ply
->column_to
]];
227 ply
->column_to
= ply
->column_from
;
228 ply
->row_from
= str
[1] - '1' + ((ply
->player
==white
)?-1:1);
229 ply
->row_to
= str
[1] - '1';
231 if (board
[locn
[ply
->row_from
][ply
->column_from
]] == no_piece
){
232 /* the pawn moved two squares */
233 ply
->row_from
+= ((ply
->player
==white
)?-1:1);
235 if (ply
->row_to
== 7 || ply
->row_to
== 0){
238 ply
->promotion_piece
= piece_from_pgn(str
[3]);
240 ply
->promotion_piece
= piece_from_pgn(str
[5]);
242 /* change the piece in the original position and wait
243 * for the code at the end to move it
245 board
[locn
[ply
->row_from
][ply
->column_from
]] = ply
->promotion_piece
;
248 /* the other pieces */
249 piece
= piece_from_pgn(str
[0]);
251 /* taken a piece and move was ambiguous */
252 ply
->column_to
= str
[3] - 'a';
253 ply
->row_to
= str
[4] - '1';
254 ply
->taken_piece
= board
[locn
[ply
->row_to
][ply
->column_to
]];
255 if (str
[1] >= 'a' && str
[1] <= 'h') {
256 ply
->column_from
= str
[1] - 'a';
258 ply
->row_from
= str
[1] - '1';
260 } else if (str
[1] == 'x') {
262 ply
->column_to
= str
[2] - 'a';
263 ply
->row_to
= str
[3] - '1';
264 ply
->taken_piece
= board
[locn
[ply
->row_to
][ply
->column_to
]];
265 } else if (str_length
>= 4 && str
[3] >= '0' && str
[3] <= '9'){
266 /* no piece taken and move was ambiguous */
267 ply
->column_to
= str
[2] - 'a';
268 ply
->row_to
= str
[3] - '1';
269 if (str
[1] >= 'a' && str
[1] <= 'h') {
270 ply
->column_from
= str
[1] - 'a';
272 ply
->row_from
= str
[1] - '1';
276 ply
->column_to
= str
[1] - 'a';
277 ply
->row_to
= str
[2] - '1';
279 if (piece
== knight
) {
281 if (ply
->row_to
+ kn_offs
[i
][0] >= 0 && ply
->row_to
+ kn_offs
[i
][0] <= 7
282 && ply
->column_to
+ kn_offs
[i
][1] >= 0 && ply
->column_to
+ kn_offs
[i
][1] <= 7
283 && board
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == knight
284 && color
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == ply
->player
285 && (ply
->row_from
== 0xFF || ply
->row_from
== ply
->row_to
+ kn_offs
[i
][0])
286 && (ply
->column_from
== 0xFF || ply
->column_from
== ply
->column_to
+ kn_offs
[i
][1])) {
287 ply
->row_from
= ply
->row_to
+ kn_offs
[i
][0];
288 ply
->column_from
= ply
->column_to
+ kn_offs
[i
][1];
292 if (piece
== rook
|| piece
== queen
|| piece
== king
){
296 while (ply
->row_to
+(j
*rk_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*rk_offs
[i
][0]) <= 7 &&
297 ply
->column_to
+(j
*rk_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*rk_offs
[i
][1]) <= 7){
298 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] != no_piece
) {
299 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == piece
&&
300 color
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == ply
->player
&&
301 (ply
->row_from
== 0xFF || ply
->row_from
== ply
->row_to
+(j
*rk_offs
[i
][0])) &&
302 (ply
->column_from
== 0xFF || ply
->column_from
== ply
->column_to
+(j
*rk_offs
[i
][1]))) {
303 ply
->row_from
= ply
->row_to
+(j
*rk_offs
[i
][0]);
304 ply
->column_from
= ply
->column_to
+(j
*rk_offs
[i
][1]);
316 if (piece
== bishop
|| ((piece
== queen
|| piece
== king
) && !found
)){
320 while (ply
->row_to
+(j
*bp_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*bp_offs
[i
][0]) <= 7 &&
321 ply
->column_to
+(j
*bp_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*bp_offs
[i
][1]) <= 7){
322 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] != no_piece
) {
323 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == piece
&&
324 color
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == ply
->player
&&
325 (ply
->row_from
== 0xFF || ply
->row_from
== ply
->row_to
+(j
*bp_offs
[i
][0])) &&
326 (ply
->column_from
== 0xFF || ply
->column_from
== ply
->column_to
+(j
*bp_offs
[i
][1]))) {
327 ply
->row_from
= ply
->row_to
+(j
*bp_offs
[i
][0]);
328 ply
->column_from
= ply
->column_to
+(j
*bp_offs
[i
][1]);
342 /* leave a very complete log of the parsing of the game while it gets stable */
345 rb
->fdprintf(loghandler
,"%c",pgn_from_piece(board
[locn
[7-i
][j
]],color
[locn
[7-i
][j
]]));
347 rb
->fdprintf(loghandler
,"\n");
350 /* update the board */
351 board
[locn
[ply
->row_to
][ply
->column_to
]] = board
[locn
[ply
->row_from
][ply
->column_from
]];
352 color
[locn
[ply
->row_to
][ply
->column_to
]] = color
[locn
[ply
->row_from
][ply
->column_from
]];
353 board
[locn
[ply
->row_from
][ply
->column_from
]] = no_piece
;
354 color
[locn
[ply
->row_from
][ply
->column_from
]] = neutral
;
357 void coords_to_pgn(struct pgn_ply_node
* ply
){
359 unsigned short moving_piece
= board
[locn
[ply
->row_from
][ply
->column_from
]];
360 char unambiguous_position
;
363 char move_buffer
[10];
365 if (moving_piece
== king
){
367 if (ply
->column_from
== 4 && ply
->column_to
== 6){
368 /* castling kingside */
369 rb
->strcpy(ply
->pgn_text
,"O-O");
371 } else if (ply
->column_from
== 4 && ply
->column_to
== 2){
372 /* castling queenside */
373 rb
->strcpy(ply
->pgn_text
,"O-O-O");
375 if (board
[locn
[ply
->row_to
][ply
->column_to
]] != no_piece
){
376 rb
->snprintf(ply
->pgn_text
,10,"Kx%c%c",'a'+ply
->column_to
,
379 rb
->snprintf(ply
->pgn_text
,10,"K%c%c",'a'+ply
->column_to
,
383 } else if (moving_piece
== pawn
){
384 if (ply
->column_from
!= ply
->column_to
){
385 /* check enpassant */
386 if (board
[locn
[ply
->row_to
][ply
->column_to
]] == no_piece
){
387 ply
->enpassant
= true;
389 /* check promotions when taking a piece */
390 if (ply
->row_to
== 0 || ply
->row_to
== 7) {
391 ply
->promotion
= true;
392 ply
->promotion_piece
= queen
;
393 rb
->snprintf(ply
->pgn_text
,10,"%cx%c%c=D", 'a'+ply
->column_from
,
394 'a'+ply
->column_to
,'1'+ply
->row_to
);
396 rb
->snprintf(ply
->pgn_text
,10,"%cx%c%c", 'a'+ply
->column_from
,
397 'a'+ply
->column_to
,'1'+ply
->row_to
);
400 /* check promotions when not taking a piece */
401 if (ply
->row_to
== 0 || ply
->row_to
== 7) {
402 ply
->promotion
= true;
403 ply
->promotion_piece
= queen
;
404 rb
->snprintf(ply
->pgn_text
,10,"%c%c=D", 'a'+ply
->column_to
,
407 rb
->snprintf(ply
->pgn_text
,10,"%c%c", 'a'+ply
->column_to
,
412 /* verify ambiguous moves for the different kinds of pieces */
413 unambiguous_position
= '\0';
414 if (moving_piece
== knight
){
416 if (ply
->row_to
+ kn_offs
[i
][0] >= 0 && ply
->row_to
+ kn_offs
[i
][0] <= 7
417 && ply
->column_to
+ kn_offs
[i
][1] >= 0 && ply
->column_to
+ kn_offs
[i
][1] <= 7
418 && board
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == knight
419 && color
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == ply
->player
420 && (ply
->row_to
+ kn_offs
[i
][0] != ply
->row_from
421 || ply
->column_to
+ kn_offs
[i
][1] != ply
->column_from
)){
422 if (ply
->row_to
+ kn_offs
[i
][0] != ply
->row_from
){
423 unambiguous_position
= '1' + ply
->row_from
;
425 unambiguous_position
= 'a' + ply
->column_from
;
431 if (moving_piece
== rook
|| moving_piece
== queen
){
435 while (ply
->row_to
+(j
*rk_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*rk_offs
[i
][0]) <= 7 &&
436 ply
->column_to
+(j
*rk_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*rk_offs
[i
][1]) <= 7){
437 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] != no_piece
) {
438 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == moving_piece
&&
439 color
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == ply
->player
&&
440 (ply
->row_to
+(j
*rk_offs
[i
][0]) != ply
->row_from
441 || ply
->column_to
+(j
*rk_offs
[i
][1]) != ply
->column_from
)) {
442 if (ply
->row_to
+(j
*rk_offs
[i
][0]) != ply
->row_from
){
443 unambiguous_position
= '1' + ply
->row_from
;
445 unambiguous_position
= 'a' + ply
->column_from
;
458 if (moving_piece
== bishop
|| (moving_piece
== queen
&& !found
)){
461 while (ply
->row_to
+(j
*bp_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*bp_offs
[i
][0]) <= 7 &&
462 ply
->column_to
+(j
*bp_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*bp_offs
[i
][1]) <= 7){
463 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] != no_piece
) {
464 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == moving_piece
&&
465 color
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == ply
->player
&&
466 (ply
->row_to
+(j
*bp_offs
[i
][0]) != ply
->row_from
467 || ply
->column_to
+(j
*bp_offs
[i
][1]) != ply
->column_from
)) {
468 if (ply
->row_to
+(j
*bp_offs
[i
][0]) != ply
->row_from
){
469 unambiguous_position
= '1' + ply
->row_from
;
471 unambiguous_position
= 'a' + ply
->column_from
;
484 /* generate the first portion of the PGN text
485 * always as white so all uppercase, black/white considerations
486 * will be useful for FEN notation but not in this case
488 if (unambiguous_position
== '\0'){
489 if (board
[locn
[ply
->row_to
][ply
->column_to
]] != no_piece
){
490 rb
->snprintf(ply
->pgn_text
,10,"%cx%c%c",
491 pgn_from_piece(moving_piece
,white
) ,
492 'a'+ply
->column_to
, '1'+ply
->row_to
);
494 rb
->snprintf(ply
->pgn_text
,10,"%c%c%c",
495 pgn_from_piece(moving_piece
,white
) ,
496 'a'+ply
->column_to
, '1'+ply
->row_to
);
499 if (board
[locn
[ply
->row_to
][ply
->column_to
]] != no_piece
){
500 rb
->snprintf(ply
->pgn_text
,10,"%c%cx%c%c",
501 pgn_from_piece(moving_piece
,white
) ,
502 unambiguous_position
, 'a'+ply
->column_to
,
505 rb
->snprintf(ply
->pgn_text
,10,"%c%c%c%c",
506 pgn_from_piece(moving_piece
,white
) ,
507 unambiguous_position
, 'a'+ply
->column_to
,
512 /* update the board */
513 rb
->snprintf(alg_move
,5,"%c%c%c%c",'a' + ply
->column_from
, '1' + ply
->row_from
,
514 'a' + ply
->column_to
, '1' + ply
->row_to
);
515 /* The function returns false if the move is invalid, but since we're
516 * replaying the game, that should not be posible
518 VerifyMove (ply
->player
, alg_move
, 0 , &move
, move_buffer
);
520 /* add check/mate indicators */
521 for (pos
=0;ply
->pgn_text
[pos
] != '\0';pos
++);
522 if (ply
->checkmate
) {
523 ply
->pgn_text
[pos
] = '#'; pos
++;
524 ply
->pgn_text
[pos
] = '\0'; pos
++;
525 } else if (move_buffer
[4] == '+'){
526 ply
->pgn_text
[pos
] = '+'; pos
++;
527 ply
->pgn_text
[pos
] = '\0'; pos
++;
531 static const char* get_game_text(int selected_item
, void *data
,
532 char *buffer
, size_t buffer_len
){
534 struct pgn_game_node
*temp_node
= (struct pgn_game_node
*)data
;
536 for (i
=0;i
<selected_item
&& temp_node
!= NULL
;i
++){
537 temp_node
= temp_node
->next_node
;
539 if (temp_node
== NULL
){
542 rb
->snprintf(buffer
, buffer_len
,"%s vs. %s (%s)", temp_node
->white_player
,
543 temp_node
->black_player
, temp_node
->game_date
);
548 void write_pgn_token(int fhandler
, char *buffer
, size_t *line_length
){
549 if (*line_length
+ rb
->strlen(buffer
) + 1 > 80){
550 rb
->fdprintf(fhandler
,"\n");
553 rb
->fdprintf(fhandler
,"%s ",buffer
);
554 *line_length
+= (rb
->strlen(buffer
) + 1);
557 /* ---- api functions ---- */
558 struct pgn_game_node
* pgn_list_games(const char* filename
){
560 char line_buffer
[128];
561 struct pgn_game_node size_node
, *first_game
= NULL
;
562 struct pgn_game_node
*curr_node
= NULL
, *temp_node
;
563 unsigned short game_count
= 1;
565 bool header_start
= true, game_start
= false;
567 if ( (fhandler
= rb
->open(filename
, O_RDONLY
)) == 0 ) return NULL
;
572 while (rb
->read_line(fhandler
, line_buffer
, sizeof line_buffer
) > 0){
574 /* looking for a game header */
576 /* a new game header is found */
577 if (line_buffer
[0] == '['){
578 temp_node
= (struct pgn_game_node
*)pl_malloc(sizeof size_node
);
579 temp_node
->next_node
= NULL
;
580 if (curr_node
== NULL
) {
581 first_game
= curr_node
= temp_node
;
583 curr_node
->next_node
= temp_node
;
584 curr_node
= temp_node
;
586 process_tag(curr_node
, line_buffer
);
587 curr_node
->game_number
= game_count
;
588 curr_node
->pgn_line
= 0;
590 header_start
= false;
594 if (line_buffer
[0] == '['){
595 process_tag(curr_node
, line_buffer
);
596 } else if (line_buffer
[0] == '\r'
597 || line_buffer
[0] == '\n'
598 || line_buffer
[0] == '\0'){
605 if (curr_node
->pgn_line
== 0) {
606 curr_node
->pgn_line
= line_count
;
615 struct pgn_game_node
* pgn_show_game_list(struct pgn_game_node
* first_game
){
616 int curr_selection
= 0;
618 struct gui_synclist games_list
;
620 struct pgn_game_node
*temp_node
= first_game
;
622 for (i
=0;temp_node
!= NULL
;i
++){
623 temp_node
= temp_node
->next_node
;
627 rb
->gui_synclist_init(&games_list
, &get_game_text
, first_game
, false, 1, NULL
);
628 rb
->gui_synclist_set_title(&games_list
, "Games", NOICON
);
629 rb
->gui_synclist_set_icon_callback(&games_list
, NULL
);
630 rb
->gui_synclist_set_nb_items(&games_list
, i
);
631 rb
->gui_synclist_limit_scroll(&games_list
, true);
632 rb
->gui_synclist_select_item(&games_list
, 0);
635 rb
->gui_synclist_draw(&games_list
);
636 curr_selection
= rb
->gui_synclist_get_sel_pos(&games_list
);
637 button
= rb
->get_action(CONTEXT_LIST
,TIMEOUT_BLOCK
);
638 if (rb
->gui_synclist_do_button(&games_list
,&button
,LIST_WRAP_OFF
)){
643 temp_node
= first_game
;
644 for (i
=0;i
<curr_selection
&& temp_node
!= NULL
;i
++){
645 temp_node
= temp_node
->next_node
;
649 case ACTION_STD_CANCEL
:
656 void pgn_parse_game(const char* filename
,
657 struct pgn_game_node
* selected_game
){
658 struct pgn_ply_node size_ply
, *first_ply
= NULL
;
659 struct pgn_ply_node
*temp_ply
= NULL
, *curr_node
= NULL
;
661 char line_buffer
[128];
662 char token_buffer
[10];
664 unsigned short curr_player
= white
;
666 fhandler
= rb
->open(filename
, O_RDONLY
);
668 /* seek the line where the pgn of the selected game starts */
669 for (i
=1;i
<selected_game
->pgn_line
;i
++){
670 rb
->read_line(fhandler
, line_buffer
, sizeof line_buffer
);
673 loghandler
= rb
->open(LOG_FILE
, O_WRONLY
| O_CREAT
);
675 GNUChess_Initialize();
677 while (rb
->read_line(fhandler
, line_buffer
, sizeof line_buffer
) > 0){
678 if (line_buffer
[0] == '\r' || line_buffer
[0] == '\n' || line_buffer
[0] == '\0'){
682 while (pos
< rb
->strlen(line_buffer
)){
683 pos
= get_next_token(line_buffer
, pos
, token_buffer
);
684 if ((token_buffer
[0] >= 'A' && token_buffer
[0] <= 'Z')
685 || (token_buffer
[0] >= 'a' && token_buffer
[0] <= 'z')
686 || (token_buffer
[0] == '0' && token_buffer
[2] != '1')){
687 temp_ply
= (struct pgn_ply_node
*)pl_malloc(sizeof size_ply
);
688 temp_ply
->player
= curr_player
;
689 curr_player
= (curr_player
==white
)?black
:white
;
690 rb
->strcpy(temp_ply
->pgn_text
, token_buffer
);
691 pgn_to_coords(temp_ply
);
692 temp_ply
->prev_node
= NULL
;
693 temp_ply
->next_node
= NULL
;
694 if (first_ply
== NULL
) {
695 first_ply
= curr_node
= temp_ply
;
697 curr_node
->next_node
= temp_ply
;
698 temp_ply
->prev_node
= curr_node
;
699 curr_node
= temp_ply
;
701 rb
->fdprintf(loghandler
,
702 "player: %u; pgn: %s; from: %u,%u; to: %u,%u; taken: %u.\n",
703 temp_ply
->player
, temp_ply
->pgn_text
, temp_ply
->row_from
,
704 temp_ply
->column_from
, temp_ply
->row_to
,
705 temp_ply
->column_to
, temp_ply
->taken_piece
);
710 rb
->close(loghandler
);
712 /* additional dummy ply to represent end of file without
713 *loosing the previous node's pointer
715 if (first_ply
!= NULL
){
716 temp_ply
= (struct pgn_ply_node
*)pl_malloc(sizeof size_ply
);
717 temp_ply
->player
= neutral
;
718 temp_ply
->prev_node
= curr_node
;
719 curr_node
->next_node
= temp_ply
;
721 selected_game
->first_ply
= first_ply
;
725 struct pgn_game_node
* pgn_init_game(void){
726 struct pgn_game_node game_size
, *game
;
727 struct pgn_ply_node ply_size
, *ply
;
728 struct tm
*current_time
;
734 /* create an "end of game" dummy ply and assign defaults */
735 ply
= (struct pgn_ply_node
*)pl_malloc(sizeof ply_size
);
736 ply
->player
= neutral
;
737 ply
->pgn_text
[0] = '\0';
738 ply
->prev_node
= NULL
;
739 ply
->next_node
= NULL
;
741 /* create the game and assign defaults */
742 game
= (struct pgn_game_node
*)pl_malloc(sizeof game_size
);
743 game
->game_number
= 0;
744 rb
->strcpy(game
->white_player
,"Player");
745 rb
->strcpy(game
->black_player
,"GnuChess");
746 current_time
= rb
->get_time();
747 if (current_time
->tm_year
< 100){
748 rb
->snprintf(game
->game_date
,11,"????.??.??");
750 rb
->snprintf(game
->game_date
,11,"%4u.%2u.%2u",current_time
->tm_year
+ 1900,
751 current_time
->tm_mon
+ 1, current_time
->tm_mday
);
753 rb
->strcpy(game
->result
,"*");
755 game
->first_ply
= ply
;
756 game
->next_node
= NULL
;
761 void pgn_append_ply(struct pgn_game_node
* game
,
762 unsigned short ply_player
, char *move_buffer
, bool is_mate
){
763 struct pgn_ply_node ply_size
, *ply
, *temp
;
765 ply
= (struct pgn_ply_node
*)pl_malloc(sizeof ply_size
);
766 ply
->player
= ply_player
;
767 ply
->column_from
= move_buffer
[0] - 'a';
768 ply
->row_from
= move_buffer
[1] - '1';
769 ply
->column_to
= move_buffer
[2] - 'a';
770 ply
->row_to
= move_buffer
[3] - '1';
772 ply
->promotion
= false;
773 ply
->enpassant
= false;
774 ply
->promotion_piece
= no_piece
;
775 ply
->taken_piece
= no_piece
;
777 ply
->checkmate
= is_mate
;
779 /* move the pointer to the "end of game" marker ply */
780 for (temp
=game
->first_ply
;temp
->next_node
!=NULL
;temp
=temp
->next_node
);
782 /* arrange the pointers to insert the ply before the marker */
783 ply
->next_node
= temp
;
784 ply
->prev_node
= temp
->prev_node
;
785 if (temp
->prev_node
== NULL
){
786 game
->first_ply
= ply
;
788 temp
->prev_node
->next_node
= ply
;
790 temp
->prev_node
= ply
;
793 void pgn_set_result(struct pgn_game_node
* game
,
796 struct pgn_ply_node
*ply
;
797 for(ply
=game
->first_ply
;ply
->next_node
!= NULL
;ply
=ply
->next_node
);
799 ply
->prev_node
->checkmate
= true;
801 ply
->prev_node
->draw
= true;
805 void pgn_store_game(struct pgn_game_node
* game
){
807 struct pgn_ply_node
*ply
;
809 size_t line_length
=0;
812 GNUChess_Initialize();
816 while (ply
->next_node
!=NULL
){
819 if (ply
->player
== white
){
820 rb
->strcpy(game
->result
,"1-0");
822 rb
->strcpy(game
->result
,"0-1");
826 rb
->strcpy(game
->result
,"1/2-1/2");
832 fhandler
= rb
->open(PGN_FILE
, O_WRONLY
|O_CREAT
|O_APPEND
);
835 /* the first 7 tags are mandatory according to the PGN specification so we
836 * have to include them even if the values don't make much sense
838 rb
->fdprintf(fhandler
,"[Event \"Casual Game\"]\n");
839 rb
->fdprintf(fhandler
,"[Site \"?\"]\n");
840 rb
->fdprintf(fhandler
,"[Date \"%s\"]\n",game
->game_date
);
841 rb
->fdprintf(fhandler
,"[Round \"?\"]\n");
842 rb
->fdprintf(fhandler
,"[White \"%s\"]\n",game
->white_player
);
843 rb
->fdprintf(fhandler
,"[Black \"%s\"]\n",game
->black_player
);
844 rb
->fdprintf(fhandler
,"[Result \"%s\"]\n",game
->result
);
845 rb
->fdprintf(fhandler
,"[PlyCount \"%u\"]\n",ply_count
);
847 /* leave a blank line between the tag section and the game section */
848 rb
->fdprintf(fhandler
,"\n");
850 /* write the plies in several lines of up to 80 characters */
851 for (ply_count
=0, ply
=game
->first_ply
;ply
->next_node
!=NULL
;
852 ply
=ply
->next_node
,ply_count
++){
853 /* write the move number */
854 if (ply
->player
== white
){
855 rb
->snprintf(buffer
,10,"%u.",(ply_count
/2)+1);
856 write_pgn_token(fhandler
, buffer
, &line_length
);
858 /* write the actual move */
859 write_pgn_token(fhandler
,ply
->pgn_text
,&line_length
);
860 /* write the result of the game at the end */
862 if (ply
->player
== white
){
863 write_pgn_token(fhandler
,"1-0",&line_length
);
865 write_pgn_token(fhandler
,"0-1",&line_length
);
868 } else if (ply
->draw
){
869 write_pgn_token(fhandler
,"1/2-1/2",&line_length
);
871 } else if (ply
->next_node
->player
== neutral
) {
872 /* unknown end of the game */
873 write_pgn_token(fhandler
,"*",&line_length
);
878 /* leave a blank line between the tag section and the game section */
879 rb
->fdprintf(fhandler
,"\n\n");