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 &currentKey : 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