1 /***************************************************************************
2 * (C) 2006 Ejner Borgbjerg <ejner@users.sourceforge.net> *
3 * (C) 2006-2009 Michal Rudolf <mrudolf@kdewebdev.org> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10
11
12 /* Documentation for managing QDataset version, see
13 http://doc.trolltech.com/4.0/qdatastream.html
14 */
15 #include "playerdatabase.h"
16
17 #if defined(_MSC_VER) && defined(_DEBUG)
18 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
19 #define new DEBUG_NEW
20 #endif // _MSC_VER
21
22 static quint32 Magic = (quint32)0xB0D0A0D0; // 'magic' number
23 static quint32 Version = (quint32)100; // file format version
24 static QString Mapfile_suffix = ".cpm";
25 static QString Datafile_suffix = ".cpd";
26
create(const QString & fname)27 bool PlayerDatabase::create(const QString& fname)
28 {
29 m_dirty = false;
30 m_mapfile.setFileName(fname + Mapfile_suffix);
31 m_datafile.setFileName(fname + Datafile_suffix);
32 if(m_mapfile.exists() || m_datafile.exists())
33 {
34 return false;
35 }
36
37 // set QDataset format version to use
38 if(Version == (quint32)100)
39 {
40 m_mapds.setVersion(6);
41 m_datads.setVersion(6);
42 }
43 else //default
44 {
45 m_mapds.setVersion(6);
46 m_datads.setVersion(6);
47 }
48
49 m_mapfile.open(QIODevice::ReadWrite);
50 m_mapds.setDevice(&m_mapfile);
51 m_mapds << Magic;
52 m_mapds << Version;
53 m_nplayers = 0;
54 m_nplayers_offset = m_mapfile.pos();
55 m_mapds << m_nplayers;
56
57 m_datafile.open(QIODevice::ReadWrite);
58 m_datads.setDevice(&m_datafile);
59 m_datads << Magic;
60 m_datads << Version;//use current version for new db's
61
62 m_mapfile.flush();
63 m_datafile.flush();
64
65 m_dataFileCurrentPosition = m_datafile.pos();
66 m_npending_adds = 0;
67
68 return true;
69 }
70
open(const QString & fname)71 bool PlayerDatabase::open(const QString& fname)
72 {
73 m_dirty = false;
74 m_mapfile.setFileName(fname + Mapfile_suffix);
75 if(!m_mapfile.open(QIODevice::ReadWrite))
76 {
77 return false;
78 }
79 m_mapds.setDevice(&m_mapfile);
80 quint32 map_magic;
81 quint32 map_version;
82 m_mapds >> map_magic;
83 m_mapds >> map_version;
84
85 if(map_magic != Magic)
86 {
87 m_mapds.setDevice(nullptr);
88 m_mapfile.close();
89 return false;
90 }
91 // set QDataset format version to use
92 if(map_version == (quint32)100)
93 {
94 m_mapds.setVersion(6);
95 }
96 else //unknown version
97 {
98 m_mapds.setDevice(nullptr);
99 m_mapfile.close();
100 return false;
101 }
102
103 quint32 data_magic;
104 quint32 data_version;
105 m_datafile.setFileName(fname + Datafile_suffix);
106 if(!m_datafile.open(QIODevice::ReadWrite))
107 {
108 m_mapds.setDevice(nullptr);
109 m_mapfile.close();
110 return false;
111 }
112 m_datads.setDevice(&m_datafile);
113 m_datads >> data_magic;
114 m_datads >> data_version;
115
116 if(data_magic != Magic)
117 {
118 m_mapds.setDevice(nullptr);
119 m_mapfile.close();
120 m_datads.setDevice(nullptr);
121 m_datafile.close();
122 return false;
123 }
124 // set QDataset format version to use
125 if(data_version == (quint32)100)
126 {
127 m_datads.setVersion(6);
128 }
129 else //unknown version
130 {
131 m_mapds.setDevice(nullptr);
132 m_mapfile.close();
133 m_datads.setDevice(nullptr);
134 m_datafile.close();
135 return false;
136 }
137
138 if(map_version != data_version)
139 {
140 m_mapds.setDevice(nullptr);
141 m_mapfile.close();
142 m_datads.setDevice(nullptr);
143 m_datafile.close();
144 return false;
145 }
146
147 m_dataFileCurrentPosition = m_datafile.pos();
148 m_nplayers_offset = m_mapfile.pos();
149 m_mapds >> m_nplayers;
150 if(m_nplayers > 0)
151 {
152 m_mapds >> m_mapping;
153 }
154 m_npending_adds = 0;
155 return true;
156
157 }
158
removeDatabase(const QString & fname)159 bool PlayerDatabase::removeDatabase(const QString& fname)
160 {
161 m_mapfile.setFileName(fname + Mapfile_suffix);
162 m_datafile.setFileName(fname + Datafile_suffix);
163 return m_mapfile.remove() && m_datafile.remove();
164 }
165
close()166 void PlayerDatabase::close()
167 {
168 commit();
169 m_mapds.setDevice(nullptr);
170 m_mapfile.flush();
171 m_mapfile.close();
172 m_datads.setDevice(nullptr);
173 m_datafile.flush();
174 m_datafile.close();
175 }
176
rollback()177 void PlayerDatabase::rollback()
178 {
179 m_pendingUpdates.clear();
180 m_dirty = false;
181 m_npending_adds = 0;
182 }
183
commit()184 void PlayerDatabase::commit()
185 {
186
187 if(m_dirty) //current player was changed
188 {
189 m_pendingUpdates.insert(m_currentPlayerName, m_currentPlayer);
190 }
191
192 m_mapfile.seek(m_nplayers_offset);
193 m_mapds << m_nplayers;
194
195 // write non-committed changes
196 m_dataFileCurrentPosition = m_datafile.size();
197 m_datafile.seek(m_dataFileCurrentPosition);
198
199 QMap<QString, PlayerData>::Iterator it;
200 for(it = m_pendingUpdates.begin(); it != m_pendingUpdates.end(); ++it)
201 {
202 m_mapping.insert(it.key(), m_datafile.pos());
203 m_datads << it.value().dateOfBirth().asString();
204 m_datads << it.value().dateOfDeath().asString();
205 m_datads << it.value().country();
206 m_datads << it.value().title();
207 m_datads << it.value().eloListData();
208 m_datads << (qint32)(it.value().firstEloListIndex());
209 m_datads << (qint32)(it.value().lastEloListIndex());
210 m_datads << (qint32)(it.value().estimatedElo());
211 m_datads << (qint32)(it.value().peakElo());
212 m_datads << it.value().photo();
213 m_datads << it.value().biography();
214 }
215 m_mapds << m_mapping;
216 m_mapfile.flush();
217 m_datafile.flush();
218 m_pendingUpdates.clear();
219 m_dirty = false;
220 m_nplayers += m_npending_adds;
221 m_npending_adds = 0;
222 }
223
224
readPlayerData(const QString & playername)225 PlayerData PlayerDatabase::readPlayerData(const QString& playername)
226 {
227 PlayerData pd;
228 QMap<QString, qint32>::Iterator it;
229 it = m_mapping.find(playername);
230 if(it == m_mapping.end())
231 {
232 //not in committed data, look in non-committed updates
233 QMap<QString, PlayerData>::Iterator it2;
234 it2 = m_pendingUpdates.find(playername);
235 if(it2 != m_pendingUpdates.end())
236 {
237 return it2.value();
238 }
239 else //give up
240 {
241 return pd;
242 }
243 }
244
245 qint32 pos = it.value();
246 m_datafile.seek(pos);//pointing to the player data
247
248 QString birthDate;
249 QString deathDate;
250 QString country;
251 QString title;
252
253 m_datads >> birthDate;
254 m_datads >> deathDate;
255 m_datads >> country;
256 m_datads >> title;
257
258 if(birthDate.contains('.'))
259 {
260 pd.setDateOfBirth(PartialDate(birthDate));
261 }
262 if(deathDate.contains('.'))
263 {
264 pd.setDateOfDeath(PartialDate(deathDate));
265 }
266
267 pd.setCountry(country);
268 pd.setTitle(title);
269
270 QList<qint32> eloList;
271 m_datads >> eloList;
272 pd.eloFromListData(eloList);
273
274 qint32 firstEloListIndex;
275 qint32 lastEloListIndex;
276 qint32 estimatedElo;
277 qint32 peakElo;
278 QImage photo;
279 QString biography;
280
281 m_datads >> firstEloListIndex;
282 m_datads >> lastEloListIndex;
283 m_datads >> estimatedElo;
284 m_datads >> peakElo;
285 m_datads >> photo;
286 m_datads >> biography;
287
288 pd.setFirstEloListIndex((int)firstEloListIndex);
289 pd.setLastEloListIndex((int)lastEloListIndex);
290 pd.setEstimatedElo((int)estimatedElo);
291 pd.setPeakElo((int)peakElo);
292 pd.setPhoto(photo);
293 pd.setBiography(biography);
294
295 return pd;
296 }
297
count() const298 unsigned int PlayerDatabase::count() const
299 {
300 return m_nplayers + m_npending_adds;
301 }
302
add(const QString & playername)303 bool PlayerDatabase::add(const QString& playername)
304 {
305 if(m_mapping.contains(playername) || m_pendingUpdates.contains(playername))
306 {
307 return false;
308 }
309 if(m_dirty) //previous current player was changed
310 {
311 m_pendingUpdates.insert(m_currentPlayerName, m_currentPlayer);
312 }
313 PlayerData pd;
314 m_currentPlayerName = playername;
315 m_currentPlayer = pd;
316 m_dirty = true;
317 ++m_npending_adds;
318 return true;
319 }
320
current() const321 QString PlayerDatabase::current() const
322 {
323 return m_currentPlayerName;
324 }
setCurrent(const QString & playername)325 void PlayerDatabase::setCurrent(const QString& playername)
326 {
327 if(m_currentPlayerName.compare(playername) == 0)
328 {
329 return;
330 }
331 if(m_dirty) //previous current player was changed
332 {
333 m_pendingUpdates.insert(m_currentPlayerName, m_currentPlayer);
334 }
335 m_currentPlayerName = playername;
336 m_currentPlayer = readPlayerData(playername);
337 m_dirty = false;
338 }
339
exists(const QString & playername) const340 bool PlayerDatabase::exists(const QString& playername) const
341 {
342 if(m_mapping.contains(playername))
343 {
344 return true;
345 }
346 if(m_pendingUpdates.contains(playername))
347 {
348 return true;
349 }
350 return false;
351 }
352
dateOfBirth() const353 PartialDate PlayerDatabase::dateOfBirth() const
354 {
355 return m_currentPlayer.dateOfBirth();
356 }
setDateOfBirth(const PartialDate & date)357 void PlayerDatabase::setDateOfBirth(const PartialDate& date)
358 {
359 m_currentPlayer.setDateOfBirth(date);
360 if(!m_dirty)
361 {
362 m_dirty = true;
363 }
364 }
365
dateOfDeath() const366 PartialDate PlayerDatabase::dateOfDeath() const
367 {
368 return m_currentPlayer.dateOfDeath();
369 }
setDateOfDeath(const PartialDate & date)370 void PlayerDatabase::setDateOfDeath(const PartialDate & date)
371 {
372 m_currentPlayer.setDateOfDeath(date);
373 if(!m_dirty)
374 {
375 m_dirty = true;
376 }
377 }
378
country() const379 QString PlayerDatabase::country() const
380 {
381 return m_currentPlayer.country();
382 }
setCountry(const QString & country)383 void PlayerDatabase::setCountry(const QString& country)
384 {
385 m_currentPlayer.setCountry(country);
386 if(!m_dirty)
387 {
388 m_dirty = true;
389 }
390 }
391
title() const392 QString PlayerDatabase::title() const
393 {
394 return m_currentPlayer.title();
395 }
setTitle(const QString & title)396 void PlayerDatabase::setTitle(const QString& title)
397 {
398 m_currentPlayer.setTitle(title);
399 if(!m_dirty)
400 {
401 m_dirty = true;
402 }
403 }
404
firstEloListIndex()405 int PlayerDatabase::firstEloListIndex()
406 {
407 return m_currentPlayer.firstEloListIndex();
408 }
lastEloListIndex()409 int PlayerDatabase::lastEloListIndex()
410 {
411 return m_currentPlayer.lastEloListIndex();
412 }
413
414
elo(const PartialDate & date) const415 int PlayerDatabase::elo(const PartialDate& date) const
416 {
417 return m_currentPlayer.elo(eloList(date));
418 }
elo(const int eloList) const419 int PlayerDatabase::elo(const int eloList) const
420 {
421 return m_currentPlayer.elo(eloList);
422 }
423
estimatedElo(const PartialDate & date)424 int PlayerDatabase::estimatedElo(const PartialDate& date)
425 {
426 return m_currentPlayer.estimatedElo(eloList(date));
427 }
estimatedEloNoCache(const PartialDate & date) const428 int PlayerDatabase::estimatedEloNoCache(const PartialDate& date) const
429 {
430 return m_currentPlayer.estimatedEloNoCache(eloList(date));
431 }
432
estimatedElo() const433 int PlayerDatabase::estimatedElo() const
434 {
435 return m_currentPlayer.estimatedElo();
436 }
highestElo() const437 int PlayerDatabase::highestElo() const
438 {
439 return m_currentPlayer.peakElo();
440 }
441
setElo(const int year,const int listIndex,const int elo)442 void PlayerDatabase::setElo(const int year, const int listIndex, const int elo)
443 {
444 m_currentPlayer.setElo(eloList(year, listIndex), elo);
445 if(!m_dirty)
446 {
447 m_dirty = true;
448 }
449 }
450
451
setEstimatedElo(const int elo)452 void PlayerDatabase::setEstimatedElo(const int elo)
453 {
454 m_currentPlayer.setEstimatedElo(elo);
455 if(!m_dirty)
456 {
457 m_dirty = true;
458 }
459 }
460
hasPhoto() const461 bool PlayerDatabase::hasPhoto() const
462 {
463 return !m_currentPlayer.photo().isNull();
464 }
photo() const465 QImage PlayerDatabase::photo() const
466 {
467 return m_currentPlayer.photo();
468 }
setPhoto(const QImage & img)469 void PlayerDatabase::setPhoto(const QImage& img)
470 {
471 m_currentPlayer.setPhoto(img);
472 if(!m_dirty)
473 {
474 m_dirty = true;
475 }
476 }
477
hasBiography() const478 bool PlayerDatabase::hasBiography() const
479 {
480 return !m_currentPlayer.biography().isNull();
481 }
biography() const482 QString PlayerDatabase::biography() const
483 {
484 return m_currentPlayer.biography();
485 }
setBiography(const QString & s)486 void PlayerDatabase::setBiography(const QString& s)
487 {
488 m_currentPlayer.setBiography(s);
489 if(!m_dirty)
490 {
491 m_dirty = true;
492 }
493 }
appendToBiography(const QString & s)494 void PlayerDatabase::appendToBiography(const QString& s)
495 {
496 m_currentPlayer.appendToBiography(s);
497 if(!m_dirty)
498 {
499 m_dirty = true;
500 }
501 }
502
503
playerNames()504 QStringList PlayerDatabase::playerNames()
505 {
506 QStringList result;
507 QMap<QString, qint32>::Iterator it;
508 for(it = m_mapping.begin(); it != m_mapping.end(); ++it)
509 {
510 result.push_back(it.key());
511 }
512 return result;
513 }
514
findPlayers(const QString & prefix,const int maxCount,const Qt::CaseSensitivity cs)515 QStringList PlayerDatabase::findPlayers(const QString& prefix, const int maxCount, const Qt::CaseSensitivity cs)
516 {
517 QStringList result;
518 QMap<QString, qint32>::Iterator it;
519 int i = 0;
520 for(it = m_mapping.begin(); it != m_mapping.end(); ++it)
521 {
522 if(it.key().startsWith(prefix, cs))
523 {
524 if(i >= maxCount)
525 {
526 break;
527 }
528 result.push_back(it.key());
529 ++i;
530 }
531 }
532 return result;
533 }
534
535
eloList(const PartialDate date) const536 int PlayerDatabase::eloList(const PartialDate date) const
537 {
538 const int year = date.year();
539 if(year < 1971)
540 {
541 return 0;
542 }
543 if(year < 2001) //2 lists in the year
544 {
545 return ((year - 1971) * 2) + 1 + (date.month() / 7);
546 }
547 return 60 + ((year - 2001) * 4) + 1 + (date.month() / 4); //4 lists in the year
548 }
549
eloList(const int year,const int index) const550 int PlayerDatabase::eloList(const int year, const int index) const
551 {
552 if(year < 1971)
553 {
554 return 0;
555 }
556 if(year < 2001) //2 lists in the year
557 {
558 return ((year - 1971) * 2) + index;
559 }
560 return 60 + ((year - 2001) * 4) + index; //4 lists in the year
561 }
562
eloListToDate(const int index)563 PartialDate PlayerDatabase::eloListToDate(const int index)
564 {
565 if(index < 1)
566 {
567 return PDInvalidDate;
568 }
569 int year;
570 int rem;
571 int month;
572 if(index < 61)
573 {
574 year = 1970 + ((index + 1) / 2);
575 rem = index % 2;
576 if(rem == 0)
577 {
578 month = 7;
579 }
580 else
581 {
582 month = 1;
583 }
584 }
585 else
586 {
587 year = 2000 + ((index - 57) / 4);
588 rem = index % 4;
589 if(rem == 0)
590 {
591 month = 10;
592 }
593 else if(rem == 1)
594 {
595 month = 1;
596 }
597 else if(rem == 2)
598 {
599 month = 4;
600 }
601 else
602 {
603 month = 7;
604 }
605 }
606 return PartialDate(year, month, 1);
607 }
608
609