Rev 66 | Rev 69 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 66 | Rev 68 | ||
|---|---|---|---|
| Line 12... | Line 12... | ||
| 12 |       { \ | 
            12 |       { \ | 
          
| 13 |          (dest)[char_index] = 0; \ | 
            13 |          (dest)[char_index] = 0; \ | 
          
| 14 |          break; \ | 
            14 |          break; \ | 
          
| 15 |       } \ | 
            15 |       } \ | 
          
| 16 | } | 
            16 | } | 
          
| - | 17 | ||
| - | 18 | ||
| - | 19 | // global variables used in this module only | 
          |
| - | 20 | static char *pgnfile_data = NULL; // mallocated  | 
          |
| - | 21 | static size_t pgnfile_size = 0;  | 
          |
| 17 | 22 | ||
| 18 | 23 | ||
| 19 | // prototypes of local functions | 
            24 | // prototypes of local functions | 
          
| 20 | static void PGNFile_GameList_Init (int entry_count);  | 
            25 | static void PGNFile_GameList_Init (int entry_count);  | 
          
| 21 | static void PGNFile_GameList_Shutdown (void);  | 
            26 | static void PGNFile_GameList_Shutdown (void);  | 
          
| Line 25... | Line 30... | ||
| 25 | bool PGNFile_Load (const wchar_t *pgnfile_pathname)  | 
            30 | bool PGNFile_Load (const wchar_t *pgnfile_pathname)  | 
          
