Fix size_t handling in plugin_get_buffer()
[kugel-rb.git] / apps / plugins / chessbox / chessbox_pgn.c
blob43da92e0a0e019714435dfd5525b19e0686c7a3c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 #include "plugin.h"
23 #include "chessbox_pgn.h"
25 #define PGN_FILE PLUGIN_GAMES_DIR "/chessbox.pgn"
26 #define LOG_FILE PLUGIN_GAMES_DIR "/chessbox.log"
27 int loghandler;
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() */
34 void *bufptr = NULL;
35 size_t bufleft;
37 /* simple function to "allocate" memory in pluginbuffer.
38 * (borrowed from dict.c)
40 void *pl_malloc(size_t size)
42 void *ptr;
43 ptr = bufptr;
45 if (bufleft < size)
47 return NULL;
49 else
51 bufptr += size;
52 bufleft -= size;
53 return ptr;
57 /* init function for pl_malloc() */
58 void pl_malloc_init(void)
60 bufptr = rb->plugin_get_buffer(&bufleft);
63 void process_tag(struct pgn_game_node* game, char* buffer){
64 char tag_type[20];
65 char tag_value[255];
66 short pos=0, pos2=0;
67 while (buffer[pos+1] != ' '){
68 tag_type[pos] = buffer[pos+1];
69 pos++;
71 tag_type[pos] = '\0';
72 pos+=3;
73 while (buffer[pos] != '"'){
74 tag_value[pos2] = buffer[pos];
75 pos++; pos2++;
78 /* truncate tag values that are too large */
79 if (pos2 > 19){
80 pos2 = 19;
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,
99 char* token_buffer){
100 unsigned short pos, token_pos=0;
101 for (pos = initial_pos;line_buffer[pos] == ' ' || line_buffer[pos] == '.';pos++);
102 do {
103 token_buffer[token_pos] = line_buffer[pos];
104 pos++; token_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] == '?'){
109 token_pos--;
111 token_buffer[token_pos] = '\0';
112 return pos;
115 unsigned short piece_from_pgn(char pgn_piece){
116 switch (pgn_piece){
117 case 'R':
118 return rook;
119 case 'N':
120 return knight;
121 case 'B':
122 return bishop;
123 case 'Q':
124 return queen;
125 case 'K':
126 return king;
128 return no_piece;
131 char pgn_from_piece(unsigned short piece, unsigned short color){
132 char pgn_piece = ' ';
133 switch (piece){
134 case pawn:
135 pgn_piece = 'P';
136 break;
137 case rook:
138 pgn_piece = 'R';
139 break;
140 case knight:
141 pgn_piece = 'N';
142 break;
143 case bishop:
144 pgn_piece = 'B';
145 break;
146 case queen:
147 pgn_piece = 'Q';
148 break;
149 case king:
150 pgn_piece = 'K';
151 break;
152 case no_piece:
153 pgn_piece = ' ';
154 break;
156 if (color == black && pgn_piece != ' '){
157 pgn_piece += 32;
159 return pgn_piece;
162 void pgn_to_coords(struct pgn_ply_node* ply){
163 unsigned short str_length = rb->strlen(ply->pgn_text);
164 char str[10];
165 rb->strcpy(str,ply->pgn_text);
166 ply->column_from = 0xFF;
167 ply->row_from = 0xFF;
168 ply->column_to = 0xFF;
169 ply->row_to = 0xFF;
170 ply->taken_piece = no_piece;
171 ply->promotion_piece = no_piece;
172 ply->enpassant = false;
173 ply->castle = false;
174 ply->promotion = false;
175 unsigned short i, j, piece;
176 bool found = false;
178 if (str_length >= 3 && (str[0] == 'O' || str[0] == '0') && str[1] == '-'
179 && (str[2] == 'O' || str[2] == '0')) {
180 /* castling */
181 ply->castle = true;
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;
190 } else {
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;
196 } else {
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;
203 } else {
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'){
211 /* pawns */
212 ply->column_from = str[0] - 'a';
213 if (str[1] == 'x'){
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;
223 } else {
224 ply->taken_piece = board[locn[ply->row_to][ply->column_to]];
226 } else {
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){
236 /* promotion */
237 if (str[2] == '='){
238 ply->promotion_piece = piece_from_pgn(str[3]);
239 } else {
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;
247 } else {
248 /* the other pieces */
249 piece = piece_from_pgn(str[0]);
250 if (str[2] == 'x'){
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';
257 } else {
258 ply->row_from = str[1] - '1';
260 } else if (str[1] == 'x') {
261 /* taken a piece */
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';
271 } else {
272 ply->row_from = str[1] - '1';
274 } else {
275 /* regular move */
276 ply->column_to = str[1] - 'a';
277 ply->row_to = str[2] - '1';
279 if (piece == knight) {
280 for (i=0;i<8;i++){
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){
293 for (i=0;i<4;i++){
294 j = 1;
295 found = false;
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]);
305 found = true;
307 break;
309 j++;
311 if (found) {
312 break;
316 if (piece == bishop || ((piece == queen || piece == king) && !found)){
317 for (i=0;i<4;i++){
318 j = 1;
319 found = false;
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]);
329 found = true;
331 break;
333 j++;
335 if (found) {
336 break;
342 /* leave a very complete log of the parsing of the game while it gets stable */
343 for (i=0;i<8;i++){
344 for (j=0;j<8;j++) {
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){
358 int pos = 0,i,j;
359 unsigned short moving_piece = board[locn[ply->row_from][ply->column_from]];
360 char unambiguous_position;
361 bool found = false;
362 char alg_move[5];
363 char move_buffer[10];
364 short move;
365 if (moving_piece == king){
366 /* check castling */
367 if (ply->column_from == 4 && ply->column_to == 6){
368 /* castling kingside */
369 rb->strcpy(ply->pgn_text,"O-O");
370 ply->castle = true;
371 } else if (ply->column_from == 4 && ply->column_to == 2){
372 /* castling queenside */
373 rb->strcpy(ply->pgn_text,"O-O-O");
374 } else {
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,
377 '1'+ply->row_to);
378 } else {
379 rb->snprintf(ply->pgn_text,10,"K%c%c",'a'+ply->column_to,
380 '1'+ply->row_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);
395 } else {
396 rb->snprintf(ply->pgn_text,10,"%cx%c%c", 'a'+ply->column_from,
397 'a'+ply->column_to,'1'+ply->row_to);
399 } else {
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,
405 '1'+ply->row_to);
406 } else {
407 rb->snprintf(ply->pgn_text,10,"%c%c", 'a'+ply->column_to,
408 '1'+ply->row_to);
411 } else {
412 /* verify ambiguous moves for the different kinds of pieces */
413 unambiguous_position = '\0';
414 if (moving_piece == knight){
415 for (i=0;i<8;i++){
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;
424 } else {
425 unambiguous_position = 'a' + ply->column_from;
427 break;
431 if (moving_piece == rook || moving_piece == queen){
432 found = false;
433 for (i=0;i<4;i++){
434 j=1;
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;
444 } else {
445 unambiguous_position = 'a' + ply->column_from;
447 found = true;
449 break;
451 j++;
453 if (found) {
454 break;
458 if (moving_piece == bishop || (moving_piece == queen && !found)){
459 for (i=0;i<4;i++){
460 j=1;
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;
470 } else {
471 unambiguous_position = 'a' + ply->column_from;
473 found = true;
475 break;
477 j++;
479 if (found) {
480 break;
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);
493 } else {
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);
498 } else {
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,
503 '1'+ply->row_to);
504 } else {
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,
508 '1'+ply->row_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){
533 int i;
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){
540 return NULL;
542 rb->snprintf(buffer, buffer_len,"%s vs. %s (%s)", temp_node->white_player,
543 temp_node->black_player, temp_node->game_date);
545 return buffer;
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");
551 *line_length = 0;
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){
559 int fhandler;
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;
564 int line_count = 0;
565 bool header_start = true, game_start = false;
567 if ( (fhandler = rb->open(filename, O_RDONLY)) < 0 ) return NULL;
569 if (bufptr == NULL){
570 pl_malloc_init();
572 while (rb->read_line(fhandler, line_buffer, sizeof line_buffer) > 0){
573 line_count++;
574 /* looking for a game header */
575 if (header_start) {
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;
582 } else {
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;
589 game_count++;
590 header_start = false;
591 game_start = true;
593 } else {
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'){
599 if (game_start) {
600 game_start = false;
601 } else {
602 header_start = true;
604 } else {
605 if (curr_node->pgn_line == 0) {
606 curr_node->pgn_line = line_count;
611 rb->close(fhandler);
612 return first_game;
615 struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
616 int curr_selection = 0;
617 int button;
618 struct gui_synclist games_list;
619 int i;
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);
634 while (true) {
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)){
639 continue;
641 switch (button) {
642 case ACTION_STD_OK:
643 temp_node = first_game;
644 for (i=0;i<curr_selection && temp_node != NULL;i++){
645 temp_node = temp_node->next_node;
647 return temp_node;
648 break;
649 case ACTION_STD_CANCEL:
650 return NULL;
651 break;
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;
660 int fhandler, i;
661 char line_buffer[128];
662 char token_buffer[10];
663 unsigned short pos;
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, 0666);
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'){
679 break;
681 pos = 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;
696 } else {
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;
722 rb->close(fhandler);
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;
730 if (bufptr == NULL){
731 pl_malloc_init();
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,"????.??.??");
749 } else {
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,"*");
754 game->pgn_line = 0;
755 game->first_ply = ply;
756 game->next_node = NULL;
758 return game;
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';
771 ply->castle = false;
772 ply->promotion = false;
773 ply->enpassant = false;
774 ply->promotion_piece = no_piece;
775 ply->taken_piece = no_piece;
776 ply->draw = false;
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;
787 } else {
788 temp->prev_node->next_node = ply;
790 temp->prev_node = ply;
793 void pgn_set_result(struct pgn_game_node* game,
794 bool is_mate){
796 struct pgn_ply_node *ply;
797 for(ply=game->first_ply;ply->next_node != NULL;ply=ply->next_node);
798 if (is_mate){
799 ply->prev_node->checkmate = true;
800 } else {
801 ply->prev_node->draw = true;
805 void pgn_store_game(struct pgn_game_node* game){
806 int fhandler;
807 struct pgn_ply_node *ply;
808 unsigned ply_count;
809 size_t line_length=0;
810 char buffer[10];
812 GNUChess_Initialize();
814 ply_count=0;
815 ply=game->first_ply;
816 while (ply->next_node!=NULL){
817 coords_to_pgn(ply);
818 if (ply->checkmate){
819 if (ply->player == white){
820 rb->strcpy(game->result,"1-0");
821 } else {
822 rb->strcpy(game->result,"0-1");
825 if (ply->draw){
826 rb->strcpy(game->result,"1/2-1/2");
828 ply=ply->next_node;
829 ply_count++;
832 fhandler = rb->open(PGN_FILE, O_WRONLY|O_CREAT|O_APPEND, 0666);
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 */
861 if (ply->checkmate){
862 if (ply->player == white){
863 write_pgn_token(fhandler,"1-0",&line_length);
864 } else {
865 write_pgn_token(fhandler,"0-1",&line_length);
867 break;
868 } else if (ply->draw){
869 write_pgn_token(fhandler,"1/2-1/2",&line_length);
870 break;
871 } else if (ply->next_node->player == neutral) {
872 /* unknown end of the game */
873 write_pgn_token(fhandler,"*",&line_length);
874 break;
878 /* leave a blank line between the tag section and the game section */
879 rb->fdprintf(fhandler,"\n\n");
881 rb->close(fhandler);