Line data Source code
1 : /*
2 : * Copyright (C) 2016-2018 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 that manages databases encoded in SCID
22 : * format v4.
23 : */
24 :
25 : #ifndef CODEC_SCID4_H
26 : #define CODEC_SCID4_H
27 :
28 : #include "codec_native.h"
29 : #include "filebuf.h"
30 : #include <limits>
31 :
32 : /**
33 : * This class manages databases encoded in SCID format v4.
34 : */
35 48 : class CodecSCID4 : public CodecNative<CodecSCID4> {
36 : std::vector<std::string> filenames_;
37 : FilebufAppend gfile_;
38 : char gamecache_[1ULL << 17];
39 :
40 : enum : uint64_t {
41 : LIMIT_GAMEOFFSET = 1ULL << 32,
42 : LIMIT_GAMELEN = 1ULL << 17,
43 : LIMIT_NUMGAMES = 16777214ULL, // Three bytes -1 because GetAutoLoad uses
44 : // 0 to mean "no autoload"
45 : LIMIT_NAMELEN = 255
46 : };
47 :
48 : public: // ICodecDatabase interface
49 3 : Codec getType() const final { return ICodecDatabase::SCID4; }
50 :
51 : /**
52 : * Returns the full path of the three files (index, namebase and gamefile)
53 : * used by the database.
54 : */
55 9 : std::vector<std::string> getFilenames() const final { return filenames_; };
56 :
57 : std::vector<std::pair<const char*, std::string>>
58 0 : getExtraInfo() const final {
59 0 : std::vector<std::pair<const char*, std::string>> res;
60 0 : res.emplace_back("type", std::to_string(idx_->GetType()));
61 0 : res.emplace_back("description", idx_->GetDescription());
62 0 : res.emplace_back("autoload", std::to_string(idx_->GetAutoLoad()));
63 0 : res.emplace_back("flag1",
64 0 : idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM1));
65 0 : res.emplace_back("flag2",
66 0 : idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM2));
67 0 : res.emplace_back("flag3",
68 0 : idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM3));
69 0 : res.emplace_back("flag4",
70 0 : idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM4));
71 0 : res.emplace_back("flag5",
72 0 : idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM5));
73 0 : res.emplace_back("flag6",
74 0 : idx_->GetCustomFlagDesc(IndexEntry::IDX_FLAG_CUSTOM6));
75 0 : return res;
76 : }
77 :
78 6000 : const byte* getGameData(uint64_t offset, uint32_t length) final {
79 6000 : if (offset >= gfile_.size())
80 0 : return NULL;
81 6000 : if (length >= LIMIT_GAMELEN)
82 0 : return NULL;
83 :
84 6000 : if (gfile_.pubseekpos(offset) == -1)
85 0 : return NULL;
86 6000 : if (gfile_.sgetn(gamecache_, length) != std::streamsize(length))
87 0 : return NULL;
88 :
89 6000 : return reinterpret_cast<const byte*>(gamecache_);
90 : }
91 :
92 : errorT flush() final;
93 :
94 : errorT dyn_open(fileModeT, const char*, const Progress&, Index*,
95 : NameBase*) final;
96 :
97 : public: // CodecNative interface
98 : /**
99 : * Stores the data into the .sg4 file.
100 : * @param src: valid pointer to a buffer that contains the game data
101 : * (encoded in native format).
102 : * @param length: the length of the buffer @e src (in bytes).
103 : * @returns
104 : * - on success, a @e std::pair containing OK and the offset of the stored
105 : * data (needed for retrieving the data with getGameData()).
106 : * - on failure, a @e std::pair containing an error code and 0.
107 : */
108 4000 : std::pair<errorT, uint64_t> dyn_addGameData(const byte* src,
109 : size_t length) {
110 4000 : ASSERT(src != 0);
111 4000 : const char* data = reinterpret_cast<const char*>(src);
112 :
113 4000 : if (length >= LIMIT_GAMELEN)
114 0 : return std::make_pair(ERROR_GameLengthLimit, 0);
115 :
116 : // The SCID4 format uses 32-bits to store games' offset.
117 4000 : uint64_t offset = gfile_.size();
118 4000 : if (offset >= LIMIT_GAMEOFFSET - length)
119 0 : return std::make_pair(ERROR_OffsetLimit, 0);
120 :
121 : // The SCID4 format stores games into blocks of 128KB.
122 : // If the current block does not have enough space, we fill it with
123 : // random data and use the next one.
124 4000 : uint64_t blockSpace = LIMIT_GAMELEN - (offset % LIMIT_GAMELEN);
125 4000 : if (blockSpace < length) {
126 795 : errorT err = gfile_.append(data, blockSpace);
127 795 : if (err != OK)
128 0 : return std::make_pair(err, 0);
129 795 : offset += blockSpace;
130 : }
131 :
132 4000 : errorT err = gfile_.append(data, length);
133 4000 : return std::make_pair(err, offset);
134 : }
135 :
136 : /**
137 : * Given a name (string), retrieve the corresponding ID.
138 : * The name is added to @e nb_ if do not already exists in the NameBase.
139 : * @param nt: nameT type of the name to retrieve.
140 : * @param name: the name to retrieve.
141 : * @returns
142 : * - on success, a @e std::pair containing OK and the ID.
143 : * - on failure, a @e std::pair containing an error code and 0.
144 : */
145 20000 : std::pair<errorT, idNumberT> dyn_addName(nameT nt, const char* name) {
146 20000 : const idNumberT MAX_ID[] = {
147 : 1048575, /* Player names: Maximum of 2^20 -1 = 1,048,575 */
148 : 524287, /* Event names: Maximum of 2^19 -1 = 524,287 */
149 : 524287, /* Site names: Maximum of 2^19 -1 = 524,287 */
150 : 262143 /* Round names: Maximum of 2^18 -1 = 262,143 */
151 : };
152 20000 : return nb_->addName(nt, name, LIMIT_NAMELEN, MAX_ID[nt]);
153 : }
154 :
155 : /**
156 : * Add an IndexEntry to @e idx_.
157 : * @param ie: the IndexEntry object to add.
158 : * @returns OK if successful or an error code.
159 : */
160 3000 : errorT dyn_addIndexEntry(const IndexEntry& ie) {
161 3000 : auto nGames = idx_->GetNumGames();
162 3000 : if (nGames >= LIMIT_NUMGAMES)
163 0 : return ERROR_NumGamesLimit;
164 :
165 3000 : return idx_->WriteEntry(&ie, nGames);
166 : }
167 :
168 : /**
169 : * Replace an IndexEntry.
170 : * @param ie: the IndexEntry with the new data.
171 : * @param replaced: valid gamenumT of the game to be replaced.
172 : * @returns OK if successful or an error code.
173 : */
174 1000 : errorT dyn_saveIndexEntry(const IndexEntry& ie, gamenumT replaced) {
175 1000 : return idx_->WriteEntry(&ie, replaced);
176 : }
177 :
178 : private:
179 : errorT readIndex(const Progress& progress);
180 : };
181 :
182 : #endif
|