| 26 | { | 
            31 | { | 
          
| 27 |    // this function loads a PGN file and builds the game databases of the games described in this file | 
            32 |    // this function loads a PGN file and builds the game databases of the games described in this file | 
          
| 28 | 33 | ||
| 29 | char line_buffer[256]; // PGN files have 256 chars max per line by design  | 
            34 | char line_buffer[256]; // PGN files have 256 chars max per line by design  | 
          
| 30 | char *file_data; // mallocated  | 
            - | |
| 31 | char *buffer;  | 
            35 | char *buffer;  | 
          
| 32 | int file_size;  | 
            - | |
| 33 | int file_index;  | 
            36 | int file_index;  | 
          
| 34 | int char_index;  | 
            37 | int char_index;  | 
          
| 35 | int entry_count;  | 
            38 | int entry_count;  | 
          
| 36 | FILE *fp;  | 
            39 | FILE *fp;  | 
          
| 37 | size_t converted_count; // used by the STRING_TO_CHAR macro  | 
            40 | size_t converted_count; // used by the STRING_TO_CHAR macro  | 
          
| Line 41... | Line 44... | ||
| 41 | if (fp == NULL)  | 
            44 | if (fp == NULL)  | 
          
| 42 | return (false); // on error, cancel  | 
            45 | return (false); // on error, cancel  | 
          
| 43 | 46 | ||
| 44 |    // get file length | 
            47 |    // get file length | 
          
| 45 | fseek (fp, 0, SEEK_END);  | 
            48 | fseek (fp, 0, SEEK_END);  | 
          
| 46 | 
  | 
            49 | pgnfile_size = ftell (fp);  | 
          
| 47 | fseek (fp, 0, SEEK_SET);  | 
            50 | fseek (fp, 0, SEEK_SET);  | 
          
| 48 | 51 | ||
| 49 |    // mallocate space for it and read it all at once | 
            52 |    // mallocate space for it and read it all at once | 
          
| 50 | 
  | 
            53 | pgnfile_data = (char *) SAFE_realloc (pgnfile_data, 0, pgnfile_size, sizeof (char), false);  | 
          
| 51 | fread (  | 
            54 | fread (pgnfile_data, pgnfile_size, 1, fp);  | 
          
| 52 | fclose (fp); // we no longer need the file, so close it  | 
            55 | fclose (fp); // we no longer need the file, so close it  | 
          
| 53 | 56 | ||
| 54 |    // now the file is fully loaded in memory | 
            57 |    // now the file is fully loaded in memory | 
          
| 55 | 58 | ||
| 56 |    // read line per line and count the number of games | 
            59 |    // read line per line and count the number of games | 
          
| 57 | buffer =  | 
            60 | buffer = pgnfile_data;  | 
          
| 58 | entry_count = 0;  | 
            61 | entry_count = 0;  | 
          
| 59 | while ((buffer = sgets (line_buffer, sizeof (line_buffer), buffer)) != NULL)  | 
            62 | while ((buffer = sgets (line_buffer, sizeof (line_buffer), buffer)) != NULL)  | 
          
| 60 | if (strncmp (line_buffer, "[Event \"", 8) == 0)  | 
            63 | if (strncmp (line_buffer, "[Event \"", 8) == 0)  | 
          
| 61 | entry_count++; // we know now one game more  | 
            64 | entry_count++; // we know now one game more  | 
          
| 62 | 65 | ||
| 63 |    // now prepare the games database for "entry_count" games | 
            66 |    // now prepare the games database for "entry_count" games | 
          
| 64 | PGNFile_GameList_Init (entry_count);  | 
            67 | PGNFile_GameList_Init (entry_count);  | 
          
| 65 | 68 | ||
| 66 |    // read line per line | 
            69 |    // read line per line | 
          
| 67 | buffer =  | 
            70 | buffer = pgnfile_data;  | 
          
| 68 | entry_count = 0;  | 
            71 | entry_count = 0;  | 
          
| 69 | file_index = 0;  | 
            72 | file_index = 0;  | 
          
| 70 | while ((buffer = sgets (line_buffer, sizeof (line_buffer), buffer)) != NULL)  | 
            73 | while ((buffer = sgets (line_buffer, sizeof (line_buffer), buffer)) != NULL)  | 
          
| 71 |    { | 
            74 |    { | 
          
| 72 |       // is it a new game ? | 
            75 |       // is it a new game ? | 
          
| Line 108... | Line 111... | ||
| 108 |          // else is it the beginning of a game ? | 
            111 |          // else is it the beginning of a game ? | 
          
| 109 | else if (strncmp (line_buffer, "1.", 2) == 0)  | 
            112 | else if (strncmp (line_buffer, "1.", 2) == 0)  | 
          
| 110 | games[entry_count - 1].gamedata_start = file_index; // remember where this game starts  | 
            113 | games[entry_count - 1].gamedata_start = file_index; // remember where this game starts  | 
          
| 111 |       } | 
            114 |       } | 
          
| 112 | 115 | ||
| 113 | file_index = buffer -  | 
            116 | file_index = buffer - pgnfile_data; // save current file pointer index  | 
          
| 114 |    } | 
            117 |    } | 
          
| 115 | - | ||
| 116 |    // we no longer need the file data space, so free it | 
            - | |
| 117 | SAFE_free ((void **) &file_data);  | 
            - | |
| 118 | 118 | ||
| 119 | return (true); // finished, return TRUE  | 
            119 | return (true); // finished, return TRUE  | 
          
| 120 | } | 
            120 | } | 
          
| 121 | 121 | ||
| 122 | 122 | ||
| Line 125... | Line 125... | ||
| 125 |    // this function loads and parses a game data in a PGN file. If the selected game is NULL, it means that | 
            125 |    // this function loads and parses a game data in a PGN file. If the selected game is NULL, it means that | 
          
| 126 |    // the user didn't want to chose any game at all, so just free the games list and return a success value. | 
            126 |    // the user didn't want to chose any game at all, so just free the games list and return a success value. | 
          
| 127 | 127 | ||
| 128 | static wchar_t pgn_comment[65536]; // declared static so as not to reallocate it  | 
            128 | static wchar_t pgn_comment[65536]; // declared static so as not to reallocate it  | 
          
| 129 | 129 | ||
| 130 | char *file_data; // mallocated  | 
            - | |
| 131 |    boardmove_t new_move; | 
            130 |    boardmove_t new_move; | 
          
| 132 | int file_size;  | 
            - | |
| 133 | int length;  | 
            131 | int length;  | 
          
| 134 | int char_index;  | 
            132 | int char_index;  | 
          
| 135 | int fieldstart;  | 
            133 | int fieldstart;  | 
          
| 136 | int fieldstop;  | 
            134 | int fieldstop;  | 
          
| 137 | int variation_depth;  | 
            135 | int variation_depth;  | 
          
| 138 | char movenumber_string[8];  | 
            136 | char movenumber_string[8];  | 
          
