blob: 9e9656207be67a25966b6258c02b6eb6441860a9 [file] [log] [blame]
Dave Chapmanbc6c62b2007-08-19 18:24:17 +00001/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2007 Mauricio Peccorini
11*
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012* 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.
Dave Chapmanbc6c62b2007-08-19 18:24:17 +000016*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
Dave Chapmanbc6c62b2007-08-19 18:24:17 +000022#include "plugin.h"
Jens Arnold106ac752008-03-22 14:20:04 +000023#include "chessbox_pgn.h"
Dave Chapmanbc6c62b2007-08-19 18:24:17 +000024
Nils Wallménius5ccf1802007-09-02 10:11:46 +000025#define PGN_FILE PLUGIN_GAMES_DIR "/chessbox.pgn"
26#define LOG_FILE PLUGIN_GAMES_DIR "/chessbox.log"
Dave Chapmanbc6c62b2007-08-19 18:24:17 +000027int loghandler;
28
Steve Bavin65265772008-05-13 09:57:56 +000029const struct plugin_api* rb;
Dave Chapmanbc6c62b2007-08-19 18:24:17 +000030
31short kn_offs[8][2] = {{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
32short rk_offs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
33short bp_offs[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
34
35/* global vars for pl_malloc() */
36void *bufptr = NULL;
37ssize_t bufleft;
38
39/* simple function to "allocate" memory in pluginbuffer.
40 * (borrowed from dict.c)
41 */
42void *pl_malloc(ssize_t size)
43{
44 void *ptr;
45 ptr = bufptr;
46
47 if (bufleft < size)
48 {
49 return NULL;
50 }
51 else
52 {
53 bufptr += size;
54 return ptr;
55 }
56}
57
58/* init function for pl_malloc() */
59void pl_malloc_init(void)
60{
61 bufptr = rb->plugin_get_buffer((size_t *)&bufleft);
62}
63
64void 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++;
71 }
72 tag_type[pos] = '\0';
73 pos+=3;
74 while (buffer[pos] != '"'){
75 tag_value[pos2] = buffer[pos];
76 pos++; pos2++;
77 }
78
79 /* truncate tag values that are too large */
80 if (pos2 > 19){
81 pos2 = 19;
82 }
83 tag_value[pos2] = '\0';
84
85 if (rb->strcmp(tag_type,"White") == 0){
86 rb->strcpy(game->white_player, tag_value);
87 }
88 if (rb->strcmp(tag_type,"Black") == 0){
89 rb->strcpy(game->black_player, tag_value);
90 }
91 if (rb->strcmp(tag_type,"Result") == 0){
92 rb->strcpy(game->result, tag_value);
93 }
94 if (rb->strcmp(tag_type,"Date") == 0){
95 rb->strcpy(game->game_date, tag_value);
96 }
97}
98
99unsigned 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--;
111 }
112 token_buffer[token_pos] = '\0';
113 return pos;
114}
115
116unsigned 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;
128 }
129 return no_piece;
130}
131
132char 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;
156 }
157 if (color == black && pgn_piece != ' '){
158 pgn_piece += 32;
159 }
160 return pgn_piece;
161}
162
163void 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;
178
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;
196 }
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;
209 }
210 }
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]];
226 }
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';
231 }
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);
235 }
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]);
242 }
243 /* change the piece in the original position and wait
244 * for the code at the end to move it
245 */
246 board[locn[ply->row_from][ply->column_from]] = ply->promotion_piece;
247 }
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';
260 }
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';
274 }
275 } else {
276 /* regular move */
277 ply->column_to = str[1] - 'a';
278 ply->row_to = str[2] - '1';
279 }
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];
290 }
291 }
292 }
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;
307 }
308 break;
309 }
310 j++;
311 }
312 if (found) {
313 break;
314 }
315 }
316 }
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;
331 }
332 break;
333 }
334 j++;
335 }
336 if (found) {
337 break;
338 }
339 }
340 }
341 }
342
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]]));
347 }
348 rb->fdprintf(loghandler,"\n");
349 }
350
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;
356}
357
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000358void 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);
382 }
383 }
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;
389 }
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);
399 }
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);
410 }
411 }
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;
427 }
428 break;
429 }
430 }
431 }
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;
447 }
448 found = true;
449 }
450 break;
451 }
452 j++;
453 }
454 if (found) {
455 break;
456 }
457 }
458 }
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;
473 }
474 found = true;
475 }
476 break;
477 }
478 j++;
479 }
480 if (found) {
481 break;
482 }
483 }
484 }
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
488 */
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);
498 }
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);
510 }
511 }
512 }
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
518 */
519 VerifyMove (ply->player, alg_move , 0 , &move, move_buffer );
520
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++;
529 }
530}
531
Nils Wallménius68489612008-04-09 15:25:17 +0000532char * get_game_text(int selected_item, void *data,
533 char *buffer, size_t buffer_len){
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000534 int i;
535 struct pgn_game_node *temp_node = (struct pgn_game_node *)data;
536 char text_buffer[50];
537
538 for (i=0;i<selected_item && temp_node != NULL;i++){
539 temp_node = temp_node->next_node;
540 }
541 if (temp_node == NULL){
542 return NULL;
543 }
544 rb->snprintf(text_buffer, 50,"%s vs. %s (%s)", temp_node->white_player,
545 temp_node->black_player, temp_node->game_date);
546
Nils Wallménius68489612008-04-09 15:25:17 +0000547 rb->strncpy(buffer, text_buffer, buffer_len);
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000548 return buffer;
549}
550
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000551void 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;
555 }
556 rb->fdprintf(fhandler,"%s ",buffer);
557 *line_length += (rb->strlen(buffer) + 1);
558}
559
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000560/* ---- api functions ---- */
Steve Bavin65265772008-05-13 09:57:56 +0000561struct pgn_game_node* pgn_list_games(const struct plugin_api* api,const char* filename){
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000562 int fhandler;
563 char line_buffer[128];
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000564 struct pgn_game_node size_node, *first_game = NULL;
565 struct pgn_game_node *curr_node = NULL, *temp_node;
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000566 unsigned short game_count = 1;
567 int line_count = 0;
568 bool header_start = true, game_start = false;
569 rb = api;
570
571 if ( (fhandler = rb->open(filename, O_RDONLY)) == 0 ) return NULL;
572
573 if (bufptr == NULL){
574 pl_malloc_init();
575 }
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;
589 }
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;
596 }
597 } else {
598 if (line_buffer[0] == '['){
599 process_tag(curr_node, line_buffer);
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000600 } else if (line_buffer[0] == '\r'
601 || line_buffer[0] == '\n'
602 || line_buffer[0] == '\0'){
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000603 if (game_start) {
604 game_start = false;
605 } else {
606 header_start = true;
607 }
608 } else {
609 if (curr_node->pgn_line == 0) {
610 curr_node->pgn_line = line_count;
611 }
612 }
613 }
614 }
615 rb->close(fhandler);
616 return first_game;
617}
618
Steve Bavin65265772008-05-13 09:57:56 +0000619struct pgn_game_node* pgn_show_game_list(const struct plugin_api* api,
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000620 struct pgn_game_node* first_game){
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000621 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;
626
627 rb=api;
628
629 for (i=0;temp_node != NULL;i++){
630 temp_node = temp_node->next_node;
631 }
632
633
Jonathan Gordon5ca15392008-03-26 03:35:24 +0000634 rb->gui_synclist_init(&games_list, &get_game_text, first_game, false, 1, NULL);
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000635 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);
640
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);
Jonathan Gordoncf1cef52007-09-17 10:08:50 +0000646 if (rb->gui_synclist_do_button(&games_list,&button,LIST_WRAP_OFF)){
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000647 continue;
648 }
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;
654 }
655 return temp_node;
656 break;
657 case ACTION_STD_CANCEL:
658 return NULL;
659 break;
660 }
661 }
662}
663
Steve Bavin65265772008-05-13 09:57:56 +0000664void pgn_parse_game(const struct plugin_api* api, const char* filename,
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000665 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;
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000668 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;
674
675 fhandler = rb->open(filename, O_RDONLY);
676
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);
680 }
681
682 loghandler = rb->open(LOG_FILE, O_WRONLY | O_CREAT);
683
684 GNUChess_Initialize();
685
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;
689 }
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;
709 }
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000710 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);
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000715 }
716 }
717 }
718
719 rb->close(loghandler);
720
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000721 /* additional dummy ply to represent end of file without
722 *loosing the previous node's pointer
723 */
Dave Chapmanbc6c62b2007-08-19 18:24:17 +0000724 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;
729 }
730 selected_game->first_ply = first_ply;
731 rb->close(fhandler);
732}
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000733
Steve Bavin65265772008-05-13 09:57:56 +0000734struct pgn_game_node* pgn_init_game(const struct plugin_api* api){
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000735 struct pgn_game_node game_size, *game;
736 struct pgn_ply_node ply_size, *ply;
737 struct tm *current_time;
738
739 rb = api;
740
741 if (bufptr == NULL){
742 pl_malloc_init();
743 }
744
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;
751
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);
763 }
764 rb->strcpy(game->result,"*");
765 game->pgn_line = 0;
766 game->first_ply = ply;
767 game->next_node = NULL;
768
769 return game;
770}
771
Steve Bavin65265772008-05-13 09:57:56 +0000772void pgn_append_ply(const struct plugin_api* api, struct pgn_game_node* game,
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000773 unsigned short ply_player, char *move_buffer, bool is_mate){
774 struct pgn_ply_node ply_size, *ply, *temp;
775
776 rb = api;
777
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;
791
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);
794
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;
802 }
803 temp->prev_node = ply;
804}
805
Steve Bavin65265772008-05-13 09:57:56 +0000806void pgn_set_result(const struct plugin_api* api, struct pgn_game_node* game,
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000807 bool is_mate){
808
809 rb = api;
810
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;
817 }
818}
819
Steve Bavin65265772008-05-13 09:57:56 +0000820void pgn_store_game(const struct plugin_api* api, struct pgn_game_node* game){
Nils Wallménius5ccf1802007-09-02 10:11:46 +0000821 int fhandler;
822 struct pgn_ply_node *ply;
823 unsigned ply_count;
824 size_t line_length=0;
825 char buffer[10];
826
827 rb = api;
828
829 GNUChess_Initialize();
830
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");
840 }
841 }
842 if (ply->draw){
843 rb->strcpy(game->result,"1/2-1/2");
844 }
845 ply=ply->next_node;
846 ply_count++;
847 }
848
849 fhandler = rb->open(PGN_FILE, O_WRONLY|O_CREAT|O_APPEND);
850
851
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
854 */
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);
863
864 /* leave a blank line between the tag section and the game section */
865 rb->fdprintf(fhandler,"\n");
866
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);
874 }
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);
883 }
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;
892 }
893 }
894
895 /* leave a blank line between the tag section and the game section */
896 rb->fdprintf(fhandler,"\n\n");
897
898 rb->close(fhandler);
899}