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 |