1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** Contact: Qt Software Information ([email protected])
|
---|
5 | **
|
---|
6 | ** This file is part of the tools applications of the Qt Toolkit.
|
---|
7 | **
|
---|
8 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
9 | ** Commercial Usage
|
---|
10 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
11 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
12 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
13 | ** a written agreement between you and Nokia.
|
---|
14 | **
|
---|
15 | ** GNU Lesser General Public License Usage
|
---|
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
17 | ** General Public License version 2.1 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
19 | ** packaging of this file. Please review the following information to
|
---|
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
22 | **
|
---|
23 | ** In addition, as a special exception, Nokia gives you certain
|
---|
24 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
---|
25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
---|
26 | ** package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you are unsure which license is appropriate for your use, please
|
---|
37 | ** contact the sales department at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "rcc.h"
|
---|
43 |
|
---|
44 | #include <QtCore/QByteArray>
|
---|
45 | #include <QtCore/QDateTime>
|
---|
46 | #include <QtCore/QDebug>
|
---|
47 | #include <QtCore/QDir>
|
---|
48 | #include <QtCore/QDirIterator>
|
---|
49 | #include <QtCore/QFile>
|
---|
50 | #include <QtCore/QIODevice>
|
---|
51 | #include <QtCore/QLocale>
|
---|
52 | #include <QtCore/QStack>
|
---|
53 |
|
---|
54 | #include <QtXml/QDomDocument>
|
---|
55 |
|
---|
56 | QT_BEGIN_NAMESPACE
|
---|
57 |
|
---|
58 | enum {
|
---|
59 | CONSTANT_USENAMESPACE = 1,
|
---|
60 | CONSTANT_COMPRESSLEVEL_DEFAULT = -1,
|
---|
61 | CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70
|
---|
62 | };
|
---|
63 |
|
---|
64 |
|
---|
65 | #define writeString(s) write(s, sizeof(s))
|
---|
66 |
|
---|
67 | void RCCResourceLibrary::write(const char *str, int len)
|
---|
68 | {
|
---|
69 | --len; // trailing \0 on string literals...
|
---|
70 | int n = m_out.size();
|
---|
71 | m_out.resize(n + len);
|
---|
72 | memcpy(m_out.data() + n, str, len);
|
---|
73 | }
|
---|
74 |
|
---|
75 | void RCCResourceLibrary::writeByteArray(const QByteArray &other)
|
---|
76 | {
|
---|
77 | m_out.append(other);
|
---|
78 | }
|
---|
79 |
|
---|
80 | static inline QString msgOpenReadFailed(const QString &fname, const QString &why)
|
---|
81 | {
|
---|
82 | return QString::fromUtf8("Unable to open %1 for reading: %2\n").arg(fname).arg(why);
|
---|
83 | }
|
---|
84 |
|
---|
85 |
|
---|
86 | ///////////////////////////////////////////////////////////
|
---|
87 | //
|
---|
88 | // RCCFileInfo
|
---|
89 | //
|
---|
90 | ///////////////////////////////////////////////////////////
|
---|
91 |
|
---|
92 | class RCCFileInfo
|
---|
93 | {
|
---|
94 | public:
|
---|
95 | enum Flags
|
---|
96 | {
|
---|
97 | NoFlags = 0x00,
|
---|
98 | Compressed = 0x01,
|
---|
99 | Directory = 0x02
|
---|
100 | };
|
---|
101 |
|
---|
102 | RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(),
|
---|
103 | QLocale::Language language = QLocale::C,
|
---|
104 | QLocale::Country country = QLocale::AnyCountry,
|
---|
105 | uint flags = NoFlags,
|
---|
106 | int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT,
|
---|
107 | int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT);
|
---|
108 | ~RCCFileInfo();
|
---|
109 |
|
---|
110 | QString resourceName() const;
|
---|
111 |
|
---|
112 | public:
|
---|
113 | qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage);
|
---|
114 | qint64 writeDataName(RCCResourceLibrary &, qint64 offset);
|
---|
115 | void writeDataInfo(RCCResourceLibrary &lib);
|
---|
116 |
|
---|
117 | int m_flags;
|
---|
118 | QString m_name;
|
---|
119 | QLocale::Language m_language;
|
---|
120 | QLocale::Country m_country;
|
---|
121 | QFileInfo m_fileInfo;
|
---|
122 | RCCFileInfo *m_parent;
|
---|
123 | QHash<QString, RCCFileInfo*> m_children;
|
---|
124 | int m_compressLevel;
|
---|
125 | int m_compressThreshold;
|
---|
126 |
|
---|
127 | qint64 m_nameOffset;
|
---|
128 | qint64 m_dataOffset;
|
---|
129 | qint64 m_childOffset;
|
---|
130 | };
|
---|
131 |
|
---|
132 | RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
|
---|
133 | QLocale::Language language, QLocale::Country country, uint flags,
|
---|
134 | int compressLevel, int compressThreshold)
|
---|
135 | {
|
---|
136 | m_name = name;
|
---|
137 | m_fileInfo = fileInfo;
|
---|
138 | m_language = language;
|
---|
139 | m_country = country;
|
---|
140 | m_flags = flags;
|
---|
141 | m_parent = 0;
|
---|
142 | m_nameOffset = 0;
|
---|
143 | m_dataOffset = 0;
|
---|
144 | m_childOffset = 0;
|
---|
145 | m_compressLevel = compressLevel;
|
---|
146 | m_compressThreshold = compressThreshold;
|
---|
147 | }
|
---|
148 |
|
---|
149 | RCCFileInfo::~RCCFileInfo()
|
---|
150 | {
|
---|
151 | qDeleteAll(m_children);
|
---|
152 | }
|
---|
153 |
|
---|
154 | QString RCCFileInfo::resourceName() const
|
---|
155 | {
|
---|
156 | QString resource = m_name;
|
---|
157 | for (RCCFileInfo *p = m_parent; p; p = p->m_parent)
|
---|
158 | resource = resource.prepend(p->m_name + QLatin1Char('/'));
|
---|
159 | return QLatin1Char(':') + resource;
|
---|
160 | }
|
---|
161 |
|
---|
162 | void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
|
---|
163 | {
|
---|
164 | const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
|
---|
165 | //some info
|
---|
166 | if (text) {
|
---|
167 | if (m_language != QLocale::C) {
|
---|
168 | lib.writeString(" // ");
|
---|
169 | lib.writeByteArray(resourceName().toLocal8Bit());
|
---|
170 | lib.writeString(" [");
|
---|
171 | lib.writeByteArray(QByteArray::number(m_country));
|
---|
172 | lib.writeString("::");
|
---|
173 | lib.writeByteArray(QByteArray::number(m_language));
|
---|
174 | lib.writeString("[\n ");
|
---|
175 | } else {
|
---|
176 | lib.writeString(" // ");
|
---|
177 | lib.writeByteArray(resourceName().toLocal8Bit());
|
---|
178 | lib.writeString("\n ");
|
---|
179 | }
|
---|
180 | }
|
---|
181 |
|
---|
182 | //pointer data
|
---|
183 | if (m_flags & RCCFileInfo::Directory) {
|
---|
184 | // name offset
|
---|
185 | lib.writeNumber4(m_nameOffset);
|
---|
186 |
|
---|
187 | // flags
|
---|
188 | lib.writeNumber2(m_flags);
|
---|
189 |
|
---|
190 | // child count
|
---|
191 | lib.writeNumber4(m_children.size());
|
---|
192 |
|
---|
193 | // first child offset
|
---|
194 | lib.writeNumber4(m_childOffset);
|
---|
195 | } else {
|
---|
196 | // name offset
|
---|
197 | lib.writeNumber4(m_nameOffset);
|
---|
198 |
|
---|
199 | // flags
|
---|
200 | lib.writeNumber2(m_flags);
|
---|
201 |
|
---|
202 | // locale
|
---|
203 | lib.writeNumber2(m_country);
|
---|
204 | lib.writeNumber2(m_language);
|
---|
205 |
|
---|
206 | //data offset
|
---|
207 | lib.writeNumber4(m_dataOffset);
|
---|
208 | }
|
---|
209 | if (text)
|
---|
210 | lib.writeChar('\n');
|
---|
211 | }
|
---|
212 |
|
---|
213 | qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
|
---|
214 | QString *errorMessage)
|
---|
215 | {
|
---|
216 | const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
|
---|
217 |
|
---|
218 | //capture the offset
|
---|
219 | m_dataOffset = offset;
|
---|
220 |
|
---|
221 | //find the data to be written
|
---|
222 | QFile file(m_fileInfo.absoluteFilePath());
|
---|
223 | if (!file.open(QFile::ReadOnly)) {
|
---|
224 | *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
|
---|
225 | return 0;
|
---|
226 | }
|
---|
227 | QByteArray data = file.readAll();
|
---|
228 |
|
---|
229 | #ifndef QT_NO_COMPRESS
|
---|
230 | // Check if compression is useful for this file
|
---|
231 | if (m_compressLevel != 0 && data.size() != 0) {
|
---|
232 | QByteArray compressed =
|
---|
233 | qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
|
---|
234 |
|
---|
235 | int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
|
---|
236 | if (compressRatio >= m_compressThreshold) {
|
---|
237 | data = compressed;
|
---|
238 | m_flags |= Compressed;
|
---|
239 | }
|
---|
240 | }
|
---|
241 | #endif // QT_NO_COMPRESS
|
---|
242 |
|
---|
243 | // some info
|
---|
244 | if (text) {
|
---|
245 | lib.writeString(" // ");
|
---|
246 | lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
|
---|
247 | lib.writeString("\n ");
|
---|
248 | }
|
---|
249 |
|
---|
250 | // write the length
|
---|
251 |
|
---|
252 | lib.writeNumber4(data.size());
|
---|
253 | if (text)
|
---|
254 | lib.writeString("\n ");
|
---|
255 | offset += 4;
|
---|
256 |
|
---|
257 | // write the payload
|
---|
258 | const char *p = data.constData();
|
---|
259 | if (text) {
|
---|
260 | for (int i = data.size(), j = 0; --i >= 0; --j) {
|
---|
261 | lib.writeHex(*p++);
|
---|
262 | if (j == 0) {
|
---|
263 | lib.writeString("\n ");
|
---|
264 | j = 16;
|
---|
265 | }
|
---|
266 | }
|
---|
267 | } else {
|
---|
268 | for (int i = data.size(); --i >= 0; )
|
---|
269 | lib.writeChar(*p++);
|
---|
270 | }
|
---|
271 | offset += data.size();
|
---|
272 |
|
---|
273 | // done
|
---|
274 | if (text)
|
---|
275 | lib.writeString("\n ");
|
---|
276 | return offset;
|
---|
277 | }
|
---|
278 |
|
---|
279 | qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
|
---|
280 | {
|
---|
281 | const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
|
---|
282 |
|
---|
283 | // capture the offset
|
---|
284 | m_nameOffset = offset;
|
---|
285 |
|
---|
286 | // some info
|
---|
287 | if (text) {
|
---|
288 | lib.writeString(" // ");
|
---|
289 | lib.writeByteArray(m_name.toLocal8Bit());
|
---|
290 | lib.writeString("\n ");
|
---|
291 | }
|
---|
292 |
|
---|
293 | // write the length
|
---|
294 | lib.writeNumber2(m_name.length());
|
---|
295 | if (text)
|
---|
296 | lib.writeString("\n ");
|
---|
297 | offset += 2;
|
---|
298 |
|
---|
299 | // write the hash
|
---|
300 | lib.writeNumber4(qHash(m_name));
|
---|
301 | if (text)
|
---|
302 | lib.writeString("\n ");
|
---|
303 | offset += 4;
|
---|
304 |
|
---|
305 | // write the m_name
|
---|
306 | const QChar *unicode = m_name.unicode();
|
---|
307 | for (int i = 0; i < m_name.length(); ++i) {
|
---|
308 | lib.writeNumber2(unicode[i].unicode());
|
---|
309 | if (text && i % 16 == 0)
|
---|
310 | lib.writeString("\n ");
|
---|
311 | }
|
---|
312 | offset += m_name.length()*2;
|
---|
313 |
|
---|
314 | // done
|
---|
315 | if (text)
|
---|
316 | lib.writeString("\n ");
|
---|
317 | return offset;
|
---|
318 | }
|
---|
319 |
|
---|
320 |
|
---|
321 | ///////////////////////////////////////////////////////////
|
---|
322 | //
|
---|
323 | // RCCResourceLibrary
|
---|
324 | //
|
---|
325 | ///////////////////////////////////////////////////////////
|
---|
326 |
|
---|
327 | RCCResourceLibrary::Strings::Strings() :
|
---|
328 | TAG_RCC(QLatin1String("RCC")),
|
---|
329 | TAG_RESOURCE(QLatin1String("qresource")),
|
---|
330 | TAG_FILE(QLatin1String("file")),
|
---|
331 | ATTRIBUTE_LANG(QLatin1String("lang")),
|
---|
332 | ATTRIBUTE_PREFIX(QLatin1String("prefix")),
|
---|
333 | ATTRIBUTE_ALIAS(QLatin1String("alias")),
|
---|
334 | ATTRIBUTE_THRESHOLD(QLatin1String("threshold")),
|
---|
335 | ATTRIBUTE_COMPRESS(QLatin1String("compress"))
|
---|
336 | {
|
---|
337 | }
|
---|
338 |
|
---|
339 | RCCResourceLibrary::RCCResourceLibrary()
|
---|
340 | : m_root(0),
|
---|
341 | m_format(C_Code),
|
---|
342 | m_verbose(false),
|
---|
343 | m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
|
---|
344 | m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
|
---|
345 | m_treeOffset(0),
|
---|
346 | m_namesOffset(0),
|
---|
347 | m_dataOffset(0),
|
---|
348 | m_useNameSpace(CONSTANT_USENAMESPACE),
|
---|
349 | m_errorDevice(0)
|
---|
350 | {
|
---|
351 | m_out.reserve(30 * 1000 * 1000);
|
---|
352 | }
|
---|
353 |
|
---|
354 | RCCResourceLibrary::~RCCResourceLibrary()
|
---|
355 | {
|
---|
356 | delete m_root;
|
---|
357 | }
|
---|
358 |
|
---|
359 | bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
|
---|
360 | const QString &fname, QString currentPath, bool ignoreErrors)
|
---|
361 | {
|
---|
362 | Q_ASSERT(m_errorDevice);
|
---|
363 | const QChar slash = QLatin1Char('/');
|
---|
364 | if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
|
---|
365 | currentPath += slash;
|
---|
366 |
|
---|
367 | QDomDocument document;
|
---|
368 | {
|
---|
369 | QString errorMsg;
|
---|
370 | int errorLine = 0;
|
---|
371 | int errorColumn = 0;
|
---|
372 | if (!document.setContent(inputDevice, &errorMsg, &errorLine, &errorColumn)) {
|
---|
373 | if (ignoreErrors)
|
---|
374 | return true;
|
---|
375 | const QString msg = QString::fromUtf8("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMsg);
|
---|
376 | m_errorDevice->write(msg.toUtf8());
|
---|
377 | return false;
|
---|
378 | }
|
---|
379 | }
|
---|
380 |
|
---|
381 | QDomElement domRoot = document.firstChildElement(m_strings.TAG_RCC).toElement();
|
---|
382 | if (!domRoot.isNull() && domRoot.tagName() == m_strings.TAG_RCC) {
|
---|
383 | for (QDomNode node = domRoot.firstChild(); !node.isNull(); node = node.nextSibling()) {
|
---|
384 | if (!node.isElement())
|
---|
385 | continue;
|
---|
386 |
|
---|
387 | QDomElement child = node.toElement();
|
---|
388 | if (!child.isNull() && child.tagName() == m_strings.TAG_RESOURCE) {
|
---|
389 | QLocale::Language language = QLocale::c().language();
|
---|
390 | QLocale::Country country = QLocale::c().country();
|
---|
391 |
|
---|
392 | if (child.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
|
---|
393 | QString attribute = child.attribute(m_strings.ATTRIBUTE_LANG);
|
---|
394 | QLocale lang = QLocale(attribute);
|
---|
395 | language = lang.language();
|
---|
396 | if (2 == attribute.length()) {
|
---|
397 | // Language only
|
---|
398 | country = QLocale::AnyCountry;
|
---|
399 | } else {
|
---|
400 | country = lang.country();
|
---|
401 | }
|
---|
402 | }
|
---|
403 |
|
---|
404 | QString prefix;
|
---|
405 | if (child.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
|
---|
406 | prefix = child.attribute(m_strings.ATTRIBUTE_PREFIX);
|
---|
407 | if (!prefix.startsWith(slash))
|
---|
408 | prefix.prepend(slash);
|
---|
409 | if (!prefix.endsWith(slash))
|
---|
410 | prefix += slash;
|
---|
411 |
|
---|
412 | for (QDomNode res = child.firstChild(); !res.isNull(); res = res.nextSibling()) {
|
---|
413 | if (res.isElement() && res.toElement().tagName() == m_strings.TAG_FILE) {
|
---|
414 |
|
---|
415 | QString fileName(res.firstChild().toText().data());
|
---|
416 | if (fileName.isEmpty()) {
|
---|
417 | const QString msg = QString::fromUtf8("RCC: Warning: Null node in XML of '%1'\n").arg(fname);
|
---|
418 | m_errorDevice->write(msg.toUtf8());
|
---|
419 | }
|
---|
420 | QString alias;
|
---|
421 | if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_ALIAS))
|
---|
422 | alias = res.toElement().attribute(m_strings.ATTRIBUTE_ALIAS);
|
---|
423 | else
|
---|
424 | alias = fileName;
|
---|
425 |
|
---|
426 | int compressLevel = m_compressLevel;
|
---|
427 | if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_COMPRESS))
|
---|
428 | compressLevel = res.toElement().attribute(m_strings.ATTRIBUTE_COMPRESS).toInt();
|
---|
429 | int compressThreshold = m_compressThreshold;
|
---|
430 | if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
|
---|
431 | compressThreshold = res.toElement().attribute(m_strings.ATTRIBUTE_THRESHOLD).toInt();
|
---|
432 |
|
---|
433 | // Special case for -no-compress. Overrides all other settings.
|
---|
434 | if (m_compressLevel == -2)
|
---|
435 | compressLevel = 0;
|
---|
436 |
|
---|
437 | alias = QDir::cleanPath(alias);
|
---|
438 | while (alias.startsWith(QLatin1String("../")))
|
---|
439 | alias.remove(0, 3);
|
---|
440 | alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
|
---|
441 |
|
---|
442 | QString absFileName = fileName;
|
---|
443 | if (QDir::isRelativePath(absFileName))
|
---|
444 | absFileName.prepend(currentPath);
|
---|
445 | QFileInfo file(absFileName);
|
---|
446 | if (!file.exists()) {
|
---|
447 | m_failedResources.push_back(absFileName);
|
---|
448 | const QString msg = QString::fromUtf8("RCC: Error in '%1': Cannot find file '%2'\n").arg(fname).arg(fileName);
|
---|
449 | m_errorDevice->write(msg.toUtf8());
|
---|
450 | if (ignoreErrors)
|
---|
451 | continue;
|
---|
452 | else
|
---|
453 | return false;
|
---|
454 | } else if (file.isFile()) {
|
---|
455 | const bool arc = addFile(alias, RCCFileInfo(alias.section(slash, -1), file, language, country,
|
---|
456 | RCCFileInfo::NoFlags, compressLevel, compressThreshold));
|
---|
457 | if (!arc)
|
---|
458 | m_failedResources.push_back(absFileName);
|
---|
459 | } else {
|
---|
460 | QDir dir;
|
---|
461 | if (file.isDir()) {
|
---|
462 | dir.setPath(file.filePath());
|
---|
463 | } else {
|
---|
464 | dir.setPath(file.path());
|
---|
465 | dir.setNameFilters(QStringList(file.fileName()));
|
---|
466 | if (alias.endsWith(file.fileName()))
|
---|
467 | alias = alias.left(alias.length()-file.fileName().length());
|
---|
468 | }
|
---|
469 | if (!alias.endsWith(slash))
|
---|
470 | alias += slash;
|
---|
471 | QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
|
---|
472 | while (it.hasNext()) {
|
---|
473 | it.next();
|
---|
474 | QFileInfo child(it.fileInfo());
|
---|
475 | if (child.fileName() != QLatin1String(".") && child.fileName() != QLatin1String("..")) {
|
---|
476 | const bool arc = addFile(alias + child.fileName(),
|
---|
477 | RCCFileInfo(child.fileName(), child, language, country,
|
---|
478 | RCCFileInfo::NoFlags, compressLevel, compressThreshold));
|
---|
479 | if (!arc)
|
---|
480 | m_failedResources.push_back(child.fileName());
|
---|
481 | }
|
---|
482 | }
|
---|
483 | }
|
---|
484 | }
|
---|
485 | }
|
---|
486 | }
|
---|
487 | }
|
---|
488 | }
|
---|
489 | if (m_root == 0) {
|
---|
490 | const QString msg = QString::fromUtf8("RCC: Warning: No resources in '%1'.\n").arg(fname);
|
---|
491 | m_errorDevice->write(msg.toUtf8());
|
---|
492 | if (!ignoreErrors && m_format == Binary) {
|
---|
493 | // create dummy entry, otherwise loading qith QResource will crash
|
---|
494 | m_root = new RCCFileInfo(QString(), QFileInfo(),
|
---|
495 | QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
|
---|
496 | }
|
---|
497 | }
|
---|
498 |
|
---|
499 | return true;
|
---|
500 | }
|
---|
501 |
|
---|
502 | bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
|
---|
503 | {
|
---|
504 | Q_ASSERT(m_errorDevice);
|
---|
505 | if (file.m_fileInfo.size() > 0xffffffff) {
|
---|
506 | const QString msg = QString::fromUtf8("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
|
---|
507 | m_errorDevice->write(msg.toUtf8());
|
---|
508 | return false;
|
---|
509 | }
|
---|
510 | if (!m_root)
|
---|
511 | m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
|
---|
512 |
|
---|
513 | RCCFileInfo *parent = m_root;
|
---|
514 | const QStringList nodes = alias.split(QLatin1Char('/'));
|
---|
515 | for (int i = 1; i < nodes.size()-1; ++i) {
|
---|
516 | const QString node = nodes.at(i);
|
---|
517 | if (node.isEmpty())
|
---|
518 | continue;
|
---|
519 | if (!parent->m_children.contains(node)) {
|
---|
520 | RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
|
---|
521 | s->m_parent = parent;
|
---|
522 | parent->m_children.insert(node, s);
|
---|
523 | parent = s;
|
---|
524 | } else {
|
---|
525 | parent = parent->m_children[node];
|
---|
526 | }
|
---|
527 | }
|
---|
528 |
|
---|
529 | const QString filename = nodes.at(nodes.size()-1);
|
---|
530 | RCCFileInfo *s = new RCCFileInfo(file);
|
---|
531 | s->m_parent = parent;
|
---|
532 | parent->m_children.insertMulti(filename, s);
|
---|
533 | return true;
|
---|
534 | }
|
---|
535 |
|
---|
536 | void RCCResourceLibrary::reset()
|
---|
537 | {
|
---|
538 | if (m_root) {
|
---|
539 | delete m_root;
|
---|
540 | m_root = 0;
|
---|
541 | }
|
---|
542 | m_errorDevice = 0;
|
---|
543 | m_failedResources.clear();
|
---|
544 | }
|
---|
545 |
|
---|
546 |
|
---|
547 | bool RCCResourceLibrary::readFiles(bool ignoreErrors, QIODevice &errorDevice)
|
---|
548 | {
|
---|
549 | reset();
|
---|
550 | m_errorDevice = &errorDevice;
|
---|
551 | //read in data
|
---|
552 | if (m_verbose) {
|
---|
553 | const QString msg = QString::fromUtf8("Processing %1 files [%2]\n")
|
---|
554 | .arg(m_fileNames.size()).arg(static_cast<int>(ignoreErrors));
|
---|
555 | m_errorDevice->write(msg.toUtf8());
|
---|
556 | }
|
---|
557 | for (int i = 0; i < m_fileNames.size(); ++i) {
|
---|
558 | QFile fileIn;
|
---|
559 | QString fname = m_fileNames.at(i);
|
---|
560 | QString pwd;
|
---|
561 | if (fname == QLatin1String("-")) {
|
---|
562 | fname = QLatin1String("(stdin)");
|
---|
563 | pwd = QDir::currentPath();
|
---|
564 | fileIn.setFileName(fname);
|
---|
565 | if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
|
---|
566 | m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
|
---|
567 | return false;
|
---|
568 | }
|
---|
569 | } else {
|
---|
570 | pwd = QFileInfo(fname).path();
|
---|
571 | fileIn.setFileName(fname);
|
---|
572 | if (!fileIn.open(QIODevice::ReadOnly)) {
|
---|
573 | m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
|
---|
574 | return false;
|
---|
575 | }
|
---|
576 | }
|
---|
577 | if (m_verbose) {
|
---|
578 | const QString msg = QString::fromUtf8("Interpreting %1\n").arg(fname);
|
---|
579 | m_errorDevice->write(msg.toUtf8());
|
---|
580 | }
|
---|
581 |
|
---|
582 | if (!interpretResourceFile(&fileIn, fname, pwd, ignoreErrors))
|
---|
583 | return false;
|
---|
584 | }
|
---|
585 | return true;
|
---|
586 | }
|
---|
587 |
|
---|
588 | QStringList RCCResourceLibrary::dataFiles() const
|
---|
589 | {
|
---|
590 | QStringList ret;
|
---|
591 | QStack<RCCFileInfo*> pending;
|
---|
592 |
|
---|
593 | if (!m_root)
|
---|
594 | return ret;
|
---|
595 | pending.push(m_root);
|
---|
596 | while (!pending.isEmpty()) {
|
---|
597 | RCCFileInfo *file = pending.pop();
|
---|
598 | for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
|
---|
599 | it != file->m_children.end(); ++it) {
|
---|
600 | RCCFileInfo *child = it.value();
|
---|
601 | if (child->m_flags & RCCFileInfo::Directory)
|
---|
602 | pending.push(child);
|
---|
603 | ret.append(child->m_fileInfo.filePath());
|
---|
604 | }
|
---|
605 | }
|
---|
606 | return ret;
|
---|
607 | }
|
---|
608 |
|
---|
609 | // Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
|
---|
610 | static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
|
---|
611 | {
|
---|
612 | typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
|
---|
613 | const QChar slash = QLatin1Char('/');
|
---|
614 | const ChildConstIterator cend = m_root->m_children.constEnd();
|
---|
615 | for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) {
|
---|
616 | const RCCFileInfo *child = it.value();
|
---|
617 | QString childName = path;
|
---|
618 | childName += slash;
|
---|
619 | childName += child->m_name;
|
---|
620 | if (child->m_flags & RCCFileInfo::Directory) {
|
---|
621 | resourceDataFileMapRecursion(child, childName, m);
|
---|
622 | } else {
|
---|
623 | m.insert(childName, child->m_fileInfo.filePath());
|
---|
624 | }
|
---|
625 | }
|
---|
626 | }
|
---|
627 |
|
---|
628 | RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap() const
|
---|
629 | {
|
---|
630 | ResourceDataFileMap rc;
|
---|
631 | if (m_root)
|
---|
632 | resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')), rc);
|
---|
633 | return rc;
|
---|
634 | }
|
---|
635 |
|
---|
636 | bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &errorDevice)
|
---|
637 | {
|
---|
638 | m_errorDevice = &errorDevice;
|
---|
639 | //write out
|
---|
640 | if (m_verbose)
|
---|
641 | m_errorDevice->write("Outputting code\n");
|
---|
642 | if (!writeHeader()) {
|
---|
643 | m_errorDevice->write("Could not write header\n");
|
---|
644 | return false;
|
---|
645 | }
|
---|
646 | if (m_root) {
|
---|
647 | if (!writeDataBlobs()) {
|
---|
648 | m_errorDevice->write("Could not write data blobs.\n");
|
---|
649 | return false;
|
---|
650 | }
|
---|
651 | if (!writeDataNames()) {
|
---|
652 | m_errorDevice->write("Could not write file names\n");
|
---|
653 | return false;
|
---|
654 | }
|
---|
655 | if (!writeDataStructure()) {
|
---|
656 | m_errorDevice->write("Could not write data tree\n");
|
---|
657 | return false;
|
---|
658 | }
|
---|
659 | }
|
---|
660 | if (!writeInitializer()) {
|
---|
661 | m_errorDevice->write("Could not write footer\n");
|
---|
662 | return false;
|
---|
663 | }
|
---|
664 | outDevice.write(m_out, m_out.size());
|
---|
665 | return true;
|
---|
666 | }
|
---|
667 |
|
---|
668 | void RCCResourceLibrary::writeHex(quint8 tmp)
|
---|
669 | {
|
---|
670 | const char * const digits = "0123456789abcdef";
|
---|
671 | writeChar('0');
|
---|
672 | writeChar('x');
|
---|
673 | if (tmp < 16) {
|
---|
674 | writeChar(digits[tmp]);
|
---|
675 | } else {
|
---|
676 | writeChar(digits[tmp >> 4]);
|
---|
677 | writeChar(digits[tmp & 0xf]);
|
---|
678 | }
|
---|
679 | writeChar(',');
|
---|
680 | }
|
---|
681 |
|
---|
682 | void RCCResourceLibrary::writeNumber2(quint16 number)
|
---|
683 | {
|
---|
684 | if (m_format == RCCResourceLibrary::Binary) {
|
---|
685 | writeChar(number >> 8);
|
---|
686 | writeChar(number);
|
---|
687 | } else {
|
---|
688 | writeHex(number >> 8);
|
---|
689 | writeHex(number);
|
---|
690 | }
|
---|
691 | }
|
---|
692 |
|
---|
693 | void RCCResourceLibrary::writeNumber4(quint32 number)
|
---|
694 | {
|
---|
695 | if (m_format == RCCResourceLibrary::Binary) {
|
---|
696 | writeChar(number >> 24);
|
---|
697 | writeChar(number >> 16);
|
---|
698 | writeChar(number >> 8);
|
---|
699 | writeChar(number);
|
---|
700 | } else {
|
---|
701 | writeHex(number >> 24);
|
---|
702 | writeHex(number >> 16);
|
---|
703 | writeHex(number >> 8);
|
---|
704 | writeHex(number);
|
---|
705 | }
|
---|
706 | }
|
---|
707 |
|
---|
708 | bool RCCResourceLibrary::writeHeader()
|
---|
709 | {
|
---|
710 | if (m_format == C_Code) {
|
---|
711 | writeString("/****************************************************************************\n");
|
---|
712 | writeString("** Resource object code\n");
|
---|
713 | writeString("**\n");
|
---|
714 | writeString("** Created: ");
|
---|
715 | writeByteArray(QDateTime::currentDateTime().toString().toLatin1());
|
---|
716 | writeString("\n** by: The Resource Compiler for Qt version ");
|
---|
717 | writeByteArray(QT_VERSION_STR);
|
---|
718 | writeString("\n**\n");
|
---|
719 | writeString("** WARNING! All changes made in this file will be lost!\n");
|
---|
720 | writeString( "*****************************************************************************/\n\n");
|
---|
721 | writeString("#include <QtCore/qglobal.h>\n\n");
|
---|
722 | } else if (m_format == Binary) {
|
---|
723 | writeString("qres");
|
---|
724 | writeNumber4(0);
|
---|
725 | writeNumber4(0);
|
---|
726 | writeNumber4(0);
|
---|
727 | writeNumber4(0);
|
---|
728 | }
|
---|
729 | return true;
|
---|
730 | }
|
---|
731 |
|
---|
732 | bool RCCResourceLibrary::writeDataBlobs()
|
---|
733 | {
|
---|
734 | Q_ASSERT(m_errorDevice);
|
---|
735 | if (m_format == C_Code)
|
---|
736 | writeString("static const unsigned char qt_resource_data[] = {\n");
|
---|
737 | else if (m_format == Binary)
|
---|
738 | m_dataOffset = m_out.size();
|
---|
739 | QStack<RCCFileInfo*> pending;
|
---|
740 |
|
---|
741 | if (!m_root)
|
---|
742 | return false;
|
---|
743 |
|
---|
744 | pending.push(m_root);
|
---|
745 | qint64 offset = 0;
|
---|
746 | QString errorMessage;
|
---|
747 | while (!pending.isEmpty()) {
|
---|
748 | RCCFileInfo *file = pending.pop();
|
---|
749 | for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
|
---|
750 | it != file->m_children.end(); ++it) {
|
---|
751 | RCCFileInfo *child = it.value();
|
---|
752 | if (child->m_flags & RCCFileInfo::Directory)
|
---|
753 | pending.push(child);
|
---|
754 | else {
|
---|
755 | offset = child->writeDataBlob(*this, offset, &errorMessage);
|
---|
756 | if (offset == 0)
|
---|
757 | m_errorDevice->write(errorMessage.toUtf8());
|
---|
758 | }
|
---|
759 | }
|
---|
760 | }
|
---|
761 | if (m_format == C_Code)
|
---|
762 | writeString("\n};\n\n");
|
---|
763 | return true;
|
---|
764 | }
|
---|
765 |
|
---|
766 | bool RCCResourceLibrary::writeDataNames()
|
---|
767 | {
|
---|
768 | if (m_format == C_Code)
|
---|
769 | writeString("static const unsigned char qt_resource_name[] = {\n");
|
---|
770 | else if (m_format == Binary)
|
---|
771 | m_namesOffset = m_out.size();
|
---|
772 |
|
---|
773 | QHash<QString, int> names;
|
---|
774 | QStack<RCCFileInfo*> pending;
|
---|
775 |
|
---|
776 | if (!m_root)
|
---|
777 | return false;
|
---|
778 |
|
---|
779 | pending.push(m_root);
|
---|
780 | qint64 offset = 0;
|
---|
781 | while (!pending.isEmpty()) {
|
---|
782 | RCCFileInfo *file = pending.pop();
|
---|
783 | for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
|
---|
784 | it != file->m_children.end(); ++it) {
|
---|
785 | RCCFileInfo *child = it.value();
|
---|
786 | if (child->m_flags & RCCFileInfo::Directory)
|
---|
787 | pending.push(child);
|
---|
788 | if (names.contains(child->m_name)) {
|
---|
789 | child->m_nameOffset = names.value(child->m_name);
|
---|
790 | } else {
|
---|
791 | names.insert(child->m_name, offset);
|
---|
792 | offset = child->writeDataName(*this, offset);
|
---|
793 | }
|
---|
794 | }
|
---|
795 | }
|
---|
796 | if (m_format == C_Code)
|
---|
797 | writeString("\n};\n\n");
|
---|
798 | return true;
|
---|
799 | }
|
---|
800 |
|
---|
801 | static bool qt_rcc_compare_hash(const RCCFileInfo *left, const RCCFileInfo *right)
|
---|
802 | {
|
---|
803 | return qHash(left->m_name) < qHash(right->m_name);
|
---|
804 | }
|
---|
805 |
|
---|
806 | bool RCCResourceLibrary::writeDataStructure()
|
---|
807 | {
|
---|
808 | if (m_format == C_Code)
|
---|
809 | writeString("static const unsigned char qt_resource_struct[] = {\n");
|
---|
810 | else if (m_format == Binary)
|
---|
811 | m_treeOffset = m_out.size();
|
---|
812 | QStack<RCCFileInfo*> pending;
|
---|
813 |
|
---|
814 | if (!m_root)
|
---|
815 | return false;
|
---|
816 |
|
---|
817 | //calculate the child offsets (flat)
|
---|
818 | pending.push(m_root);
|
---|
819 | int offset = 1;
|
---|
820 | while (!pending.isEmpty()) {
|
---|
821 | RCCFileInfo *file = pending.pop();
|
---|
822 | file->m_childOffset = offset;
|
---|
823 |
|
---|
824 | //sort by hash value for binary lookup
|
---|
825 | QList<RCCFileInfo*> m_children = file->m_children.values();
|
---|
826 | qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
|
---|
827 |
|
---|
828 | //write out the actual data now
|
---|
829 | for (int i = 0; i < m_children.size(); ++i) {
|
---|
830 | RCCFileInfo *child = m_children.at(i);
|
---|
831 | ++offset;
|
---|
832 | if (child->m_flags & RCCFileInfo::Directory)
|
---|
833 | pending.push(child);
|
---|
834 | }
|
---|
835 | }
|
---|
836 |
|
---|
837 | //write out the structure (ie iterate again!)
|
---|
838 | pending.push(m_root);
|
---|
839 | m_root->writeDataInfo(*this);
|
---|
840 | while (!pending.isEmpty()) {
|
---|
841 | RCCFileInfo *file = pending.pop();
|
---|
842 |
|
---|
843 | //sort by hash value for binary lookup
|
---|
844 | QList<RCCFileInfo*> m_children = file->m_children.values();
|
---|
845 | qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
|
---|
846 |
|
---|
847 | //write out the actual data now
|
---|
848 | for (int i = 0; i < m_children.size(); ++i) {
|
---|
849 | RCCFileInfo *child = m_children.at(i);
|
---|
850 | child->writeDataInfo(*this);
|
---|
851 | if (child->m_flags & RCCFileInfo::Directory)
|
---|
852 | pending.push(child);
|
---|
853 | }
|
---|
854 | }
|
---|
855 | if (m_format == C_Code)
|
---|
856 | writeString("\n};\n\n");
|
---|
857 |
|
---|
858 | return true;
|
---|
859 | }
|
---|
860 |
|
---|
861 | void RCCResourceLibrary::writeMangleNamespaceFunction(const QByteArray &name)
|
---|
862 | {
|
---|
863 | if (m_useNameSpace) {
|
---|
864 | writeString("QT_MANGLE_NAMESPACE(");
|
---|
865 | writeByteArray(name);
|
---|
866 | writeChar(')');
|
---|
867 | } else {
|
---|
868 | writeByteArray(name);
|
---|
869 | }
|
---|
870 | }
|
---|
871 |
|
---|
872 | void RCCResourceLibrary::writeAddNamespaceFunction(const QByteArray &name)
|
---|
873 | {
|
---|
874 | if (m_useNameSpace) {
|
---|
875 | writeString("QT_PREPEND_NAMESPACE(");
|
---|
|
---|