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 const struct plugin_api
* rb
;
31 short kn_offs
[8][2] = {{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
32 short rk_offs
[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
33 short bp_offs
[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
35 /* global vars for pl_malloc() */
39 /* simple function to "allocate" memory in pluginbuffer.
40 * (borrowed from dict.c)
42 void *pl_malloc(ssize_t size
)
58 /* init function for pl_malloc() */
59 void pl_malloc_init(void)
61 bufptr
= rb
->plugin_get_buffer((size_t *)&bufleft
);
64 void process_tag(struct pgn_game_node
* game
, char* buffer
){
68 while (buffer
[pos
+1] != ' '){
69 tag_type
[pos
] = buffer
[pos
+1];
74 while (buffer
[pos
] != '"'){
75 tag_value
[pos2
] = buffer
[pos
];
79 /* truncate tag values that are too large */
83 tag_value
[pos2
] = '\0';
85 if (rb
->strcmp(tag_type
,"White") == 0){
86 rb
->strcpy(game
->white_player
, tag_value
);
88 if (rb
->strcmp(tag_type
,"Black") == 0){
89 rb
->strcpy(game
->black_player
, tag_value
);
91 if (rb
->strcmp(tag_type
,"Result") == 0){
92 rb
->strcpy(game
->result
, tag_value
);
94 if (rb
->strcmp(tag_type
,"Date") == 0){
95 rb
->strcpy(game
->game_date
, tag_value
);
99 unsigned short get_next_token(const char* line_buffer
, unsigned short initial_pos
,
101 unsigned short pos
, token_pos
=0;
102 for (pos
= initial_pos
;line_buffer
[pos
] == ' ' || line_buffer
[pos
] == '.';pos
++);
104 token_buffer
[token_pos
] = line_buffer
[pos
];
106 } while (line_buffer
[pos
] != ' ' && line_buffer
[pos
] != '.'
107 && line_buffer
[pos
] != '\0');
108 /* ignore annotations */
109 while (token_buffer
[token_pos
-1] == '!' || token_buffer
[token_pos
-1] == '?'){
112 token_buffer
[token_pos
] = '\0';
116 unsigned short piece_from_pgn(char pgn_piece
){
132 char pgn_from_piece(unsigned short piece
, unsigned short color
){
133 char pgn_piece
= ' ';
157 if (color
== black
&& pgn_piece
!= ' '){
163 void pgn_to_coords(struct pgn_ply_node
* ply
){
164 unsigned short str_length
= rb
->strlen(ply
->pgn_text
);
166 rb
->strcpy(str
,ply
->pgn_text
);
167 ply
->column_from
= 0xFF;
168 ply
->row_from
= 0xFF;
169 ply
->column_to
= 0xFF;
171 ply
->taken_piece
= no_piece
;
172 ply
->promotion_piece
= no_piece
;
173 ply
->enpassant
= false;
175 ply
->promotion
= false;
176 unsigned short i
, j
, piece
;
179 if (str_length
>= 3 && (str
[0] == 'O' || str
[0] == '0') && str
[1] == '-'
180 && (str
[2] == 'O' || str
[2] == '0')) {
183 if (str_length
>= 5 && str
[3] == '-' && (str
[4] == 'O' || str
[4] == '0')){
184 /* castle queenside */
185 if (ply
->player
== white
){
186 ply
->row_from
= 0; ply
->column_from
= 4;
187 ply
->row_to
= 0; ply
->column_to
= 2;
188 /* update the rook's position, the king's position will be updated later */
189 board
[locn
[0][3]] = rook
; color
[locn
[0][3]] = white
;
190 board
[locn
[0][0]] = no_piece
; color
[locn
[0][0]] = neutral
;
192 ply
->row_from
= 7; ply
->column_from
= 4;
193 ply
->row_to
= 7; ply
->column_to
= 2;
194 board
[locn
[7][3]] = rook
; color
[locn
[7][3]] = black
;
195 board
[locn
[7][0]] = no_piece
; color
[locn
[7][0]] = neutral
;
198 /* castle kingside */
199 if (ply
->player
== white
){
200 ply
->row_from
= 0; ply
->column_from
= 4;
201 ply
->row_to
= 0; ply
->column_to
= 6;
202 board
[locn
[0][5]] = rook
; color
[locn
[0][5]] = white
;
203 board
[locn
[0][7]] = no_piece
; color
[locn
[0][7]] = neutral
;
205 ply
->row_from
= 7; ply
->column_from
= 4;
206 ply
->row_to
= 7; ply
->column_to
= 6;
207 board
[locn
[7][5]] = rook
; color
[locn
[7][5]] = black
;
208 board
[locn
[7][7]] = no_piece
; color
[locn
[7][7]] = neutral
;
211 } else if (str
[0] >= 'a' && str
[0] <= 'h'){
213 ply
->column_from
= str
[0] - 'a';
215 ply
->row_from
= str
[3] - '1' + ((ply
->player
==white
)?-1:1);
216 ply
->row_to
= str
[3] - '1';
217 ply
->column_to
= str
[2] - 'a';
218 if (board
[locn
[ply
->row_to
][ply
->column_to
]] == no_piece
){
219 /* en-passant, remove the pawn */
220 ply
->enpassant
= true;
221 board
[locn
[ply
->row_from
][ply
->column_to
]] = no_piece
;
222 color
[locn
[ply
->row_from
][ply
->column_to
]] = neutral
;
223 ply
->taken_piece
= pawn
;
225 ply
->taken_piece
= board
[locn
[ply
->row_to
][ply
->column_to
]];
228 ply
->column_to
= ply
->column_from
;
229 ply
->row_from
= str
[1] - '1' + ((ply
->player
==white
)?-1:1);
230 ply
->row_to
= str
[1] - '1';
232 if (board
[locn
[ply
->row_from
][ply
->column_from
]] == no_piece
){
233 /* the pawn moved two squares */
234 ply
->row_from
+= ((ply
->player
==white
)?-1:1);
236 if (ply
->row_to
== 7 || ply
->row_to
== 0){
239 ply
->promotion_piece
= piece_from_pgn(str
[3]);
241 ply
->promotion_piece
= piece_from_pgn(str
[5]);
243 /* change the piece in the original position and wait
244 * for the code at the end to move it
246 board
[locn
[ply
->row_from
][ply
->column_from
]] = ply
->promotion_piece
;
249 /* the other pieces */
250 piece
= piece_from_pgn(str
[0]);
252 /* taken a piece and move was ambiguous */
253 ply
->column_to
= str
[3] - 'a';
254 ply
->row_to
= str
[4] - '1';
255 ply
->taken_piece
= board
[locn
[ply
->row_to
][ply
->column_to
]];
256 if (str
[1] >= 'a' && str
[1] <= 'h') {
257 ply
->column_from
= str
[1] - 'a';
259 ply
->row_from
= str
[1] - '1';
261 } else if (str
[1] == 'x') {
263 ply
->column_to
= str
[2] - 'a';
264 ply
->row_to
= str
[3] - '1';
265 ply
->taken_piece
= board
[locn
[ply
->row_to
][ply
->column_to
]];
266 } else if (str_length
>= 4 && str
[3] >= '0' && str
[3] <= '9'){
267 /* no piece taken and move was ambiguous */
268 ply
->column_to
= str
[2] - 'a';
269 ply
->row_to
= str
[3] - '1';
270 if (str
[1] >= 'a' && str
[1] <= 'h') {
271 ply
->column_from
= str
[1] - 'a';
273 ply
->row_from
= str
[1] - '1';
277 ply
->column_to
= str
[1] - 'a';
278 ply
->row_to
= str
[2] - '1';
280 if (piece
== knight
) {
282 if (ply
->row_to
+ kn_offs
[i
][0] >= 0 && ply
->row_to
+ kn_offs
[i
][0] <= 7
283 && ply
->column_to
+ kn_offs
[i
][1] >= 0 && ply
->column_to
+ kn_offs
[i
][1] <= 7
284 && board
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == knight
285 && color
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == ply
->player
286 && (ply
->row_from
== 0xFF || ply
->row_from
== ply
->row_to
+ kn_offs
[i
][0])
287 && (ply
->column_from
== 0xFF || ply
->column_from
== ply
->column_to
+ kn_offs
[i
][1])) {
288 ply
->row_from
= ply
->row_to
+ kn_offs
[i
][0];
289 ply
->column_from
= ply
->column_to
+ kn_offs
[i
][1];
293 if (piece
== rook
|| piece
== queen
|| piece
== king
){
297 while (ply
->row_to
+(j
*rk_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*rk_offs
[i
][0]) <= 7 &&
298 ply
->column_to
+(j
*rk_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*rk_offs
[i
][1]) <= 7){
299 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] != no_piece
) {
300 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == piece
&&
301 color
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == ply
->player
&&
302 (ply
->row_from
== 0xFF || ply
->row_from
== ply
->row_to
+(j
*rk_offs
[i
][0])) &&
303 (ply
->column_from
== 0xFF || ply
->column_from
== ply
->column_to
+(j
*rk_offs
[i
][1]))) {
304 ply
->row_from
= ply
->row_to
+(j
*rk_offs
[i
][0]);
305 ply
->column_from
= ply
->column_to
+(j
*rk_offs
[i
][1]);
317 if (piece
== bishop
|| ((piece
== queen
|| piece
== king
) && !found
)){
321 while (ply
->row_to
+(j
*bp_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*bp_offs
[i
][0]) <= 7 &&
322 ply
->column_to
+(j
*bp_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*bp_offs
[i
][1]) <= 7){
323 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] != no_piece
) {
324 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == piece
&&
325 color
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == ply
->player
&&
326 (ply
->row_from
== 0xFF || ply
->row_from
== ply
->row_to
+(j
*bp_offs
[i
][0])) &&
327 (ply
->column_from
== 0xFF || ply
->column_from
== ply
->column_to
+(j
*bp_offs
[i
][1]))) {
328 ply
->row_from
= ply
->row_to
+(j
*bp_offs
[i
][0]);
329 ply
->column_from
= ply
->column_to
+(j
*bp_offs
[i
][1]);
343 /* leave a very complete log of the parsing of the game while it gets stable */
346 rb
->fdprintf(loghandler
,"%c",pgn_from_piece(board
[locn
[7-i
][j
]],color
[locn
[7-i
][j
]]));
348 rb
->fdprintf(loghandler
,"\n");
351 /* update the board */
352 board
[locn
[ply
->row_to
][ply
->column_to
]] = board
[locn
[ply
->row_from
][ply
->column_from
]];
353 color
[locn
[ply
->row_to
][ply
->column_to
]] = color
[locn
[ply
->row_from
][ply
->column_from
]];
354 board
[locn
[ply
->row_from
][ply
->column_from
]] = no_piece
;
355 color
[locn
[ply
->row_from
][ply
->column_from
]] = neutral
;
358 void coords_to_pgn(struct pgn_ply_node
* ply
){
360 unsigned short moving_piece
= board
[locn
[ply
->row_from
][ply
->column_from
]];
361 char unambiguous_position
;
364 char move_buffer
[10];
366 if (moving_piece
== king
){
368 if (ply
->column_from
== 4 && ply
->column_to
== 6){
369 /* castling kingside */
370 rb
->strcpy(ply
->pgn_text
,"O-O");
372 } else if (ply
->column_from
== 4 && ply
->column_to
== 2){
373 /* castling queenside */
374 rb
->strcpy(ply
->pgn_text
,"O-O-O");
376 if (board
[locn
[ply
->row_to
][ply
->column_to
]] != no_piece
){
377 rb
->snprintf(ply
->pgn_text
,10,"Kx%c%c",'a'+ply
->column_to
,
380 rb
->snprintf(ply
->pgn_text
,10,"K%c%c",'a'+ply
->column_to
,
384 } else if (moving_piece
== pawn
){
385 if (ply
->column_from
!= ply
->column_to
){
386 /* check enpassant */
387 if (board
[locn
[ply
->row_to
][ply
->column_to
]] == no_piece
){
388 ply
->enpassant
= true;
390 /* check promotions when taking a piece */
391 if (ply
->row_to
== 0 || ply
->row_to
== 7) {
392 ply
->promotion
= true;
393 ply
->promotion_piece
= queen
;
394 rb
->snprintf(ply
->pgn_text
,10,"%cx%c%c=D", 'a'+ply
->column_from
,
395 'a'+ply
->column_to
,'1'+ply
->row_to
);
397 rb
->snprintf(ply
->pgn_text
,10,"%cx%c%c", 'a'+ply
->column_from
,
398 'a'+ply
->column_to
,'1'+ply
->row_to
);
401 /* check promotions when not taking a piece */
402 if (ply
->row_to
== 0 || ply
->row_to
== 7) {
403 ply
->promotion
= true;
404 ply
->promotion_piece
= queen
;
405 rb
->snprintf(ply
->pgn_text
,10,"%c%c=D", 'a'+ply
->column_to
,
408 rb
->snprintf(ply
->pgn_text
,10,"%c%c", 'a'+ply
->column_to
,
413 /* verify ambiguous moves for the different kinds of pieces */
414 unambiguous_position
= '\0';
415 if (moving_piece
== knight
){
417 if (ply
->row_to
+ kn_offs
[i
][0] >= 0 && ply
->row_to
+ kn_offs
[i
][0] <= 7
418 && ply
->column_to
+ kn_offs
[i
][1] >= 0 && ply
->column_to
+ kn_offs
[i
][1] <= 7
419 && board
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == knight
420 && color
[locn
[ply
->row_to
+ kn_offs
[i
][0]][ply
->column_to
+ kn_offs
[i
][1]]] == ply
->player
421 && (ply
->row_to
+ kn_offs
[i
][0] != ply
->row_from
422 || ply
->column_to
+ kn_offs
[i
][1] != ply
->column_from
)){
423 if (ply
->row_to
+ kn_offs
[i
][0] != ply
->row_from
){
424 unambiguous_position
= '1' + ply
->row_from
;
426 unambiguous_position
= 'a' + ply
->column_from
;
432 if (moving_piece
== rook
|| moving_piece
== queen
){
436 while (ply
->row_to
+(j
*rk_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*rk_offs
[i
][0]) <= 7 &&
437 ply
->column_to
+(j
*rk_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*rk_offs
[i
][1]) <= 7){
438 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] != no_piece
) {
439 if (board
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == moving_piece
&&
440 color
[locn
[ply
->row_to
+(j
*rk_offs
[i
][0])][ply
->column_to
+(j
*rk_offs
[i
][1])]] == ply
->player
&&
441 (ply
->row_to
+(j
*rk_offs
[i
][0]) != ply
->row_from
442 || ply
->column_to
+(j
*rk_offs
[i
][1]) != ply
->column_from
)) {
443 if (ply
->row_to
+(j
*rk_offs
[i
][0]) != ply
->row_from
){
444 unambiguous_position
= '1' + ply
->row_from
;
446 unambiguous_position
= 'a' + ply
->column_from
;
459 if (moving_piece
== bishop
|| (moving_piece
== queen
&& !found
)){
462 while (ply
->row_to
+(j
*bp_offs
[i
][0]) >= 0 && ply
->row_to
+(j
*bp_offs
[i
][0]) <= 7 &&
463 ply
->column_to
+(j
*bp_offs
[i
][1]) >= 0 && ply
->column_to
+(j
*bp_offs
[i
][1]) <= 7){
464 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] != no_piece
) {
465 if (board
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == moving_piece
&&
466 color
[locn
[ply
->row_to
+(j
*bp_offs
[i
][0])][ply
->column_to
+(j
*bp_offs
[i
][1])]] == ply
->player
&&
467 (ply
->row_to
+(j
*bp_offs
[i
][0]) != ply
->row_from
468 || ply
->column_to
+(j
*bp_offs
[i
][1]) != ply
->column_from
)) {
469 if (ply
->row_to
+(j
*bp_offs
[i
][0]) != ply
->row_from
){
470 unambiguous_position
= '1' + ply
->row_from
;
472 unambiguous_position
= 'a' + ply
->column_from
;
485 /* generate the first portion of the PGN text
486 * always as white so all uppercase, black/white considerations
487 * will be useful for FEN notation but not in this case
489 if (unambiguous_position
== '\0'){
490 if (board
[locn
[ply
->row_to
][ply
->column_to
]] != no_piece
){
491 rb
->snprintf(ply
->pgn_text
,10,"%cx%c%c",
492 pgn_from_piece(moving_piece
,white
) ,
493 'a'+ply
->column_to
, '1'+ply
->row_to
);
495 rb
->snprintf(ply
->pgn_text
,10,"%c%c%c",
496 pgn_from_piece(moving_piece
,white
) ,
497 'a'+ply
->column_to
, '1'+ply
->row_to
);
500 if (board
[locn
[ply
->row_to
][ply
->column_to
]] != no_piece
){
501 rb
->snprintf(ply
->pgn_text
,10,"%c%cx%c%c",
502 pgn_from_piece(moving_piece
,white
) ,
503 unambiguous_position
, 'a'+ply
->column_to
,
506 rb
->snprintf(ply
->pgn_text
,10,"%c%c%c%c",
507 pgn_from_piece(moving_piece
,white
) ,
508 unambiguous_position
, 'a'+ply
->column_to
,
513 /* update the board */
514 rb
->snprintf(alg_move
,5,"%c%c%c%c",'a' + ply
->column_from
, '1' + ply
->row_from
,
515 'a' + ply
->column_to
, '1' + ply
->row_to
);
516 /* The function returns false if the move is invalid, but since we're
517 * replaying the game, that should not be posible
519 VerifyMove (ply
->player
, alg_move
, 0 , &move
, move_buffer
);
521 /* add check/mate indicators */
522 for (pos
=0;ply
->pgn_text
[pos
] != '\0';pos
++);
523 if (ply
->checkmate
) {
524 ply
->pgn_text
[pos
] = '#'; pos
++;
525 ply
->pgn_text
[pos
] = '\0'; pos
++;
526 } else if (move_buffer
[4] == '+'){
527 ply
->pgn_text
[pos
] = '+'; pos
++;
528 ply
->pgn_text
[pos
] = '\0'; pos
++;
532 char * get_game_text(int selected_item
, void *data
,
533 char *buffer
, size_t buffer_len
){
535 struct pgn_game_node
*temp_node
= (struct pgn_game_node
*)data
;
536 char text_buffer
[50];
538 for (i
=0;i
<selected_item
&& temp_node
!= NULL
;i
++){
539 temp_node
= temp_node
->next_node
;
541 if (temp_node
== NULL
){
544 rb
->snprintf(text_buffer
, 50,"%s vs. %s (%s)", temp_node
->white_player
,
545 temp_node
->black_player
, temp_node
->game_date
);
547 rb
->strncpy(buffer
, text_buffer
, buffer_len
);
551 void write_pgn_token(int fhandler
, char *buffer
, size_t *line_length
){
552 if (*line_length
+ rb
->strlen(buffer
) + 1 > 80){
553 rb
->fdprintf(fhandler
,"\n");
556 rb
->fdprintf(fhandler
,"%s ",buffer
);
557 *line_length
+= (rb
->strlen(buffer
) + 1);
560 /* ---- api functions ---- */
561 struct pgn_game_node
* pgn_list_games(const struct plugin_api
* api
,const char* filename
){
563 char line_buffer
[128];
564 struct pgn_game_node size_node
, *first_game
= NULL
;
565 struct pgn_game_node
*curr_node
= NULL
, *temp_node
;
566 unsigned short game_count
= 1;
568 bool header_start
= true, game_start
= false;
571 if ( (fhandler
= rb
->open(filename
, O_RDONLY
)) == 0 ) return NULL
;
576 while (rb
->read_line(fhandler
, line_buffer
, sizeof line_buffer
) > 0){
578 /* looking for a game header */
580 /* a new game header is found */
581 if (line_buffer
[0] == '['){
582 temp_node
= (struct pgn_game_node
*)pl_malloc(sizeof size_node
);
583 temp_node
->next_node
= NULL
;
584 if (curr_node
== NULL
) {
585 first_game
= curr_node
= temp_node
;
587 curr_node
->next_node
= temp_node
;
588 curr_node
= temp_node
;
590 process_tag(curr_node
, line_buffer
);
591 curr_node
->game_number
= game_count
;
592 curr_node
->pgn_line
= 0;
594 header_start
= false;
598 if (line_buffer
[0] == '['){
599 process_tag(curr_node
, line_buffer
);
600 } else if (line_buffer
[0] == '\r'
601 || line_buffer
[0] == '\n'
602 || line_buffer
[0] == '\0'){
609 if (curr_node
->pgn_line
== 0) {
610 curr_node
->pgn_line
= line_count
;
619 struct pgn_game_node
* pgn_show_game_list(const struct plugin_api
* api
,
620 struct pgn_game_node
* first_game
){
621 int curr_selection
= 0;
623 struct gui_synclist games_list
;
625 struct pgn_game_node
*temp_node
= first_game
;
629 for (i
=0;temp_node
!= NULL
;i
++){
630 temp_node
= temp_node
->next_node
;
634 rb
->gui_synclist_init(&games_list
, &get_game_text
, first_game
, false, 1, NULL
);
635 rb
->gui_synclist_set_title(&games_list
, "Games", NOICON
);
636 rb
->gui_synclist_set_icon_callback(&games_list
, NULL
);
637 rb
->gui_synclist_set_nb_items(&games_list
, i
);
638 rb
->gui_synclist_limit_scroll(&games_list
, true);
639 rb
->gui_synclist_select_item(&games_list
, 0);
642 rb
->gui_syncstatusbar_draw(rb
->statusbars
, true);
643 rb
->gui_synclist_draw(&games_list
);
644 curr_selection
= rb
->gui_synclist_get_sel_pos(&games_list
);
645 button
= rb
->get_action(CONTEXT_LIST
,TIMEOUT_BLOCK
);
646 if (rb
->gui_synclist_do_button(&games_list
,&button
,LIST_WRAP_OFF
)){
651 temp_node
= first_game
;
652 for (i
=0;i
<curr_selection
&& temp_node
!= NULL
;i
++){
653 temp_node
= temp_node
->next_node
;
657 case ACTION_STD_CANCEL
:
664 void pgn_parse_game(const struct plugin_api
* api
, const char* filename
,
665 struct pgn_game_node
* selected_game
){
666 struct pgn_ply_node size_ply
, *first_ply
= NULL
;
667 struct pgn_ply_node
*temp_ply
= NULL
, *curr_node
= NULL
;
669 char line_buffer
[128];
670 char token_buffer
[10];
672 unsigned short curr_player
= white
;
675 fhandler
= rb
->open(filename
, O_RDONLY
);
677 /* seek the line where the pgn of the selected game starts */
678 for (i
=1;i
<selected_game
->pgn_line
;i
++){
679 rb
->read_line(fhandler
, line_buffer
, sizeof line_buffer
);
682 loghandler
= rb
->open(LOG_FILE
, O_WRONLY
| O_CREAT
);
684 GNUChess_Initialize();
686 while (rb
->read_line(fhandler
, line_buffer
, sizeof line_buffer
) > 0){
687 if (line_buffer
[0] == '\r' || line_buffer
[0] == '\n' || line_buffer
[0] == '\0'){
691 while (pos
< rb
->strlen(line_buffer
)){
692 pos
= get_next_token(line_buffer
, pos
, token_buffer
);
693 if ((token_buffer
[0] >= 'A' && token_buffer
[0] <= 'Z')
694 || (token_buffer
[0] >= 'a' && token_buffer
[0] <= 'z')
695 || (token_buffer
[0] == '0' && token_buffer
[2] != '1')){
696 temp_ply
= (struct pgn_ply_node
*)pl_malloc(sizeof size_ply
);
697 temp_ply
->player
= curr_player
;
698 curr_player
= (curr_player
==white
)?black
:white
;
699 rb
->strcpy(temp_ply
->pgn_text
, token_buffer
);
700 pgn_to_coords(temp_ply
);
701 temp_ply
->prev_node
= NULL
;
702 temp_ply
->next_node
= NULL
;
703 if (first_ply
== NULL
) {
704 first_ply
= curr_node
= temp_ply
;
706 curr_node
->next_node
= temp_ply
;
707 temp_ply
->prev_node
= curr_node
;
708 curr_node
= temp_ply
;
710 rb
->fdprintf(loghandler
,
711 "player: %u; pgn: %s; from: %u,%u; to: %u,%u; taken: %u.\n",
712 temp_ply
->player
, temp_ply
->pgn_text
, temp_ply
->row_from
,
713 temp_ply
->column_from
, temp_ply
->row_to
,
714 temp_ply
->column_to
, temp_ply
->taken_piece
);
719 rb
->close(loghandler
);
721 /* additional dummy ply to represent end of file without
722 *loosing the previous node's pointer
724 if (first_ply
!= NULL
){
725 temp_ply
= (struct pgn_ply_node
*)pl_malloc(sizeof size_ply
);
726 temp_ply
->player
= neutral
;
727 temp_ply
->prev_node
= curr_node
;
728 curr_node
->next_node
= temp_ply
;
730 selected_game
->first_ply
= first_ply
;
734 struct pgn_game_node
* pgn_init_game(const struct plugin_api
* api
){
735 struct pgn_game_node game_size
, *game
;
736 struct pgn_ply_node ply_size
, *ply
;
737 struct tm
*current_time
;
745 /* create an "end of game" dummy ply and assign defaults */
746 ply
= (struct pgn_ply_node
*)pl_malloc(sizeof ply_size
);
747 ply
->player
= neutral
;
748 ply
->pgn_text
[0] = '\0';
749 ply
->prev_node
= NULL
;
750 ply
->next_node
= NULL
;
752 /* create the game and assign defaults */
753 game
= (struct pgn_game_node
*)pl_malloc(sizeof game_size
);
754 game
->game_number
= 0;
755 rb
->strcpy(game
->white_player
,"Player");
756 rb
->strcpy(game
->black_player
,"GnuChess");
757 current_time
= rb
->get_time();
758 if (current_time
->tm_year
< 100){
759 rb
->snprintf(game
->game_date
,11,"????.??.??");
761 rb
->snprintf(game
->game_date
,11,"%4u.%2u.%2u",current_time
->tm_year
+ 1900,
762 current_time
->tm_mon
+ 1, current_time
->tm_mday
);
764 rb
->strcpy(game
->result
,"*");
766 game
->first_ply
= ply
;
767 game
->next_node
= NULL
;
772 void pgn_append_ply(const struct plugin_api
* api
, struct pgn_game_node
* game
,
773 unsigned short ply_player
, char *move_buffer
, bool is_mate
){
774 struct pgn_ply_node ply_size
, *ply
, *temp
;
778 ply
= (struct pgn_ply_node
*)pl_malloc(sizeof ply_size
);
779 ply
->player
= ply_player
;
780 ply
->column_from
= move_buffer
[0] - 'a';
781 ply
->row_from
= move_buffer
[1] - '1';
782 ply
->column_to
= move_buffer
[2] - 'a';
783 ply
->row_to
= move_buffer
[3] - '1';
785 ply
->promotion
= false;
786 ply
->enpassant
= false;
787 ply
->promotion_piece
= no_piece
;
788 ply
->taken_piece
= no_piece
;
790 ply
->checkmate
= is_mate
;
792 /* move the pointer to the "end of game" marker ply */
793 for (temp
=game
->first_ply
;temp
->next_node
!=NULL
;temp
=temp
->next_node
);
795 /* arrange the pointers to insert the ply before the marker */
796 ply
->next_node
= temp
;
797 ply
->prev_node
= temp
->prev_node
;
798 if (temp
->prev_node
== NULL
){
799 game
->first_ply
= ply
;
801 temp
->prev_node
->next_node
= ply
;
803 temp
->prev_node
= ply
;
806 void pgn_set_result(const struct plugin_api
* api
, struct pgn_game_node
* game
,
811 struct pgn_ply_node
*ply
;
812 for(ply
=game
->first_ply
;ply
->next_node
!= NULL
;ply
=ply
->next_node
);
814 ply
->prev_node
->checkmate
= true;
816 ply
->prev_node
->draw
= true;
820 void pgn_store_game(const struct plugin_api
* api
, struct pgn_game_node
* game
){
822 struct pgn_ply_node
*ply
;
824 size_t line_length
=0;
829 GNUChess_Initialize();
833 while (ply
->next_node
!=NULL
){
836 if (ply
->player
== white
){
837 rb
->strcpy(game
->result
,"1-0");
839 rb
->strcpy(game
->result
,"0-1");
843 rb
->strcpy(game
->result
,"1/2-1/2");
849 fhandler
= rb
->open(PGN_FILE
, O_WRONLY
|O_CREAT
|O_APPEND
);
852 /* the first 7 tags are mandatory according to the PGN specification so we
853 * have to include them even if the values don't make much sense
855 rb
->fdprintf(fhandler
,"[Event \"Casual Game\"]\n");
856 rb
->fdprintf(fhandler
,"[Site \"?\"]\n");
857 rb
->fdprintf(fhandler
,"[Date \"%s\"]\n",game
->game_date
);
858 rb
->fdprintf(fhandler
,"[Round \"?\"]\n");
859 rb
->fdprintf(fhandler
,"[White \"%s\"]\n",game
->white_player
);
860 rb
->fdprintf(fhandler
,"[Black \"%s\"]\n",game
->black_player
);
861 rb
->fdprintf(fhandler
,"[Result \"%s\"]\n",game
->result
);
862 rb
->fdprintf(fhandler
,"[PlyCount \"%u\"]\n",ply_count
);
864 /* leave a blank line between the tag section and the game section */
865 rb
->fdprintf(fhandler
,"\n");
867 /* write the plies in several lines of up to 80 characters */
868 for (ply_count
=0, ply
=game
->first_ply
;ply
->next_node
!=NULL
;
869 ply
=ply
->next_node
,ply_count
++){
870 /* write the move number */
871 if (ply
->player
== white
){
872 rb
->snprintf(buffer
,10,"%u.",(ply_count
/2)+1);
873 write_pgn_token(fhandler
, buffer
, &line_length
);
875 /* write the actual move */
876 write_pgn_token(fhandler
,ply
->pgn_text
,&line_length
);
877 /* write the result of the game at the end */
879 if (ply
->player
== white
){
880 write_pgn_token(fhandler
,"1-0",&line_length
);
882 write_pgn_token(fhandler
,"0-1",&line_length
);
885 } else if (ply
->draw
){
886 write_pgn_token(fhandler
,"1/2-1/2",&line_length
);
888 } else if (ply
->next_node
->player
== neutral
) {
889 /* unknown end of the game */
890 write_pgn_token(fhandler
,"*",&line_length
);
895 /* leave a blank line between the tag section and the game section */
896 rb
->fdprintf(fhandler
,"\n\n");