Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
rcc.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// Copyright (C) 2018 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4// Qt-Security score:significant reason:default
5
6/* Note: This is a copy of qtbase/src/tools/rcc/rcc.cpp. */
7
8#include "rcc_p.h"
9
10#include <qbytearray.h>
11#include <qdatetime.h>
12#include <qdebug.h>
13#include <qdir.h>
14#include <qdiriterator.h>
15#include <qfile.h>
16#include <qiodevice.h>
17#include <qlocale.h>
18#include <qstack.h>
19#include <qxmlstream.h>
20
21#include <algorithm>
22
23#if QT_CONFIG(zstd)
24# include <zstd.h>
25#endif
26
27QT_BEGIN_NAMESPACE
28
29using namespace Qt::StringLiterals;
30
31enum {
34 CONSTANT_ZSTDCOMPRESSLEVEL_CHECK = 1, // Zstd level to check if compressing is a good idea
35 CONSTANT_ZSTDCOMPRESSLEVEL_STORE = 14, // Zstd level to actually store the data
37};
38
39void RCCResourceLibrary::write(const char *str, int len)
40{
41 int n = m_out.size();
42 m_out.resize(n + len);
43 memcpy(m_out.data() + n, str, len);
44}
45
46void RCCResourceLibrary::writeByteArray(const QByteArray &other)
47{
48 if (m_format == Pass2) {
49 m_outDevice->write(other);
50 } else {
51 m_out.append(other);
52 }
53}
54
55static inline QString msgOpenReadFailed(const QString &fname, const QString &why)
56{
57 return QString::fromLatin1("Unable to open %1 for reading: %2\n").arg(fname, why);
58}
59
60
61///////////////////////////////////////////////////////////
62//
63// RCCFileInfo
64//
65///////////////////////////////////////////////////////////
66
67class RCCFileInfo
68{
69public:
70 enum Flags
71 {
72 // must match qresource.cpp
73 NoFlags = 0x00,
74 Compressed = 0x01,
75 Directory = 0x02,
76 CompressedZstd = 0x04
77 };
78
79
80 RCCFileInfo() = default;
81 RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language,
82 QLocale::Territory territory, uint flags,
83 RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel,
84 int compressThreshold, bool noZstd, bool isEmpty);
85
87 RCCFileInfo(const RCCFileInfo &) = delete;
88 RCCFileInfo &operator=(const RCCFileInfo &) = delete;
89 RCCFileInfo(RCCFileInfo &&) = default;
90 RCCFileInfo &operator=(RCCFileInfo &&other) = delete;
91
92 QString resourceName() const;
93
94public:
95 qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage);
98
99 int m_flags = NoFlags;
102 QString m_name;
104 RCCFileInfo *m_parent = nullptr;
106
110 bool m_noZstd = false;
111 bool m_isEmpty = false;
112
116};
117
118RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language,
119 QLocale::Territory territory, uint flags,
120 RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel,
121 int compressThreshold, bool noZstd, bool isEmpty)
122 : m_flags(flags),
125 m_name(name),
127 m_compressAlgo(compressAlgo),
128 m_compressLevel(compressLevel),
129 m_compressThreshold(compressThreshold),
130 m_noZstd(noZstd),
131 m_isEmpty(isEmpty)
132{
133}
134
136{
137 qDeleteAll(m_children);
138}
139
140QString RCCFileInfo::resourceName() const
141{
142 QString resource = m_name;
143 for (RCCFileInfo *p = m_parent; p; p = p->m_parent)
144 resource = resource.prepend(p->m_name + u'/');
145 resource.prepend(u':');
146 return resource;
147}
148
150{
151 const bool text = lib.m_format == RCCResourceLibrary::C_Code;
152 const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1;
153 const bool python = lib.m_format == RCCResourceLibrary::Python_Code;
154 //some info
155 if (text || pass1) {
156 if (m_language != QLocale::C) {
157 lib.writeString(" // ");
158 lib.writeByteArray(resourceName().toLocal8Bit());
159 lib.writeString(" [");
160 lib.writeByteArray(QByteArray::number(m_territory));
161 lib.writeString("::");
162 lib.writeByteArray(QByteArray::number(m_language));
163 lib.writeString("[\n ");
164 } else {
165 lib.writeString(" // ");
166 lib.writeByteArray(resourceName().toLocal8Bit());
167 lib.writeString("\n ");
168 }
169 }
170
171 //pointer data
173 // name offset
174 lib.writeNumber4(m_nameOffset);
175
176 // flags
177 lib.writeNumber2(m_flags);
178
179 // child count
180 lib.writeNumber4(m_children.size());
181
182 // first child offset
183 lib.writeNumber4(m_childOffset);
184 } else {
185 // name offset
186 lib.writeNumber4(m_nameOffset);
187
188 // flags
189 lib.writeNumber2(m_flags);
190
191 // locale
192 lib.writeNumber2(m_territory);
193 lib.writeNumber2(m_language);
194
195 //data offset
196 lib.writeNumber4(m_dataOffset);
197 }
198 if (text || pass1)
199 lib.writeChar('\n');
200 else if (python)
201 lib.writeString("\\\n");
202
203 if (lib.formatVersion() >= 2) {
204 // last modified time stamp
205 const QDateTime lastModified = m_fileInfo.lastModified(QTimeZone::UTC);
206 quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
207 static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
208 if (sourceDate != 0)
209 lastmod = sourceDate;
210 static const quint64 sourceDate2 = 1000 * qgetenv("SOURCE_DATE_EPOCH").toULongLong();
211 if (sourceDate2 != 0)
212 lastmod = sourceDate2;
213 lib.writeNumber8(lastmod);
214 if (text || pass1)
215 lib.writeChar('\n');
216 else if (python)
217 lib.writeString("\\\n");
218 }
219}
220
222 QString *errorMessage)
223{
224 const bool text = lib.m_format == RCCResourceLibrary::C_Code;
225 const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1;
226 const bool pass2 = lib.m_format == RCCResourceLibrary::Pass2;
227 const bool binary = lib.m_format == RCCResourceLibrary::Binary;
228 const bool python = lib.m_format == RCCResourceLibrary::Python_Code;
229
230 //capture the offset
231 m_dataOffset = offset;
232 QByteArray data;
233
234 if (!m_isEmpty) {
235 //find the data to be written
236 QFile file(m_fileInfo.absoluteFilePath());
237 if (!file.open(QFile::ReadOnly)) {
238 *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
239 return 0;
240 }
241
242 data = file.readAll();
243 }
244
245 // Check if compression is useful for this file
246 if (data.size() != 0) {
247#if QT_CONFIG(zstd)
248 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best && !m_noZstd) {
249 m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd;
250 m_compressLevel = 19; // not ZSTD_maxCLevel(), as 20+ are experimental
251 }
252 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd && !m_noZstd) {
253 if (lib.m_zstdCCtx == nullptr)
254 lib.m_zstdCCtx = ZSTD_createCCtx();
255 qsizetype size = data.size();
256 size = ZSTD_COMPRESSBOUND(size);
257
258 int compressLevel = m_compressLevel;
259 if (compressLevel < 0)
260 compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK;
261
262 QByteArray compressed(size, Qt::Uninitialized);
263 char *dst = const_cast<char *>(compressed.constData());
264 size_t n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
265 data.constData(), data.size(),
266 compressLevel);
267 if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) {
268 // compressing is worth it
269 if (m_compressLevel < 0) {
270 // heuristic compression, so recompress
271 n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
272 data.constData(), data.size(),
273 CONSTANT_ZSTDCOMPRESSLEVEL_STORE);
274 }
275 if (ZSTD_isError(n)) {
276 QString msg = QString::fromLatin1("%1: error: compression with zstd failed: %2\n")
277 .arg(m_name, QString::fromUtf8(ZSTD_getErrorName(n)));
278 lib.m_errorDevice->write(msg.toUtf8());
279 } else if (lib.verbose()) {
280 QString msg = QString::fromLatin1("%1: note: compressed using zstd (%2 -> %3)\n")
281 .arg(m_name).arg(data.size()).arg(n);
282 lib.m_errorDevice->write(msg.toUtf8());
283 }
284
285 lib.m_overallFlags |= CompressedZstd;
286 m_flags |= CompressedZstd;
287 data = std::move(compressed);
288 data.truncate(n);
289 } else if (lib.verbose()) {
290 QString msg = QString::fromLatin1("%1: note: not compressed\n").arg(m_name);
291 lib.m_errorDevice->write(msg.toUtf8());
292 }
293 }
294#endif
295#ifndef QT_NO_COMPRESS
298 m_compressLevel = 9;
299 }
301 QByteArray compressed =
302 qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
303
304 int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
305 if (compressRatio >= m_compressThreshold) {
306 if (lib.verbose()) {
307 QString msg = QString::fromLatin1("%1: note: compressed using zlib (%2 -> %3)\n")
308 .arg(m_name).arg(data.size()).arg(compressed.size());
309 lib.m_errorDevice->write(msg.toUtf8());
310 }
311 data = std::move(compressed);
312 lib.m_overallFlags |= Compressed;
314 } else if (lib.verbose()) {
315 QString msg = QString::fromLatin1("%1: note: not compressed\n").arg(m_name);
316 lib.m_errorDevice->write(msg.toUtf8());
317 }
318 }
319#endif // QT_NO_COMPRESS
320 }
321
322 // some info
323 if (text || pass1) {
324 lib.writeString(" // ");
325 lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
326 lib.writeString("\n ");
327 }
328
329 // write the length
330 if (text || binary || pass2 || python)
331 lib.writeNumber4(data.size());
332 if (text || pass1)
333 lib.writeString("\n ");
334 else if (python)
335 lib.writeString("\\\n");
336 offset += 4;
337
338 // write the payload
339 const char *p = data.constData();
340 if (text || python) {
341 for (int i = data.size(), j = 0; --i >= 0; --j) {
342 lib.writeHex(*p++);
343 if (j == 0) {
344 if (text)
345 lib.writeString("\n ");
346 else
347 lib.writeString("\\\n");
348 j = 16;
349 }
350 }
351 } else if (binary || pass2) {
352 lib.writeByteArray(data);
353 }
354 offset += data.size();
355
356 // done
357 if (text || pass1)
358 lib.writeString("\n ");
359 else if (python)
360 lib.writeString("\\\n");
361
362 return offset;
363}
364
366{
367 const bool text = lib.m_format == RCCResourceLibrary::C_Code;
368 const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1;
369 const bool python = lib.m_format == RCCResourceLibrary::Python_Code;
370
371 // capture the offset
372 m_nameOffset = offset;
373
374 // some info
375 if (text || pass1) {
376 lib.writeString(" // ");
377 lib.writeByteArray(m_name.toLocal8Bit());
378 lib.writeString("\n ");
379 }
380
381 // write the length
382 lib.writeNumber2(m_name.size());
383 if (text || pass1)
384 lib.writeString("\n ");
385 else if (python)
386 lib.writeString("\\\n");
387 offset += 2;
388
389 // write the hash
390 lib.writeNumber4(qt_hash(m_name));
391 if (text || pass1)
392 lib.writeString("\n ");
393 else if (python)
394 lib.writeString("\\\n");
395 offset += 4;
396
397 // write the m_name
398 const QChar *unicode = m_name.unicode();
399 for (int i = 0; i < m_name.size(); ++i) {
400 lib.writeNumber2(unicode[i].unicode());
401 if ((text || pass1) && i % 16 == 0)
402 lib.writeString("\n ");
403 else if (python && i % 16 == 0)
404 lib.writeString("\\\n");
405 }
406 offset += m_name.size()*2;
407
408 // done
409 if (text || pass1)
410 lib.writeString("\n ");
411 else if (python)
412 lib.writeString("\\\n");
413
414 return offset;
415}
416
417
418///////////////////////////////////////////////////////////
419//
420// RCCResourceLibrary
421//
422///////////////////////////////////////////////////////////
423
424RCCResourceLibrary::Strings::Strings() :
425 TAG_RCC("RCC"_L1),
426 TAG_RESOURCE("qresource"_L1),
427 TAG_FILE("file"_L1),
428 ATTRIBUTE_LANG("lang"_L1),
429 ATTRIBUTE_PREFIX("prefix"_L1),
430 ATTRIBUTE_ALIAS("alias"_L1),
431 ATTRIBUTE_EMPTY("empty"_L1),
432 ATTRIBUTE_THRESHOLD("threshold"_L1),
433 ATTRIBUTE_COMPRESS("compress"_L1),
434 ATTRIBUTE_COMPRESSALGO(QStringLiteral("compression-algorithm"))
435{
436}
437
438RCCResourceLibrary::RCCResourceLibrary(quint8 formatVersion)
439 : m_root(nullptr),
440 m_format(C_Code),
441 m_verbose(false),
442 m_compressionAlgo(CompressionAlgorithm::Best),
443 m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
444 m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
445 m_treeOffset(0),
446 m_namesOffset(0),
447 m_dataOffset(0),
449 m_useNameSpace(CONSTANT_USENAMESPACE),
450 m_errorDevice(nullptr),
451 m_outDevice(nullptr),
453 m_noZstd(false)
454{
455 m_out.reserve(30 * 1000 * 1000);
456#if QT_CONFIG(zstd)
457 m_zstdCCtx = nullptr;
458#endif
459}
460
462{
463 delete m_root;
464#if QT_CONFIG(zstd)
465 ZSTD_freeCCtx(m_zstdCCtx);
466#endif
467}
468
475
476static bool parseBoolean(QStringView value, QString *errorMsg)
477{
478 if (value.compare("true"_L1, Qt::CaseInsensitive) == 0)
479 return true;
480 if (value.compare("false"_L1, Qt::CaseInsensitive) == 0)
481 return false;
482
483 *errorMsg = QString::fromLatin1("Invalid value for boolean attribute: '%1'").arg(value);
484 return false;
485}
486
487bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
488 const QString &fname, QString currentPath, bool listMode)
489{
490 Q_ASSERT(m_errorDevice);
491 const QChar slash = u'/';
492 if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
493 currentPath += slash;
494
495 QXmlStreamReader reader(inputDevice);
496 QStack<RCCXmlTag> tokens;
497
498 QString prefix;
499 QLocale::Language language = QLocale::c().language();
500 QLocale::Territory territory = QLocale::c().territory();
501 QString alias;
502 bool empty = false;
503 auto compressAlgo = m_compressionAlgo;
504 int compressLevel = m_compressLevel;
505 int compressThreshold = m_compressThreshold;
506
507 while (!reader.atEnd()) {
508 QXmlStreamReader::TokenType t = reader.readNext();
509 switch (t) {
510 case QXmlStreamReader::StartElement:
511 if (reader.name() == m_strings.TAG_RCC) {
512 if (!tokens.isEmpty())
513 reader.raiseError("expected <RCC> tag"_L1);
514 else
515 tokens.push(RccTag);
516 } else if (reader.name() == m_strings.TAG_RESOURCE) {
517 if (tokens.isEmpty() || tokens.top() != RccTag) {
518 reader.raiseError("unexpected <RESOURCE> tag"_L1);
519 } else {
520 tokens.push(ResourceTag);
521
522 QXmlStreamAttributes attributes = reader.attributes();
523 language = QLocale::c().language();
524 territory = QLocale::c().territory();
525
526 if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
527 QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
528 QLocale lang = QLocale(attribute);
529 language = lang.language();
530 if (2 == attribute.size()) {
531 // Language only
532 territory = QLocale::AnyTerritory;
533 } else {
534 territory = lang.territory();
535 }
536 }
537
538 prefix.clear();
539 if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
540 prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
541 if (!prefix.startsWith(slash))
542 prefix.prepend(slash);
543 if (!prefix.endsWith(slash))
544 prefix += slash;
545 }
546 } else if (reader.name() == m_strings.TAG_FILE) {
547 if (tokens.isEmpty() || tokens.top() != ResourceTag) {
548 reader.raiseError("unexpected <FILE> tag"_L1);
549 } else {
550 tokens.push(FileTag);
551
552 QXmlStreamAttributes attributes = reader.attributes();
553 alias.clear();
554 if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
555 alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
556
557 compressAlgo = m_compressionAlgo;
558 compressLevel = m_compressLevel;
559 compressThreshold = m_compressThreshold;
560
561 QString errorString;
562 if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
563 empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
564 else
565 empty = false;
566
567 if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
568 compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
569 if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
570 QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
571 compressLevel = parseCompressionLevel(compressAlgo, value, &errorString);
572 }
573
574 // Special case for -no-compress
575 if (m_compressLevel == -2)
576 compressAlgo = CompressionAlgorithm::None;
577
578 if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
579 compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
580
581 if (!errorString.isEmpty())
582 reader.raiseError(errorString);
583 }
584 } else {
585 reader.raiseError("unexpected tag: %1"_L1.arg(reader.name().toString()));
586 }
587 break;
588
589 case QXmlStreamReader::EndElement:
590 if (reader.name() == m_strings.TAG_RCC) {
591 if (!tokens.isEmpty() && tokens.top() == RccTag)
592 tokens.pop();
593 else
594 reader.raiseError("unexpected closing tag"_L1);
595 } else if (reader.name() == m_strings.TAG_RESOURCE) {
596 if (!tokens.isEmpty() && tokens.top() == ResourceTag)
597 tokens.pop();
598 else
599 reader.raiseError("unexpected closing tag"_L1);
600 } else if (reader.name() == m_strings.TAG_FILE) {
601 if (!tokens.isEmpty() && tokens.top() == FileTag)
602 tokens.pop();
603 else
604 reader.raiseError("unexpected closing tag"_L1);
605 }
606 break;
607
608 case QXmlStreamReader::Characters:
609 if (reader.isWhitespace())
610 break;
611 if (tokens.isEmpty() || tokens.top() != FileTag) {
612 reader.raiseError("unexpected text"_L1);
613 } else {
614 QString fileName = reader.text().toString();
615 if (fileName.isEmpty()) {
616 const QString msg = QString::fromLatin1("RCC: Warning: Null node in XML of '%1'\n").arg(fname);
617 m_errorDevice->write(msg.toUtf8());
618 }
619
620 if (alias.isNull())
621 alias = fileName;
622
623 alias = QDir::cleanPath(alias);
624 while (alias.startsWith("../"_L1))
625 alias.remove(0, 3);
626 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
627
628 QString absFileName = fileName;
629 if (QDir::isRelativePath(absFileName))
630 absFileName.prepend(currentPath);
631 QFileInfo file(absFileName);
632 if (file.isDir()) {
633 QDir dir(file.filePath());
634 if (!alias.endsWith(slash))
635 alias += slash;
636
637 QStringList filePaths;
638 QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
639 while (it.hasNext()) {
640 it.next();
641 if (it.fileName() == "."_L1 || it.fileName() == ".."_L1)
642 continue;
643 filePaths.append(it.filePath());
644 }
645
646 // make rcc output deterministic
647 std::sort(filePaths.begin(), filePaths.end());
648
649 for (const QString &filePath : filePaths) {
650 QFileInfo child(filePath);
651 const bool arc =
652 addFile(alias + child.fileName(),
653 RCCFileInfo(child.fileName(), child, language, territory,
654 child.isDir() ? RCCFileInfo::Directory
655 : RCCFileInfo::NoFlags,
656 compressAlgo, compressLevel, compressThreshold,
657 m_noZstd, empty));
658 if (!arc)
659 m_failedResources.push_back(child.fileName());
660 }
661 } else if (listMode || file.isFile()) {
662 const bool arc =
663 addFile(alias,
664 RCCFileInfo(alias.section(slash, -1),
665 file,
666 language,
667 territory,
669 compressAlgo,
670 compressLevel,
671 compressThreshold,
672 m_noZstd, empty)
673 );
674 if (!arc)
675 m_failedResources.push_back(absFileName);
676 } else if (file.exists()) {
677 m_failedResources.push_back(absFileName);
678 const QString msg = QString::fromLatin1("RCC: Error in '%1': Entry '%2' is neither a file nor a directory\n")
679 .arg(fname, fileName);
680 m_errorDevice->write(msg.toUtf8());
681 return false;
682 } else {
683 m_failedResources.push_back(absFileName);
684 const QString msg = QString::fromLatin1("RCC: Error in '%1': Cannot find file '%2'\n")
685 .arg(fname, fileName);
686 m_errorDevice->write(msg.toUtf8());
687 return false;
688 }
689 }
690 break;
691
692 default:
693 break;
694 }
695 }
696
697 if (reader.hasError()) {
698 int errorLine = reader.lineNumber();
699 int errorColumn = reader.columnNumber();
700 QString errorMessage = reader.errorString();
701 QString msg = QString::fromLatin1("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
702 m_errorDevice->write(msg.toUtf8());
703 return false;
704 }
705
706 if (m_root == nullptr) {
707 const QString msg = QString::fromLatin1("RCC: Warning: No resources in '%1'.\n").arg(fname);
708 m_errorDevice->write(msg.toUtf8());
709 if (!listMode && m_format == Binary) {
710 // create dummy entry, otherwise loading with QResource will crash
711 m_root = new RCCFileInfo{};
713 }
714 }
715
716 return true;
717}
718
719bool RCCResourceLibrary::addFile(const QString &alias, RCCFileInfo file)
720{
721 Q_ASSERT(m_errorDevice);
722 if (file.m_fileInfo.size() > 0xffffffff) {
723 const QString msg = QString::fromLatin1("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
724 m_errorDevice->write(msg.toUtf8());
725 return false;
726 }
727 if (!m_root) {
728 m_root = new RCCFileInfo{};
730 }
731
732 RCCFileInfo *parent = m_root;
733 const QStringList nodes = alias.split(u'/');
734 for (int i = 1; i < nodes.size()-1; ++i) {
735 const QString node = nodes.at(i);
736 if (node.isEmpty())
737 continue;
738 if (!parent->m_children.contains(node)) {
739 RCCFileInfo *s = new RCCFileInfo{};
740 s->m_name = node;
742 s->m_parent = parent;
743 parent->m_children.insert(node, s);
744 parent = s;
745 } else {
746 parent = *parent->m_children.constFind(node);
747 }
748 }
749
750 const QString filename = nodes.at(nodes.size()-1);
751 RCCFileInfo *s = new RCCFileInfo(std::move(file));
752 s->m_parent = parent;
753 auto cbegin = parent->m_children.constFind(filename);
754 auto cend = parent->m_children.constEnd();
755 for (auto it = cbegin; it != cend; ++it) {
756 if (it.key() == filename && it.value()->m_language == s->m_language &&
757 it.value()->m_territory == s->m_territory) {
758 for (const QString &name : std::as_const(m_fileNames)) {
759 qWarning("%s: Warning: potential duplicate alias detected: '%s'",
760 qPrintable(name), qPrintable(filename));
761 }
762 break;
763 }
764 }
765 parent->m_children.insert(filename, s);
766 return true;
767}
768
769void RCCResourceLibrary::reset()
770{
771 if (m_root) {
772 delete m_root;
773 m_root = nullptr;
774 }
775 m_errorDevice = nullptr;
776 m_failedResources.clear();
777}
778
779
780bool RCCResourceLibrary::readFiles(bool listMode, QIODevice &errorDevice)
781{
782 reset();
783 m_errorDevice = &errorDevice;
784 //read in data
785 if (m_verbose) {
786 const QString msg = QString::fromLatin1("Processing %1 files [listMode=%2]\n")
787 .arg(m_fileNames.size()).arg(static_cast<int>(listMode));
788 m_errorDevice->write(msg.toUtf8());
789 }
790 for (int i = 0; i < m_fileNames.size(); ++i) {
791 QFile fileIn;
792 QString fname = m_fileNames.at(i);
793 QString pwd;
794 if (fname == "-"_L1) {
795 fname = "(stdin)"_L1;
796 pwd = QDir::currentPath();
797 fileIn.setFileName(fname);
798 if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
799 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
800 return false;
801 }
802 } else {
803 pwd = QFileInfo(fname).path();
804 fileIn.setFileName(fname);
805 if (!fileIn.open(QIODevice::ReadOnly)) {
806 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
807 return false;
808 }
809 }
810 if (m_verbose) {
811 const QString msg = QString::fromLatin1("Interpreting %1\n").arg(fname);
812 m_errorDevice->write(msg.toUtf8());
813 }
814
815 if (!interpretResourceFile(&fileIn, fname, std::move(pwd), listMode))
816 return false;
817 }
818 return true;
819}
820
822{
823 QStringList ret;
824 QStack<RCCFileInfo*> pending;
825
826 if (!m_root)
827 return ret;
828 pending.push(m_root);
829 while (!pending.isEmpty()) {
830 RCCFileInfo *file = pending.pop();
831 for (auto it = file->m_children.begin();
832 it != file->m_children.end(); ++it) {
833 RCCFileInfo *child = it.value();
835 pending.push(child);
836 else
837 ret.append(child->m_fileInfo.filePath());
838 }
839 }
840 return ret;
841}
842
843// Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
844static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
845{
846 const QChar slash = u'/';
847 const auto cend = m_root->m_children.constEnd();
848 for (auto it = m_root->m_children.constBegin(); it != cend; ++it) {
849 const RCCFileInfo *child = it.value();
850 const QString childName = path + slash + child->m_name;
852 resourceDataFileMapRecursion(child, childName, m);
853 } else {
854 m.insert(childName, child->m_fileInfo.filePath());
855 }
856 }
857}
858
860{
862 if (m_root)
863 resourceDataFileMapRecursion(m_root, QString(u':'), rc);
864 return rc;
865}
866
867RCCResourceLibrary::CompressionAlgorithm RCCResourceLibrary::parseCompressionAlgorithm(QStringView value, QString *errorMsg)
868{
869 if (value == "best"_L1)
871 if (value == "zlib"_L1) {
872#ifdef QT_NO_COMPRESS
873 *errorMsg = "zlib support not compiled in"_L1;
874#else
876#endif
877 } else if (value == "zstd"_L1) {
878#if QT_CONFIG(zstd)
879 return CompressionAlgorithm::Zstd;
880#else
881 *errorMsg = "Zstandard support not compiled in"_L1;
882#endif
883 } else if (value != "none"_L1) {
884 *errorMsg = QString::fromLatin1("Unknown compression algorithm '%1'").arg(value);
885 }
886
888}
889
890int RCCResourceLibrary::parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg)
891{
892 bool ok;
893 int c = level.toInt(&ok);
894 if (ok) {
895 switch (algo) {
898 return 0;
900 if (c >= 1 && c <= 9)
901 return c;
902 break;
904#if QT_CONFIG(zstd)
905 if (c >= 0 && c <= ZSTD_maxCLevel())
906 return c;
907#endif
908 break;
909 }
910 }
911
912 *errorMsg = QString::fromLatin1("invalid compression level '%1'").arg(level);
913 return 0;
914}
915
916bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
917{
918 m_errorDevice = &errorDevice;
919
920 if (m_format == Pass2) {
921 const char pattern[] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };
922 bool foundSignature = false;
923
924 while (true) {
925 char c;
926 for (int i = 0; i < 8; ) {
927 if (!tempDevice.getChar(&c)) {
928 if (foundSignature)
929 return true;
930 m_errorDevice->write("No data signature found\n");
931 return false;
932 }
933
934 if (c != pattern[i]) {
935 for (int k = 0; k < i; ++k)
936 outDevice.putChar(pattern[k]);
937 i = 0;
938 }
939
940 if (c == pattern[i]) {
941 ++i;
942 } else {
943 outDevice.putChar(c);
944 }
945 }
946
947 m_outDevice = &outDevice;
948 quint64 start = outDevice.pos();
949 writeDataBlobs();
950 quint64 len = outDevice.pos() - start;
951
952 tempDevice.seek(tempDevice.pos() + len - 8);
953 foundSignature = true;
954 }
955 }
956
957 //write out
958 if (m_verbose)
959 m_errorDevice->write("Outputting code\n");
960 if (!writeHeader()) {
961 m_errorDevice->write("Could not write header\n");
962 return false;
963 }
964 if (m_root) {
965 if (!writeDataBlobs()) {
966 m_errorDevice->write("Could not write data blobs.\n");
967 return false;
968 }
969 if (!writeDataNames()) {
970 m_errorDevice->write("Could not write file names\n");
971 return false;
972 }
973 if (!writeDataStructure()) {
974 m_errorDevice->write("Could not write data tree\n");
975 return false;
976 }
977 }
978 if (!writeInitializer()) {
979 m_errorDevice->write("Could not write footer\n");
980 return false;
981 }
982 outDevice.write(m_out.constData(), m_out.size());
983 return true;
984}
985
986void RCCResourceLibrary::writeDecimal(int value)
987{
988 Q_ASSERT(m_format != RCCResourceLibrary::Binary);
989 char buf[std::numeric_limits<int>::digits10 + 2];
990 int n = snprintf(buf, sizeof(buf), "%d", value);
991 write(buf, n);
992}
993
994static const char hexDigits[] = "0123456789abcdef";
995
996inline void RCCResourceLibrary::write2HexDigits(quint8 number)
997{
998 writeChar(hexDigits[number >> 4]);
999 writeChar(hexDigits[number & 0xf]);
1000}
1001
1002void RCCResourceLibrary::writeHex(quint8 tmp)
1003{
1004 switch (m_format) {
1006 if (tmp >= 32 && tmp < 127 && tmp != '"' && tmp != '\\') {
1007 writeChar(char(tmp));
1008 } else {
1009 writeChar('\\');
1010 writeChar('x');
1011 write2HexDigits(tmp);
1012 }
1013 break;
1014 default:
1015 writeChar('0');
1016 writeChar('x');
1017 if (tmp < 16)
1018 writeChar(hexDigits[tmp]);
1019 else
1020 write2HexDigits(tmp);
1021 writeChar(',');
1022 break;
1023 }
1024}
1025
1026void RCCResourceLibrary::writeNumber2(quint16 number)
1027{
1028 if (m_format == RCCResourceLibrary::Binary) {
1029 writeChar(number >> 8);
1030 writeChar(number);
1031 } else {
1032 writeHex(number >> 8);
1033 writeHex(number);
1034 }
1035}
1036
1037void RCCResourceLibrary::writeNumber4(quint32 number)
1038{
1039 if (m_format == RCCResourceLibrary::Pass2) {
1040 m_outDevice->putChar(char(number >> 24));
1041 m_outDevice->putChar(char(number >> 16));
1042 m_outDevice->putChar(char(number >> 8));
1043 m_outDevice->putChar(char(number));
1044 } else if (m_format == RCCResourceLibrary::Binary) {
1045 writeChar(number >> 24);
1046 writeChar(number >> 16);
1047 writeChar(number >> 8);
1048 writeChar(number);
1049 } else {
1050 writeHex(number >> 24);
1051 writeHex(number >> 16);
1052 writeHex(number >> 8);
1053 writeHex(number);
1054 }
1055}
1056
1057void RCCResourceLibrary::writeNumber8(quint64 number)
1058{
1059 if (m_format == RCCResourceLibrary::Pass2) {
1060 m_outDevice->putChar(char(number >> 56));
1061 m_outDevice->putChar(char(number >> 48));
1062 m_outDevice->putChar(char(number >> 40));
1063 m_outDevice->putChar(char(number >> 32));
1064 m_outDevice->putChar(char(number >> 24));
1065 m_outDevice->putChar(char(number >> 16));
1066 m_outDevice->putChar(char(number >> 8));
1067 m_outDevice->putChar(char(number));
1068 } else if (m_format == RCCResourceLibrary::Binary) {
1069 writeChar(number >> 56);
1070 writeChar(number >> 48);
1071 writeChar(number >> 40);
1072 writeChar(number >> 32);
1073 writeChar(number >> 24);
1074 writeChar(number >> 16);
1075 writeChar(number >> 8);
1076 writeChar(number);
1077 } else {
1078 writeHex(number >> 56);
1079 writeHex(number >> 48);
1080 writeHex(number >> 40);
1081 writeHex(number >> 32);
1082 writeHex(number >> 24);
1083 writeHex(number >> 16);
1084 writeHex(number >> 8);
1085 writeHex(number);
1086 }
1087}
1088
1089bool RCCResourceLibrary::writeHeader()
1090{
1091 switch (m_format) {
1092 case C_Code:
1093 case Pass1:
1094 writeString("/****************************************************************************\n");
1095 writeString("** Resource object code\n");
1096 writeString("**\n");
1097 writeString("** Created by: The Resource Compiler for Qt version ");
1098 writeByteArray(QT_VERSION_STR);
1099 writeString("\n**\n");
1100 writeString("** WARNING! All changes made in this file will be lost!\n");
1101 writeString( "*****************************************************************************/\n\n");
1102 break;
1103 case Python_Code:
1104 writeString("# Resource object code (Python 3)\n");
1105 writeString("# Created by: object code\n");
1106 writeString("# Created by: The Resource Compiler for Qt version ");
1107 writeByteArray(QT_VERSION_STR);
1108 writeString("\n");
1109 writeString("# WARNING! All changes made in this file will be lost!\n\n");
1110 writeString("from PySide");
1111 writeByteArray(QByteArray::number(QT_VERSION_MAJOR));
1112 writeString(" import QtCore\n\n");
1113 break;
1114 case Binary:
1115 writeString("qres");
1116 writeNumber4(0);
1117 writeNumber4(0);
1118 writeNumber4(0);
1119 writeNumber4(0);
1120 if (m_formatVersion >= 3)
1121 writeNumber4(m_overallFlags);
1122 break;
1123 default:
1124 break;
1125 }
1126 return true;
1127}
1128
1129bool RCCResourceLibrary::writeDataBlobs()
1130{
1131 Q_ASSERT(m_errorDevice);
1132 switch (m_format) {
1133 case C_Code:
1134 writeString("static const unsigned char qt_resource_data[] = {\n");
1135 break;
1136 case Python_Code:
1137 writeString("qt_resource_data = b\"\\\n");
1138 break;
1139 case Binary:
1140 m_dataOffset = m_out.size();
1141 break;
1142 default:
1143 break;
1144 }
1145
1146 if (!m_root)
1147 return false;
1148
1149 QStack<RCCFileInfo*> pending;
1150 pending.push(m_root);
1151 qint64 offset = 0;
1152 QString errorMessage;
1153 while (!pending.isEmpty()) {
1154 RCCFileInfo *file = pending.pop();
1155 for (auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1156 RCCFileInfo *child = it.value();
1158 pending.push(child);
1159 else {
1160 offset = child->writeDataBlob(*this, offset, &errorMessage);
1161 if (offset == 0) {
1162 m_errorDevice->write(errorMessage.toUtf8());
1163 return false;
1164 }
1165 }
1166 }
1167 }
1168 switch (m_format) {
1169 case C_Code:
1170 writeString("\n};\n\n");
1171 break;
1172 case Python_Code:
1173 writeString("\"\n\n");
1174 break;
1175 case Pass1:
1176 if (offset < 8)
1177 offset = 8;
1178 writeString("\nstatic const unsigned char qt_resource_data[");
1179 writeByteArray(QByteArray::number(offset));
1180 writeString("] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n");
1181 break;
1182 default:
1183 break;
1184 }
1185 return true;
1186}
1187
1188bool RCCResourceLibrary::writeDataNames()
1189{
1190 switch (m_format) {
1191 case C_Code:
1192 case Pass1:
1193 writeString("static const unsigned char qt_resource_name[] = {\n");
1194 break;
1195 case Python_Code:
1196 writeString("qt_resource_name = b\"\\\n");
1197 break;
1198 case Binary:
1199 m_namesOffset = m_out.size();
1200 break;
1201 default:
1202 break;
1203 }
1204
1205 QHash<QString, int> names;
1206 QStack<RCCFileInfo*> pending;
1207
1208 if (!m_root)
1209 return false;
1210
1211 pending.push(m_root);
1212 qint64 offset = 0;
1213 while (!pending.isEmpty()) {
1214 RCCFileInfo *file = pending.pop();
1215 for (auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1216 RCCFileInfo *child = it.value();
1218 pending.push(child);
1219 if (names.contains(child->m_name)) {
1220 child->m_nameOffset = names.value(child->m_name);
1221 } else {
1222 names.insert(child->m_name, offset);
1223 offset = child->writeDataName(*this, offset);
1224 }
1225 }
1226 }
1227 switch (m_format) {
1228 case C_Code:
1229 case Pass1:
1230 writeString("\n};\n\n");
1231 break;
1232 case Python_Code:
1233 writeString("\"\n\n");
1234 break;
1235 default:
1236 break;
1237 }
1238 return true;
1239}
1240
1242{
1243 typedef bool result_type;
1244 result_type operator()(const RCCFileInfo *left, const RCCFileInfo *right) const
1245 {
1246 return qt_hash(left->m_name) < qt_hash(right->m_name);
1247 }
1248};
1249
1250bool RCCResourceLibrary::writeDataStructure()
1251{
1252 switch (m_format) {
1253 case C_Code:
1254 case Pass1:
1255 writeString("static const unsigned char qt_resource_struct[] = {\n");
1256 break;
1257 case Python_Code:
1258 writeString("qt_resource_struct = b\"\\\n");
1259 break;
1260 case Binary:
1261 m_treeOffset = m_out.size();
1262 break;
1263 default:
1264 break;
1265 }
1266
1267 QStack<RCCFileInfo*> pending;
1268
1269 if (!m_root)
1270 return false;
1271
1272 //calculate the child offsets (flat)
1273 pending.push(m_root);
1274 int offset = 1;
1275 while (!pending.isEmpty()) {
1276 RCCFileInfo *file = pending.pop();
1277 file->m_childOffset = offset;
1278
1279 //sort by hash value for binary lookup
1280 QList<RCCFileInfo*> m_children = file->m_children.values();
1281 std::sort(m_children.begin(), m_children.end(), qt_rcc_compare_hash());
1282
1283 //write out the actual data now
1284 for (int i = 0; i < m_children.size(); ++i) {
1285 RCCFileInfo *child = m_children.at(i);
1286 ++offset;
1288 pending.push(child);
1289 }
1290 }
1291
1292 //write out the structure (ie iterate again!)
1293 pending.push(m_root);
1294 m_root->writeDataInfo(*this);
1295 while (!pending.isEmpty()) {
1296 RCCFileInfo *file = pending.pop();
1297
1298 //sort by hash value for binary lookup
1299 QList<RCCFileInfo*> m_children = file->m_children.values();
1300 std::sort(m_children.begin(), m_children.end(), qt_rcc_compare_hash());
1301
1302 //write out the actual data now
1303 for (int i = 0; i < m_children.size(); ++i) {
1304 RCCFileInfo *child = m_children.at(i);
1305 child->writeDataInfo(*this);
1307 pending.push(child);
1308 }
1309 }
1310 switch (m_format) {
1311 case C_Code:
1312 case Pass1:
1313 writeString("\n};\n\n");
1314 break;
1315 case Python_Code:
1316 writeString("\"\n\n");
1317 break;
1318 default:
1319 break;
1320 }
1321
1322 return true;
1323}
1324
1325void RCCResourceLibrary::writeMangleNamespaceFunction(const QByteArray &name)
1326{
1327 if (m_useNameSpace) {
1328 writeString("QT_RCC_MANGLE_NAMESPACE(");
1329 writeByteArray(name);
1330 writeChar(')');
1331 } else {
1332 writeByteArray(name);
1333 }
1334}
1335
1336void RCCResourceLibrary::writeAddNamespaceFunction(const QByteArray &name)
1337{
1338 if (m_useNameSpace) {
1339 writeString("QT_RCC_PREPEND_NAMESPACE(");
1340 writeByteArray(name);
1341 writeChar(')');
1342 } else {
1343 writeByteArray(name);
1344 }
1345}
1346
1347bool RCCResourceLibrary::writeInitializer()
1348{
1349 if (m_format == C_Code || m_format == Pass1) {
1350 //write("\nQT_BEGIN_NAMESPACE\n");
1351 QString initNameStr = m_initName;
1352 if (!initNameStr.isEmpty()) {
1353 initNameStr.prepend(u'_');
1354 auto isAsciiLetterOrNumber = [] (QChar c) -> bool {
1355 ushort ch = c.unicode();
1356 return (ch >= '0' && ch <= '9') ||
1357 (ch >= 'A' && ch <= 'Z') ||
1358 (ch >= 'a' && ch <= 'z') ||
1359 ch == '_';
1360 };
1361 for (QChar &c : initNameStr) {
1362 if (!isAsciiLetterOrNumber(c))
1363 c = u'_';
1364 }
1365 }
1366 QByteArray initName = initNameStr.toLatin1();
1367
1368 //init
1369 if (m_useNameSpace) {
1370 writeString("#ifdef QT_NAMESPACE\n"
1371 "# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name\n"
1372 "# define QT_RCC_MANGLE_NAMESPACE0(x) x\n"
1373 "# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b\n"
1374 "# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)\n"
1375 "# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \\\n"
1376 " QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))\n"
1377 "#else\n"
1378 "# define QT_RCC_PREPEND_NAMESPACE(name) name\n"
1379 "# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
1380 "#endif\n\n");
1381
1382 writeString("#ifdef QT_NAMESPACE\n"
1383 "namespace QT_NAMESPACE {\n"
1384 "#endif\n\n");
1385 }
1386
1387 if (m_root) {
1388 writeString("bool qRegisterResourceData"
1389 "(int, const unsigned char *, "
1390 "const unsigned char *, const unsigned char *);\n");
1391 writeString("bool qUnregisterResourceData"
1392 "(int, const unsigned char *, "
1393 "const unsigned char *, const unsigned char *);\n\n");
1394
1395 if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) {
1396 // use variable relocations with ELF and Mach-O
1397 writeString("#if defined(__ELF__) || defined(__APPLE__)\n");
1398 if (m_overallFlags & RCCFileInfo::Compressed) {
1399 writeString("static inline unsigned char qResourceFeatureZlib()\n"
1400 "{\n"
1401 " extern const unsigned char qt_resourceFeatureZlib;\n"
1402 " return qt_resourceFeatureZlib;\n"
1403 "}\n");
1404 }
1405 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1406 writeString("static inline unsigned char qResourceFeatureZstd()\n"
1407 "{\n"
1408 " extern const unsigned char qt_resourceFeatureZstd;\n"
1409 " return qt_resourceFeatureZstd;\n"
1410 "}\n");
1411 }
1412 writeString("#else\n");
1413 if (m_overallFlags & RCCFileInfo::Compressed)
1414 writeString("unsigned char qResourceFeatureZlib();\n");
1415 if (m_overallFlags & RCCFileInfo::CompressedZstd)
1416 writeString("unsigned char qResourceFeatureZstd();\n");
1417 writeString("#endif\n\n");
1418 }
1419 }
1420
1421 if (m_useNameSpace)
1422 writeString("#ifdef QT_NAMESPACE\n}\n#endif\n\n");
1423
1424 QByteArray initResources = "qInitResources";
1425 initResources += initName;
1426
1427 // Work around -Wmissing-declarations warnings.
1428 writeString("int ");
1429 writeMangleNamespaceFunction(initResources);
1430 writeString("();\n");
1431
1432 writeString("int ");
1433 writeMangleNamespaceFunction(initResources);
1434 writeString("()\n{\n");
1435
1436 if (m_root) {
1437 writeString(" int version = ");
1438 writeDecimal(m_formatVersion);
1439 writeString(";\n ");
1440 writeAddNamespaceFunction("qRegisterResourceData");
1441 writeString("\n (version, qt_resource_struct, "
1442 "qt_resource_name, qt_resource_data);\n");
1443 }
1444 writeString(" return 1;\n");
1445 writeString("}\n\n");
1446
1447 //cleanup
1448 QByteArray cleanResources = "qCleanupResources";
1449 cleanResources += initName;
1450
1451 // Work around -Wmissing-declarations warnings.
1452 writeString("int ");
1453 writeMangleNamespaceFunction(cleanResources);
1454 writeString("();\n");
1455
1456 writeString("int ");
1457 writeMangleNamespaceFunction(cleanResources);
1458 writeString("()\n{\n");
1459 if (m_root) {
1460 writeString(" int version = ");
1461 writeDecimal(m_formatVersion);
1462 writeString(";\n ");
1463
1464 // ODR-use certain symbols from QtCore if we require optional features
1465 if (m_overallFlags & RCCFileInfo::Compressed) {
1466 writeString("version += ");
1467 writeAddNamespaceFunction("qResourceFeatureZlib()");
1468 writeString(";\n ");
1469 }
1470 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1471 writeString("version += ");
1472 writeAddNamespaceFunction("qResourceFeatureZstd()");
1473 writeString(";\n ");
1474 }
1475
1476 writeAddNamespaceFunction("qUnregisterResourceData");
1477 writeString("\n (version, qt_resource_struct, "
1478 "qt_resource_name, qt_resource_data);\n");
1479 }
1480 writeString(" return 1;\n");
1481 writeString("}\n\n");
1482
1483 // -Wexit-time-destructors was added to clang 3.0.0 in 2011.
1484 writeString("#ifdef __clang__\n"
1485 "# pragma clang diagnostic push\n"
1486 "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
1487 "#endif\n\n");
1488
1489 writeString("namespace {\n"
1490 " struct initializer {\n");
1491
1492 if (m_useNameSpace) {
1493 writeByteArray(" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources + ")(); }\n"
1494 " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources + ")(); }\n");
1495 } else {
1496 writeByteArray(" initializer() { " + initResources + "(); }\n"
1497 " ~initializer() { " + cleanResources + "(); }\n");
1498 }
1499 writeString(" } dummy;\n"
1500 "}\n\n");
1501
1502 writeString("#ifdef __clang__\n"
1503 "# pragma clang diagnostic pop\n"
1504 "#endif\n");
1505
1506
1507 } else if (m_format == Binary) {
1508 int i = 4;
1509 char *p = m_out.data();
1510 p[i++] = 0;
1511 p[i++] = 0;
1512 p[i++] = 0;
1513 p[i++] = m_formatVersion;
1514
1515 p[i++] = (m_treeOffset >> 24) & 0xff;
1516 p[i++] = (m_treeOffset >> 16) & 0xff;
1517 p[i++] = (m_treeOffset >> 8) & 0xff;
1518 p[i++] = (m_treeOffset >> 0) & 0xff;
1519
1520 p[i++] = (m_dataOffset >> 24) & 0xff;
1521 p[i++] = (m_dataOffset >> 16) & 0xff;
1522 p[i++] = (m_dataOffset >> 8) & 0xff;
1523 p[i++] = (m_dataOffset >> 0) & 0xff;
1524
1525 p[i++] = (m_namesOffset >> 24) & 0xff;
1526 p[i++] = (m_namesOffset >> 16) & 0xff;
1527 p[i++] = (m_namesOffset >> 8) & 0xff;
1528 p[i++] = (m_namesOffset >> 0) & 0xff;
1529
1530 if (m_formatVersion >= 3) {
1531 p[i++] = (m_overallFlags >> 24) & 0xff;
1532 p[i++] = (m_overallFlags >> 16) & 0xff;
1533 p[i++] = (m_overallFlags >> 8) & 0xff;
1534 p[i++] = (m_overallFlags >> 0) & 0xff;
1535 }
1536 } else if (m_format == Python_Code) {
1537 writeString("def qInitResources():\n");
1538 writeString(" QtCore.qRegisterResourceData(0x");
1539 write2HexDigits(m_formatVersion);
1540 writeString(", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1541 writeString("def qCleanupResources():\n");
1542 writeString(" QtCore.qUnregisterResourceData(0x");
1543 write2HexDigits(m_formatVersion);
1544 writeString(", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1545 writeString("qInitResources()\n");
1546 }
1547 return true;
1548}
1549
1550QT_END_NAMESPACE
\inmodule QtCore
Definition qdir.h:22
Definition qfile.h:71
\inmodule QtCore
Definition qhash.h:844
int m_flags
Definition rcc.cpp:121
bool m_isEmpty
Definition rcc.cpp:133
int m_compressLevel
Definition rcc.cpp:130
QFileInfo m_fileInfo
Definition rcc.cpp:125
@ CompressedZstd
Definition rcc.cpp:78
@ Compressed
Definition rcc.cpp:76
@ Directory
Definition rcc.cpp:77
@ NoFlags
Definition rcc.cpp:75
RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language, QLocale::Territory territory, uint flags, RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel, int compressThreshold, bool noZstd, bool isEmpty)
int m_compressThreshold
Definition rcc.cpp:131
RCCFileInfo()=default
RCCFileInfo & operator=(RCCFileInfo &&other)=delete
bool m_noZstd
Definition rcc.cpp:132
RCCFileInfo & operator=(const RCCFileInfo &)=delete
RCCFileInfo * m_parent
Definition rcc.cpp:126
qint64 m_childOffset
Definition rcc.cpp:137
RCCResourceLibrary::CompressionAlgorithm m_compressAlgo
Definition rcc.cpp:129
qint64 m_nameOffset
Definition rcc.cpp:135
qint64 writeDataName(RCCResourceLibrary &, qint64 offset)
Definition rcc.cpp:422
RCCFileInfo(RCCFileInfo &&)=default
QLocale::Language m_language
Definition rcc.cpp:122
qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage)
Definition rcc.cpp:221
QString m_name
Definition rcc.cpp:124
QString resourceName() const
Definition rcc.cpp:167
qint64 m_dataOffset
Definition rcc.cpp:136
~RCCFileInfo()
Definition rcc.cpp:162
void writeDataInfo(RCCResourceLibrary &lib)
Definition rcc.cpp:176
RCCFileInfo(const RCCFileInfo &)=delete
QLocale::Territory m_territory
Definition rcc.cpp:123
QMultiHash< QString, RCCFileInfo * > m_children
Definition rcc.cpp:127
RCCResourceLibrary(quint8 formatVersion)
Definition rcc.cpp:495
ResourceDataFileMap resourceDataFileMap() const
Definition rcc.cpp:916
bool readFiles(bool listMode, QIODevice &errorDevice)
Definition rcc.cpp:837
bool verbose() const
Definition rcc.h:49
QStringList dataFiles() const
Definition rcc.cpp:878
int formatVersion() const
Definition rcc.h:84
static int parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg)
Definition rcc.cpp:947
bool output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
Definition rcc.cpp:973
QHash< QString, QString > ResourceDataFileMap
Definition rcc_p.h:56
#define qPrintable(string)
Definition qstring.h:1705
#define QStringLiteral(str)
Definition qstring.h:1847
static bool parseBoolean(QStringView value, QString *errorMsg)
Definition rcc.cpp:527
static const char hexDigits[]
Definition rcc.cpp:1051
RCCXmlTag
Definition rcc.cpp:520
@ RccTag
Definition rcc.cpp:521
@ ResourceTag
Definition rcc.cpp:522
@ FileTag
Definition rcc.cpp:523
@ CONSTANT_USENAMESPACE
Definition rcc.cpp:34
@ CONSTANT_ZSTDCOMPRESSLEVEL_CHECK
Definition rcc.cpp:36
@ CONSTANT_COMPRESSLEVEL_DEFAULT
Definition rcc.cpp:35
@ CONSTANT_ZSTDCOMPRESSLEVEL_STORE
Definition rcc.cpp:37
@ CONSTANT_COMPRESSTHRESHOLD_DEFAULT
Definition rcc.cpp:38
static QString msgOpenReadFailed(const QString &fname, const QString &why)
Definition rcc.cpp:57
Q_DECLARE_TYPEINFO(RCCXmlTag, Q_PRIMITIVE_TYPE)
static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
Definition rcc.cpp:901
result_type operator()(const RCCFileInfo *left, const RCCFileInfo *right) const
Definition rcc.cpp:1317