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