1 #include "launchersettings.hpp"
2
3 #include <QTextStream>
4 #include <QString>
5 #include <QRegExp>
6 #include <QMultiMap>
7
8 #include <QDebug>
9
10 const char Config::LauncherSettings::sCurrentContentListKey[] = "Profiles/currentprofile";
11 const char Config::LauncherSettings::sLauncherConfigFileName[] = "launcher.cfg";
12 const char Config::LauncherSettings::sContentListsSectionPrefix[] = "Profiles/";
13 const char Config::LauncherSettings::sContentListSuffix[] = "/content";
14
subKeys(const QString & key)15 QStringList Config::LauncherSettings::subKeys(const QString &key)
16 {
17 QMultiMap<QString, QString> settings = SettingsBase::getSettings();
18 QStringList keys = settings.uniqueKeys();
19
20 QRegExp keyRe("(.+)/");
21
22 QStringList result;
23
24 for (const QString ¤tKey : keys)
25 {
26
27 if (keyRe.indexIn(currentKey) != -1)
28 {
29 QString prefixedKey = keyRe.cap(1);
30
31 if(prefixedKey.startsWith(key))
32 {
33 QString subKey = prefixedKey.remove(key);
34 if (!subKey.isEmpty())
35 result.append(subKey);
36 }
37 }
38 }
39
40 result.removeDuplicates();
41 return result;
42 }
43
44
writeFile(QTextStream & stream)45 bool Config::LauncherSettings::writeFile(QTextStream &stream)
46 {
47 QString sectionPrefix;
48 QRegExp sectionRe("([^/]+)/(.+)$");
49 QMultiMap<QString, QString> settings = SettingsBase::getSettings();
50
51 QMapIterator<QString, QString> i(settings);
52 i.toBack();
53
54 while (i.hasPrevious()) {
55 i.previous();
56
57 QString prefix;
58 QString key;
59
60 if (sectionRe.exactMatch(i.key())) {
61 prefix = sectionRe.cap(1);
62 key = sectionRe.cap(2);
63 }
64
65 // Get rid of legacy settings
66 if (key.contains(QChar('\\')))
67 continue;
68
69 if (key == QLatin1String("CurrentProfile"))
70 continue;
71
72 if (sectionPrefix != prefix) {
73 sectionPrefix = prefix;
74 stream << "\n[" << prefix << "]\n";
75 }
76
77 stream << key << "=" << i.value() << "\n";
78 }
79
80 return true;
81
82 }
83
getContentLists()84 QStringList Config::LauncherSettings::getContentLists()
85 {
86 return subKeys(QString(sContentListsSectionPrefix));
87 }
88
makeContentListKey(const QString & contentListName)89 QString Config::LauncherSettings::makeContentListKey(const QString& contentListName)
90 {
91 return QString(sContentListsSectionPrefix) + contentListName + QString(sContentListSuffix);
92 }
93
setContentList(const GameSettings & gameSettings)94 void Config::LauncherSettings::setContentList(const GameSettings& gameSettings)
95 {
96 // obtain content list from game settings (if present)
97 const QStringList files(gameSettings.getContentList());
98
99 // if openmw.cfg has no content, exit so we don't create an empty content list.
100 if (files.isEmpty())
101 {
102 return;
103 }
104
105 // if any existing profile in launcher matches the content list, make that profile the default
106 for (const QString &listName : getContentLists())
107 {
108 if (isEqual(files, getContentListFiles(listName)))
109 {
110 setCurrentContentListName(listName);
111 return;
112 }
113 }
114
115 // otherwise, add content list
116 QString newContentListName(makeNewContentListName());
117 setCurrentContentListName(newContentListName);
118 setContentList(newContentListName, files);
119 }
120
removeContentList(const QString & contentListName)121 void Config::LauncherSettings::removeContentList(const QString &contentListName)
122 {
123 remove(makeContentListKey(contentListName));
124 }
125
setCurrentContentListName(const QString & contentListName)126 void Config::LauncherSettings::setCurrentContentListName(const QString &contentListName)
127 {
128 remove(QString(sCurrentContentListKey));
129 setValue(QString(sCurrentContentListKey), contentListName);
130 }
131
setContentList(const QString & contentListName,const QStringList & fileNames)132 void Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& fileNames)
133 {
134 removeContentList(contentListName);
135 QString key = makeContentListKey(contentListName);
136 for (const QString& fileName : fileNames)
137 {
138 setMultiValue(key, fileName);
139 }
140 }
141
getCurrentContentListName() const142 QString Config::LauncherSettings::getCurrentContentListName() const
143 {
144 return value(QString(sCurrentContentListKey));
145 }
146
getContentListFiles(const QString & contentListName) const147 QStringList Config::LauncherSettings::getContentListFiles(const QString& contentListName) const
148 {
149 // QMap returns multiple rows in LIFO order, so need to reverse
150 return reverse(getSettings().values(makeContentListKey(contentListName)));
151 }
152
reverse(const QStringList & toReverse)153 QStringList Config::LauncherSettings::reverse(const QStringList& toReverse)
154 {
155 QStringList result;
156 result.reserve(toReverse.size());
157 std::reverse_copy(toReverse.begin(), toReverse.end(), std::back_inserter(result));
158 return result;
159 }
160
isEqual(const QStringList & list1,const QStringList & list2)161 bool Config::LauncherSettings::isEqual(const QStringList& list1, const QStringList& list2)
162 {
163 if (list1.count() != list2.count())
164 {
165 return false;
166 }
167
168 for (int i = 0; i < list1.count(); ++i)
169 {
170 if (list1.at(i) != list2.at(i))
171 {
172 return false;
173 }
174 }
175
176 // if get here, lists are same
177 return true;
178 }
179
makeNewContentListName()180 QString Config::LauncherSettings::makeNewContentListName()
181 {
182 // basically, use date and time as the name e.g. YYYY-MM-DDThh:mm:ss
183 time_t rawtime;
184 struct tm * timeinfo;
185
186 time(&rawtime);
187 timeinfo = localtime(&rawtime);
188 int base = 10;
189 QChar zeroPad('0');
190 return QString("%1-%2-%3T%4:%5:%6")
191 .arg(timeinfo->tm_year + 1900, 4).arg(timeinfo->tm_mon + 1, 2, base, zeroPad).arg(timeinfo->tm_mday, 2, base, zeroPad)
192 .arg(timeinfo->tm_hour, 2, base, zeroPad).arg(timeinfo->tm_min, 2, base, zeroPad).arg(timeinfo->tm_sec, 2, base, zeroPad);
193 }
194
195
196