| 139 | FILE *fp;  | 
            - | |
| 140 | 137 | ||
| 141 |    // did we chose NO game ? | 
            138 |    // did we chose NO game ? | 
          
| 142 | if (game == NULL)  | 
            139 | if (game == NULL)  | 
          
| 143 |    { | 
            140 |    { | 
          
| 144 | PGNFile_GameList_Shutdown (); // free the games list  | 
            141 | PGNFile_GameList_Shutdown (); // free the games list  | 
          
| - | 142 | SAFE_free ((void **) &pgnfile_data); // free the file data space  | 
          |
| 145 | return (true); // return success as there's nothing to load  | 
            143 | return (true); // return success as there's nothing to load  | 
          
| 146 |    } | 
            144 |    } | 
          
| 147 | - | ||
| 148 |    // try to open file for reading in BINARY mode so as NOT to convert end of lines | 
            - | |
| 149 | _wfopen_s (&fp, pgnfile_pathname, L"rb");  | 
            - | |
| 150 | if (fp == NULL)  | 
            - | |
| 151 |    { | 
            - | |
| 152 | PGNFile_GameList_Shutdown (); // free the games list  | 
            - | |
| 153 | return (false); // on error, cancel  | 
            - | |
| 154 |    } | 
            - | |
| 155 | - | ||
| 156 |    // get file length | 
            - | |
| 157 | fseek (fp, 0, SEEK_END);  | 
            - | |
| 158 | file_size = ftell (fp);  | 
            - | |
| 159 | fseek (fp, 0, SEEK_SET);  | 
            - | |
| 160 | - | ||
| 161 |    // mallocate space for it and read it all at once | 
            - | |
| 162 | file_data = (char *) SAFE_malloc (file_size, sizeof (char), false);  | 
            - | |
| 163 | fread (file_data, file_size, 1, fp);  | 
            - | |
| 164 | fclose (fp); // we no longer need the file, so close it  | 
            - | |
| 165 | - | ||
| 166 |    // now the file is fully loaded in memory | 
            - | |
| 167 | 145 | ||
| 168 |    // reset the board (but NOT the players, just their view angles) | 
            146 |    // reset the board (but NOT the players, just their view angles) | 
          
| 169 | Board_Reset (board, game->fen_str);  | 
            147 | Board_Reset (board, game->fen_str);  | 
          
| 170 | animation_endtime = current_time + 2.0f; // HACK: this sorta prevents the "load file" dialog box trailing clicks to be misinterpreted  | 
            148 | animation_endtime = current_time + 2.0f; // HACK: this sorta prevents the "load file" dialog box trailing clicks to be misinterpreted  | 
          
