LCOV - code coverage report
Current view: top level - src - codec_scid4.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 219 263 83.3 %
Date: 2018-02-05 16:49:44 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2017  Fulvio Benini
       3             : 
       4             :  * This file is part of Scid (Shane's Chess Information Database).
       5             :  *
       6             :  * Scid is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation.
       9             :  *
      10             :  * Scid is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
      17             :  *
      18             :  */
      19             : 
      20             : /** @file
      21             :  * Implements the CodecSCID4 class, which manages the databases encoded
      22             :  * in Scid format version 4.
      23             :  */
      24             : 
      25             : #include "codec_scid4.h"
      26             : #include <algorithm>
      27             : 
      28             : namespace {
      29             : 
      30             : /**
      31             :  * A NameBase file starts with an header containing:
      32             :  * - header_magic (8 bytes): identify the file format
      33             :  * - unused (4 bytes):  obsolete timeStamp
      34             :  * - number of NAME_PLAYER names stored in the file (3 bytes)
      35             :  * - number of NAME_EVENT names stored in the file (3 bytes)
      36             :  * - number of NAME_SITE names stored in the file (3 bytes)
      37             :  * - number of NAME_ROUND names stored in the file (3 bytes)
      38             :  * - unused (12 bytes): obsolete max frequency
      39             :  * Names are stored in alphabetical order using front-coding and each record is
      40             :  * composed by:
      41             :  * - name_id (2-3 bytes): the idx (idNumberT) stored in the Index (.si4) file
      42             :  * - unused (1-3 bytes): obsolete frequency
      43             :  * - length (1 byte): the total number of bytes of the name (max 255)
      44             :  * - prefix (1 byte): the number of bytes in common with the previous name
      45             :  * - name (0-255 bytes): the part of the name that differs from the previous
      46             :  *   one.
      47             :  */
      48             : const char* NAMEBASE_MAGIC = "Scid.sn";
      49             : 
      50             : /**
      51             :  * Read a SCIDv4 NameBase file into memory.
      52             :  * @param filename: the full path of the file to open.
      53             :  * @param fMode:    a valid file mode.
      54             :  * @param nb:       reference to the object where the names will be stored.
      55             :  * @returns OK if successful or an error code.
      56             :  */
      57           7 : errorT namefileRead(const char* filename, fileModeT fmode, NameBase& nb) {
      58           7 :         auto nb_data = nb.getData();
      59           7 :         auto& map = std::get<0>(nb_data);
      60           7 :         auto& names = std::get<1>(nb_data);
      61           7 :         auto& eloV = std::get<2>(nb_data);
      62             : 
      63          14 :         Filebuf file;
      64           7 :         if (file.Open(filename, fmode) != OK)
      65           0 :                 return ERROR_FileOpen;
      66             : 
      67           7 :         char Header_magic[9] = {0}; // magic identifier must be "Scid.sn"
      68           7 :         file.sgetn(Header_magic, 8);
      69           7 :         if (strcmp(Header_magic, NAMEBASE_MAGIC) != 0)
      70           0 :                 return ERROR_BadMagic;
      71             : 
      72             :         // *** Compatibility ***
      73             :         // Even if timeStamp is not used we still need to read the bytes
      74           7 :         file.ReadFourBytes();
      75             :         // ***
      76             : 
      77             :         idNumberT Header_numNames[NUM_NAME_TYPES];
      78           7 :         Header_numNames[NAME_PLAYER] = file.ReadThreeBytes();
      79           7 :         Header_numNames[NAME_EVENT] = file.ReadThreeBytes();
      80           7 :         Header_numNames[NAME_SITE] = file.ReadThreeBytes();
      81           7 :         Header_numNames[NAME_ROUND] = file.ReadThreeBytes();
      82             : 
      83             :         // *** Compatibility ***
      84             :         // Even if frequency is no longer used we still need to read the bytes
      85             :         uint obsolete_maxFreq[NUM_NAME_TYPES];
      86           7 :         obsolete_maxFreq[NAME_PLAYER] = file.ReadThreeBytes();
      87           7 :         obsolete_maxFreq[NAME_EVENT] = file.ReadThreeBytes();
      88           7 :         obsolete_maxFreq[NAME_SITE] = file.ReadThreeBytes();
      89           7 :         obsolete_maxFreq[NAME_ROUND] = file.ReadThreeBytes();
      90             :         // ***
      91             : 
      92           7 :         eloV.resize(Header_numNames[NAME_PLAYER], 0);
      93          35 :         for (nameT nt : {NAME_PLAYER, NAME_EVENT, NAME_SITE, NAME_ROUND}) {
      94          28 :                 names[nt].resize(Header_numNames[nt]);
      95             :                 idNumberT id;
      96          56 :                 std::string prevName;
      97        2425 :                 for (idNumberT i = 0; i < Header_numNames[nt]; i++) {
      98        2397 :                         if (Header_numNames[nt] >= 65536) {
      99           0 :                                 id = file.ReadThreeBytes();
     100             :                         } else {
     101        2397 :                                 id = file.ReadTwoBytes();
     102             :                         }
     103             : 
     104             :                         // *** Compatibility ***
     105             :                         // Even if frequency is no longer used we still need to read the
     106             :                         // bytes Frequencies can be stored in 1, 2 or 3 bytes:
     107        2397 :                         if (obsolete_maxFreq[nt] >= 65536) {
     108           0 :                                 file.ReadThreeBytes();
     109        2397 :                         } else if (obsolete_maxFreq[nt] >= 256) {
     110          13 :                                 file.ReadTwoBytes();
     111             :                         } else { // Frequencies all <= 255: fit in one byte
     112        2384 :                                 file.ReadOneByte();
     113             :                         }
     114             :                         // ***
     115             : 
     116             :                         // Read the name string.
     117             :                         // All strings EXCEPT the first are front-coded.
     118        2397 :                         int length = file.ReadOneByte();
     119        2397 :                         int prefix = (i > 0) ? file.ReadOneByte() : 0;
     120        2397 :                         if (prefix > length)
     121           0 :                                 return ERROR_Corrupt;
     122             : 
     123        2397 :                         char* name = new char[length + 1];
     124        2397 :                         std::copy_n(prevName.c_str(), prefix, name);
     125        2397 :                         std::streamsize extra_chars = length - prefix;
     126        2397 :                         if (extra_chars != file.sgetn(name + prefix, extra_chars)) {
     127           0 :                                 delete[] name;
     128           0 :                                 return ERROR_FileRead;
     129             :                         }
     130        2397 :                         name[length] = 0;
     131        2397 :                         prevName.assign(name, length);
     132             : 
     133        2397 :                         if (id < Header_numNames[nt] && names[nt][id] == 0) {
     134        2397 :                                 names[nt][id].reset(name);
     135        2397 :                                 map[nt].insert(map[nt].end(), std::make_pair(name, id));
     136             :                         } else {
     137           0 :                                 delete[] name;
     138           0 :                                 return ERROR_Corrupt;
     139             :                         }
     140             :                 }
     141             : 
     142          28 :                 if (map[nt].size() != names[nt].size())
     143           0 :                         return ERROR_Corrupt;
     144             :         }
     145             : 
     146           7 :         return OK;
     147             : }
     148             : 
     149        2381 : bool assert_sorted(const char* str1, const char* str2) {
     150             :         // *** Compatibility ***
     151             :         // Older code used a custom StrTree class with a peculiar sorting:
     152             :         // - the first char was interpreted as an unsigned char;
     153             :         // - the remaining part was compared with the function
     154             :         // strComapare(),
     155             :         //   which converts the chars to ints, and is not consistent with
     156             :         //   the standard function strcmp().
     157             :         // The old StrTree class did also have unpredictable behaviors when
     158             :         // fed with names not sorted according to that criteria, for example
     159             :         // it could create Namebase objects with duplicate entries.
     160             :         // ***
     161        2381 :         if (*str1 == *str2)
     162        2277 :                 return strCompare(str1, str2) < 0;
     163             : 
     164         104 :         return static_cast<uint>(*str1) < static_cast<uint>(*str2);
     165             : }
     166             : 
     167             : /**
     168             :  * Write a SCIDv4 NameBase file.
     169             :  * @param filename: the full path of the file to open.
     170             :  * @param nb:       reference to the object where the names will be stored.
     171             :  * @returns OK if successful or an error code.
     172             :  */
     173             : template <typename TCont, typename TFreq>
     174          13 : errorT namefileWrite(const char* filename, const TCont& names_ids,
     175             :                      const TFreq& freq) {
     176          26 :         Filebuf file;
     177          13 :         if (file.Open(filename, FMODE_WriteOnly) != OK)
     178           0 :                 return ERROR_FileOpen;
     179             : 
     180          13 :         file.sputn(NAMEBASE_MAGIC, 8);
     181             : 
     182             :         // *** Compatibility ***
     183             :         // Even if timeStamp is not used we still need to write these bytes
     184          13 :         file.WriteFourBytes(0);
     185             :         // ***
     186             : 
     187          13 :         ASSERT(1ULL << 24 > names_ids[NAME_PLAYER].size());
     188          13 :         ASSERT(1ULL << 24 > names_ids[NAME_EVENT].size());
     189          13 :         ASSERT(1ULL << 24 > names_ids[NAME_SITE].size());
     190          13 :         ASSERT(1ULL << 24 > names_ids[NAME_ROUND].size());
     191          13 :         file.WriteThreeBytes((uint32_t)names_ids[NAME_PLAYER].size());
     192          13 :         file.WriteThreeBytes((uint32_t)names_ids[NAME_EVENT].size());
     193          13 :         file.WriteThreeBytes((uint32_t)names_ids[NAME_SITE].size());
     194          13 :         file.WriteThreeBytes((uint32_t)names_ids[NAME_ROUND].size());
     195             : 
     196             :         // *** Compatibility ***
     197             :         // even if maxFrequency is no longer used we still need to write these bytes
     198          13 :         unsigned maxFreq[NUM_NAME_TYPES] = {0};
     199          65 :         for (nameT nt : {NAME_PLAYER, NAME_EVENT, NAME_SITE, NAME_ROUND}) {
     200          52 :                 auto it = std::max_element(freq[nt].begin(), freq[nt].end());
     201          52 :                 maxFreq[nt] = (it == freq[nt].end()) ? 0 : *it;
     202          52 :                 file.WriteThreeBytes(maxFreq[nt]);
     203             :         }
     204             :         // ***
     205             : 
     206          65 :         for (nameT nt : {NAME_PLAYER, NAME_EVENT, NAME_SITE, NAME_ROUND}) {
     207          52 :                 const char* prevName = nullptr;
     208          52 :                 size_t numNames = names_ids[nt].size();
     209        2453 :                 for (const auto& it : names_ids[nt]) {
     210        2401 :                         const char* name = it.first;
     211        2401 :                         idNumberT id = it.second;
     212             : 
     213        2401 :                         ASSERT(prevName == nullptr || assert_sorted(prevName, name));
     214             : 
     215             :                         // write idNumber in 2 bytes if possible, otherwise 3.
     216        2401 :                         if (numNames >= 65536) {
     217           0 :                                 file.WriteThreeBytes(id);
     218             :                         } else {
     219        2401 :                                 file.WriteTwoBytes(id);
     220             :                         }
     221             : 
     222             :                         // *** Compatibility ***
     223             :                         // write these bytes even if they are not used anymore
     224        2401 :                         if (maxFreq[nt] >= 65536) {
     225           0 :                                 file.WriteThreeBytes(freq[nt][id]);
     226        2401 :                         } else if (maxFreq[nt] >= 256) {
     227          17 :                                 file.WriteTwoBytes(freq[nt][id]);
     228             :                         } else {
     229        2384 :                                 file.WriteOneByte(static_cast<byte>(freq[nt][id]));
     230             :                         }
     231             :                         // ***
     232             : 
     233        2401 :                         ASSERT(strlen(name) < 256);
     234        2401 :                         byte length = static_cast<byte>(strlen(name));
     235        2401 :                         file.WriteOneByte(length);
     236        2401 :                         byte prefix = 0;
     237        2401 :                         if (prevName) {
     238        2381 :                                 prefix = (byte)strPrefix(name, prevName);
     239        2381 :                                 file.WriteOneByte(prefix);
     240             :                         }
     241        2401 :                         file.sputn(name + prefix, (length - prefix));
     242        2401 :                         prevName = name;
     243             :                 }
     244             :         }
     245          13 :         return OK;
     246             : }
     247             : 
     248             : } // namespace
     249             : 
     250      204339 : void CodecSCID4::decodeIndexEntry(const char* buf_it, versionT version,
     251             :                                   IndexEntry* ie) {
     252     7969221 :         auto ReadOneByte = [&buf_it]() {
     253     7969221 :                 uint8_t res = *buf_it++;
     254     7969221 :                 return res;
     255      204339 :         };
     256     6947526 :         auto ReadTwoBytes = [&ReadOneByte]() {
     257     3473763 :                 uint16_t high = ReadOneByte();
     258     6947526 :                 uint16_t res = (high << 8) | ReadOneByte();
     259     3473763 :                 return res;
     260      204339 :         };
     261     1226034 :         auto ReadFourBytes = [&ReadTwoBytes]() {
     262      613017 :                 uint32_t high = ReadTwoBytes();
     263     1226034 :                 uint32_t res = (high << 16) | ReadTwoBytes();
     264      613017 :                 return res;
     265      204339 :         };
     266             : 
     267             :         // Offset of the gamefile record (32 bits).
     268      204339 :         ie->SetOffset(ReadFourBytes());
     269             : 
     270             :         // Length of gamefile record for this game: 17 bits are used so the max
     271             :         // length is 128 ko (131071).
     272             :         // Lower bits of the extra byte are used for custom flags: LxFFFFFF ( L =
     273             :         // length for long games, x = spare, F = custom flags)
     274      204339 :         uint32_t len_Low = ReadTwoBytes();
     275      204339 :         uint32_t len_flags = (version < 400) ? 0 : ReadOneByte();
     276      204339 :         ie->SetLength(((len_flags & 0x80) << 9) | len_Low);
     277      204339 :         uint32_t Flags = ReadTwoBytes();
     278      204339 :         ie->clearFlags();
     279      204339 :         ie->SetFlag(((len_flags & 0x3F) << 16) | Flags, true);
     280             : 
     281             :         // WhiteID and BlackID are 20-bit values, EventID and SiteID are
     282             :         // 19-bit values, and RoundID is an 18-bit value.
     283             :         // WhiteID high 4 bits = bits 4-7 of WhiteBlack_High.
     284             :         // BlackID high 4 bits = bits 0-3 of WhiteBlack_High.
     285             :         // EventID high 3 bits = bits 5-7 of EventSiteRnd_high.
     286             :         // SiteID  high 3 bits = bits 2-4 of EventSiteRnd_high.
     287             :         // RoundID high 2 bits = bits 0-1 of EventSiteRnd_high.
     288      204339 :         uint32_t WhiteBlack_High = ReadOneByte();
     289      204339 :         uint32_t WhiteID_Low = ReadTwoBytes();
     290      204339 :         ie->SetWhite(((WhiteBlack_High & 0xF0) << 12) | WhiteID_Low);
     291      204339 :         uint32_t BlackID_Low = ReadTwoBytes();
     292      204339 :         ie->SetBlack(((WhiteBlack_High & 0x0F) << 16) | BlackID_Low);
     293      204339 :         uint32_t EventSiteRnd_High = ReadOneByte();
     294      204339 :         uint32_t EventID_Low = ReadTwoBytes();
     295      204339 :         ie->SetEvent(((EventSiteRnd_High & 0xE0) << 11) | EventID_Low);
     296      204339 :         uint32_t SiteID_Low = ReadTwoBytes();
     297      204339 :         ie->SetSite(((EventSiteRnd_High & 0x1C) << 14) | SiteID_Low);
     298      204339 :         uint32_t RoundID_Low = ReadTwoBytes();
     299      204339 :         ie->SetRound(((EventSiteRnd_High & 0x03) << 16) | RoundID_Low);
     300             : 
     301             :         // Counters for comments, variations, etc. (4 bits each)
     302             :         // VarCounts also stores the result (4 bits).
     303      204339 :         uint32_t varCounts = ReadTwoBytes();
     304      204339 :         ie->SetRawVariationCount(varCounts & 0x0F);
     305      204339 :         ie->SetRawCommentCount((varCounts >> 4) & 0x0F);
     306      204339 :         ie->SetRawNagCount((varCounts >> 8) & 0x0F);
     307      204339 :         ie->SetResult((varCounts >> 12) & 0x0F);
     308             : 
     309             :         // ECO code (16 bits)
     310      204339 :         ie->SetEcoCode(ReadTwoBytes());
     311             : 
     312             :         // Date and EventDate are stored in four bytes.
     313             :         // Due to a compact encoding format, the EventDate
     314             :         // must be within a few years of the Date.
     315      204339 :         uint32_t date_edate = ReadFourBytes();
     316      204339 :         uint32_t date = date_edate & 0xFFFFF;
     317      204339 :         ie->SetDate(date);
     318      204339 :         uint32_t edate = date_edate >> 20;
     319      204339 :         uint32_t eyear = date_GetYear(edate) & 0x07;
     320      204339 :         if (eyear == 0) {
     321      200280 :                 edate = ZERO_DATE;
     322             :         } else {
     323        4059 :                 eyear += date_GetYear(date);
     324        4059 :                 eyear = (eyear < 4) ? 0 : eyear - 4;
     325        4059 :                 edate = DATE_MAKE(eyear, date_GetMonth(edate), date_GetDay(edate));
     326             :         }
     327      204339 :         ie->SetEventDate(edate);
     328             : 
     329             :         // The two ELO ratings and rating types take 2 bytes each.
     330      204339 :         uint16_t whiteElo = ReadTwoBytes();
     331      204339 :         ie->SetWhiteElo(whiteElo & 0xFFF);
     332      204339 :         ie->SetWhiteRatingType(whiteElo >> 12);
     333      204339 :         uint16_t blackElo = ReadTwoBytes();
     334      204339 :         ie->SetBlackElo(blackElo & 0xFFF);
     335      204339 :         ie->SetBlackRatingType(blackElo >> 12);
     336             : 
     337             :         // material of the final position in the game,
     338             :         // and the StoredLineCode in the top 8 bits.
     339      204339 :         uint32_t finalMatSig = ReadFourBytes();
     340      204339 :         ie->SetFinalMatSig(finalMatSig & 0xFFFFFF);
     341      204339 :         ie->SetStoredLineCode(finalMatSig >> 24);
     342             : 
     343             :         // Read the 9-byte homePawnData array:
     344             :         // The first byte of HomePawnData has high bits of the NumHalfMoves
     345             :         // counter in its top two bits:
     346      204339 :         uint16_t NumHalfMoves = ReadOneByte();
     347      204339 :         uint16_t pawnData0 = ReadOneByte();
     348      204339 :         ie->SetNumHalfMoves(((pawnData0 & 0xC0) << 2) | NumHalfMoves);
     349      204339 :         byte* pb = ie->GetHomePawnData();
     350      204339 :         *pb++ = pawnData0 & 0x3F;
     351      204339 :         std::copy_n(buf_it, HPSIG_SIZE - 1, pb);
     352      204339 : }
     353             : 
     354          16 : errorT CodecSCID4::dyn_open(fileModeT fMode, const char* filename,
     355             :                             const Progress& progress, Index* idx,
     356             :                             NameBase* nb) {
     357          16 :         if (filename == nullptr || idx == nullptr || nb == nullptr)
     358           0 :                 return ERROR;
     359          16 :         if (*filename == '\0')
     360           1 :                 return ERROR_FileOpen;
     361             : 
     362          15 :         idx_ = idx;
     363          15 :         nb_ = nb;
     364          15 :         filenames_.resize(3);
     365          15 :         filenames_[0] = std::string(filename) + ".si4";
     366          15 :         filenames_[1] = std::string(filename) + ".sn4";
     367          15 :         filenames_[2] = std::string(filename) + ".sg4";
     368             : 
     369          15 :         errorT err = gfile_.open(filenames_[2], fMode);
     370          15 :         if (err != OK)
     371           1 :                 return err;
     372             : 
     373          14 :         if (fMode == FMODE_Create) {
     374           6 :                 err = idx->Create(filename);
     375           6 :                 if (err == OK) {
     376           6 :                         err = namefileWrite(filenames_[1].c_str(), nb_->getNames(),
     377          12 :                                             idx_->calcNameFreq(*nb_));
     378             :                 }
     379             :         } else {
     380           8 :                 err = idx->Open(filename, fMode);
     381           8 :                 if (err == OK)
     382           7 :                         err = namefileRead(filenames_[1].c_str(), fMode, *nb_);
     383           8 :                 if (err == OK)
     384           7 :                         err = readIndex(progress);
     385             :         }
     386             : 
     387          14 :         return err;
     388             : }
     389             : 
     390           7 : errorT CodecSCID4::flush() {
     391           7 :         errorT err = idx_->flush();
     392           7 :         if (err == OK) {
     393             :                 // *** Compatibility ***
     394             :                 // Even if name's frequency is no longer used, it's necessary to
     395             :                 // keep the compatibility with older Scid versions, forcing a
     396             :                 // recalculation.
     397           7 :                 err = namefileWrite(filenames_[1].c_str(), nb_->getNames(),
     398          14 :                                     idx_->calcNameFreq(*nb_));
     399             :         }
     400           7 :         errorT errGfile = (gfile_.pubsync() == 0) ? OK : ERROR_FileWrite;
     401             : 
     402           7 :         return (err == OK) ? errGfile : err;
     403             : }
     404             : 
     405             : /**
     406             :  * Reads the entire index file into memory.
     407             :  * Invalid name IDs are replaced with "?" if possible.
     408             :  * @param progress: a Progress object used for GUI communications.
     409             :  * @returns OK if successful or an error code.
     410             :  */
     411           7 : inline errorT CodecSCID4::readIndex(const Progress& progress) {
     412           7 :         gamenumT nUnknowIDs = 0;
     413             :         idNumberT maxID[NUM_NAME_TYPES];
     414          35 :         for (nameT nt = NAME_PLAYER; nt < NUM_NAME_TYPES; nt++) {
     415          28 :                 maxID[nt] = nb_->GetNumNames(nt);
     416             :         }
     417        4339 :         auto validateNameIDs = [&](IndexEntry* ie) {
     418       21695 :                 if (ie->GetWhite() >= maxID[NAME_PLAYER]) {
     419           0 :                         auto unknown = dyn_addName(NAME_PLAYER, "?");
     420           0 :                         if (unknown.first != OK)
     421           0 :                                 return false;
     422           0 :                         ie->SetWhite(unknown.second);
     423           0 :                         ++nUnknowIDs;
     424             :                 }
     425        8678 :                 if (ie->GetBlack() >= maxID[NAME_PLAYER]) {
     426           0 :                         auto unknown = dyn_addName(NAME_PLAYER, "?");
     427           0 :                         if (unknown.first != OK)
     428           0 :                                 return false;
     429           0 :                         ie->SetBlack(unknown.second);
     430           0 :                         ++nUnknowIDs;
     431             :                 }
     432        8678 :                 if (ie->GetEvent() >= maxID[NAME_EVENT]) {
     433           0 :                         auto unknown = dyn_addName(NAME_EVENT, "?");
     434           0 :                         if (unknown.first != OK)
     435           0 :                                 return false;
     436           0 :                         ie->SetEvent(unknown.second);
     437           0 :                         ++nUnknowIDs;
     438             :                 }
     439        8678 :                 if (ie->GetSite() >= maxID[NAME_SITE]) {
     440           0 :                         auto unknown = dyn_addName(NAME_SITE, "?");
     441           0 :                         if (unknown.first != OK)
     442           0 :                                 return false;
     443           0 :                         ie->SetSite(unknown.second);
     444           0 :                         ++nUnknowIDs;
     445             :                 }
     446        8678 :                 if (ie->GetRound() >= maxID[NAME_ROUND]) {
     447           0 :                         auto unknown = dyn_addName(NAME_ROUND, "?");
     448           0 :                         if (unknown.first != OK)
     449           0 :                                 return false;
     450           0 :                         ie->SetRound(unknown.second);
     451           0 :                         ++nUnknowIDs;
     452             :                 }
     453        4339 :                 return true;
     454           7 :         };
     455             : 
     456           7 :         auto idxFile = idx_->FilePtr;
     457           7 :         auto version = idx_->Header.version;
     458           7 :         auto nGames = idx_->GetNumGames();
     459           7 :         idx_->entries_.resize(nGames);
     460             : 
     461           7 :         auto nBytes = (version < 400) ? OLD_INDEX_ENTRY_SIZE : INDEX_ENTRY_SIZE;
     462        4346 :         for (gamenumT gNum = 0; idxFile->sgetc() != EOF; ++gNum) {
     463        4339 :                 if (gNum == nGames)
     464           0 :                         return ERROR_CorruptData;
     465             : 
     466        4339 :                 if ((gNum % 8192) == 0) {
     467           4 :                         if (!progress.report(gNum, nGames))
     468           0 :                                 return ERROR_UserCancel;
     469             :                 }
     470             : 
     471             :                 char buf[INDEX_ENTRY_SIZE];
     472        4339 :                 if (idxFile->sgetn(buf, nBytes) != nBytes)
     473           0 :                         return ERROR_FileRead;
     474             : 
     475        4339 :                 IndexEntry* ie = idx_->FetchEntry(gNum);
     476        4339 :                 decodeIndexEntry(buf, version, ie);
     477             : 
     478        4339 :                 if (!validateNameIDs(ie))
     479           0 :                         return ERROR_CorruptData;
     480             : 
     481        4339 :                 nb_->AddElo(ie->GetWhite(), ie->GetWhiteElo());
     482        4339 :                 nb_->AddElo(ie->GetBlack(), ie->GetBlackElo());
     483             :         }
     484           7 :         progress.report(1, 1);
     485             : 
     486           7 :         if (nGames != idx_->GetNumGames())
     487           0 :                 return ERROR_FileRead;
     488             : 
     489           7 :         idx_->nInvalidNameId_ = nUnknowIDs;
     490           7 :         return (nUnknowIDs == 0) ? OK : ERROR_NameDataLoss;
     491           3 : }

Generated by: LCOV version 1.13