1 #include "inisettings.hpp"
2 
3 #include <QTextStream>
4 #include <QFile>
5 #include <QStringList>
6 #include <QString>
7 #include <QRegExp>
8 #include <QDebug>
9 
IniSettings()10 Wizard::IniSettings::IniSettings()
11 {
12 }
13 
~IniSettings()14 Wizard::IniSettings::~IniSettings()
15 {
16 }
17 
findKeys(const QString & text)18 QStringList Wizard::IniSettings::findKeys(const QString &text)
19 {
20     QStringList result;
21 
22     for (const QString &key : mSettings.keys())
23     {
24 
25         if (key.startsWith(text))
26             result << key;
27 
28     }
29 
30     return result;
31 }
32 
readFile(QTextStream & stream)33 bool Wizard::IniSettings::readFile(QTextStream &stream)
34 {
35     // Look for a square bracket, "'\\["
36     // that has one or more "not nothing" in it, "([^]]+)"
37     // and is closed with a square bracket, "\\]"
38     QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]"));
39 
40     // Find any character(s) that is/are not equal sign(s), "[^=]+"
41     // followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
42     // and one or more periods, "(.+)"
43     QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$"));
44 
45     QString currentSection;
46 
47     while (!stream.atEnd())
48     {
49         const QString line(stream.readLine());
50 
51         if (line.isEmpty() || line.startsWith(QLatin1Char(';')))
52             continue;
53 
54         if (sectionRe.exactMatch(line))
55         {
56             currentSection = sectionRe.cap(1);
57         }
58         else if (keyRe.indexIn(line) != -1)
59         {
60             QString key = keyRe.cap(1).trimmed();
61             QString value = keyRe.cap(2).trimmed();
62 
63             // Append the section, but only if there is one
64             if (!currentSection.isEmpty())
65                 key = currentSection + QLatin1Char('/') + key;
66 
67             mSettings[key] = QVariant(value);
68         }
69     }
70 
71     return true;
72 }
73 
writeFile(const QString & path,QTextStream & stream)74 bool Wizard::IniSettings::writeFile(const QString &path, QTextStream &stream)
75 {
76     // Look for a square bracket, "'\\["
77     // that has one or more "not nothing" in it, "([^]]+)"
78     // and is closed with a square bracket, "\\]"
79     QRegExp sectionRe(QLatin1String("^\\[([^]]+)\\]"));
80 
81     // Find any character(s) that is/are not equal sign(s), "[^=]+"
82     // followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*"
83     // and one or more periods, "(.+)"
84     QRegExp keyRe(QLatin1String("^([^=]+)\\s*=\\s*(.+)$"));
85 
86     const QStringList keys(mSettings.keys());
87 
88     QString currentSection;
89     QString buffer;
90 
91     while (!stream.atEnd()) {
92 
93         const QString line(stream.readLine());
94 
95         if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) {
96             buffer.append(line + QLatin1String("\n"));
97             continue;
98         }
99 
100         if (sectionRe.exactMatch(line)) {
101             buffer.append(line + QLatin1String("\n"));
102             currentSection = sectionRe.cap(1);
103         } else  if (keyRe.indexIn(line) != -1) {
104             QString key(keyRe.cap(1).trimmed());
105             QString lookupKey(key);
106 
107             // Append the section, but only if there is one
108             if (!currentSection.isEmpty())
109                 lookupKey = currentSection + QLatin1Char('/') + key;
110 
111             buffer.append(key + QLatin1Char('=') + mSettings[lookupKey].toString() + QLatin1String("\n"));
112             mSettings.remove(lookupKey);
113         }
114     }
115 
116     // Add the new settings to the buffer
117     QHashIterator<QString, QVariant> i(mSettings);
118     while (i.hasNext()) {
119         i.next();
120 
121         QStringList fullKey(i.key().split(QLatin1Char('/')));
122         QString section(fullKey.at(0));
123         section.prepend(QLatin1Char('['));
124         section.append(QLatin1Char(']'));
125         QString key(fullKey.at(1));
126 
127         int index = buffer.lastIndexOf(section);
128         if (index == -1) {
129             // Add the section to the end of the file, because it's not found
130             buffer.append(QString("\n%1\n").arg(section));
131             index = buffer.lastIndexOf(section);
132         }
133 
134         // Look for the next section
135         index = buffer.indexOf(QLatin1Char('['), index + 1);
136 
137         if (index == -1 ) {
138             // We are at the last section, append it to the bottom of the file
139             buffer.append(QString("\n%1=%2").arg(key, i.value().toString()));
140             mSettings.remove(i.key());
141             continue;
142         } else {
143             // Not at last section, add the key at the index
144             buffer.insert(index - 1, QString("\n%1=%2").arg(key, i.value().toString()));
145             mSettings.remove(i.key());
146         }
147     }
148 
149     // Now we reopen the file, this time we write
150     QFile file(path);
151 
152     if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
153         QTextStream in(&file);
154         in.setCodec(stream.codec());
155 
156         // Write the updated buffer to an empty file
157         in << buffer;
158         file.flush();
159         file.close();
160     } else {
161         return false;
162     }
163 
164     return true;
165 }
166 
parseInx(const QString & path)167 bool Wizard::IniSettings::parseInx(const QString &path)
168 {
169     QFile file(path);
170 
171     if (file.open(QIODevice::ReadOnly))
172     {
173 
174         const QByteArray data(file.readAll());
175         const QByteArray pattern("\x21\x00\x1A\x01\x04\x00\x04\x97\xFF\x06", 10);
176 
177         int i = 0;
178         while ((i = data.indexOf(pattern, i)) != -1) {
179 
180             int next = data.indexOf(pattern, i + 1);
181             if (next == -1)
182                 break;
183 
184             QByteArray array(data.mid(i, (next - i)));
185 
186             // Skip some invalid entries
187             if (array.contains("\x04\x96\xFF")) {
188                 ++i;
189                 continue;
190             }
191 
192             // Remove the pattern from the beginning
193             array.remove(0, 12);
194 
195             int index = array.indexOf("\x06");
196             const QString section(array.left(index));
197 
198             // Figure how many characters to read for the key
199             int length = array.indexOf("\x06", section.length() + 3) - (section.length() + 3);
200             const QString key(array.mid(section.length() + 3, length));
201 
202             QString value(array.mid(section.length() + key.length() + 6));
203 
204             // Add the value
205             setValue(section + QLatin1Char('/') + key, QVariant(value));
206 
207             ++i;
208         }
209 
210         file.close();
211     } else {
212         qDebug() << "Failed to open INX file: " << path;
213         return false;
214     }
215 
216     return true;
217 }
218