| 171 | 149 | ||
| Line 181... | Line 159... | ||
| 181 |    { | 
            159 |    { | 
          
| 182 |       // build the move number string | 
            160 |       // build the move number string | 
          
| 183 | sprintf_s (movenumber_string, sizeof (movenumber_string), "%d.", 1 + board->move_count / 2);  | 
            161 | sprintf_s (movenumber_string, sizeof (movenumber_string), "%d.", 1 + board->move_count / 2);  | 
          
| 184 | 162 | ||
| 185 |       // is it a space ? | 
            163 |       // is it a space ? | 
          
| 186 | if (isspace (  | 
            164 | if (isspace (pgnfile_data[char_index]))  | 
          
| 187 |       { | 
            165 |       { | 
          
| 188 | char_index++; // if so, skip it  | 
            166 | char_index++; // if so, skip it  | 
          
| 189 | continue; // and proceed to the next data  | 
            167 | continue; // and proceed to the next data  | 
          
| 190 |       } | 
            168 |       } | 
          
| 191 | 169 | ||
| 192 |       // else is what we're reading a move number ? | 
            170 |       // else is what we're reading a move number ? | 
          
| 193 | else if (strncmp (&  | 
            171 | else if (strncmp (&pgnfile_data[char_index], movenumber_string, strlen (movenumber_string)) == 0)  | 
          
| 194 |       { | 
            172 |       { | 
          
| 195 | char_index += strlen (movenumber_string); // if so, skip it  | 
            173 | char_index += strlen (movenumber_string); // if so, skip it  | 
          
| 196 | continue; // and proceed to the next data  | 
            174 | continue; // and proceed to the next data  | 
          
| 197 |       } | 
            175 |       } | 
          
| 198 | 176 | ||
| 199 |       // else is it a dot ? | 
            177 |       // else is it a dot ? | 
          
| 200 | else if (  | 
            178 | else if (pgnfile_data[char_index] == '.')  | 
          
| 201 |       { | 
            179 |       { | 
          
| 202 | char_index++; // if so, skip it  | 
            180 | char_index++; // if so, skip it  | 
          
| 203 | continue; // and proceed to the next data  | 
            181 | continue; // and proceed to the next data  | 
          
| 204 |       } | 
            182 |       } | 
          
| 205 | 183 | ||
| 206 |       // else is it an en passant notification ? | 
            184 |       // else is it an en passant notification ? | 
          
| 207 | else if (strncmp (&  | 
            185 | else if (strncmp (&pgnfile_data[char_index], "e.p.", 4) == 0)  | 
          
| 208 |       { | 
            186 |       { | 
          
| 209 | char_index += 4; // this notification is superfluous, skip it  | 
            187 | char_index += 4; // this notification is superfluous, skip it  | 
          
| 210 | continue; // and proceed to the next data  | 
            188 | continue; // and proceed to the next data  | 
          
| 211 |       } | 
            189 |       } | 
          
| 212 | 190 | ||
| 213 |       // else is it a comment ? | 
            191 |       // else is it a comment ? | 
          
| 214 | else if (  | 
            192 | else if (pgnfile_data[char_index] == '{')  | 
          
| 215 |       { | 
            193 |       { | 
          
| 216 | fieldstart = char_index + 1; // skip the leading brace  | 
            194 | fieldstart = char_index + 1; // skip the leading brace  | 
          
| 217 | 195 | ||
| 218 | while ((fieldstart <  | 
            196 | while ((fieldstart < (int) pgnfile_size) && isspace (pgnfile_data[fieldstart]))  | 
          
| 219 | fieldstart++; // skip any leading spaces  | 
            197 | fieldstart++; // skip any leading spaces  | 
          
| 220 | 198 | ||
| 221 |          // move through all the other characters... | 
            199 |          // move through all the other characters... | 
          
| 222 | for (fieldstop = fieldstart; fieldstop <  | 
            200 | for (fieldstop = fieldstart; fieldstop < (int) pgnfile_size; fieldstop++)  | 
          
| 223 | if (  | 
            201 | if (pgnfile_data[fieldstop] == '}')  | 
          
| 224 | break; // and stop at the first closing brace we find  | 
            202 | break; // and stop at the first closing brace we find  | 
          
| 225 | 203 | ||
| 226 | char_index = fieldstop + 1; // remember where to continue reading (that is, after the closing brace)  | 
            204 | char_index = fieldstop + 1; // remember where to continue reading (that is, after the closing brace)  | 
          
| 227 | 205 | ||
| 228 | while ((fieldstop > 0) && isspace (  | 
            206 | while ((fieldstop > 0) && isspace (pgnfile_data[fieldstop]))  | 
          
| 229 | fieldstop--; // chop off any trailing spaces  | 
            207 | fieldstop--; // chop off any trailing spaces  | 
          
| 230 | 208 | ||
| 231 | 
  | 
            209 | pgnfile_data[fieldstop] = 0; // break the string at this location  | 
          
| 232 | 210 | ||
| 233 |          // now copy out the commentary by appending it to the one we know already | 
            211 |          // now copy out the commentary by appending it to the one we know already | 
          
| 234 | if (pgn_comment[0] != 0)  | 
            212 | if (pgn_comment[0] != 0)  | 
          
| 235 | wcscat_s (pgn_comment, WCHAR_SIZEOF (pgn_comment), L" ");  | 
            213 | wcscat_s (pgn_comment, WCHAR_SIZEOF (pgn_comment), L" ");  | 
          
| 236 | length = wcslen (pgn_comment);  | 
            214 | length = wcslen (pgn_comment);  | 
          
| 237 | ConvertToWideChar (&pgn_comment[length], WCHAR_SIZEOF (pgn_comment) - length, &  | 
            215 | ConvertToWideChar (&pgn_comment[length], WCHAR_SIZEOF (pgn_comment) - length, &pgnfile_data[fieldstart]);  | 
          
| 238 | ConvertCRLFsToSingleSpaces (pgn_comment); // linearize string  | 
            216 | ConvertCRLFsToSingleSpaces (pgn_comment); // linearize string  | 
          
| 239 | 217 | ||
| 240 | continue; // and proceed to the next data  | 
            218 | continue; // and proceed to the next data  | 
          
| 241 |       } | 
            219 |       } | 
          
| 242 | 220 | ||
| 243 |       // else is it a numeric annotation glyph ? if so, just ignore it (FIXME: better support this) | 
            221 |       // else is it a numeric annotation glyph ? if so, just ignore it (FIXME: better support this) | 
          
| 244 | else if (  | 
            222 | else if (pgnfile_data[char_index] == '$')  | 
          
| 245 |       { | 
            223 |       { | 
          
| 246 | while ((char_index <  | 
            224 | while ((char_index < (int) pgnfile_size) && !isspace (pgnfile_data[char_index]))  | 
          
| 247 | char_index++; // figure out where it stops  | 
            225 | char_index++; // figure out where it stops  | 
          
| 248 | while ((char_index <  | 
            226 | while ((char_index < (int) pgnfile_size) && isspace (pgnfile_data[char_index]))  | 
          
| 249 | char_index++; // figure out where the next word starts  | 
            227 | char_index++; // figure out where the next word starts  | 
          
| 250 | 228 | ||
| 251 | continue; // and proceed to the next data  | 
            229 | continue; // and proceed to the next data  | 
          
| 252 |       } | 
            230 |       } | 
          
| 253 | 231 | ||
| 254 |       // else is it a variation ? if so, just ignore it (FIXME: better support this) | 
            232 |       // else is it a variation ? if so, just ignore it (FIXME: better support this) | 
          
| 255 | else if (  | 
            233 | else if (pgnfile_data[char_index] == '(')  | 
          
| 256 |       { | 
            234 |       { | 
          
| 257 | variation_depth = 1;  | 
            235 | variation_depth = 1;  | 
          
| 258 | while ((char_index <  | 
            236 | while ((char_index < (int) pgnfile_size) && (variation_depth != 0))  | 
          
| 259 |          { | 
            237 |          { | 
          
| 260 | char_index++; // move through file data and cope with nested variations  | 
            238 | char_index++; // move through file data and cope with nested variations  | 
          
| 261 | if (  | 
            239 | if (pgnfile_data[char_index] == '(') variation_depth++;  | 
          
| 262 | else if (  | 
            240 | else if (pgnfile_data[char_index] == ')') variation_depth--;  | 
          
| 263 |          } | 
            241 |          } | 
          
| 264 | char_index++; // skip the closing parenthese  | 
            242 | char_index++; // skip the closing parenthese  | 
          
| 265 | while ((char_index <  | 
            243 | while ((char_index < (int) pgnfile_size) && isspace (pgnfile_data[char_index]))  | 
          
| 266 | char_index++; // figure out where the next word starts  | 
            244 | char_index++; // figure out where the next word starts  | 
          
| 267 | 245 | ||
| 268 | continue; // and proceed to the next data  | 
            246 | continue; // and proceed to the next data  | 
          
| 269 |       } | 
            247 |       } | 
          
| 270 | 248 | ||
| 271 |       // else is it a game result ? | 
            249 |       // else is it a game result ? | 
          
| 272 | else if ((strncmp (&  | 
            250 | else if ((strncmp (&pgnfile_data[char_index], "1/2-1/2", 7) == 0)  | 
          
| 273 | || (strncmp (&  | 
            251 | || (strncmp (&pgnfile_data[char_index], "1-0", 3) == 0)  | 
          
| 274 | || (strncmp (&  | 
            252 | || (strncmp (&pgnfile_data[char_index], "0-1", 3) == 0)  | 
          
| 275 | || (  | 
            253 | || (pgnfile_data[char_index] == '*'))  | 
          
| 276 |       { | 
            254 |       { | 
          
| 277 |          // if there's a move pending, validate it | 
            255 |          // if there's a move pending, validate it | 
          
| 278 | if ((new_move.source[0] != -1) && (new_move.source[1] != -1) && (new_move.target[0] != -1) && (new_move.target[1] != -1))  | 
            256 | if ((new_move.source[0] != -1) && (new_move.source[1] != -1) && (new_move.target[0] != -1) && (new_move.target[1] != -1))  | 
          
| 279 |          { | 
            257 |          { | 
          
| 280 | Board_AppendMove (board, new_move.source[0], new_move.source[1], new_move.target[0], new_move.target[1], new_move.promotion_type, pgn_comment); // save move  | 
            258 | Board_AppendMove (board, new_move.source[0], new_move.source[1], new_move.target[0], new_move.target[1], new_move.promotion_type, pgn_comment); // save move  | 
          
| Line 307... | Line 285... | ||
| 307 | new_move.promotion_type = 0;  | 
            285 | new_move.promotion_type = 0;  | 
          
| 308 | pgn_comment[0] = 0; // reset comment  | 
            286 | pgn_comment[0] = 0; // reset comment  | 
          
| 309 |          } | 
            287 |          } | 
          
| 310 | 288 | ||
| 311 |          // convert the move string data to wide char | 
            289 |          // convert the move string data to wide char | 
          
| 312 | ConvertToWideChar (new_move.pgntext, WCHAR_SIZEOF (new_move.pgntext), &  | 
            290 | ConvertToWideChar (new_move.pgntext, WCHAR_SIZEOF (new_move.pgntext), &pgnfile_data[char_index]);  | 
          
| 313 | 291 | ||
| 314 |          // evaluate the string in Standard Algebraic Notation and find the source, destination, part type and promotion | 
            292 |          // evaluate the string in Standard Algebraic Notation and find the source, destination, part type and promotion | 
          
| 315 | if (!Move_SetupFromSAN (&board->moves[board->move_count - 1], &new_move, Board_ColorToMove (board)))  | 
            293 | if (!Move_SetupFromSAN (&board->moves[board->move_count - 1], &new_move, Board_ColorToMove (board)))  | 
          
| 316 |          { | 
            294 |          { | 
          
| 317 | PGNFile_GameList_Shutdown (); // free the games list  | 
            295 | PGNFile_GameList_Shutdown (); // free the games list  | 
          
| 318 | SAFE_free ((void **) &  | 
            296 | SAFE_free ((void **) &pgnfile_data); // free the file data space  | 
          
| 319 | return (false); // on error, cancel  | 
            297 | return (false); // on error, cancel  | 
          
| 320 |          } | 
            298 |          } | 
          
| 321 | 299 | ||
| 322 |          // find where it stops | 
            300 |          // find where it stops | 
          
| 323 | while ((char_index <  | 
            301 | while ((char_index < (int) pgnfile_size) && !isspace (pgnfile_data[char_index]))  | 
          
| 324 | char_index++; // reach the next space  | 
            302 | char_index++; // reach the next space  | 
          
| 325 | 303 | ||
| 326 | char_index++; // remember where to continue reading (that is, after the next space)  | 
            304 | char_index++; // remember where to continue reading (that is, after the next space)  | 
          
| 327 | continue; // and proceed to the next data  | 
            305 | continue; // and proceed to the next data  | 
          
| 328 |       } | 
            306 |       } | 
          
| Line 334... | Line 312... | ||
| 334 | 312 | ||
| 335 |    // we loaded the game we want, we no longer need the games array | 
            313 |    // we loaded the game we want, we no longer need the games array | 
          
| 336 | PGNFile_GameList_Shutdown ();  | 
            314 | PGNFile_GameList_Shutdown ();  | 
          
| 337 | 315 | ||
| 338 |    // we no longer need the file data space, so free it | 
            316 |    // we no longer need the file data space, so free it | 
          
| 339 | SAFE_free ((void **) &  | 
            317 | SAFE_free ((void **) &pgnfile_data);  | 
          
| 340 | 318 | ||
| 341 | return (true); // game loaded successfully, return TRUE  | 
            319 | return (true); // game loaded successfully, return TRUE  | 
          
| 342 | } | 
            320 | } | 
          
| 343 | 321 | ||
| 344 | 322 | ||