doomseekerconfig.cpp
1 //------------------------------------------------------------------------------
2 // doomseekerconfig.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library 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 GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2010 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "doomseekerconfig.h"
24 
25 #include "configuration/queryspeed.h"
26 #include "datapaths.h"
27 #include "fileutils.h"
28 #include "gui/models/serverlistproxymodel.h"
29 #include "ini/ini.h"
30 #include "ini/inisection.h"
31 #include "ini/inivariable.h"
32 #include "ini/settingsproviderqt.h"
33 #include "localizationinfo.h"
34 #include "log.h"
35 #include "pathfinder/filealias.h"
36 #include "pathfinder/filesearchpath.h"
37 #include "plugins/engineplugin.h"
38 #include "scanner.h"
39 #include "strings.hpp"
40 #include "updater/updatechannel.h"
41 #include "version.h"
42 #include "wadseeker/wadseeker.h"
43 
44 #include <QLocale>
48 static PatternList readPre1Point2BuddiesList(const QString &configEntry)
49 {
50  PatternList patterns;
51 
52  Scanner listReader(configEntry.toUtf8().constData(), configEntry.length());
53  // Syntax: {basic|advanced} "pattern";...
54  while (listReader.tokensLeft())
55  {
56  if (!listReader.checkToken(TK_Identifier))
57  break; // Invalid so lets just use what we have.
58 
59  QRegExp::PatternSyntax syntax;
60  if (listReader->str().compare("basic") == 0)
61  syntax = QRegExp::Wildcard;
62  else
63  syntax = QRegExp::RegExp;
64 
65  if (!listReader.checkToken(TK_StringConst))
66  break;
67 
68  QRegExp pattern(listReader->str(), Qt::CaseInsensitive, syntax);
69  if (pattern.isValid())
70  patterns << pattern;
71 
72  if (!listReader.checkToken(';'))
73  break;
74  }
75  return patterns;
76 }
78 DoomseekerConfig *DoomseekerConfig::instance = nullptr;
79 
80 DoomseekerConfig::DoomseekerConfig()
81 {
82  this->dummySection = new IniSection(nullptr, QString());
83 }
84 
85 DoomseekerConfig::~DoomseekerConfig()
86 {
87  delete this->dummySection;
88 }
89 
91 {
92  if (instance == nullptr)
93  instance = new DoomseekerConfig();
94 
95  return *instance;
96 }
97 
99 {
100  if (instance != nullptr)
101  {
102  delete instance;
103  instance = nullptr;
104  }
105 }
106 
108 {
109  if (pluginName.isEmpty())
110  {
111  gLog << QObject::tr("DoomseekerConfig.iniSectionForPlugin(): empty plugin name has been specified, returning dummy IniSection.");
112  return *dummySection;
113  }
114 
115  if (!isValidPluginName(pluginName))
116  {
117  gLog << QObject::tr("DoomseekerConfig.iniSectionForPlugin(): plugin name is invalid: %1").arg(pluginName);
118  return *dummySection;
119  }
120 
121  if (this->pIni == nullptr)
122  setIniFile("");
123 
124  QString sectionName = pluginName;
125  sectionName = sectionName.replace(' ', "");
126  return this->pIni->section(sectionName);
127 }
128 
130 {
131  return iniSectionForPlugin(plugin->data()->name);
132 }
133 
134 bool DoomseekerConfig::isValidPluginName(const QString &pluginName) const
135 {
136  QString invalids[] = { "doomseeker", "wadseeker", "" };
137 
138  for (int i = 0; !invalids[i].isEmpty(); ++i)
139  {
140  if (pluginName.compare(invalids[i], Qt::CaseInsensitive) == 0)
141  return false;
142  }
143 
144  return true;
145 }
146 
148 {
149  if (pIni == nullptr)
150  return false;
151 
152  IniSection sectionDoomseeker = pIni->section(doomseeker.SECTION_NAME);
153  doomseeker.load(sectionDoomseeker);
154 
155  IniSection sectionServerFilter = pIni->section(serverFilter.SECTION_NAME);
156  serverFilter.load(sectionServerFilter);
157 
158  IniSection sectionWadseeker = pIni->section(wadseeker.SECTION_NAME);
159  wadseeker.load(sectionWadseeker);
160 
161  IniSection sectionAutoUpdates = pIni->section(autoUpdates.SECTION_NAME);
162  autoUpdates.load(sectionAutoUpdates);
163 
164  // Plugins should read their sections manually.
165 
166  return true;
167 }
168 
170 {
171  if (pIni == nullptr)
172  return false;
173 
174  /*
175  TODO:
176  Find a way to work around this.
177  const QString TOP_COMMENT = QObject::tr("This is %1 configuration file.\n\
178  Any modification done manually to this file is on your own risk.").arg(Version::fullVersionInfo());
179 
180  pIni->setIniTopComment(TOP_COMMENT);
181  */
182  IniSection sectionDoomseeker = pIni->section(doomseeker.SECTION_NAME);
183  doomseeker.save(sectionDoomseeker);
184 
185  IniSection sectionServerFilter = pIni->section(serverFilter.SECTION_NAME);
186  serverFilter.save(sectionServerFilter);
187 
188  IniSection sectionWadseeker = pIni->section(wadseeker.SECTION_NAME);
189  wadseeker.save(sectionWadseeker);
190 
191  IniSection sectionAutoUpdates = pIni->section(autoUpdates.SECTION_NAME);
192  autoUpdates.save(sectionAutoUpdates);
193 
194  // Plugins should save their sections manually.
195  if (this->settings->isWritable())
196  {
197  this->settings->sync();
198  return true;
199  }
200  return false;
201 }
202 
203 bool DoomseekerConfig::setIniFile(const QString &filePath)
204 {
205  // Delete old instances if necessary.
206  this->pIni.reset();
207  this->settingsProvider.reset();
208  this->settings.reset();
209 
210  gLog << QObject::tr("Setting INI file: %1").arg(filePath);
211  // Create new instances.
212  this->settings.reset(new QSettings(filePath, QSettings::IniFormat));
213  this->settingsProvider.reset(new SettingsProviderQt(this->settings.data()));
214  this->pIni.reset(new Ini(this->settingsProvider.data()));
215 
216  // Init.
217  IniSection section;
218 
219  section = this->pIni->section(doomseeker.SECTION_NAME);
220  doomseeker.init(section);
221 
222  section = this->pIni->section(serverFilter.SECTION_NAME);
223  serverFilter.init(section);
224 
225  section = this->pIni->section(wadseeker.SECTION_NAME);
226  wadseeker.init(section);
227 
228  section = this->pIni->section(autoUpdates.SECTION_NAME);
229  autoUpdates.init(section);
230 
231  return true;
232 }
233 
234 QList<FileSearchPath> DoomseekerConfig::combinedWadseekPaths() const
235 {
236  QList<FileSearchPath> paths = doomseeker.wadPaths;
237  paths << wadseeker.targetDirectory;
238  return paths;
239 }
240 
242 DClass<DoomseekerConfig::DoomseekerCfg>
243 {
244 public:
245  IniSection section;
246  QuerySpeed querySpeed;
247 
248  QString slotStyle() const
249  {
250  // Slot styles were indexed in older versions of Doomseeker.
251  // This here provides compatibility layer that allows to load configuration
252  // files from those versions.
253  const int NUM_SLOTSTYLES = 2;
254  const char *indexedSlotStyles[NUM_SLOTSTYLES] = { "marines", "blocks" };
255  bool isInt = false;
256  int numeric = section["SlotStyle"].value().toInt(&isInt);
257  if (isInt && numeric >= 0 && numeric < NUM_SLOTSTYLES)
258  return indexedSlotStyles[numeric];
259  else
260  return section["SlotStyle"].valueString();
261  }
262 };
263 
265 
266 const QString DoomseekerConfig::DoomseekerCfg::SECTION_NAME = "Doomseeker";
267 
268 DoomseekerConfig::DoomseekerCfg::DoomseekerCfg()
269 {
270  this->bBotsAreNotPlayers = true;
271  this->bCloseToTrayIcon = false;
272  this->bColorizeServerConsole = true;
273  this->bDrawGridInServerTable = false;
274  this->bHidePasswords = false;
275  this->bGroupServersWithPlayersAtTheTopOfTheList = true;
276  this->bIP2CountryAutoUpdate = true;
277  this->bLogCreatedServer = false;
278  this->bLookupHosts = true;
279  this->bQueryAutoRefreshDontIfActive = true;
280  this->bQueryAutoRefreshEnabled = false;
281  this->bQueryBeforeLaunch = true;
282  this->bQueryOnStartup = true;
283  this->bRecordDemo = false;
284  this->bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn = true;
285  this->bCheckTheIntegrityOfWads = true;
286  this->bUseTrayIcon = false;
287  this->bMarkServersWithBuddies = true;
288  this->buddyServersColor = "#5ecf75";
289  this->customServersColor = "#ffaa00";
290  this->lanServersColor = "#92ebe5";
291  this->localization = LocalizationInfo::SYSTEM_FOLLOW.localeName;
292  this->mainWindowState = "";
293  this->mainWindowGeometry = "";
294  this->queryAutoRefreshEverySeconds = 180;
295  setQuerySpeed(QuerySpeed::aggressive());
296  this->previousCreateServerConfigDir = "";
297  this->previousCreateServerExecDir = "";
298  this->previousCreateServerLogDir = "";
299  this->previousCreateServerWadDir = "";
300  this->slotStyle = 1;
301  this->serverListSortIndex = -1;
302  this->serverListSortDirection = Qt::DescendingOrder;
303  this->wadPaths = FileSearchPath::fromStringList(gDefaultDataPaths->defaultWadPaths());
304 }
305 
306 DoomseekerConfig::DoomseekerCfg::~DoomseekerCfg()
307 {
308 }
309 
310 QList<ColumnSort> DoomseekerConfig::DoomseekerCfg::additionalSortColumns() const
311 {
312  QList<ColumnSort> list;
313  QVariantList varList = d->section.value("AdditionalSortColumns").toList();
314  for (const QVariant &var : varList)
315  {
316  list << ColumnSort::deserializeQVariant(var);
317  }
318  return list;
319 }
320 
321 void DoomseekerConfig::DoomseekerCfg::setAdditionalSortColumns(const QList<ColumnSort> &val)
322 {
323  QVariantList varList;
324  for (const ColumnSort &elem : val)
325  {
326  varList << elem.serializeQVariant();
327  }
328  d->section.setValue("AdditionalSortColumns", varList);
329 }
330 
332 {
333  // TODO: Make all methods use d->section
334  d->section = section;
335  section.createSetting("Localization", this->localization);
336  section.createSetting("BotsAreNotPlayers", this->bBotsAreNotPlayers);
337  section.createSetting("CloseToTrayIcon", this->bCloseToTrayIcon);
338  section.createSetting("ColorizeServerConsole", this->bColorizeServerConsole);
339  section.createSetting("DrawGridInServerTable", this->bDrawGridInServerTable);
340  section.createSetting("HidePasswords", this->bHidePasswords);
341  section.createSetting("GroupServersWithPlayersAtTheTopOfTheList", this->bGroupServersWithPlayersAtTheTopOfTheList);
342  section.createSetting("IP2CAutoUpdate", this->bIP2CountryAutoUpdate);
343  section.createSetting("LogCreatedServers", this->bLogCreatedServer);
344  section.createSetting("LookupHosts", this->bLookupHosts);
345  section.createSetting("QueryAutoRefreshDontIfActive", this->bQueryAutoRefreshDontIfActive);
346  section.createSetting("QueryAutoRefreshEnabled", this->bQueryAutoRefreshEnabled);
347  section.createSetting("QueryOnStartup", this->bQueryOnStartup);
348  section.createSetting("RecordDemo", this->bRecordDemo);
349  section.createSetting("TellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn", this->bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn);
350  section.createSetting("CheckTheIntegrityOfWads", this->bCheckTheIntegrityOfWads);
351  section.createSetting("UseTrayIcon", this->bUseTrayIcon);
352  section.createSetting("MarkServersWithBuddies", this->bMarkServersWithBuddies);
353  section.createSetting("BuddyServersColor", this->buddyServersColor);
354  section.createSetting("CustomServersColor", this->customServersColor);
355  section.createSetting("LanServersColor", this->lanServersColor);
356  section.createSetting("QueryAutoRefreshEverySeconds", this->queryAutoRefreshEverySeconds);
357  section.createSetting("QueryServerInterval", this->querySpeed().intervalBetweenServers);
358  section.createSetting("QueryServerTimeout", this->querySpeed().delayBetweenSingleServerAttempts);
359  section.createSetting("QueryAttemptsPerServer", this->querySpeed().attemptsPerServer);
360  section.createSetting("SlotStyle", this->slotStyle);
361  section.createSetting("ServerListSortIndex", this->serverListSortIndex);
362  section.createSetting("ServerListSortDirection", this->serverListSortDirection);
363  section.createSetting("WadPaths", FileSearchPath::toVariantList(this->wadPaths));
364 
365  initWadAlias();
366 }
367 
368 void DoomseekerConfig::DoomseekerCfg::initWadAlias()
369 {
370  if (!d->section.hasSetting("WadAliases"))
371  setWadAliases(FileAlias::standardWadAliases());
372 }
373 
374 void DoomseekerConfig::DoomseekerCfg::load(IniSection &section)
375 {
376  this->localization = (const QString &)section["Localization"];
377  this->bBotsAreNotPlayers = section["BotsAreNotPlayers"];
378  this->bCloseToTrayIcon = section["CloseToTrayIcon"];
379  this->bColorizeServerConsole = section["ColorizeServerConsole"];
380  this->bDrawGridInServerTable = section["DrawGridInServerTable"];
381  this->bHidePasswords = section["HidePasswords"];
382  this->bGroupServersWithPlayersAtTheTopOfTheList = section["GroupServersWithPlayersAtTheTopOfTheList"];
383  this->bIP2CountryAutoUpdate = section["IP2CAutoUpdate"];
384  this->bLogCreatedServer = section["LogCreatedServers"];
385  this->bLookupHosts = section["LookupHosts"];
386  this->bQueryAutoRefreshDontIfActive = section["QueryAutoRefreshDontIfActive"];
387  this->bQueryAutoRefreshEnabled = section["QueryAutoRefreshEnabled"];
388  this->bQueryBeforeLaunch = section["QueryBeforeLaunch"];
389  this->bQueryOnStartup = section["QueryOnStartup"];
390  this->bRecordDemo = section["RecordDemo"];
391  this->bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn = section["TellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn"];
392  this->bCheckTheIntegrityOfWads = section["CheckTheIntegrityOfWads"];
393  this->bUseTrayIcon = section["UseTrayIcon"];
394  this->bMarkServersWithBuddies = section["MarkServersWithBuddies"];
395  this->buddyServersColor = (const QString &)section["BuddyServersColor"];
396  this->customServersColor = (const QString &)section["CustomServersColor"];
397  this->lanServersColor = (const QString &)section["LanServersColor"];
398  this->mainWindowState = (const QString &)section["MainWindowState"];
399  this->mainWindowGeometry = section.value("MainWindowGeometry", "").toByteArray();
400  this->queryAutoRefreshEverySeconds = section["QueryAutoRefreshEverySeconds"];
401  d->querySpeed.intervalBetweenServers = section["QueryServerInterval"];
402  d->querySpeed.delayBetweenSingleServerAttempts = section["QueryServerTimeout"];
403  d->querySpeed.attemptsPerServer = section["QueryAttemptsPerServer"];
404  this->previousCreateServerConfigDir = (const QString &)section["PreviousCreateServerConfigDir"];
405  this->previousCreateServerExecDir = (const QString &)section["PreviousCreateServerExecDir"];
406  this->previousCreateServerLogDir = (const QString &)section["PreviousCreateServerLogDir"];
407  this->previousCreateServerWadDir = (const QString &)section["PreviousCreateServerWadDir"];
408  this->serverListColumnState = (const QString &)section["ServerListColumnState"];
409  this->serverListSortIndex = section["ServerListSortIndex"];
410  this->serverListSortDirection = section["ServerListSortDirection"];
411  this->slotStyle = d->slotStyle();
412 
413  // Complex data variables.
414 
415  // Custom servers
416  // CustomServers2 is for Doomseeker >= 1.2.
417  // Deserialization of this setting is not backwards-compatible,
418  // hence we need an extra compatibility layer. The amount of
419  // information that the user could potentially lose if I had
420  // been too lazy to code this would be insufferable.
421  QList<CustomServerInfo> customServersList;
422  CustomServers::decodeConfigEntries(section["CustomServers2"], customServersList);
423  QList<CustomServerInfo> backwardCompatibleServers;
424  CustomServers::decodeConfigEntries(section["CustomServers"], backwardCompatibleServers);
425  for (const CustomServerInfo &backwardCompatibleServer : backwardCompatibleServers)
426  {
427  bool known = false;
428  for (const CustomServerInfo &knownServer : customServersList)
429  {
430  if (knownServer.isSameServer(backwardCompatibleServer))
431  {
432  known = true;
433  break;
434  }
435  }
436  if (!known)
437  customServersList << backwardCompatibleServer;
438  }
439  this->customServers = customServersList.toVector();
440 
441  // WAD paths
442  // Backward compatibility, XXX once new stable is released:
443  QVariant variantWadPaths = section["WadPaths"].value();
444  if (variantWadPaths.isValid() && variantWadPaths.toList().isEmpty())
445  {
446  // Backward compatibility continued:
447  wadPaths.clear();
448  QStringList paths = variantWadPaths.toString().split(";");
449  for (const QString &path : paths)
450  {
451  wadPaths << FileSearchPath(path);
452  }
453  }
454  else
455  {
456  // This is not a part of XXX, this is proper, current behavior:
457  wadPaths = FileSearchPath::fromVariantList(section["WadPaths"].value().toList());
458  }
459  // End of backward compatibility for WAD paths.
460 
461  // Buddies list
462  if (section.hasSetting("Buddies"))
463  this->buddies = PatternList::deserializeQVariant(section.value("Buddies"));
464  else if (section.hasSetting("BuddiesList"))
465  {
466  // Backward compatibility, pre 1.2.
467  this->buddies = readPre1Point2BuddiesList(section["BuddiesList"]);
468  }
469 }
470 
471 void DoomseekerConfig::DoomseekerCfg::save(IniSection &section)
472 {
473  section["Localization"] = this->localization;
474  section["BotsAreNotPlayers"] = this->bBotsAreNotPlayers;
475  section["CloseToTrayIcon"] = this->bCloseToTrayIcon;
476  section["ColorizeServerConsole"] = this->bColorizeServerConsole;
477  section["DrawGridInServerTable"] = this->bDrawGridInServerTable;
478  section["HidePasswords"] = this->bHidePasswords;
479  section["GroupServersWithPlayersAtTheTopOfTheList"] = this->bGroupServersWithPlayersAtTheTopOfTheList;
480  section["IP2CAutoUpdate"] = this->bIP2CountryAutoUpdate;
481  section["LogCreatedServers"] = this->bLogCreatedServer;
482  section["LookupHosts"] = this->bLookupHosts;
483  section["QueryAutoRefreshDontIfActive"] = this->bQueryAutoRefreshDontIfActive;
484  section["QueryAutoRefreshEnabled"] = this->bQueryAutoRefreshEnabled;
485  section["QueryBeforeLaunch"] = this->bQueryBeforeLaunch;
486  section["QueryOnStartup"] = this->bQueryOnStartup;
487  section["RecordDemo"] = this->bRecordDemo;
488  section["TellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn"] = this->bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn;
489  section["CheckTheIntegrityOfWads"] = this->bCheckTheIntegrityOfWads;
490  section["UseTrayIcon"] = this->bUseTrayIcon;
491  section["MarkServersWithBuddies"] = this->bMarkServersWithBuddies;
492  section["BuddyServersColor"] = this->buddyServersColor;
493  section["CustomServersColor"] = this->customServersColor;
494  section["LanServersColor"] = this->lanServersColor;
495  section["MainWindowState"] = this->mainWindowState;
496  section.setValue("MainWindowGeometry", this->mainWindowGeometry);
497  section["QueryAutoRefreshEverySeconds"] = this->queryAutoRefreshEverySeconds;
498  section["QueryServerInterval"] = this->querySpeed().intervalBetweenServers;
499  section["QueryServerTimeout"] = this->querySpeed().delayBetweenSingleServerAttempts;
500  section["QueryAttemptsPerServer"] = this->querySpeed().attemptsPerServer;
501  section["PreviousCreateServerConfigDir"] = this->previousCreateServerConfigDir;
502  section["PreviousCreateServerExecDir"] = this->previousCreateServerExecDir;
503  section["PreviousCreateServerLogDir"] = this->previousCreateServerLogDir;
504  section["PreviousCreateServerWadDir"] = this->previousCreateServerWadDir;
505  section["ServerListColumnState"] = this->serverListColumnState;
506  section["ServerListSortIndex"] = this->serverListSortIndex;
507  section["ServerListSortDirection"] = this->serverListSortDirection;
508  section["SlotStyle"] = this->slotStyle;
509 
510  // Complex data variables.
511 
512  // Custom servers
513  QStringList allCustomServers; // backward compatibility for Doomseeker <1.2
514  QStringList allCustomServers2; // Doomseeker >=1.2
515  for (const CustomServerInfo &customServer : this->customServers)
516  {
517  QString engineName = QUrl::toPercentEncoding(customServer.engine, "", "()");
518  QString address = QUrl::toPercentEncoding(customServer.host, "", "()");
519 
520  QString customServerStringPrefix = QString("(%1;%2;%3")
521  .arg(engineName).arg(address)
522  .arg(customServer.port);
523  QString customServerString2 = QString("%1;%2)")
524  .arg(customServerStringPrefix)
525  .arg(customServer.enabled ? 1 : 0);
526 
527  allCustomServers << customServerStringPrefix + ")";
528  allCustomServers2 << customServerString2;
529  }
530  section["CustomServers"] = allCustomServers.join(";");
531  section["CustomServers2"] = allCustomServers2.join(";");
532 
533  section["WadPaths"].setValue(FileSearchPath::toVariantList(this->wadPaths));
534  section["Buddies"].setValue(this->buddies.serializeQVariant());
535 }
536 
537 const QuerySpeed &DoomseekerConfig::DoomseekerCfg::querySpeed() const
538 {
539  return d->querySpeed;
540 }
541 
542 void DoomseekerConfig::DoomseekerCfg::setQuerySpeed(const QuerySpeed &val)
543 {
544  d->querySpeed = val;
545 }
546 
547 QList<FileAlias> DoomseekerConfig::DoomseekerCfg::wadAliases() const
548 {
549  QList<FileAlias> list;
550  QVariantList varList = d->section.value("WadAliases").toList();
551  for (const QVariant &var : varList)
552  {
553  list << FileAlias::deserializeQVariant(var);
554  }
555  return FileAliasList::mergeDuplicates(list);
556 }
557 
558 void DoomseekerConfig::DoomseekerCfg::setWadAliases(const QList<FileAlias> &val)
559 {
560  QVariantList varList;
561  for (const FileAlias &elem : val)
562  {
563  varList << elem.serializeQVariant();
564  }
565  d->section.setValue("WadAliases", varList);
566 }
567 
568 void DoomseekerConfig::DoomseekerCfg::enableFreedoomInstallation(const QString &dir)
569 {
570  if (!FileUtils::containsPath(wadPathsOnly(), dir))
571  wadPaths.prepend(dir);
572  QList<FileAlias> aliases = wadAliases();
573  aliases << FileAlias::freeDoom1Aliases();
574  aliases << FileAlias::freeDoom2Aliases();
575  aliases = FileAliasList::mergeDuplicates(aliases);
576  setWadAliases(aliases);
577 }
578 
579 QStringList DoomseekerConfig::DoomseekerCfg::wadPathsOnly() const
580 {
581  QStringList result;
582  for (const FileSearchPath &path : wadPaths)
583  {
584  result << path.path();
585  }
586  return result;
587 }
589 const QString DoomseekerConfig::AutoUpdates::SECTION_NAME = "Doomseeker_AutoUpdates";
590 
591 void DoomseekerConfig::AutoUpdates::init(IniSection &section)
592 {
593  section.createSetting("UpdateChannelName", UpdateChannel::mkStable().name());
594  section.createSetting("UpdateMode", (int) UM_NotifyOnly);
595  section.createSetting("LastKnownUpdateRevisions", QVariant());
596  section.createSetting("bPerformUpdateOnNextRun", false);
597 }
598 
599 void DoomseekerConfig::AutoUpdates::load(IniSection &section)
600 {
601  updateChannelName = (const QString &)section["UpdateChannelName"];
602  updateMode = (UpdateMode)section["UpdateMode"].value().toInt();
603  QVariantMap lastKnownUpdateRevisionsVariant = section["LastKnownUpdateRevisions"].value().toMap();
604  lastKnownUpdateRevisions.clear();
605  for (const QString &package : lastKnownUpdateRevisionsVariant.keys())
606  {
607  QVariant revisionVariant = lastKnownUpdateRevisionsVariant[package];
608  lastKnownUpdateRevisions.insert(package, revisionVariant.toString());
609  }
610  bPerformUpdateOnNextRun = section["bPerformUpdateOnNextRun"].value().toBool();
611 }
612 
613 void DoomseekerConfig::AutoUpdates::save(IniSection &section)
614 {
615  section["UpdateChannelName"] = updateChannelName;
616  section["UpdateMode"] = updateMode;
617  QVariantMap revisionsVariantMap;
618  for (const QString &package : lastKnownUpdateRevisions.keys())
619  {
620  revisionsVariantMap.insert(package, lastKnownUpdateRevisions[package]);
621  }
622  section["LastKnownUpdateRevisions"].setValue(revisionsVariantMap);
623  section["bPerformUpdateOnNextRun"].setValue(bPerformUpdateOnNextRun);
624 }
626 const QString DoomseekerConfig::ServerFilter::SECTION_NAME = "ServerFilter";
627 
628 void DoomseekerConfig::ServerFilter::init(IniSection &section)
629 {
630  section.createSetting("bEnabled", true);
631  section.createSetting("bShowEmpty", true);
632  section.createSetting("bShowFull", true);
633  section.createSetting("bShowOnlyValid", false);
634  section.createSetting("GameModes", QStringList());
635  section.createSetting("GameModesExcluded", QStringList());
636  section.createSetting("MaxPing", 0);
637  section.createSetting("ServerName", "");
638  section.createSetting("TestingServers", Doomseeker::Indifferent);
639  section.createSetting("WADs", QStringList());
640  section.createSetting("WADsExcluded", QStringList());
641 }
642 
643 void DoomseekerConfig::ServerFilter::load(IniSection &section)
644 {
645  info.bEnabled = section["bEnabled"];
646  info.bShowEmpty = section["bShowEmpty"];
647  info.bShowFull = section["bShowFull"];
648  info.bShowOnlyValid = section["bShowOnlyValid"];
649  info.gameModes = section["GameModes"].value().toStringList();
650  info.gameModesExcluded = section["GameModesExcluded"].value().toStringList();
651  info.maxPing = section["MaxPing"];
652  info.serverName = (const QString &)section["ServerName"];
653  info.testingServers = static_cast<Doomseeker::ShowMode>(section.value("TestingServers").toInt());
654  info.wads = section["WADs"].value().toStringList();
655  info.wadsExcluded = section["WADsExcluded"].value().toStringList();
656 }
657 
658 void DoomseekerConfig::ServerFilter::save(IniSection &section)
659 {
660  section["bEnabled"] = info.bEnabled;
661  section["bShowEmpty"] = info.bShowEmpty;
662  section["bShowFull"] = info.bShowFull;
663  section["bShowOnlyValid"] = info.bShowOnlyValid;
664  section["GameModes"].setValue(info.gameModes);
665  section["GameModesExcluded"].setValue(info.gameModesExcluded);
666  section["MaxPing"] = info.maxPing;
667  section["ServerName"] = info.serverName;
668  section["TestingServers"] = info.testingServers;
669  section["WADs"].setValue(info.wads);
670  section["WADsExcluded"].setValue(info.wadsExcluded);
671 }
673 const QString DoomseekerConfig::WadseekerCfg::SECTION_NAME = "Wadseeker";
674 
675 DoomseekerConfig::WadseekerCfg::WadseekerCfg()
676 {
677  this->bAlwaysUseDefaultSites = true;
678  this->bSearchInIdgames = true;
679  this->bSearchInWadArchive = true;
680  this->colorMessageCriticalError = "#ff0000";
681  this->colorMessageError = "#ff0000";
682  this->colorMessageNotice = "#000000";
683  this->connectTimeoutSeconds = WADSEEKER_CONNECT_TIMEOUT_SECONDS_DEFAULT;
684  this->downloadTimeoutSeconds = WADSEEKER_DOWNLOAD_TIMEOUT_SECONDS_DEFAULT;
685  this->idgamesURL = Wadseeker::defaultIdgamesUrl();
686  this->maxConcurrentSiteDownloads = 3;
687  this->maxConcurrentWadDownloads = 2;
688  this->targetDirectory = gDefaultDataPaths->programsDataDirectoryPath();
689 
690  // Search URLs remains uninitialized here. It will be initialized
691  // by init() and then load() since Doomseeker always calls these
692  // methods in this order.
693 }
694 
696 {
697  section.createSetting("AlwaysUseDefaultSites", this->bAlwaysUseDefaultSites);
698  section.createSetting("SearchInIdgames", this->bSearchInIdgames);
699  section.createSetting("SearchInWadArchive", this->bSearchInWadArchive);
700  section.createSetting("ColorMessageCriticalError", this->colorMessageCriticalError);
701  section.createSetting("ColorMessageError", this->colorMessageError);
702  section.createSetting("ColorMessageNotice", this->colorMessageNotice);
703  section.createSetting("ConnectTimeoutSeconds", this->connectTimeoutSeconds);
704  section.createSetting("DownloadTimeoutSeconds", this->downloadTimeoutSeconds);
705  section.createSetting("IdgamesApiURL", this->idgamesURL);
706  section.createSetting("MaxConcurrentSiteDownloads", this->maxConcurrentSiteDownloads);
707  section.createSetting("MaxConcurrentWadDownloads", this->maxConcurrentWadDownloads);
708  section.createSetting("SearchURLs", Wadseeker::defaultSitesListEncoded().join(";"));
709  section.createSetting("TargetDirectory", this->targetDirectory);
710 }
711 
712 void DoomseekerConfig::WadseekerCfg::load(IniSection &section)
713 {
714  this->bAlwaysUseDefaultSites = section["AlwaysUseDefaultSites"];
715  this->bSearchInIdgames = section["SearchInIdgames"];
716  this->bSearchInWadArchive = section["SearchInWadArchive"];
717  this->colorMessageCriticalError = (const QString &)section["ColorMessageCriticalError"];
718  this->colorMessageError = (const QString &)section["ColorMessageError"];
719  this->colorMessageNotice = (const QString &)section["ColorMessageNotice"];
720  this->connectTimeoutSeconds = section["ConnectTimeoutSeconds"];
721  this->downloadTimeoutSeconds = section["DownloadTimeoutSeconds"];
722  this->idgamesURL = (const QString &)section["IdgamesApiURL"];
723  this->maxConcurrentSiteDownloads = section["MaxConcurrentSiteDownloads"];
724  this->maxConcurrentWadDownloads = section["MaxConcurrentWadDownloads"];
725  this->targetDirectory = (const QString &)section["TargetDirectory"];
726 
727  // Complex data values
728  this->searchURLs.clear();
729  QStringList urlList = section["SearchURLs"].valueString().split(";", QString::SkipEmptyParts);
730  for (const QString &url : urlList)
731  {
732  this->searchURLs << QUrl::fromPercentEncoding(url.toUtf8());
733  }
734 }
735 
736 void DoomseekerConfig::WadseekerCfg::save(IniSection &section)
737 {
738  section["AlwaysUseDefaultSites"] = this->bAlwaysUseDefaultSites;
739  section["SearchInIdgames"] = this->bSearchInIdgames;
740  section["SearchInWadArchive"] = this->bSearchInWadArchive;
741  section["ColorMessageCriticalError"] = this->colorMessageCriticalError;
742  section["ColorMessageError"] = this->colorMessageError;
743  section["ColorMessageNotice"] = this->colorMessageNotice;
744  section["ConnectTimeoutSeconds"] = this->connectTimeoutSeconds;
745  section["DownloadTimeoutSeconds"] = this->downloadTimeoutSeconds;
746  section["IdgamesApiURL"] = this->idgamesURL;
747  section["MaxConcurrentSiteDownloads"] = this->maxConcurrentSiteDownloads;
748  section["MaxConcurrentWadDownloads"] = this->maxConcurrentWadDownloads;
749  section["TargetDirectory"] = this->targetDirectory;
750 
751  // Complex data values
752  QStringList urlEncodedList;
753  for (const QString &url : this->searchURLs)
754  {
755  urlEncodedList << QUrl::toPercentEncoding(url);
756  }
757  section["SearchURLs"] = urlEncodedList.join(";");
758 }
IniVariable createSetting(const QString &name, const QVariant &data)
Inits specified variable with specified data.
Definition: inisection.cpp:57
void init(IniSection &section)
Initializes values that are not present in the section yet.
bool saveToFile()
Saves current settings to ini file. This file must be previously set by setIniFile() method...
QString localeName
Compliant with language_country standard. See QLocale::name()
This Singleton holds entire Doomseeker configuration in memory.
static bool containsPath(const QStringList &candidates, const QString &path)
Uses QFileInfo::operator== to see if &#39;path&#39; is on &#39;candidates&#39; list.
Definition: fileutils.cpp:69
QVariant value(const QString &key) const
Retrieves a variable directly; omits the IniVariable system.
Definition: inisection.cpp:148
IniSection iniSectionForPlugin(const QString &pluginName)
This will assume that the .ini file is initialized.
static QList< FileAlias > standardWadAliases()
Standard/default aliases for configuration init.
Definition: filealias.cpp:169
bool hasSetting(const QString &name) const
true if setting of given name exists within the section.
Definition: inisection.cpp:78
static void decodeConfigEntries(const QString &str, QList< CustomServerInfo > &outCustomServerInfoList)
void init(IniSection &section)
Initializes values that are not present in the section yet.
Configuration handler.
Definition: ini.h:69
bool readFromFile()
Reads settings from ini file. This file must be previously set by setIniFile() method.
static UpdateChannel mkStable()
Creates "stable" channel object.
static DoomseekerConfig & config()
Returns the Singleton.
void setValue(const QString &key, const QVariant &value)
Sets a variable directly. Omits the IniVariable system.
Definition: inisection.cpp:140
INI section representation.
Definition: inisection.h:40
Scanner reads scripts by checking individual tokens.
Definition: scanner.h:75
bool setIniFile(const QString &filePath)
Initializes the Ini class instance to point to a file.
static void dispose()
Disposes of the Singleton.
static const LocalizationInfo SYSTEM_FOLLOW