Colour targets: Revert an optimisation from almost 18 months ago that actually turned...
[Rockbox.git] / apps / plugins / chessbox / chessbox_pgn.c
blob9e9656207be67a25966b6258c02b6eb6441860a9
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 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() */
36 void *bufptr = NULL;
37 ssize_t bufleft;
39 /* simple function to "allocate" memory in pluginbuffer.
40 * (borrowed from dict.c)
42 void *pl_malloc(ssize_t size)
44 void *ptr;
45 ptr = bufptr;
47 if (bufleft < size)
49 return NULL;
51 else
53 bufptr += size;
54 return ptr;
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){
65 char tag_type[20];
66 char tag_value[255];
67 short pos=0, pos2=0;
68 while (buffer[pos+1] != ' '){
69 tag_type[pos] = buffer[pos+1];
70 pos++;
72 tag_type[pos] = '\0';
73 pos+=3;
74 while (buffer[pos] != '"'){
75 tag_value[pos2] = buffer[pos];
76 pos++; pos2++;
79 /* truncate tag values that are too large */
80 if (pos2 > 19){
81 pos2 = 19;
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,
100 char* token_buffer){
101 unsigned short pos, token_pos=0;
102 for (pos = initial_pos;line_buffer[pos] == ' ' || line_buffer[pos] == '.';pos++);
103 do {
104 token_buffer[token_pos] = line_buffer[pos];
105 pos++; token_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] == '?'){
110 token_pos--;
112 token_buffer[token_pos] = '\0';
113 return pos;
116 unsigned short piece_from_pgn(char pgn_piece){
117 switch (pgn_piece){
118 case 'R':
119 return rook;
120 case 'N':
121 return knight;
122 case 'B':
123 return bishop;
124 case 'Q':
125 return queen;
126 case 'K':
127 return king;
129 return no_piece;
132 char pgn_from_piece(unsigned short piece, unsigned short color){
133 char pgn_piece = ' ';
134 switch (piece){
135 case pawn:
136 pgn_piece = 'P';
137 break;
138 case rook:
139 pgn_piece = 'R';
140 break;
141 case knight:
142 pgn_piece = 'N';
143 break;
144 case bishop:
145 pgn_piece = 'B';
146 break;
147 case queen:
148 pgn_piece = 'Q';
149 break;
150 case king:
151 pgn_piece = 'K';
152 break;
153 case no_piece:
154 pgn_piece = ' ';
155 break;
157 if (color == black && pgn_piece != ' '){
158 pgn_piece += 32;
160 return pgn_piece;
163 void pgn_to_coords(struct pgn_ply_node* ply){
164 unsigned short str_length = rb->strlen(ply->pgn_text);
165 char str[10];
166 rb->strcpy(str,ply->pgn_text);
167 ply->column_from = 0xFF;
168 ply->row_from = 0xFF;
169 ply->column_to = 0xFF;
170 ply->row_to = 0xFF;
171 ply->taken_piece = no_piece;
172 ply->promotion_piece = no_piece;
173 ply->enpassant = false;
174 ply->castle = false;
175 ply->promotion = false;
176 unsigned short i, j, piece;
177 bool found = false;
179 if (str_length >= 3 && (str[0] == 'O' || str[0] == '0') && str[1] == '-'
180 && (str[2] == 'O' || str[2] == '0')) {
181 /* castling */
182 ply->castle = true;
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;
191 } else {
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;
197 } else {
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;
204 } else {
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'){
212 /* pawns */
213 ply->column_from = str[0] - 'a';
214 if (str[1] == 'x'){
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;
224 } else {
225 ply->taken_piece = board[locn[ply->row_to][ply->column_to]];
227 } else {
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){
237 /* promotion */
238 if (str[2] == '='){
239 ply->promotion_piece = piece_from_pgn(str[3]);
240 } else {
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;
248 } else {
249 /* the other pieces */
250 piece = piece_from_pgn(str[0]);
251 if (str[2] == 'x'){
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';
258 } else {
259 ply->row_from = str[1] - '1';
261 } else if (str[1] == 'x') {
262 /* taken a piece */
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';
272 } else {
273 ply->row_from = str[1] - '1';
275 } else {
276 /* regular move */
277 ply->column_to = str[1] - 'a';
278 ply->row_to = str[2] - '1';
280 if (piece == knight) {
281 for (i=0;i<8;i++){
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){
294 for (i=0;i<4;i++){
295 j = 1;
296 found = false;
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]);
306 found = true;
308 break;
310 j++;
312 if (found) {
313 break;
317 if (piece == bishop || ((piece == queen || piece == king) && !found)){
318 for (i=0;i<4;i++){
319 j = 1;
320 found = false;
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]);
330 found = true;
332 break;
334 j++;
336 if (found) {
337 break;
343 /* leave a very complete log of the parsing of the game while it gets stable */
344 for (i=0;i<8;i++){
345 for (j=0;j<8;j++) {
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){
359 int pos = 0,i,j;
360 unsigned short moving_piece = board[locn[ply->row_from][ply->column_from]];
361 char unambiguous_position;
362 bool found = false;
363 char alg_move[5];
364 char move_buffer[10];
365 short move;
366 if (moving_piece == king){
367 /* check castling */
368 if (ply->column_from == 4 && ply->column_to == 6){
369 /* castling kingside */
370 rb->strcpy(ply->pgn_text,"O-O");
371 ply->castle = true;
372 } else if (ply->column_from == 4 && ply->column_to == 2){
373 /* castling queenside */
374 rb->strcpy(ply->pgn_text,"O-O-O");
375 } else {
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,
378 '1'+ply->row_to);
379 } else {
380 rb->snprintf(ply->pgn_text,10,"K%c%c",'a'+ply->column_to,
381 '1'+ply->row_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);
396 } else {
397 rb->snprintf(ply->pgn_text,10,"%cx%c%c", 'a'+ply->column_from,
398 'a'+ply->column_to,'1'+ply->row_to);
400 } else {
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,
406 '1'+ply->row_to);
407 } else {
408 rb->snprintf(ply->pgn_text,10,"%c%c", 'a'+ply->column_to,
409 '1'+ply->row_to);
412 } else {
413 /* verify ambiguous moves for the different kinds of pieces */
414 unambiguous_position = '\0';
415 if (moving_piece == knight){
416 for (i=0;i<8;i++){
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;
425 } else {
426 unambiguous_position = 'a' + ply->column_from;
428 break;
432 if (moving_piece == rook || moving_piece == queen){
433 found = false;
434 for (i=0;i<4;i++){
435 j=1;
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;
445 } else {
446 unambiguous_position = 'a' + ply->column_from;
448 found = true;
450 break;
452 j++;
454 if (found) {
455 break;
459 if (moving_piece == bishop || (moving_piece == queen && !found)){
460 for (i=0;i<4;i++){
461 j=1;
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;
471 } else {
472 unambiguous_position = 'a' + ply->column_from;
474 found = true;
476 break;
478 j++;
480 if (found) {
481 break;
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);
494 } else {
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);
499 } else {
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,
504 '1'+ply->row_to);
505 } else {
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,
509 '1'+ply->row_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){
534 int i;
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){
542 return 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);
548 return buffer;
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");
554 *line_length = 0;
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){
562 int fhandler;
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;
567 int line_count = 0;
568 bool header_start = true, game_start = false;
569 rb = api;
571 if ( (fhandler = rb->open(filename, O_RDONLY)) == 0 ) return NULL;
573 if (bufptr == NULL){
574 pl_malloc_init();
576 while (rb->read_line(fhandler, line_buffer, sizeof line_buffer) > 0){
577 line_count++;
578 /* looking for a game header */
579 if (header_start) {
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;
586 } else {
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;
593 game_count++;
594 header_start = false;
595 game_start = true;
597 } else {
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'){
603 if (game_start) {
604 game_start = false;
605 } else {
606 header_start = true;
608 } else {
609 if (curr_node->pgn_line == 0) {
610 curr_node->pgn_line = line_count;
615 rb->close(fhandler);
616 return first_game;
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;
622 int button;
623 struct gui_synclist games_list;
624 int i;
625 struct pgn_game_node *temp_node = first_game;
627 rb=api;
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);
641 while (true) {
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)){
647 continue;
649 switch (button) {
650 case ACTION_STD_OK:
651 temp_node = first_game;
652 for (i=0;i<curr_selection && temp_node != NULL;i++){
653 temp_node = temp_node->next_node;
655 return temp_node;
656 break;
657 case ACTION_STD_CANCEL:
658 return NULL;
659 break;
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;
668 int fhandler, i;
669 char line_buffer[128];
670 char token_buffer[10];
671 unsigned short pos;
672 unsigned short curr_player = white;
673 rb = api;
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'){
688 break;
690 pos = 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;
705 } else {
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;
731 rb->close(fhandler);
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;
739 rb = api;
741 if (bufptr == NULL){
742 pl_malloc_init();
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,"????.??.??");
760 } else {
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,"*");
765 game->pgn_line = 0;
766 game->first_ply = ply;
767 game->next_node = NULL;
769 return game;
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;
776 rb = api;
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';
784 ply->castle = false;
785 ply->promotion = false;
786 ply->enpassant = false;
787 ply->promotion_piece = no_piece;
788 ply->taken_piece = no_piece;
789 ply->draw = false;
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;
800 } else {
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,
807 bool is_mate){
809 rb = api;
811 struct pgn_ply_node *ply;
812 for(ply=game->first_ply;ply->next_node != NULL;ply=ply->next_node);
813 if (is_mate){
814 ply->prev_node->checkmate = true;
815 } else {
816 ply->prev_node->draw = true;
820 void pgn_store_game(const struct plugin_api* api, struct pgn_game_node* game){
821 int fhandler;
822 struct pgn_ply_node *ply;
823 unsigned ply_count;
824 size_t line_length=0;
825 char buffer[10];
827 rb = api;
829 GNUChess_Initialize();
831 ply_count=0;
832 ply=game->first_ply;
833 while (ply->next_node!=NULL){
834 coords_to_pgn(ply);
835 if (ply->checkmate){
836 if (ply->player == white){
837 rb->strcpy(game->result,"1-0");
838 } else {
839 rb->strcpy(game->result,"0-1");
842 if (ply->draw){
843 rb->strcpy(game->result,"1/2-1/2");
845 ply=ply->next_node;
846 ply_count++;
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 */
878 if (ply->checkmate){
879 if (ply->player == white){
880 write_pgn_token(fhandler,"1-0",&line_length);
881 } else {
882 write_pgn_token(fhandler,"0-1",&line_length);
884 break;
885 } else if (ply->draw){
886 write_pgn_token(fhandler,"1/2-1/2",&line_length);
887 break;
888 } else if (ply->next_node->player == neutral) {
889 /* unknown end of the game */
890 write_pgn_token(fhandler,"*",&line_length);
891 break;
895 /* leave a blank line between the tag section and the game section */
896 rb->fdprintf(fhandler,"\n\n");
898 rb->close(fhandler);