Mercurial > projects > qtd
annotate tools/drcc/rcc.cpp @ 287:b6984b290e46 signals
(none)
author | eldar |
---|---|
date | Sun, 08 Nov 2009 19:20:53 +0000 |
parents | 39337877e05c |
children | a032df77b6ab |
rev | line source |
---|---|
57 | 1 /**************************************************************************** |
2 ** | |
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). | |
4 ** Contact: Qt Software Information (qt-info@nokia.com) | |
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 qt-sales@nokia.com. | |
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), | |
61
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
349 m_errorDevice(0), |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
350 m_staticInitialize(true) |
57 | 351 { |
352 m_out.reserve(30 * 1000 * 1000); | |
353 } | |
354 | |
355 RCCResourceLibrary::~RCCResourceLibrary() | |
356 { | |
357 delete m_root; | |
358 } | |
359 | |
360 bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice, | |
361 const QString &fname, QString currentPath, bool ignoreErrors) | |
362 { | |
363 Q_ASSERT(m_errorDevice); | |
364 const QChar slash = QLatin1Char('/'); | |
365 if (!currentPath.isEmpty() && !currentPath.endsWith(slash)) | |
366 currentPath += slash; | |
367 | |
368 QDomDocument document; | |
369 { | |
370 QString errorMsg; | |
371 int errorLine = 0; | |
372 int errorColumn = 0; | |
373 if (!document.setContent(inputDevice, &errorMsg, &errorLine, &errorColumn)) { | |
374 if (ignoreErrors) | |
375 return true; | |
376 const QString msg = QString::fromUtf8("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMsg); | |
377 m_errorDevice->write(msg.toUtf8()); | |
378 return false; | |
379 } | |
380 } | |
381 | |
382 QDomElement domRoot = document.firstChildElement(m_strings.TAG_RCC).toElement(); | |
383 if (!domRoot.isNull() && domRoot.tagName() == m_strings.TAG_RCC) { | |
384 for (QDomNode node = domRoot.firstChild(); !node.isNull(); node = node.nextSibling()) { | |
385 if (!node.isElement()) | |
386 continue; | |
387 | |
388 QDomElement child = node.toElement(); | |
389 if (!child.isNull() && child.tagName() == m_strings.TAG_RESOURCE) { | |
390 QLocale::Language language = QLocale::c().language(); | |
391 QLocale::Country country = QLocale::c().country(); | |
392 | |
393 if (child.hasAttribute(m_strings.ATTRIBUTE_LANG)) { | |
394 QString attribute = child.attribute(m_strings.ATTRIBUTE_LANG); | |
395 QLocale lang = QLocale(attribute); | |
396 language = lang.language(); | |
397 if (2 == attribute.length()) { | |
398 // Language only | |
399 country = QLocale::AnyCountry; | |
400 } else { | |
401 country = lang.country(); | |
402 } | |
403 } | |
404 | |
405 QString prefix; | |
406 if (child.hasAttribute(m_strings.ATTRIBUTE_PREFIX)) | |
407 prefix = child.attribute(m_strings.ATTRIBUTE_PREFIX); | |
408 if (!prefix.startsWith(slash)) | |
409 prefix.prepend(slash); | |
410 if (!prefix.endsWith(slash)) | |
411 prefix += slash; | |
412 | |
413 for (QDomNode res = child.firstChild(); !res.isNull(); res = res.nextSibling()) { | |
414 if (res.isElement() && res.toElement().tagName() == m_strings.TAG_FILE) { | |
415 | |
416 QString fileName(res.firstChild().toText().data()); | |
417 if (fileName.isEmpty()) { | |
418 const QString msg = QString::fromUtf8("RCC: Warning: Null node in XML of '%1'\n").arg(fname); | |
419 m_errorDevice->write(msg.toUtf8()); | |
420 } | |
421 QString alias; | |
422 if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_ALIAS)) | |
423 alias = res.toElement().attribute(m_strings.ATTRIBUTE_ALIAS); | |
424 else | |
425 alias = fileName; | |
426 | |
427 int compressLevel = m_compressLevel; | |
428 if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) | |
429 compressLevel = res.toElement().attribute(m_strings.ATTRIBUTE_COMPRESS).toInt(); | |
430 int compressThreshold = m_compressThreshold; | |
431 if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_THRESHOLD)) | |
432 compressThreshold = res.toElement().attribute(m_strings.ATTRIBUTE_THRESHOLD).toInt(); | |
433 | |
434 // Special case for -no-compress. Overrides all other settings. | |
435 if (m_compressLevel == -2) | |
436 compressLevel = 0; | |
437 | |
438 alias = QDir::cleanPath(alias); | |
439 while (alias.startsWith(QLatin1String("../"))) | |
440 alias.remove(0, 3); | |
441 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias; | |
442 | |
443 QString absFileName = fileName; | |
444 if (QDir::isRelativePath(absFileName)) | |
445 absFileName.prepend(currentPath); | |
446 QFileInfo file(absFileName); | |
447 if (!file.exists()) { | |
448 m_failedResources.push_back(absFileName); | |
449 const QString msg = QString::fromUtf8("RCC: Error in '%1': Cannot find file '%2'\n").arg(fname).arg(fileName); | |
450 m_errorDevice->write(msg.toUtf8()); | |
451 if (ignoreErrors) | |
452 continue; | |
453 else | |
454 return false; | |
455 } else if (file.isFile()) { | |
456 const bool arc = addFile(alias, RCCFileInfo(alias.section(slash, -1), file, language, country, | |
457 RCCFileInfo::NoFlags, compressLevel, compressThreshold)); | |
458 if (!arc) | |
459 m_failedResources.push_back(absFileName); | |
460 } else { | |
461 QDir dir; | |
462 if (file.isDir()) { | |
463 dir.setPath(file.filePath()); | |
464 } else { | |
465 dir.setPath(file.path()); | |
466 dir.setNameFilters(QStringList(file.fileName())); | |
467 if (alias.endsWith(file.fileName())) | |
468 alias = alias.left(alias.length()-file.fileName().length()); | |
469 } | |
470 if (!alias.endsWith(slash)) | |
471 alias += slash; | |
472 QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories); | |
473 while (it.hasNext()) { | |
474 it.next(); | |
475 QFileInfo child(it.fileInfo()); | |
476 if (child.fileName() != QLatin1String(".") && child.fileName() != QLatin1String("..")) { | |
477 const bool arc = addFile(alias + child.fileName(), | |
478 RCCFileInfo(child.fileName(), child, language, country, | |
479 RCCFileInfo::NoFlags, compressLevel, compressThreshold)); | |
480 if (!arc) | |
481 m_failedResources.push_back(child.fileName()); | |
482 } | |
483 } | |
484 } | |
485 } | |
486 } | |
487 } | |
488 } | |
489 } | |
490 if (m_root == 0) { | |
491 const QString msg = QString::fromUtf8("RCC: Warning: No resources in '%1'.\n").arg(fname); | |
492 m_errorDevice->write(msg.toUtf8()); | |
493 if (!ignoreErrors && m_format == Binary) { | |
494 // create dummy entry, otherwise loading qith QResource will crash | |
495 m_root = new RCCFileInfo(QString(), QFileInfo(), | |
496 QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); | |
497 } | |
498 } | |
499 | |
500 return true; | |
501 } | |
502 | |
503 bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file) | |
504 { | |
505 Q_ASSERT(m_errorDevice); | |
506 if (file.m_fileInfo.size() > 0xffffffff) { | |
507 const QString msg = QString::fromUtf8("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath()); | |
508 m_errorDevice->write(msg.toUtf8()); | |
509 return false; | |
510 } | |
511 if (!m_root) | |
512 m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); | |
513 | |
514 RCCFileInfo *parent = m_root; | |
515 const QStringList nodes = alias.split(QLatin1Char('/')); | |
516 for (int i = 1; i < nodes.size()-1; ++i) { | |
517 const QString node = nodes.at(i); | |
518 if (node.isEmpty()) | |
519 continue; | |
520 if (!parent->m_children.contains(node)) { | |
521 RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); | |
522 s->m_parent = parent; | |
523 parent->m_children.insert(node, s); | |
524 parent = s; | |
525 } else { | |
526 parent = parent->m_children[node]; | |
527 } | |
528 } | |
529 | |
530 const QString filename = nodes.at(nodes.size()-1); | |
531 RCCFileInfo *s = new RCCFileInfo(file); | |
532 s->m_parent = parent; | |
533 parent->m_children.insertMulti(filename, s); | |
534 return true; | |
535 } | |
536 | |
537 void RCCResourceLibrary::reset() | |
538 { | |
539 if (m_root) { | |
540 delete m_root; | |
541 m_root = 0; | |
542 } | |
543 m_errorDevice = 0; | |
544 m_failedResources.clear(); | |
545 } | |
546 | |
547 | |
548 bool RCCResourceLibrary::readFiles(bool ignoreErrors, QIODevice &errorDevice) | |
549 { | |
550 reset(); | |
551 m_errorDevice = &errorDevice; | |
552 //read in data | |
553 if (m_verbose) { | |
554 const QString msg = QString::fromUtf8("Processing %1 files [%2]\n") | |
555 .arg(m_fileNames.size()).arg(static_cast<int>(ignoreErrors)); | |
556 m_errorDevice->write(msg.toUtf8()); | |
557 } | |
558 for (int i = 0; i < m_fileNames.size(); ++i) { | |
559 QFile fileIn; | |
560 QString fname = m_fileNames.at(i); | |
561 QString pwd; | |
562 if (fname == QLatin1String("-")) { | |
563 fname = QLatin1String("(stdin)"); | |
564 pwd = QDir::currentPath(); | |
565 fileIn.setFileName(fname); | |
566 if (!fileIn.open(stdin, QIODevice::ReadOnly)) { | |
567 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8()); | |
568 return false; | |
569 } | |
570 } else { | |
571 pwd = QFileInfo(fname).path(); | |
572 fileIn.setFileName(fname); | |
573 if (!fileIn.open(QIODevice::ReadOnly)) { | |
574 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8()); | |
575 return false; | |
576 } | |
577 } | |
578 if (m_verbose) { | |
579 const QString msg = QString::fromUtf8("Interpreting %1\n").arg(fname); | |
580 m_errorDevice->write(msg.toUtf8()); | |
581 } | |
582 | |
583 if (!interpretResourceFile(&fileIn, fname, pwd, ignoreErrors)) | |
584 return false; | |
585 } | |
586 return true; | |
587 } | |
588 | |
589 QStringList RCCResourceLibrary::dataFiles() const | |
590 { | |
591 QStringList ret; | |
592 QStack<RCCFileInfo*> pending; | |
593 | |
594 if (!m_root) | |
595 return ret; | |
596 pending.push(m_root); | |
597 while (!pending.isEmpty()) { | |
598 RCCFileInfo *file = pending.pop(); | |
599 for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin(); | |
600 it != file->m_children.end(); ++it) { | |
601 RCCFileInfo *child = it.value(); | |
602 if (child->m_flags & RCCFileInfo::Directory) | |
603 pending.push(child); | |
604 ret.append(child->m_fileInfo.filePath()); | |
605 } | |
606 } | |
607 return ret; | |
608 } | |
609 | |
610 // Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion | |
611 static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m) | |
612 { | |
613 typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator; | |
614 const QChar slash = QLatin1Char('/'); | |
615 const ChildConstIterator cend = m_root->m_children.constEnd(); | |
616 for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) { | |
617 const RCCFileInfo *child = it.value(); | |
618 QString childName = path; | |
619 childName += slash; | |
620 childName += child->m_name; | |
621 if (child->m_flags & RCCFileInfo::Directory) { | |
622 resourceDataFileMapRecursion(child, childName, m); | |
623 } else { | |
624 m.insert(childName, child->m_fileInfo.filePath()); | |
625 } | |
626 } | |
627 } | |
628 | |
629 RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap() const | |
630 { | |
631 ResourceDataFileMap rc; | |
632 if (m_root) | |
633 resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')), rc); | |
634 return rc; | |
635 } | |
636 | |
637 bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &errorDevice) | |
638 { | |
639 m_errorDevice = &errorDevice; | |
640 //write out | |
641 if (m_verbose) | |
642 m_errorDevice->write("Outputting code\n"); | |
643 if (!writeHeader()) { | |
644 m_errorDevice->write("Could not write header\n"); | |
645 return false; | |
646 } | |
647 if (m_root) { | |
648 if (!writeDataBlobs()) { | |
649 m_errorDevice->write("Could not write data blobs.\n"); | |
650 return false; | |
651 } | |
652 if (!writeDataNames()) { | |
653 m_errorDevice->write("Could not write file names\n"); | |
654 return false; | |
655 } | |
656 if (!writeDataStructure()) { | |
657 m_errorDevice->write("Could not write data tree\n"); | |
658 return false; | |
659 } | |
660 } | |
661 if (!writeInitializer()) { | |
662 m_errorDevice->write("Could not write footer\n"); | |
663 return false; | |
664 } | |
665 outDevice.write(m_out, m_out.size()); | |
666 return true; | |
667 } | |
668 | |
669 void RCCResourceLibrary::writeHex(quint8 tmp) | |
670 { | |
671 const char * const digits = "0123456789abcdef"; | |
672 writeChar('0'); | |
673 writeChar('x'); | |
674 if (tmp < 16) { | |
675 writeChar(digits[tmp]); | |
676 } else { | |
677 writeChar(digits[tmp >> 4]); | |
678 writeChar(digits[tmp & 0xf]); | |
679 } | |
680 writeChar(','); | |
681 } | |
682 | |
683 void RCCResourceLibrary::writeNumber2(quint16 number) | |
684 { | |
685 if (m_format == RCCResourceLibrary::Binary) { | |
686 writeChar(number >> 8); | |
687 writeChar(number); | |
688 } else { | |
689 writeHex(number >> 8); | |
690 writeHex(number); | |
691 } | |
692 } | |
693 | |
694 void RCCResourceLibrary::writeNumber4(quint32 number) | |
695 { | |
696 if (m_format == RCCResourceLibrary::Binary) { | |
697 writeChar(number >> 24); | |
698 writeChar(number >> 16); | |
699 writeChar(number >> 8); | |
700 writeChar(number); | |
701 } else { | |
702 writeHex(number >> 24); | |
703 writeHex(number >> 16); | |
704 writeHex(number >> 8); | |
705 writeHex(number); | |
706 } | |
707 } | |
708 | |
709 bool RCCResourceLibrary::writeHeader() | |
710 { | |
711 if (m_format == C_Code) { | |
712 writeString("/****************************************************************************\n"); | |
713 writeString("** Resource object code\n"); | |
714 writeString("**\n"); | |
715 writeString("** Created: "); | |
716 writeByteArray(QDateTime::currentDateTime().toString().toLatin1()); | |
717 writeString("\n** by: The Resource Compiler for Qt version "); | |
718 writeByteArray(QT_VERSION_STR); | |
719 writeString("\n**\n"); | |
720 writeString("** WARNING! All changes made in this file will be lost!\n"); | |
721 writeString( "*****************************************************************************/\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 ubyte[] 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 ubyte[] 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 ubyte[] 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 // qtd writeString("QT_MANGLE_NAMESPACE("); | |
865 writeByteArray(name); | |
866 // qtd 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("); | |
876 writeByteArray(name); | |
877 writeChar(')'); | |
878 } else { | |
879 writeByteArray(name); | |
880 } | |
881 } | |
882 | |
883 bool RCCResourceLibrary::writeInitializer() | |
884 { | |
885 if (m_format == C_Code) { | |
886 //write("\nQT_BEGIN_NAMESPACE\n"); | |
887 QString initName = m_initName; | |
888 if (!initName.isEmpty()) { | |
889 initName.prepend(QLatin1Char('_')); | |
890 initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_")); | |
891 } | |
892 | |
893 //init | |
894 if (m_useNameSpace) | |
895 writeString("// QT_BEGIN_NAMESPACE\n\n"); | |
896 if (m_root) { | |
196 | 897 writeString("extern(C) bool qtd_register_resource_data(int version_, in ubyte *tree, in ubyte *name, in ubyte *data);\n\n"); |
898 writeString("extern(C) bool qtd_unregister_resource_data(int version_, in ubyte *tree, in ubyte *name, in ubyte *data);\n\n"); | |
57 | 899 } |
900 if (m_useNameSpace) | |
901 writeString("// QT_END_NAMESPACE\n\n\n"); | |
902 QString initResources = QLatin1String("qtd_init_resources"); | |
903 initResources += initName; | |
904 writeString("extern(C) int "); | |
905 writeMangleNamespaceFunction(initResources.toLatin1()); | |
906 writeString("()\n{\n"); | |
907 | |
908 if (m_root) { | |
909 writeString(" "); | |
910 writeString("qtd_register_resource_data(0x01, qt_resource_struct.ptr, " | |
911 "qt_resource_name.ptr, qt_resource_data.ptr);\n"); | |
912 } | |
913 writeString(" return 1;\n"); | |
914 writeString("}\n\n"); | |
915 | |
916 //cleanup | |
917 QString cleanResources = QLatin1String("qtd_cleanup_resources"); | |
918 cleanResources += initName; | |
919 writeString("extern(C) int "); | |
920 writeMangleNamespaceFunction(cleanResources.toLatin1()); | |
921 writeString("()\n{\n"); | |
922 if (m_root) { | |
923 writeString(" "); | |
924 writeString("qtd_unregister_resource_data(0x01, qt_resource_struct.ptr, " | |
925 "qt_resource_name.ptr, qt_resource_data.ptr);\n"); | |
926 } | |
927 writeString(" return 1;\n"); | |
928 writeString("}\n\n"); | |
61
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
929 |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
930 if(staticInitialize()) |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
931 { |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
932 writeString("static this() \n{\n "); |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
933 writeMangleNamespaceFunction(initResources.toLatin1()); |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
934 writeString("();\n}\n\n"); |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
935 |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
936 writeString("static ~this() \n{\n "); |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
937 writeMangleNamespaceFunction(cleanResources.toLatin1()); |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
938 writeString("();\n}\n\n"); |
a2871e6b8b15
drcc: automatic initialization of resources with program start (disabled with option -no-static-initialize)
SokoL_SD
parents:
57
diff
changeset
|
939 } |
57 | 940 } else if (m_format == Binary) { |
941 int i = 4; | |
942 char *p = m_out.data(); | |
943 p[i++] = 0; // 0x01 | |
944 p[i++] = 0; | |
945 p[i++] = 0; | |
946 p[i++] = 1; | |
947 | |
948 p[i++] = (m_treeOffset >> 24) & 0xff; | |
949 p[i++] = (m_treeOffset >> 16) & 0xff; | |
950 p[i++] = (m_treeOffset >> 8) & 0xff; | |
951 p[i++] = (m_treeOffset >> 0) & 0xff; | |
952 | |
953 p[i++] = (m_dataOffset >> 24) & 0xff; | |
954 p[i++] = (m_dataOffset >> 16) & 0xff; | |
955 p[i++] = (m_dataOffset >> 8) & 0xff; | |
956 p[i++] = (m_dataOffset >> 0) & 0xff; | |
957 | |
958 p[i++] = (m_namesOffset >> 24) & 0xff; | |
959 p[i++] = (m_namesOffset >> 16) & 0xff; | |
960 p[i++] = (m_namesOffset >> 8) & 0xff; | |
961 p[i++] = (m_namesOffset >> 0) & 0xff; | |
962 } | |
963 return true; | |
964 } | |
965 | |
966 QT_END_NAMESPACE |