Scid  4.6.5
pgnparse.cpp
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////
2 //
3 // FILE: pgnparse.cpp
4 // PgnParser class methods
5 //
6 // Part of: Scid (Shane's Chess Information Database)
7 // Version: 3.5
8 //
9 // Notice: Copyright (c) 2001-2003 Shane Hudson. All rights reserved.
10 // Copyright (C) 2015 Fulvio Benini
11 //
12 //////////////////////////////////////////////////////////////////////
13 
14 
15 #include "pgnparse.h"
16 #include "game.h"
17 #include <stdio.h>
18 
19 #if defined(_MSC_VER) && _MSC_VER <= 1800
20  #define snprintf _snprintf
21 #endif
22 
23 namespace {
24 
25 const uint MAX_COMMENT_SIZE = 16000;
26 
27 // ADDCHAR() macro: Adds one character to a buffer and increments the
28 // buffer pointer.
29 //
30 #define ADDCHAR(buf,ch) *(buf) = (ch); (buf)++; *(buf) = 0
31 
32 /**
33  * charIsSpace() - Checks whether @c is a white-space character.
34  *
35  * Return false for non-breaking spaces (ASCII-160 or A0 hex) because
36  * they can be part of a multi-byte utf-8 character.
37  */
38 bool charIsSpace (unsigned char c) {
39  return (c == ' ' ||
40  c == '\t' ||
41  c == '\n' ||
42  c == '\v' ||
43  c == '\f' ||
44  c == '\r');
45 }
46 
47 /**
48  * Convert a string form ISO 8859/1 (Latin1) to UTF-8.
49  * The PGN standard use a subset of ISO 8859/1 (Latin 1):
50  * Code value from 0 to 126 are the standard ASCII character set
51  * Code value from 127 to 191 are not used for PGN data representation.
52  * Code value from 192 to 255 are mostly alphabetic printing characters with
53  * various diacritical marks; their use is encouraged for those languages
54  * that require such characters.
55  * Latin1 chars must be converted because the Tcl/tk framework uses UTF-8.
56  * @param s: the string to be converted.
57  * @returns @e true is the string was modified.
58  */
59 bool pgnLatin1_to_UTF8(std::string& s) {
60  bool res = false;
61  for (std::string::iterator it = s.begin(); it != s.end(); ++it) {
62  unsigned char v = *it;
63  if (v >= 192) {
64  std::string::iterator next = it + 1;
65  if (next != s.end()) {
66  unsigned char nextCh = *next;
67  if ((nextCh >> 6) == 0x02)
68  continue;
69  }
70  // Not a valid UTF-8 sequence: assume it's a Latin1 char and
71  // convert it.
72  res = true;
73  it = s.insert(it, char(0xC3));
74  *++it = v & 0xBF;
75  }
76  }
77  return res;
78 }
79 
80 } // end of anonymous namespace.
81 
82 void
83 PgnParser::Reset()
84 {
85  UnGetCount = 0;
86  NumErrors = 0;
87  BytesSeen = 0;
88  LineCounter = 0;
89  GameCounter = 0;
90  StorePreGameText = true;
91  EndOfInputWarnings = true;
92  ResultWarnings = true;
93  NumIgnoredTags = 0;
94 }
95 
96 void
97 PgnParser::Reset (MFile * infile)
98 {
99  Reset();
100  InFile = infile;
101  InBuffer = InCurrent = NULL;
102  EndChar = EOF;
103 }
104 
105 void
106 PgnParser::Init (const char * inbuffer)
107 {
108  Reset();
109  InFile = NULL;
110  InBuffer = InCurrent = inbuffer;
111  EndChar = 0;
112 }
113 
114 void
115 PgnParser::Reset (const char * inbuffer)
116 {
117  Reset();
118  InFile = NULL;
119  InBuffer = InCurrent = inbuffer;
120  EndChar = 0;
121 }
122 
123 int
124 PgnParser::GetChar ()
125 {
126  int ch = 0;
127  BytesSeen++;
128  if (UnGetCount > 0) {
129  UnGetCount--;
130  ch = UnGetCh[UnGetCount];
131  } else if (InFile != NULL) {
132  ch = InFile->ReadOneByte();
133  } else {
134  ch = *InCurrent;
135  if (ch != 0) { InCurrent++; }
136  }
137  if (ch == '\n') { LineCounter++; }
138  return ch;
139 }
140 
141 void
142 PgnParser::UnGetChar (int ch)
143 {
144  if (UnGetCount == MAX_UNGETCHARS) { return; }
145  UnGetCh[UnGetCount] = ch;
146  UnGetCount++;
147  BytesSeen--;
148  if (ch == '\n') { LineCounter--; }
149 }
150 
151 void
152 PgnParser::AddIgnoredTag (const char * tag)
153 {
154  if (NumIgnoredTags >= MAX_IGNORED_TAGS) { return; }
155  if (tag == NULL || tag[0] == 0) { return; }
156  IgnoredTags [NumIgnoredTags] = strDuplicate (tag);
157  NumIgnoredTags++;
158 }
159 
160 void
162 {
163  for (uint i = 0; i < NumIgnoredTags; i++) {
164  delete[] IgnoredTags[i];
165  }
166  NumIgnoredTags = 0;
167 }
168 
169 bool
170 PgnParser::IsIgnoredTag (const char * tag)
171 {
172  for (uint i = 0; i < NumIgnoredTags; i++) {
173  if (strEqual (tag, IgnoredTags[i])) { return true; }
174  }
175  return false;
176 }
177 
178 void
179 PgnParser::LogError (const char * errMessage, const char * text)
180 {
181  NumErrors++;
182  ErrorBuffer += "(game " + to_string(GameCounter);
183  ErrorBuffer += ", line " + to_string(LineCounter) + ") ";
184  ErrorBuffer += errMessage;
185  ErrorBuffer += text;;
186  ErrorBuffer += "\n";
187 }
188 
189 void
190 PgnParser::GetLine (char * buffer, uint bufSize)
191 {
192  ASSERT (bufSize > 0);
193  while (true) {
194  int ch = GetChar();
195  if (ch == EndChar || ch == 10) {
196  break;
197  }
198  if (ch == 13) {
199  // Handle ascii-13 followed by ascii-10 as a single newline:
200  ch = GetChar();
201  if (ch != 10) {
202  UnGetChar (ch);
203  }
204  break;
205  }
206  bufSize--;
207  if (bufSize == 0) {
208  break;
209  }
210  *buffer++ = ch;
211  }
212  *buffer = 0;
213  return;
214 }
215 
216 
217 
218 // If STANDARD_PLAYER_NAMES is defined, then player names are
219 // processed with a simple algorithm before adding to the name base,
220 // to reduce multiple instances of the same player. First, the
221 // number of spaces after a comma is made consistent (the default
222 // is one space; see NUM_SPACES_AFTER_COMMA). Second, a dot (".") at
223 // the END of the name string is removed.
224 
225 #define STANDARD_PLAYER_NAMES
226 
227 
228 // NUM_SPACES_AFTER_COMMA: number of spaces to follow every comma in
229 // a player name, when standardising. Commom values are 0 and 1.
230 
231 #define NUM_SPACES_AFTER_COMMA 1
232 
233 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
234 // standardDutchName(): standardises various combinations of upper
235 // and lower case "v" and "d" in the common Dutch name
236 // prefixes "van der", "van de" and "van den" to a capital
237 // V and small d, for consistency to avoid multiple names.
238 //
239 static void
240 standardDutchName (char * s)
241 {
242  if (*s != 'v' && *s != 'V') { return; }
243  if (strIsPrefix ("van ", s)) { s[0] = 'V'; }
244  if (strIsPrefix ("Van Der ", s) ||
245  strIsPrefix ("Van Den ", s) ||
246  strIsPrefix ("Van De ", s))
247  {
248  s[4] = 'd';
249  }
250 }
251 
252 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
253 // standardPlayerName(): standardises player names to reduce the number
254 // of different names referring to the same player. Algorithm:
255 // (1) The number of spaces after a comma is standardised, to
256 // NUM_SPACES_AFTER_COMMA spaces.
257 // (2) Spaces and dots are removed from the END of the name.
258 // So "Anand,V" "Anand, V" and "Anand, V." would all become "Anand, V"
259 //
260 static void
261 standardPlayerName (char * source)
262 {
263  char tempName [1024];
264  strCopy (tempName, source);
265  char * from = tempName;
266  char * to = source;
267  int afterComma = 0;
268  bool startSpaces = true;
269 
270  while (*from) {
271  if (*from == ',') {
272  *to++ = ',';
273  afterComma = 1;
274  startSpaces = false;
275  } else if (*from == ' ') {
276  if (!afterComma && !startSpaces) {
277  // Not after a ',' or at start of name, so add the space:
278  *to++ = ' ';
279  }
280  } else { // any other character:
281  startSpaces = false;
282  if (afterComma) {
283  // First, insert spaces after the comma:
285  while (x > 0) { *to++ = ' '; x--; }
286  }
287  *to = *from;
288  to++;
289  afterComma = 0;
290  }
291  from++;
292  }
293  *to = 0;
294 
295  // Now trim any trailing spaces, tabs :
296  strTrimRight (source, " \t");
297 
298  // Now standardise the capital letters of Dutch/etc prefix names:
299  standardDutchName(source);
300  return;
301 }
302 
303 errorT
304 PgnParser::ExtractPgnTag (const char * buffer, Game * game)
305 {
306  const uint maxTagLength = 255;
307  char tag [255];
308  char value [512];
309 
310  // Skip any initial whitespace:
311  while (charIsSpace(*buffer) && *buffer != 0) { buffer++; }
312  // Skip the '[' character and any whitespace:
313  ASSERT (*buffer == '[');
314  buffer++;
315  while (charIsSpace(*buffer) && *buffer != 0) { buffer++; }
316 
317  // Now at the start of the tag name:
318  uint length = 0;
319  while (!charIsSpace(*buffer) && *buffer != 0) {
320  tag[length] = *buffer++;
321  length++;
322  if (length == maxTagLength) { return ERROR_PGNTag; }
323  }
324  if (*buffer == 0) { return ERROR_PGNTag; }
325  tag[length] = 0;
326 
327  // Find the start of the tag value:
328  while (*buffer != '"' && *buffer != 0) { buffer++; }
329  if (*buffer != '"') { return ERROR_PGNTag; }
330  buffer++;
331  // Find the end of the tag value: it is the last double-quote (")
332  // on this line.
333  length = 0;
334  uint lastQuoteIndex = 0;
335  bool seenEndQuote = false;
336  while (*buffer != 0) {
337  if (*buffer == '"') {
338  lastQuoteIndex = length;
339  seenEndQuote = true;
340  }
341  value[length] = *buffer;
342  buffer++;
343  length++;
344  if (length == maxTagLength) { return ERROR_PGNTag; }
345  }
346  if (! seenEndQuote) { return ERROR_PGNTag; }
347  value[lastQuoteIndex] = 0;
348 
349  std::string tmpUTF8(value, value + lastQuoteIndex);
350  if (pgnLatin1_to_UTF8(tmpUTF8)) {
351  ASSERT(tmpUTF8.length() < sizeof(value));
352  std::copy(tmpUTF8.begin(), tmpUTF8.end(), value);
353  value[tmpUTF8.length()] = 0;
354  }
355 
356  // Now decide what to add to the game based on this tag:
357  if (strEqual (tag, "White")) {
358 #ifdef STANDARD_PLAYER_NAMES
359  standardPlayerName (value);
360 #endif
361  // Check for a rating in parentheses at the end of the player name:
362  uint len = strLength (value);
363  if (len > 7 && value[len-1] == ')'
364  && isdigit(static_cast<unsigned char>(value[len-2]))
365  && isdigit(static_cast<unsigned char>(value[len-3]))
366  && isdigit(static_cast<unsigned char>(value[len-4]))
367  && isdigit(static_cast<unsigned char>(value[len-5]))
368  && value[len-6] == '(' && value[len-7] == ' ') {
369  uint elo = strGetUnsigned (&(value[len-5]));
370  if (elo <= MAX_ELO) {
371  value[len - 7] = 0;
372  game->SetWhiteElo(elo);
374  }
375  }
376  game->SetWhiteStr (value);
377 
378  } else if (strEqual (tag, "Black")) {
379 #ifdef STANDARD_PLAYER_NAMES
380  standardPlayerName (value);
381 #endif
382  // Check for a rating in parentheses at the end of the player name:
383  uint len = strLength (value);
384  if (len > 7 && value[len-1] == ')'
385  && isdigit(static_cast<unsigned char>(value[len-2]))
386  && isdigit(static_cast<unsigned char>(value[len-3]))
387  && isdigit(static_cast<unsigned char>(value[len-4]))
388  && isdigit(static_cast<unsigned char>(value[len-5]))
389  && value[len-6] == '(' && value[len-7] == ' ') {
390  uint elo = strGetUnsigned (&(value[len-5]));
391  if (elo <= MAX_ELO) {
392  value[len - 7] = 0;
393  game->SetBlackElo(elo);
395  }
396  }
397  game->SetBlackStr (value);
398 
399  } else if (strEqual (tag, "Event")) {
400  game->SetEventStr (value);
401 
402  } else if (strEqual (tag, "Site")) {
403  game->SetSiteStr (value);
404 
405  } else if (strEqual (tag, "Round")) {
406  game->SetRoundStr (value);
407 
408  } else if (strEqual (tag, "Result")) {
409  if (strIsPrefix ("0-1", value)) {
410  game->SetResult (RESULT_Black);
411  } else if (strIsPrefix ("1-0", value)) {
412  game->SetResult (RESULT_White);
413  } else if (strIsPrefix ("1/2", value)) {
414  game->SetResult (RESULT_Draw);
415  } else {
416  game->SetResult (RESULT_None);
417  }
418 
419  } else if (strEqual (tag, "Date")) {
420  game->SetDate (date_EncodeFromString (value));
421 
422  } else if (strEqual (tag, "EventDate")) {
423  game->SetEventDate (date_EncodeFromString (value));
424 
425  } else if (strEqual (tag, "ECO")) {
426  game->SetEco (eco_FromString (value));
427 
428  } else if (strEqual (tag, "ScidFlags")) {
429  game->SetScidFlags (value);
430 
431  } else if (strEqual (tag, "FEN")) {
432  if (game->SetStartFen (value) != OK) {
433  LogError ("Error: Invalid FEN: ", value);
434  return ERROR_InvalidFEN;
435  }
436 
437  } else {
438  // Look for Rating Types: only the first Rating type found for
439  // each player is added as the rating. Any extra ratings are
440  // just added as normal tags.
441 
442  bool isRatingType = false;
443 
444  if (strIsPrefix ("White", tag) && game->GetWhiteElo() == 0) {
445  char * tagSuffix = tag + 5;
446  uint i = 0;
447  while (ratingTypeNames[i] != NULL) {
448  if (strEqual (tagSuffix, ratingTypeNames[i])) {
449  uint elo = strGetUnsigned (value);
450  if (elo > MAX_ELO) {
451  LogError ("Warning: rating too large: ", value);
452  elo = MAX_ELO;
453  }
454  game->SetWhiteElo (elo);
455  game->SetWhiteRatingType (i);
456  isRatingType = true;
457  break;
458  }
459  i++;
460  }
461  }
462  if (strIsPrefix ("Black", tag) && game->GetBlackElo() == 0) {
463  char * tagSuffix = tag + 5;
464  uint i = 0;
465  while (ratingTypeNames[i] != NULL) {
466  if (strEqual (tagSuffix, ratingTypeNames[i])) {
467  uint elo = strGetUnsigned (value);
468  if (elo > MAX_ELO) {
469  LogError ("Warning: rating too large: ", value);
470  elo = MAX_ELO;
471  }
472  game->SetBlackElo (elo);
473  game->SetBlackRatingType (i);
474  isRatingType = true;
475  break;
476  }
477  i++;
478  }
479  }
480 
481  if (! isRatingType && ! IsIgnoredTag (tag)) {
482  game->AddPgnTag (tag, value);
483  }
484  }
485  return OK;
486 }
487 
488 bool
489 PgnParser::EndOfInput()
490 {
491  if (InFile != NULL) { return InFile->EndOfFile(); }
492  int ch = GetChar();
493  if (ch == EndChar) { return true; }
494  UnGetChar (ch);
495  return false;
496 }
497 
498 // Modifies the parameter string in-place, trimming all
499 // whitespace at the start and end of the string, and reducing
500 // all other sequences of whitespace to a single space.
501 //
502 // Example: "\t\n A \t\n B C " (where \t and \n are tabs
503 // and newlines) becomes "A B C".
504 std::string PgnParser::GetComment()
505 {
506  std::string res;
507 
508  int ch = GetChar();
509  for (; ch != EndChar && ch != '}'; ch = GetChar()) {
510  if (charIsSpace(ch)) {
511  if (res.length() == 0) continue;
512  if (*(res.rbegin()) == ' ') continue;
513  ch = ' ';
514  }
515  res += ch;
516  }
517  if (res.length() != 0 && *(res.rbegin()) == ' ') {
518  res.resize(res.length() -1);
519  }
520 
521  if (ch == EndChar) {
522  char tempStr[80];
523  sprintf (tempStr, "started on line %u\n", LineCounter);
524  LogError ("Error: Open Comment at end of input", tempStr);
525  }
526 
527  return res;
528 }
529 
530 void
531 PgnParser::GetRestOfSuffix (char * buffer, char firstChar)
532 {
533  if (firstChar == '!' || firstChar == '?') {
534  int ch = GetChar();
535  // Only get successive ! or ? characters, so a complex
536  // annotation like "!!+-" can be parsed as two separate
537  // entities, "!!" and "+-":
538  while (ch == '!' || ch == '?') {
539  *buffer++ = ch;
540  ch = GetChar();
541  }
542  UnGetChar (ch);
543  *buffer = 0;
544  } else {
545  // Some other Suffix like "+/-" so just get rest of word:
546  GetRestOfWord_NoDots (buffer);
547  }
548 }
549 
550 void
551 PgnParser::GetRestOfWord_NoDots (char * buffer)
552 {
553  int ch = GetChar();
554  while (!charIsSpace (ch) && ch != '.' && ch != ')' && ch != EndChar) {
555  *buffer++ = ch;
556  ch = GetChar();
557  }
558  UnGetChar (ch);
559  *buffer = 0;
560 }
561 
562 void
563 PgnParser::GetRestOfWord_WithDots (char * buffer)
564 {
565  int ch = GetChar();
566  while (!charIsSpace (ch) && ch != ')' && ch != EndChar) {
567  *buffer++ = ch;
568  ch = GetChar();
569  }
570  UnGetChar (ch);
571  *buffer = 0;
572 }
573 
574 void
575 PgnParser::GetRestOfWord_Letters (char * buffer)
576 {
577  int ch = GetChar();
578  while (isalpha(ch)) {
579  *buffer++ = ch;
580  ch = GetChar();
581  }
582  UnGetChar (ch);
583  *buffer = 0;
584 }
585 
586 tokenT
587 PgnParser::GetRestOfCastling (char * buffer)
588 {
589  int ch;
590  int numOhsSeen = 1;
591  while (true) {
592  ch = GetChar();
593  if (ch == 'O' || ch == 'o' || ch == '0') {
594  numOhsSeen++;
595  ADDCHAR (buffer, ch);
596  continue;
597  }
598  if (ch == '-') {
599  // Check for "-+" or "-/+" after the move:
600  int nextCh = GetChar();
601  UnGetChar (nextCh);
602  if (nextCh == '+' || nextCh == '/') {
603  // Seen "-+" or "-/+", e.g. "O-O-+"
604  UnGetChar (ch);
605  break;
606  }
607  ADDCHAR (buffer, ch);
608  continue;
609  }
610  if (charIsSpace(ch) || ch == '+' || ch == '#' || ch == '=' ||
611  ch == '!' || ch == '?' || ch == ')' || ch == EndChar) {
612  UnGetChar (ch);
613  switch (numOhsSeen) {
614  case 2: return TOKEN_Move_Castle_King;
615  case 3: return TOKEN_Move_Castle_Queen;
616  default: return TOKEN_Invalid;
617  }
618  }
619  break;
620  }
621  // If we reach here, it is not a valid castling move:
622  GetRestOfWord_WithDots (buffer);
623  return TOKEN_Invalid;
624 }
625 
626 tokenT
627 PgnParser::GetRestOfMove (char * buffer)
628 {
629  int moveLength = 1;
630  int ch;
631  while (true) {
632  ch = GetChar();
633  if (charIsSpace(ch)) {
634  UnGetChar (ch);
635  return (moveLength == 1 ? TOKEN_Suffix : TOKEN_Move_Piece);
636  }
637  if ((ch >= '1' && ch <= '8') || (ch >= 'a' && ch <= 'h')) {
638  ADDCHAR (buffer, ch);
639  moveLength++;
640  continue;
641  }
642  if (ch == '-') {
643  // Check for "-+" or "-/+" after the move:
644  int nextCh = GetChar();
645  UnGetChar (nextCh);
646  if (nextCh == '+' || nextCh == '/') {
647  // Seen "-+" or "-/+", e.g. "Bb5-+"
648  UnGetChar (ch);
649  break;
650  }
651  // Otherwise, just ignore "-" in a move:
652  moveLength++;
653  continue;
654  }
655  if (ch == 'x' || ch == ':') {
656  // We allow ":" as a capture as well as "x".
657  moveLength++;
658  continue;
659  }
660  if (ch == ')' || ch == '+' || ch == '!' || ch == '=' ||
661  ch == '?' || ch == '#' || ch == EndChar) {
662  // Put c back into the infile buffer for next token.
663  UnGetChar (ch);
664  return (moveLength == 1 ? TOKEN_Suffix : TOKEN_Move_Piece);
665  }
666  break;
667  }
668 
669  // If we get here, it is an invalid Move character:
670  return TOKEN_Invalid;
671 }
672 
673 
674 tokenT
675 PgnParser::GetRestOfPawnMove (char * buffer)
676 {
677  int ch;
678  bool seenDigit = false;
679 
680  // allows for using lowercase 'b' for bishop promotion
681  // eg. OliThink uses a7b8b for FEN "1q6/P6k/8/5N1K/8/8/8/8 w - - 0 1"
682  bool pawn2seen = false;
683 
684  // First, check for "ep" or "e.p." on its own, not a move at all:
685  if (*(buffer-1) == 'e') {
686  ch = GetChar ();
687  UnGetChar (ch);
688  if (ch == 'p' || ch == '.') {
689  GetRestOfWord_WithDots (buffer);
690  return TOKEN_Ignore;
691  }
692  }
693 
694  while (true) {
695  ch = GetChar ();
696  if (charIsSpace (ch)) {
697  UnGetChar (ch);
698  return TOKEN_Move_Pawn;
699  }
700  // Check for "ep" or "e.p." after a digit:
701  if (seenDigit) {
702  if (ch == 'e') {
703  char nextCh = GetChar ();
704  UnGetChar (nextCh);
705  if (nextCh == 'p' || nextCh == 'p') { continue; }
706  }
707  if (ch == 'p' || ch == '.') { continue; }
708  }
709  if (ch >= '1' && ch <= '8') {
710  seenDigit = true;
711  ADDCHAR (buffer, ch);
712  continue;
713  }
714  if (ch >= 'a' && ch <= 'h' && !pawn2seen) {
715  pawn2seen = true;
716  ADDCHAR (buffer, ch);
717  continue;
718  }
719 
720  if (ch == '-') {
721  // Check for "-+" or "-/+" after the move:
722  int nextCh = GetChar();
723  UnGetChar (nextCh);
724  if (nextCh == '+' || nextCh == '/') {
725  // Seen "-+" or "-/+", e.g. "e4-+"
726  UnGetChar (ch);
727  return TOKEN_Move_Pawn;
728  }
729  // Otherwise, just ignore "-" in a move:
730  continue;
731  }
732  if (ch == 'x' || ch == ':') {
733  // Omit capture symbols, etc:
734  continue;
735  }
736  if (ch == '=') { // A promotion!
737  ch = GetChar();
738  // Convert "K" for promoted piece from King to Knight:
739  //if (ch == 'K') { ch = 'N'; }
740  if (ch == 'Q' || ch == 'R' || ch == 'B' || ch == 'N') {
741  ADDCHAR (buffer, '=');
742  ADDCHAR (buffer, ch);
743  return TOKEN_Move_Promote;
744  } else {
745  // OK, the "=" is NOT a promotion, but may be part of
746  // a symbol like "e4=" or "e4=+" so put it back:
747  UnGetChar (ch);
748  UnGetChar ('=');
749  return TOKEN_Move_Pawn;
750  }
751  }
752  // Convert "K" for promoted piece from King to Knight:
753  //if (ch == 'K') { ch = 'N'; }
754  if (ch == 'q' || ch == 'r' || ch == 'b' || ch == 'n') {
755  // Promotion with the "=" sign missing.
756  // Faile and Spike use lower case letters.. Will this break anything else ? S.A.
757  ADDCHAR (buffer, '=');
758  ADDCHAR (buffer, toupper(ch));
759  return TOKEN_Move_Promote;
760  }
761  if (ch == 'Q' || ch == 'R' || ch == 'B' || ch == 'N') {
762  // Promotion with the "=" sign missing. We insert it.
763  ADDCHAR (buffer, '=');
764  ADDCHAR (buffer, ch);
765  return TOKEN_Move_Promote;
766  }
767  if (ch == ')' || ch == '+' || ch == '!' ||
768  ch == '?' || ch == '#' || ch == EndChar) {
769  UnGetChar (ch);
770  return TOKEN_Move_Pawn;
771  }
772  break;
773  }
774  // If we reach here, it is an invalid move:
775  return TOKEN_Invalid;
776 }
777 
778 tokenT
779 PgnParser::GetGameToken (char * buffer, uint bufSize)
780 {
781  char * buf = buffer;
782  int ch = GetChar();
783  if (ch == EndChar) { return TOKEN_EndOfInput; }
784 
785  // Read past any whitespace, dots and newlines.
786  while ((charIsSpace(ch) || (ch == '.'))) {
787  ch = GetChar();
788  if (ch == EndChar) { return TOKEN_EndOfInput; }
789  }
790  ADDCHAR (buf, ch);
791 
792  // Now try to figure out what sort of token we have...
793 
794  if (isdigit(ch)) { // MoveNumber, or result, or invalid
795  int allDigits = 1; // Set to zero when a non-digit is found.
796  GetRestOfWord_NoDots (buf);
797  char *temp = buffer;
798  // Verify if token is all digits, or could be a result:
799  while (*temp) {
800  if (! isdigit(static_cast<unsigned char>(*temp))) {
801  allDigits = 0;
802  break;
803  }
804  temp++;
805  }
806  if (allDigits) { // Token was all digits.
807  // We should just return TOKEN_MoveNum now, unless we
808  // want to check for the ugly "00" and "000" (with zeroes) for
809  // castling. PGN input that bad doesn't deserve to get accepted!
810  return TOKEN_MoveNum;
811  }
812 
813  // Now we check each acceptable result string.
814  // Note that we also check for the awful "0-0" (castling with zeroes
815  // instead of big-Ohs) although it is BAD PGN input.
816 
817  if (*buffer == '0') { // token starts with '0'
818  if (strEqual (buffer, "0-1")) { return TOKEN_Result_Black; }
819  if (strEqual (buffer, "0:1")) { return TOKEN_Result_Black; }
820  if (strIsPrefix ("0-0-0", buffer)) { return TOKEN_Move_Castle_Queen; }
821  if (strIsPrefix ("000", buffer)) { return TOKEN_Move_Castle_Queen; }
822  if (strIsPrefix ("0-0", buffer)) { return TOKEN_Move_Castle_King; }
823  if (strIsPrefix ("00", buffer)) { return TOKEN_Move_Castle_King; }
824  } else if (*buffer == '1') { // token starts with '1'
825  if (strEqual (buffer, "1-0")) { return TOKEN_Result_White; }
826  if (strEqual (buffer, "1:0")) { return TOKEN_Result_White; }
827  if (strEqual (buffer, "1/2")) { return TOKEN_Result_Draw; }
828  if (strEqual (buffer, "1/2-1/2")) { return TOKEN_Result_Draw; }
829  if (strEqual (buffer, "1/2:1/2")) { return TOKEN_Result_Draw; }
830  }
831 
832  // If we get here, it must be invalid (Not a move number or a result)
833  return TOKEN_Invalid;
834  }
835 
836  // Now we check for Moves.
837 
838  if (ch >= 'a' && ch <= 'h') { // Pawn move.
839  return GetRestOfPawnMove (buf);
840  }
841  if (ch == 'P') {
842  // Treat "P..." as a pawn move, ignoring the initial "P":
843  buf = buffer;
844  ADDCHAR (buf, GetChar());
845  return GetRestOfPawnMove (buf);
846  }
847  if (ch == 'N' || ch == 'B' || ch == 'R' || ch == 'Q' || ch == 'K') {
848  return GetRestOfMove (buf);
849  }
850  if (ch == 'O' || ch == 'o') { //letter "O": must be Castling or invalid
851  return GetRestOfCastling (buf);
852  }
853 
854  // Check for null move:
855  if (ch == 'n') {
856  GetRestOfWord_Letters (buf);
857  if (strEqual (buffer, "null")) {
858  return TOKEN_Move_Null;
859  }
860  return TOKEN_Invalid;
861  }
862 
863  // Now we check for other tokens.......
864  if (ch == ';' || ch == '%') { // LineComment.
865  // "%" should only mark a comment if at the start of the line, but we allow it anywhere on a line.
866  // S.A - There's a bug here with the parser, but not sure if it's fixable:
867  // stray ';' before variations and/or comments SPLIT OVER MULTIPLE LINES cause chaos
868  GetLine (buf, bufSize-1);
869  return TOKEN_LineComment;
870  }
871  if (ch == '{') { // regular comment. We let caller read until a "}".
872  return TOKEN_Comment;
873  }
874 
875  if (ch == '}') { // Close-brace outside a comment. Should not happen.
876  return TOKEN_CommentEnd;
877  }
878 
879  if (ch == '(') { // variation. We let caller parse it out.
880  return TOKEN_VarStart;
881  }
882  if (ch == ')') { return TOKEN_VarEnd; }
883 
884  if (ch == '!' || ch == '?' || ch == '=' || ch == '-') { // Suffix
885  GetRestOfSuffix (buf, ch);
886  // Treat the sequence "--" as a null move:
887  if (strEqual (buffer, "--")) {
888  return TOKEN_Move_Null;
889  }
890  return TOKEN_Suffix;
891  }
892  if (ch == '$') { // NAG
893  GetRestOfWord_NoDots (buf);
894  return TOKEN_Nag;
895  }
896 
897  if (ch == '+' || ch == '#') { // Check or mate or invalid
898  tokenT t = (ch == '+' ? TOKEN_Check : TOKEN_Mate);
899  // Can be followed by: space, !, ? or $. So peek at next input char
900  char nextc = GetChar();
901  // If "+" is followed by another "+", treat it as a double-check:
902  if (ch == '+' && nextc == '+') { return t; }
903  UnGetChar (nextc);
904  if (charIsSpace(nextc) || nextc == '!' || nextc == '?' ||
905  nextc == '$' || nextc == ')' || nextc == EndChar) {
906  return t; // Token was a valid "+" or "#".
907  }
908  // If we get here, token looks invalid.
909  // It could be a suffix, e.g. "+=", so return as that:
910  GetRestOfSuffix (buf, ch);
911  return TOKEN_Suffix;
912  }
913 
914  if (ch == '*') { // "*" (Result). Must be followed by whitespace.
915  GetRestOfWord_NoDots (buf);
916  if (buf[0] != '\0') { // We have a word with more than just "*"
917  return TOKEN_Invalid;
918  }
919  return TOKEN_Result_Star;
920  }
921 
922  if (ch == '[') { // Tag! This shouldn't happen! But return TOKEN_Tag.
923  // Put the '[' back so it can be read as a tag of the next game:
924  UnGetChar (ch);
925  return TOKEN_Tag;
926  }
927 
928  if (ch == 'D') { // Diagram symbol:
929  GetRestOfWord_NoDots (buf);
930  return TOKEN_Nag;
931  }
932 
933  if (ch == '~') { // "Unclear" annotation symbol:
934  GetRestOfSuffix (buf, ch);
935  return TOKEN_Suffix;
936  }
937 
938  // Convert Z0 to Null Move (thanks to Marcin Kasperski)
939  if (ch == 'Z') { // Z0 - nullmove in CA notation
940  int nextCh = GetChar();
941  if (nextCh == '0')
942  return TOKEN_Move_Null;
943  UnGetChar(nextCh);
944  }
945 
946  // If we get down this far, the first character of our token is invalid.
947  // Probably a letter like C or z, or punctuation or nonprintable.
948 
949  GetRestOfWord_WithDots (buf);
950  // Any other null-move notations to be checked for here?
951  return TOKEN_Invalid;
952 }
953 
954 
955 static inline char *
956 firstNonBlank (char * s)
957 {
958  char *x = s;
959  while (*x) {
960  if (! charIsSpace(*x)) { return x; }
961  x++;
962  }
963  return x;
964 }
965 
966 tokenT
967 PgnParser::GetNextToken (char * buffer, uint bufSize)
968 {
969  if (ParseMode == PARSE_Header) {
970  if (EndOfInput()) { return TOKEN_EndOfInput; }
971 
972  // We want to read a while line, but first we need to
973  // peek at the first character of the line to see if
974  // we are past the tags and already at the moves.
975  // If this happens, it means there was no blank line
976  // between the tags and the moves which is not good PGN,
977  // but it is very common we need to accept it.
978 
979  char * buf = buffer;
980  int ch = GetChar();
981  ADDCHAR (buf,ch);
982  if (ch == EndChar) { return TOKEN_EndOfInput; }
983 
984  // Read past any whitespace, dots and newlines.
985  // but preserve them in buffer.
986  while ((charIsSpace(ch) || (ch == '.'))) {
987  ch = GetChar();
988  ADDCHAR (buf, ch);
989  if (ch == EndChar) { return TOKEN_EndOfInput; }
990  }
991 
992  if ((ch == '%')||(ch ==';')) {
993  GetLine (buf, bufSize-(buf-buffer));
994  return TOKEN_LineComment;
995  }
996 
997  if (ch == '[') {
998  GetLine (buf, bufSize-(buf-buffer));
999  return TOKEN_Tag;
1000  }
1001 
1002  if (ch == '\0') {
1003  return TOKEN_TagEnd;
1004  }
1005 
1006  // We've got the start of the moves.
1007  UnGetChar (ch);
1008  return TOKEN_TagEnd;
1009  } // End of Header Mode
1010 
1011  if (ParseMode == PARSE_Searching) {
1012  // Looking for first Header Tag of game. In this mode, nothing is
1013  // invalid. Lines without a PGN Header Tag are treated as a Line
1014  // Comment, even if they don't start with "%" or ";".
1015  if (EndOfInput()) { return TOKEN_EndOfInput; }
1016  GetLine (buffer, bufSize);
1017  char * s = firstNonBlank (buffer);
1018  if (*s == '[') { return TOKEN_Tag; }
1019  return TOKEN_LineComment;
1020  } // End of Searching mode.
1021 
1022  // If we reach here, we are in Game mode, the most complex.
1023  return GetGameToken (buffer, bufSize);
1024 }
1025 
1026 
1027 errorT
1029 {
1030  char * buffer = new char [MAX_COMMENT_SIZE];
1031  errorT err = ParseMoves (game, buffer, MAX_COMMENT_SIZE);
1032  delete[] buffer;
1033  return err;
1034 }
1035 
1036 
1037 errorT
1038 PgnParser::ParseMoves (Game * game, char * buffer, uint bufSize)
1039 {
1040  errorT err = OK;
1041  uint moveErrorCount = 0;
1042  const uint maxMoveErrorsPerGame = 1;
1043  uint commentErrorCount = 0;
1044  const uint maxCommentErrorsPerGame = 1;
1045  simpleMoveT sm;
1046  byte nag;
1047 
1048  // Uncomment next line to allow castling after King or Rook have moved:
1049  // game->GetCurrentPos()->SetStrictCastling (false);
1050  ParseMode = PARSE_Game;
1051  tokenT token = GetNextToken (buffer, bufSize);
1052  while (! TOKEN_isResult(token)) {
1053  switch (token) {
1054  case TOKEN_Move_Pawn:
1055  case TOKEN_Move_Promote:
1056  case TOKEN_Move_Piece:
1059  case TOKEN_Move_Null:
1060  err = game->GetCurrentPos()->ReadMove (&sm, buffer, token);
1061 
1062  // If king castling failed, maybe it's OO meaning castle queen-side
1063  if (err != OK && token == TOKEN_Move_Castle_King) {
1064  err = game->GetCurrentPos()->ReadMove (&sm, buffer, TOKEN_Move_Castle_Queen);
1065  }
1066 
1067  // The most common type of "illegal" move in standard
1068  // chess is castling when the king or rook have already
1069  // moved. So if a castling move failed, turn off
1070  // strict checking of castling rights and try again,
1071  // but still print a warning if that succeeded:
1072 
1073  if (err != OK && (token == TOKEN_Move_Castle_King ||
1074  token == TOKEN_Move_Castle_Queen)) {
1075  bool prevFlag = game->GetCurrentPos()->GetStrictCastling();
1076  game->GetCurrentPos()->SetStrictCastling (false);
1077  err = game->GetCurrentPos()->ReadMove (&sm, buffer, token);
1078  game->GetCurrentPos()->SetStrictCastling (prevFlag);
1079 
1080  // If no longer an error, castling without strict checking
1081  // worked, but still print a warning about it:
1082  if (err == OK) {
1083  char tempStr[500];
1084  snprintf (tempStr, sizeof(tempStr), "(%s) in game %s - %s, %u",
1085  buffer, game->GetWhiteStr(), game->GetBlackStr(),
1086  date_GetYear (game->GetDate()));
1087  LogError ("Warning: illegal castling ", tempStr);
1088  }
1089  }
1090 
1091  if (err == OK && moveErrorCount == 0) {
1092  err = game->AddMove (&sm, NULL);
1093  }
1094 
1095  // Report an error if the move could not be added:
1096  if (err != OK) {
1097  moveErrorCount++;
1098  if (moveErrorCount <= maxMoveErrorsPerGame) {
1099  char tempStr [500];
1100  // Add an error comment to the game:
1101  snprintf (tempStr, sizeof(tempStr), "Error reading move: %s", buffer);
1102  game->SetMoveComment (tempStr);
1103  snprintf (tempStr, sizeof(tempStr), "Error reading move in game %s - %s, %u: ",
1104  game->GetWhiteStr(), game->GetBlackStr(),
1105  date_GetYear (game->GetDate()));
1106  LogError (tempStr, buffer);
1107  }
1108  }
1109  break;
1110 
1111  case TOKEN_Ignore:
1112  case TOKEN_MoveNum:
1113  case TOKEN_Check:
1114  case TOKEN_Mate:
1115  break; // Move numbers, check and made symbols: just ignore.
1116 
1117  case TOKEN_Nag:
1118  nag = game_parseNag (buffer);
1119  if (moveErrorCount == 0) { game->AddNag (nag); }
1120  break;
1121 
1122  case TOKEN_Suffix:
1123  nag = game_parseNag (buffer);
1124  if (nag == 0) {
1125  LogError ("Warning: Invalid annotation symbol: ", buffer);
1126  } else {
1127  if (moveErrorCount == 0) {
1128  game->AddNag (nag);
1129  }
1130  }
1131  break;
1132 
1133  case TOKEN_VarStart:
1134  if (game->AddVariation() != OK) {
1135  LogError ("Error: Unable to add variation", "");
1136  return ERROR_Game;
1137  }
1138  break;
1139 
1140  case TOKEN_VarEnd:
1141  game->MoveExitVariation();
1142  game->MoveForward();
1143  break;
1144 
1145  case TOKEN_Comment: {
1146  std::string comment = GetComment();
1147  pgnLatin1_to_UTF8(comment);
1148  game->SetMoveComment(comment.c_str());
1149  } break;
1150 
1151  case TOKEN_LineComment:
1152  break; // Line comments inside a game are just ignored.
1153 
1154  case TOKEN_CommentEnd:
1155  if (commentErrorCount < maxCommentErrorsPerGame) {
1156  char tempStr [500];
1157  snprintf (tempStr, sizeof(tempStr), " in game %s - %s, %u: ",
1158  game->GetWhiteStr(), game->GetBlackStr(),
1159  date_GetYear (game->GetDate()));
1160  LogError ("Warning: \"}\" seen outside a comment", tempStr);
1161  commentErrorCount++;
1162  }
1163  break;
1164 
1165  case TOKEN_Tag:
1166  // This is often seen when missing TOKEN_Result
1167  LogError ("PGN header '[' seen inside game (result missing ?)", "");
1168  return ERROR_Game;
1169 
1170  case TOKEN_EndOfInput:
1171  if (EndOfInputWarnings) {
1172  LogError ("End of input reached in game (result missing ?)", "");
1173  return ERROR_Game;
1174  } else {
1175  return OK;
1176  }
1177 
1178  default:
1179  LogError ("Error: Unexpected symbol: ", buffer);
1180  }
1181 
1182  token = GetNextToken (buffer, bufSize);
1183  }
1184 
1185  // Now the token value is the game result:
1186  resultT r = RESULT_None;
1187  switch (token) {
1188  case TOKEN_Result_White: r = RESULT_White; break;
1189  case TOKEN_Result_Black: r = RESULT_Black; break;
1190  case TOKEN_Result_Draw: r = RESULT_Draw; break;
1191  default:
1192  r = RESULT_None;
1193  }
1194 
1195  // Verify the result matches that from the header:
1196  if (r != game->GetResult()) {
1197  // Use the end-of-game result instead of the header tag result:
1198  game->SetResult (r);
1199  if (ResultWarnings) {
1200  LogError ("Result did not match the header result", "");
1201  }
1202  }
1203  return OK;
1204 }
1205 
1206 
1207 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1208 // PgnParser::ParseGame():
1209 // Parses the next game from the input source.
1210 // Returns: OK if a game was found and free of fatal errors;
1211 // ERROR_NotFound if no game was found;
1212 // or some other appropriate error code upon error.
1213 //
1214 errorT
1216 {
1217  char * buffer = new char [MAX_COMMENT_SIZE];
1218  uint preGameTextLength = 0;
1219 
1220  char * preGameTextBuffer = new char [MAX_COMMENT_SIZE];
1221 
1222  GameCounter++;
1223  errorT err = ERROR_NotFound;
1224  ParseMode = PARSE_Searching;
1225  tokenT token = GetNextToken (buffer, MAX_COMMENT_SIZE);
1226  while (token != TOKEN_EndOfInput) {
1227  if (TOKEN_isTag (token)) {
1228  // Found a PGN Header tag, e.g. [Event "..."]
1229  if (ParseMode == PARSE_Searching) {
1230  // This is the first tag of a new game:
1231  game->Clear();
1232  if (StorePreGameText && preGameTextLength > 0
1233  && ! strIsAllWhitespace (preGameTextBuffer)) {
1234  // Remove last newline and store pre-game comment:
1235  preGameTextBuffer[preGameTextLength-1] = 0;
1236  game->SetMoveComment (preGameTextBuffer);
1237  }
1238  ParseMode = PARSE_Header;
1239  }
1240  if (ExtractPgnTag (buffer, game) != OK) {
1241  LogError ("Error reading tag: ", buffer);
1242  }
1243 
1244  } else if (token == TOKEN_LineComment) {
1245  static Position epd;
1246  if (epd.ReadFromFEN(buffer) == OK) {
1247  //EPD line
1248  game->Clear();
1249  game->SetStartFen(buffer);
1250  uint spaces = 0;
1251  const char* buffer_end = buffer + MAX_COMMENT_SIZE;
1252  for (const char* i = buffer; *i != 0 && i != buffer_end; i++) {
1253  if (*i == ' ') {
1254  spaces++;
1255  continue;
1256  }
1257  if (spaces >= 4) {
1258  game->SetMoveComment(i);
1259  break;
1260  }
1261  }
1262  ParseMode = PARSE_Game;
1263  err = OK;
1264  break;
1265  }
1266  // Add the line to the pre-game text if necessary:
1267  if (preGameTextLength > 0 || buffer[0] != 0) {
1268  uint len = strLength (buffer);
1269  if (preGameTextLength + len < MAX_COMMENT_SIZE) {
1270  strCopy (&(preGameTextBuffer[preGameTextLength]), buffer);
1271  preGameTextLength += len;
1272  preGameTextBuffer[preGameTextLength] = '\n';
1273  preGameTextLength++;
1274  }
1275  }
1276 
1277  } else if (token == TOKEN_Comment) {
1278  // Get, but ignore, this comment:
1279  GetComment();
1280 
1281  } else if (token == TOKEN_TagEnd) {
1282  // A blank line after the PGN header tags:
1283  ParseMode = PARSE_Game;
1284  err = ParseMoves (game, buffer, MAX_COMMENT_SIZE);
1285  break;
1286  } else {
1287  // Any other token is invalid here:
1288  LogError ("Warning: Invalid text in PGN game header: ", buffer);
1289  }
1290 
1291  token = GetNextToken (buffer, MAX_COMMENT_SIZE);
1292  }
1293  delete[] buffer;
1294  delete[] preGameTextBuffer;
1295 
1296  if (ParseMode == PARSE_Header) {
1297  if (EndOfInputWarnings) {
1298  LogError ("Warning: End of input in PGN header tags section", "");
1299  err = ERROR_Game;
1300  } else {
1301  err = OK;
1302  }
1303  }
1304  return err;
1305 }
1306 
1307 
1308 //////////////////////////////////////////////////////////////////////
1309 // EOF: pgnparse.cpp
1310 //////////////////////////////////////////////////////////////////////
const tokenT TOKEN_MoveNum
Definition: tokens.h:30
bool strIsAllWhitespace(const char *str)
Definition: misc.cpp:528
unsigned char byte
Definition: common.h:97
const tokenT TOKEN_Result_White
Definition: tokens.h:61
const tokenT TOKEN_Move_Pawn
Definition: tokens.h:34
uint date_GetYear(dateT date)
Definition: date.h:55
errorT AddMove(simpleMoveT *sm, char *san)
Definition: game.cpp:916
byte resultT
Definition: common.h:183
const tokenT TOKEN_Suffix
Definition: tokens.h:70
const tokenT TOKEN_Invalid
Definition: tokens.h:29
uint strLength(const char *str)
Definition: misc.h:492
void SetEventStr(const char *str)
Definition: game.h:424
Definition: mfile.h:52
#define TOKEN_isResult(x)
Definition: tokens.h:65
#define NUM_SPACES_AFTER_COMMA
Definition: pgnparse.cpp:231
bool strEqual(const char *str1, const char *str2)
Definition: misc.h:305
const char * GetBlackStr()
Definition: game.h:442
const errorT OK
Definition: error.h:23
void SetMoveComment(const char *comment)
Definition: game.cpp:779
bool strIsPrefix(const char *prefix, const char *longStr)
Definition: misc.h:412
void AddIgnoredTag(const char *tag)
Definition: pgnparse.cpp:152
void SetBlackRatingType(byte b)
Definition: game.h:437
char * strDuplicate(const char *original)
Definition: misc.cpp:239
errorT AddVariation()
Definition: game.cpp:958
errorT SetStartFen(const char *fenStr)
Definition: game.cpp:671
#define ASSERT(f)
Definition: common.h:67
const errorT ERROR_PGNTag
Definition: error.h:75
#define MAX_UNGETCHARS
Definition: pgnparse.h:32
void SetWhiteElo(eloT elo)
Definition: game.h:432
const char * ratingTypeNames[17]
Definition: game.cpp:116
void AddPgnTag(const char *tag, const char *value)
Definition: game.cpp:697
const resultT RESULT_Black
Definition: common.h:187
std::string to_string(int val)
Definition: misc.h:153
void SetEventDate(dateT date)
Definition: game.h:430
const tokenT TOKEN_CommentEnd
Definition: tokens.h:77
const tokenT TOKEN_VarStart
Definition: tokens.h:75
const tokenT TOKEN_Move_Promote
Definition: tokens.h:35
const tokenT TOKEN_Result_Star
Definition: tokens.h:64
const tokenT TOKEN_VarEnd
Definition: tokens.h:76
errorT ReadFromFEN(const char *s)
Definition: position.cpp:2812
const tokenT TOKEN_Nag
Definition: tokens.h:69
tokenT GetNextToken(char *buffer, uint bufSize)
Definition: pgnparse.cpp:967
void SetResult(resultT res)
Definition: game.h:431
const resultT RESULT_Draw
Definition: common.h:188
const tokenT TOKEN_Ignore
Definition: tokens.h:31
void SetWhiteStr(const char *str)
Definition: game.h:426
byte game_parseNag(const char *str)
Definition: game.cpp:186
const errorT ERROR_InvalidFEN
Definition: error.h:60
bool IsIgnoredTag(const char *tag)
Definition: pgnparse.cpp:170
errorT ParseMoves(Game *game)
Definition: pgnparse.cpp:1028
void SetStrictCastling(bool b)
Definition: position.h:206
eloT GetBlackElo()
Definition: game.h:448
const tokenT TOKEN_EndOfInput
Definition: tokens.h:28
const uint MAX_ELO
Definition: indexentry.h:36
void SetSiteStr(const char *str)
Definition: game.h:425
uint strTrimRight(char *target, const char *trimChars)
Definition: misc.h:508
const tokenT TOKEN_Move_Null
Definition: tokens.h:39
bool GetStrictCastling(void)
Definition: position.h:207
const tokenT TOKEN_Move_Castle_King
Definition: tokens.h:37
const resultT RESULT_White
Definition: common.h:186
void SetRoundStr(const char *str)
Definition: game.h:428
uint32_t uint
Definition: common.h:99
uint tokenT
Definition: tokens.h:23
#define ADDCHAR(buf, ch)
Definition: pgnparse.cpp:30
const tokenT TOKEN_Move_Piece
Definition: tokens.h:36
void SetBlackStr(const char *str)
Definition: game.h:427
void SetDate(dateT date)
Definition: game.h:429
const tokenT TOKEN_Result_Draw
Definition: tokens.h:63
errorT ReadMove(simpleMoveT *m, const char *s, tokenT t)
Definition: position.cpp:2317
const errorT ERROR_Game
Definition: error.h:65
#define TOKEN_isTag(x)
Definition: tokens.h:57
Position * GetCurrentPos()
Definition: game.h:348
void ClearIgnoredTags()
Definition: pgnparse.cpp:161
errorT MoveForward()
Definition: game.cpp:796
uint32_t strGetUnsigned(const char *str)
Definition: misc.h:276
unsigned short errorT
Definition: error.h:20
eloT GetWhiteElo()
Definition: game.h:447
int ReadOneByte()
Definition: mfile.h:143
const byte RATING_Elo
Definition: common.h:168
errorT MoveExitVariation()
Definition: game.cpp:888
Definition: game.h:227
void SetWhiteRatingType(byte b)
Definition: game.h:436
resultT GetResult()
Definition: game.h:446
const tokenT TOKEN_Comment
Definition: tokens.h:73
errorT AddNag(byte nag)
Definition: game.h:572
const tokenT TOKEN_LineComment
Definition: tokens.h:74
void strCopy(char *target, const char *original)
Definition: misc.h:379
ecoT eco_FromString(const char *ecoStr)
Definition: misc.cpp:69
const tokenT TOKEN_Tag
Definition: tokens.h:55
void SetScidFlags(const char *s)
Definition: game.h:342
const tokenT TOKEN_TagEnd
Definition: tokens.h:56
errorT ParseGame(Game *game)
Definition: pgnparse.cpp:1215
const char * GetWhiteStr()
Definition: game.h:441
dateT GetDate()
Definition: game.h:444
bool EndOfFile()
Definition: mfile.h:108
void Clear()
Definition: game.cpp:585
const tokenT TOKEN_Check
Definition: tokens.h:71
void SetEco(ecoT eco)
Definition: game.h:438
const resultT RESULT_None
Definition: common.h:185
void SetBlackElo(eloT elo)
Definition: game.h:433
const errorT ERROR_NotFound
Definition: error.h:50
const tokenT TOKEN_Result_Black
Definition: tokens.h:62
const tokenT TOKEN_Move_Castle_Queen
Definition: tokens.h:38
dateT date_EncodeFromString(const char *str)
Definition: date.h:130
const tokenT TOKEN_Mate
Definition: tokens.h:72