universe@50: /* universe@50: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@50: * universe@50: * Copyright 2014 Mike Becker. All rights reserved. universe@50: * universe@50: * Redistribution and use in source and binary forms, with or without universe@50: * modification, are permitted provided that the following conditions are met: universe@50: * universe@50: * 1. Redistributions of source code must retain the above copyright universe@50: * notice, this list of conditions and the following disclaimer. universe@50: * universe@50: * 2. Redistributions in binary form must reproduce the above copyright universe@50: * notice, this list of conditions and the following disclaimer in the universe@50: * documentation and/or other materials provided with the distribution. universe@50: * universe@50: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@50: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@50: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@50: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@50: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@50: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@50: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@50: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@50: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@50: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@50: * POSSIBILITY OF SUCH DAMAGE. universe@50: * universe@50: */ universe@50: universe@50: #include "pgn.h" universe@50: #include universe@50: #include universe@50: #include universe@50: universe@50: int read_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { universe@50: int c, i; universe@50: universe@50: char result[8]; universe@50: universe@50: char tagkey[32]; universe@50: char tagvalue[128]; universe@50: universe@50: // read tag pairs universe@50: _Bool readmoves = 0; universe@50: while (!readmoves) { universe@50: while (isspace(c = fgetc(stream))); universe@50: if (c == '1') { universe@50: readmoves = 1; universe@50: break; universe@50: } universe@50: if (c != '[') { universe@50: return EXIT_FAILURE; universe@50: } universe@50: while (isspace(c = fgetc(stream))); universe@50: i = 0; universe@50: do { universe@50: tagkey[i++] = c; universe@50: } while (!isspace(c = fgetc(stream))); universe@50: tagkey[i] = '\0'; universe@50: while (isspace(c = fgetc(stream))); universe@50: if (c != '"') { universe@50: return EXIT_FAILURE; universe@50: } universe@50: i = 0; universe@50: while ((c = fgetc(stream)) != '"') { universe@50: if (c == '\n') { universe@50: return EXIT_FAILURE; universe@50: } universe@50: tagvalue[i++] = c; universe@50: } universe@50: tagvalue[i] = '\0'; universe@50: if (fgetc(stream) != ']') { universe@50: return EXIT_FAILURE; universe@50: } universe@50: universe@50: if (strcmp("Result", tagkey) == 0) { universe@50: memcpy(result, tagvalue, 8); universe@50: } universe@50: } universe@50: universe@50: // read moves universe@50: if (fgetc(stream) != '.') { universe@50: return EXIT_FAILURE; universe@50: } universe@50: universe@50: char movestr[10]; universe@50: Move move; universe@50: uint8_t curcol = WHITE; universe@50: universe@50: while (readmoves) { universe@50: // move universe@50: while (isspace(c = fgetc(stream))); universe@50: i = 0; universe@50: do { universe@50: movestr[i++] = c; universe@50: if (i >= 10) { universe@50: return EXIT_FAILURE; universe@50: } universe@50: } while (!isspace(c = fgetc(stream))); universe@50: movestr[i] = '\0'; universe@50: if (eval_move(gamestate, movestr, &move, curcol) universe@50: != VALID_MOVE_SYNTAX) { universe@50: return EXIT_FAILURE; universe@50: } universe@50: if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { universe@50: return EXIT_FAILURE; universe@50: } universe@50: apply_move(gamestate, &move); universe@50: universe@50: // TODO: parse comments universe@50: while (isspace(c = fgetc(stream))); universe@50: universe@50: // end of game data encountered universe@50: if (c == EOF) { universe@50: break; universe@50: } universe@50: if (c == '1' || c == '0') { universe@50: c = fgetc(stream); universe@50: if (c == '-') { universe@50: gamestate->resign = !gamestate->checkmate; universe@50: break; universe@50: } else if (c == '/') { universe@50: gamestate->remis = !gamestate->stalemate; universe@50: break; universe@50: } else { universe@50: // oops, it was a move number, go back! universe@50: fseek(stream, -1, SEEK_CUR); universe@50: } universe@50: } universe@50: universe@50: // we have eaten the next valuable byte, so go back universe@50: fseek(stream, -1, SEEK_CUR); universe@50: universe@50: // skip move number after black move universe@50: if (curcol == BLACK) { universe@50: while (isdigit(c = fgetc(stream))); universe@50: if (c != '.') { universe@50: return EXIT_FAILURE; universe@50: } universe@50: } universe@50: curcol = opponent_color(curcol); universe@50: } universe@50: universe@50: return EXIT_SUCCESS; universe@50: } universe@50: universe@50: size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { universe@50: // TODO: tag pairs universe@50: size_t bytes = 0; universe@50: universe@50: // Result universe@50: char *result; universe@50: if (gamestate->stalemate || gamestate->remis) { universe@50: result = "1/2-1/2"; universe@50: } else if (gamestate->checkmate || gamestate->resign) { universe@50: if (gamestate->lastmove) { universe@50: result = (gamestate->lastmove->move.piece & COLOR_MASK) == WHITE ? universe@50: "1-0" : "0-1"; universe@50: } else { universe@50: result = "0-1"; universe@50: } universe@50: } else { universe@50: result = "*"; universe@50: } universe@50: fprintf(stream, "[Result \"%s\"]\n\n", result); universe@50: universe@50: // moves universe@50: int i = 1; universe@50: for (MoveList *movelist = gamestate->movelist ; universe@50: movelist ; movelist = movelist->next) { universe@50: universe@50: if (++i % 2 == 0) { universe@50: fprintf(stream, "%d. %s", i/2, movelist->move.string); universe@50: } else { universe@50: fprintf(stream, " %s", movelist->move.string); universe@50: } universe@50: universe@50: // TODO: move time and maybe other comments universe@50: universe@50: // line break every 10 moves universe@50: if (i % 20) { universe@50: fputc(' ', stream); universe@50: } else { universe@50: fputc('\n', stream); universe@50: } universe@50: } universe@50: universe@50: if (result[0] == '*') { universe@50: fputc('\n', stream); universe@50: } else { universe@50: fprintf(stream, "%s\n", result); universe@50: } universe@50: universe@50: universe@50: return bytes; universe@50: }