11#include <qdirlisting.h>
16#include <qxmlstream.h>
28using namespace Qt::StringLiterals;
41 m_out.resize(n + len);
42 memcpy(m_out.data() + n, str, len);
47 if (m_format ==
Pass2) {
48 m_outDevice->write(other);
56 return QString::fromLatin1(
"Unable to open %1 for reading: %2\n").arg(fname, why);
80 RCCFileInfo(
const QString &name,
const QFileInfo &fileInfo, QLocale::Language language,
81 QLocale::Territory territory, uint flags,
83 int compressThreshold,
bool noZstd,
bool isEmpty);
118 QLocale::Territory territory, uint flags,
120 int compressThreshold,
bool noZstd,
bool isEmpty)
136 qDeleteAll(m_children);
141 QString resource = m_name;
143 resource = resource.prepend(p->m_name + u'/');
144 resource.prepend(u':');
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 ");
164 lib.writeString(
" // ");
165 lib.writeByteArray(resourceName().toLocal8Bit());
166 lib.writeString(
"\n ");
173 lib.writeNumber4(m_nameOffset);
179 lib.writeNumber4(m_children.size());
182 lib.writeNumber4(m_childOffset);
185 lib.writeNumber4(m_nameOffset);
191 lib.writeNumber2(m_territory);
192 lib.writeNumber2(m_language);
195 lib.writeNumber4(m_dataOffset);
200 lib.writeString(
"\\\n");
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();
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);
216 lib.writeString(
"\\\n");
221 QString *errorMessage)
230 m_dataOffset = offset;
235 QFile file(m_fileInfo.absoluteFilePath());
236 if (!file.open(QFile::ReadOnly)) {
237 *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
241 data = file.readAll();
245 if (data.size() != 0) {
247 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best && !m_noZstd) {
248 m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd;
249 m_compressLevel = 19;
251 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd && !m_noZstd) {
252 if (lib.m_zstdCCtx ==
nullptr)
253 lib.m_zstdCCtx = ZSTD_createCCtx();
254 qsizetype size = data.size();
255 size = ZSTD_COMPRESSBOUND(size);
257 int compressLevel = m_compressLevel;
258 if (compressLevel < 0)
259 compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK;
261 QByteArray compressed(size, Qt::Uninitialized);
262 char *dst =
const_cast<
char *>(compressed.constData());
263 size_t n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
264 data.constData(), data.size(),
266 if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) {
268 if (m_compressLevel < 0) {
270 n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
271 data.constData(), data.size(),
272 CONSTANT_ZSTDCOMPRESSLEVEL_STORE);
274 if (ZSTD_isError(n)) {
275 QString msg =
"%1: error: compression with zstd failed: %2\n"_L1
276 .arg(m_name, 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());
284 lib.m_overallFlags |= CompressedZstd;
285 m_flags |= CompressedZstd;
286 data = std::move(compressed);
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());
294#ifndef QT_NO_COMPRESS
301 qCompress(
reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
303 int compressRatio =
int(100.0 * (data.size() - compressed.size()) / data.size());
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());
314 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
315 lib.m_errorDevice->write(msg.toUtf8());
323 lib.writeString(
" // ");
324 lib.writeByteArray(m_fileInfo.fileName().toLocal8Bit());
325 lib.writeString(
"\n ");
329 if (text || binary || pass2 || python)
330 lib.writeNumber4(data.size());
332 lib.writeString(
"\n ");
334 lib.writeString(
"\\\n");
338 const char *p = data.constData();
339 if (text || python) {
340 for (
int i = data.size(), j = 0; --i >= 0; --j) {
344 lib.writeString(
"\n ");
346 lib.writeString(
"\\\n");
350 }
else if (binary || pass2) {
351 lib.writeByteArray(data);
353 offset += data.size();
357 lib.writeString(
"\n ");
359 lib.writeString(
"\\\n");
371 m_nameOffset = offset;
375 lib.writeString(
" // ");
376 lib.writeByteArray(m_name.toLocal8Bit());
377 lib.writeString(
"\n ");
381 lib.writeNumber2(m_name.size());
383 lib.writeString(
"\n ");
385 lib.writeString(
"\\\n");
389 lib.writeNumber4(qt_hash(m_name));
391 lib.writeString(
"\n ");
393 lib.writeString(
"\\\n");
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");
405 offset += m_name.size()*2;
409 lib.writeString(
"\n ");
411 lib.writeString(
"\\\n");
425 TAG_RESOURCE(
"qresource"_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),
449 m_errorDevice(
nullptr),
450 m_outDevice(
nullptr),
454 m_out.reserve(30 * 1000 * 1000);
456 m_zstdCCtx =
nullptr;
464 ZSTD_freeCCtx(m_zstdCCtx);
477 if (value.compare(
"true"_L1, Qt::CaseInsensitive) == 0)
479 if (value.compare(
"false"_L1, Qt::CaseInsensitive) == 0)
482 *errorMsg = QString::fromLatin1(
"Invalid value for boolean attribute: '%1'").arg(value);
487 const QString &fname, QString currentPath,
bool listMode)
489 Q_ASSERT(m_errorDevice);
490 const QChar slash = u'/';
491 if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
492 currentPath += slash;
494 QXmlStreamReader reader(inputDevice);
495 QStack<RCCXmlTag> tokens;
498 QLocale::Language language = QLocale::c().language();
499 QLocale::Territory territory = QLocale::c().territory();
502 auto compressAlgo = m_compressionAlgo;
503 int compressLevel = m_compressLevel;
504 int compressThreshold = m_compressThreshold;
506 while (!reader.atEnd()) {
507 QXmlStreamReader::TokenType t = reader.readNext();
509 case QXmlStreamReader::StartElement:
510 if (reader.name() == m_strings.TAG_RCC) {
511 if (!tokens.isEmpty())
512 reader.raiseError(
"expected <RCC> tag"_L1);
515 }
else if (reader.name() == m_strings.TAG_LEGAL) {
516 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
517 reader.raiseError(
"unexpected <legal> tag"_L1);
519 m_legal = reader.readElementText().trimmed();
521 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
522 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
523 reader.raiseError(
"unexpected <RESOURCE> tag"_L1);
527 QXmlStreamAttributes attributes = reader.attributes();
528 language = QLocale::c().language();
529 territory = QLocale::c().territory();
531 if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
532 QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
533 QLocale lang = QLocale(attribute);
534 language = lang.language();
535 if (2 == attribute.size()) {
537 territory = QLocale::AnyTerritory;
539 territory = lang.territory();
544 if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
545 prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
546 if (!prefix.startsWith(slash))
547 prefix.prepend(slash);
548 if (!prefix.endsWith(slash))
551 }
else if (reader.name() == m_strings.TAG_FILE) {
552 if (tokens.isEmpty() || tokens.top() !=
ResourceTag) {
553 reader.raiseError(
"unexpected <FILE> tag"_L1);
557 QXmlStreamAttributes attributes = reader.attributes();
559 if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
560 alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
562 compressAlgo = m_compressionAlgo;
563 compressLevel = m_compressLevel;
564 compressThreshold = m_compressThreshold;
567 if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
568 empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
572 if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
573 compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
574 if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
575 QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
580 if (m_compressLevel == -2)
583 if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
584 compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
586 if (!errorString.isEmpty())
587 reader.raiseError(errorString);
590 reader.raiseError(
"unexpected tag: %1"_L1.arg(reader.name().toString()));
594 case QXmlStreamReader::EndElement:
595 if (reader.name() == m_strings.TAG_RCC) {
596 if (!tokens.isEmpty() && tokens.top() ==
RccTag)
599 reader.raiseError(
"unexpected closing tag"_L1);
600 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
601 if (!tokens.isEmpty() && tokens.top() ==
ResourceTag)
604 reader.raiseError(
"unexpected closing tag"_L1);
605 }
else if (reader.name() == m_strings.TAG_FILE) {
606 if (!tokens.isEmpty() && tokens.top() ==
FileTag)
609 reader.raiseError(
"unexpected closing tag"_L1);
613 case QXmlStreamReader::Characters:
614 if (reader.isWhitespace())
616 if (tokens.isEmpty() || tokens.top() !=
FileTag) {
617 reader.raiseError(
"unexpected text"_L1);
619 QString fileName = reader.text().toString();
620 if (fileName.isEmpty()) {
621 const QString msg = QString::fromLatin1(
"RCC: Warning: Null node in XML of '%1'\n").arg(fname);
622 m_errorDevice->write(msg.toUtf8());
628 alias = QDir::cleanPath(alias);
629 while (alias.startsWith(
"../"_L1))
631 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
633 QString absFileName = fileName;
634 if (QDir::isRelativePath(absFileName))
635 absFileName.prepend(currentPath);
636 QFileInfo file(absFileName);
638 if (!alias.endsWith(slash))
641 QStringList filePaths;
644 for (
const auto &entry : QDirListing(file.filePath(), flags)) {
645 const QString &fileName = entry.fileName();
646 if (fileName ==
"."_L1 || fileName ==
".."_L1)
648 filePaths.emplace_back(entry.filePath());
652 std::sort(filePaths.begin(), filePaths.end());
654 for (
const QString &filePath : filePaths) {
655 QFileInfo child(filePath);
657 addFile(alias + child.fileName(),
658 RCCFileInfo(child.fileName(), child, language, territory,
659 child.isDir() ? RCCFileInfo::Directory
660 : RCCFileInfo::NoFlags,
661 compressAlgo, compressLevel, compressThreshold,
664 m_failedResources.push_back(child.fileName());
666 }
else if (listMode || file.isFile()) {
680 m_failedResources.push_back(absFileName);
681 }
else if (file.exists()) {
682 m_failedResources.push_back(absFileName);
683 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Entry '%2' is neither a file nor a directory\n")
684 .arg(fname, fileName);
685 m_errorDevice->write(msg.toUtf8());
688 m_failedResources.push_back(absFileName);
689 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Cannot find file '%2'\n")
690 .arg(fname, fileName);
691 m_errorDevice->write(msg.toUtf8());
702 if (reader.hasError()) {
703 int errorLine = reader.lineNumber();
704 int errorColumn = reader.columnNumber();
705 QString errorMessage = reader.errorString();
706 QString msg = QString::fromLatin1(
"RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
707 m_errorDevice->write(msg.toUtf8());
711 if (m_root ==
nullptr) {
712 const QString msg = QString::fromLatin1(
"RCC: Warning: No resources in '%1'.\n").arg(fname);
713 m_errorDevice->write(msg.toUtf8());
714 if (!listMode && m_format ==
Binary) {
716 m_root =
new RCCFileInfo{};
726 Q_ASSERT(m_errorDevice);
727 if (file.m_fileInfo.size() > 0xffffffff) {
728 const QString msg = QString::fromLatin1(
"File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
729 m_errorDevice->write(msg.toUtf8());
733 m_root =
new RCCFileInfo{};
738 const QStringList nodes = alias.split(u'/');
739 for (
int i = 1; i < nodes.size()-1; ++i) {
740 const QString node = nodes.at(i);
743 if (!parent->m_children.contains(node)) {
748 parent->m_children.insert(node, s);
751 parent = *parent->m_children.constFind(node);
755 const QString filename = nodes.at(nodes.size()-1);
758 auto cbegin = parent->m_children.constFind(filename);
759 auto cend = parent->m_children.constEnd();
760 for (
auto it = cbegin; it != cend; ++it) {
761 if (it.key() == filename && it.value()->m_language == s->m_language &&
762 it.value()->m_territory == s->m_territory) {
763 for (
const QString &name : std::as_const(m_fileNames)) {
764 qWarning(
"%s: Warning: potential duplicate alias detected: '%s'",
770 parent->m_children.insert(filename, s);
780 m_errorDevice =
nullptr;
781 m_failedResources.clear();
788 m_errorDevice = &errorDevice;
791 const QString msg = QString::fromLatin1(
"Processing %1 files [listMode=%2]\n")
792 .arg(m_fileNames.size()).arg(
static_cast<
int>(listMode));
793 m_errorDevice->write(msg.toUtf8());
795 for (
int i = 0; i < m_fileNames.size(); ++i) {
797 QString fname = m_fileNames.at(i);
799 if (fname ==
"-"_L1) {
800 fname =
"(stdin)"_L1;
801 pwd = QDir::currentPath();
802 fileIn.setFileName(fname);
803 if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
804 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
808 pwd = QFileInfo(fname).path();
809 fileIn.setFileName(fname);
810 if (!fileIn.open(QIODevice::ReadOnly)) {
811 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
816 const QString msg = QString::fromLatin1(
"Interpreting %1\n").arg(fname);
817 m_errorDevice->write(msg.toUtf8());
820 if (!interpretResourceFile(&fileIn, fname, pwd, listMode))
829 QStack<RCCFileInfo*> pending;
833 pending.push(m_root);
834 while (!pending.isEmpty()) {
836 for (
auto it = file->m_children.begin();
837 it != file->m_children.end(); ++it) {
842 ret.append(child->m_fileInfo.filePath());
851 const QChar slash = u'/';
852 const auto cend = m_root->m_children.constEnd();
853 for (
auto it = m_root->m_children.constBegin(); it != cend; ++it) {
855 const QString childName = path + slash + child->m_name;
857 resourceDataFileMapRecursion(child, childName, m);
859 m.insert(childName, child->m_fileInfo.filePath());
868 resourceDataFileMapRecursion(m_root, QString(u':'), rc);
874 if (value ==
"best"_L1)
876 if (value ==
"zlib"_L1) {
878 *errorMsg =
"zlib support not compiled in"_L1;
882 }
else if (value ==
"zstd"_L1) {
884 return CompressionAlgorithm::Zstd;
886 *errorMsg =
"Zstandard support not compiled in"_L1;
888 }
else if (value !=
"none"_L1) {
889 *errorMsg = QString::fromLatin1(
"Unknown compression algorithm '%1'").arg(value);
898 int c = level.toInt(&ok);
905 if (c >= 1 && c <= 9)
910 if (c >= 0 && c <= ZSTD_maxCLevel())
917 *errorMsg = QString::fromLatin1(
"invalid compression level '%1'").arg(level);
923 m_errorDevice = &errorDevice;
925 if (m_format ==
Pass2) {
926 const char pattern[] = {
'Q',
'R',
'C',
'_',
'D',
'A',
'T',
'A' };
927 bool foundSignature =
false;
931 for (
int i = 0; i < 8; ) {
932 if (!tempDevice.getChar(&c)) {
935 m_errorDevice->write(
"No data signature found\n");
939 if (c != pattern[i]) {
940 for (
int k = 0; k < i; ++k)
941 outDevice.putChar(pattern[k]);
945 if (c == pattern[i]) {
948 outDevice.putChar(c);
952 m_outDevice = &outDevice;
953 quint64 start = outDevice.pos();
955 quint64 len = outDevice.pos() - start;
957 tempDevice.seek(tempDevice.pos() + len - 8);
958 foundSignature =
true;
964 m_errorDevice->write(
"Outputting code\n");
965 if (!writeHeader()) {
966 m_errorDevice->write(
"Could not write header\n");
970 if (!writeDataBlobs()) {
971 m_errorDevice->write(
"Could not write data blobs.\n");
974 if (!writeDataNames()) {
975 m_errorDevice->write(
"Could not write file names\n");
978 if (!writeDataStructure()) {
979 m_errorDevice->write(
"Could not write data tree\n");
983 if (!writeInitializer()) {
984 m_errorDevice->write(
"Could not write footer\n");
987 outDevice.write(m_out.constData(), m_out.size());
994 char buf[
std::numeric_limits<
int>::digits10 + 2];
995 int n = snprintf(buf,
sizeof(buf),
"%d", value);
1011 if (tmp >= 32 && tmp < 127 && tmp !=
'"' && tmp !=
'\\') {
1012 writeChar(
char(tmp));
1016 write2HexDigits(tmp);
1025 write2HexDigits(tmp);
1034 writeChar(number >> 8);
1037 writeHex(number >> 8);
1045 m_outDevice->putChar(
char(number >> 24));
1046 m_outDevice->putChar(
char(number >> 16));
1047 m_outDevice->putChar(
char(number >> 8));
1048 m_outDevice->putChar(
char(number));
1050 writeChar(number >> 24);
1051 writeChar(number >> 16);
1052 writeChar(number >> 8);
1055 writeHex(number >> 24);
1056 writeHex(number >> 16);
1057 writeHex(number >> 8);
1065 m_outDevice->putChar(
char(number >> 56));
1066 m_outDevice->putChar(
char(number >> 48));
1067 m_outDevice->putChar(
char(number >> 40));
1068 m_outDevice->putChar(
char(number >> 32));
1069 m_outDevice->putChar(
char(number >> 24));
1070 m_outDevice->putChar(
char(number >> 16));
1071 m_outDevice->putChar(
char(number >> 8));
1072 m_outDevice->putChar(
char(number));
1074 writeChar(number >> 56);
1075 writeChar(number >> 48);
1076 writeChar(number >> 40);
1077 writeChar(number >> 32);
1078 writeChar(number >> 24);
1079 writeChar(number >> 16);
1080 writeChar(number >> 8);
1083 writeHex(number >> 56);
1084 writeHex(number >> 48);
1085 writeHex(number >> 40);
1086 writeHex(number >> 32);
1087 writeHex(number >> 24);
1088 writeHex(number >> 16);
1089 writeHex(number >> 8);
1096 auto writeCopyright = [
this](QByteArrayView prefix) {
1097 const QStringList lines = m_legal.split(u'\n', Qt::SkipEmptyParts);
1098 for (
const QString &line : lines) {
1099 write(prefix.data(), prefix.size());
1100 writeString(line.toUtf8().trimmed());
1107 writeString(
"/****************************************************************************\n");
1108 writeString(
"** Resource object code\n");
1109 writeCopyright(
"** ");
1110 writeString(
"**\n");
1111 writeString(
"** Created by: The Resource Compiler for Qt version ");
1112 writeByteArray(QT_VERSION_STR);
1113 writeString(
"\n**\n");
1114 writeString(
"** WARNING! All changes made in this file will be lost!\n");
1115 writeString(
"*****************************************************************************/\n\n");
1116 writeString(
"#ifdef _MSC_VER\n"
1117 "// disable informational message \"function ... selected for automatic inline expansion\"\n"
1118 "#pragma warning (disable: 4711)\n"
1122 writeString(
"# Resource object code (Python 3)\n");
1123 writeCopyright(
"# ");
1124 writeString(
"# Created by: object code\n");
1125 writeString(
"# Created by: The Resource Compiler for Qt version ");
1126 writeByteArray(QT_VERSION_STR);
1128 writeString(
"# WARNING! All changes made in this file will be lost!\n\n");
1129 writeString(
"from PySide");
1130 writeByteArray(QByteArray::number(QT_VERSION_MAJOR));
1131 writeString(
" import QtCore\n\n");
1134 writeString(
"qres");
1139 if (m_formatVersion >= 3)
1140 writeNumber4(m_overallFlags);
1150 Q_ASSERT(m_errorDevice);
1153 writeString(
"static const unsigned char qt_resource_data[] = {\n");
1156 writeString(
"qt_resource_data = b\"\\\n");
1159 m_dataOffset = m_out.size();
1168 QStack<RCCFileInfo*> pending;
1169 pending.push(m_root);
1171 QString errorMessage;
1172 while (!pending.isEmpty()) {
1174 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1177 pending.push(child);
1179 offset = child->writeDataBlob(*
this, offset, &errorMessage);
1181 m_errorDevice->write(errorMessage.toUtf8());
1189 writeString(
"\n};\n\n");
1192 writeString(
"\"\n\n");
1197 writeString(
"\nstatic const unsigned char qt_resource_data[");
1198 writeByteArray(QByteArray::number(offset));
1199 writeString(
"] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n");
1212 writeString(
"static const unsigned char qt_resource_name[] = {\n");
1215 writeString(
"qt_resource_name = b\"\\\n");
1218 m_namesOffset = m_out.size();
1224 QHash<QString,
int> names;
1225 QStack<RCCFileInfo*> pending;
1230 pending.push(m_root);
1232 while (!pending.isEmpty()) {
1234 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1237 pending.push(child);
1238 if (names.contains(child->m_name)) {
1239 child->m_nameOffset = names.value(child->m_name);
1241 names.insert(child->m_name, offset);
1242 offset = child->writeDataName(*
this, offset);
1249 writeString(
"\n};\n\n");
1252 writeString(
"\"\n\n");
1265 return qt_hash(left->m_name) < qt_hash(right->m_name);
1274 writeString(
"static const unsigned char qt_resource_struct[] = {\n");
1277 writeString(
"qt_resource_struct = b\"\\\n");
1280 m_treeOffset = m_out.size();
1286 QStack<RCCFileInfo*> pending;
1292 pending.push(m_root);
1294 while (!pending.isEmpty()) {
1296 file->m_childOffset = offset;
1299 QList<RCCFileInfo*> m_children = file->m_children.values();
1303 for (
int i = 0; i < m_children.size(); ++i) {
1307 pending.push(child);
1312 pending.push(m_root);
1314 while (!pending.isEmpty()) {
1318 QList<RCCFileInfo*> m_children = file->m_children.values();
1322 for (
int i = 0; i < m_children.size(); ++i) {
1326 pending.push(child);
1332 writeString(
"\n};\n\n");
1335 writeString(
"\"\n\n");
1346 if (m_useNameSpace) {
1347 writeString(
"QT_RCC_MANGLE_NAMESPACE(");
1348 writeByteArray(name);
1351 writeByteArray(name);
1357 if (m_useNameSpace) {
1358 writeString(
"QT_RCC_PREPEND_NAMESPACE(");
1359 writeByteArray(name);
1362 writeByteArray(name);
1370 QString initNameStr = m_initName;
1371 if (!initNameStr.isEmpty()) {
1372 initNameStr.prepend(u'_');
1373 auto isAsciiLetterOrNumber = [] (QChar c) ->
bool {
1374 ushort ch = c.unicode();
1375 return (ch >=
'0' && ch <=
'9') ||
1376 (ch >=
'A' && ch <=
'Z') ||
1377 (ch >=
'a' && ch <=
'z') ||
1380 for (QChar &c : initNameStr) {
1381 if (!isAsciiLetterOrNumber(c))
1385 QByteArray initName = initNameStr.toLatin1();
1388 if (m_useNameSpace) {
1389 writeString(
"#ifdef QT_NAMESPACE\n"
1390 "# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name\n"
1391 "# define QT_RCC_MANGLE_NAMESPACE0(x) x\n"
1392 "# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b\n"
1393 "# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)\n"
1394 "# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \\\n"
1395 " QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))\n"
1397 "# define QT_RCC_PREPEND_NAMESPACE(name) name\n"
1398 "# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
1401 writeString(
"#if defined(QT_INLINE_NAMESPACE)\n"
1402 "inline namespace QT_NAMESPACE {\n"
1403 "#elif defined(QT_NAMESPACE)\n"
1404 "namespace QT_NAMESPACE {\n"
1409 writeString(
"bool qRegisterResourceData"
1410 "(int, const unsigned char *, "
1411 "const unsigned char *, const unsigned char *);\n");
1412 writeString(
"bool qUnregisterResourceData"
1413 "(int, const unsigned char *, "
1414 "const unsigned char *, const unsigned char *);\n\n");
1416 if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) {
1418 writeString(
"#if defined(__ELF__) || defined(__APPLE__)\n");
1419 if (m_overallFlags & RCCFileInfo::Compressed) {
1420 writeString(
"static inline unsigned char qResourceFeatureZlib()\n"
1422 " extern const unsigned char qt_resourceFeatureZlib;\n"
1423 " return qt_resourceFeatureZlib;\n"
1426 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1427 writeString(
"static inline unsigned char qResourceFeatureZstd()\n"
1429 " extern const unsigned char qt_resourceFeatureZstd;\n"
1430 " return qt_resourceFeatureZstd;\n"
1433 writeString(
"#else\n");
1434 if (m_overallFlags & RCCFileInfo::Compressed)
1435 writeString(
"unsigned char qResourceFeatureZlib();\n");
1436 if (m_overallFlags & RCCFileInfo::CompressedZstd)
1437 writeString(
"unsigned char qResourceFeatureZstd();\n");
1438 writeString(
"#endif\n\n");
1443 writeString(
"#ifdef QT_NAMESPACE\n}\n#endif\n\n");
1446 initResources += initName;
1449 writeString(
"int ");
1450 writeMangleNamespaceFunction(initResources);
1451 writeString(
"();\n");
1453 writeString(
"int ");
1454 writeMangleNamespaceFunction(initResources);
1455 writeString(
"()\n{\n");
1458 writeString(
" int version = ");
1459 writeDecimal(m_formatVersion);
1460 writeString(
";\n ");
1461 writeAddNamespaceFunction(
"qRegisterResourceData");
1462 writeString(
"\n (version, qt_resource_struct, "
1463 "qt_resource_name, qt_resource_data);\n");
1465 writeString(
" return 1;\n");
1466 writeString(
"}\n\n");
1469 QByteArray cleanResources =
"qCleanupResources";
1470 cleanResources += initName;
1473 writeString(
"int ");
1474 writeMangleNamespaceFunction(cleanResources);
1475 writeString(
"();\n");
1477 writeString(
"int ");
1478 writeMangleNamespaceFunction(cleanResources);
1479 writeString(
"()\n{\n");
1481 writeString(
" int version = ");
1482 writeDecimal(m_formatVersion);
1483 writeString(
";\n ");
1486 if (m_overallFlags & RCCFileInfo::Compressed) {
1487 writeString(
"version += ");
1488 writeAddNamespaceFunction(
"qResourceFeatureZlib()");
1489 writeString(
";\n ");
1491 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1492 writeString(
"version += ");
1493 writeAddNamespaceFunction(
"qResourceFeatureZstd()");
1494 writeString(
";\n ");
1497 writeAddNamespaceFunction(
"qUnregisterResourceData");
1498 writeString(
"\n (version, qt_resource_struct, "
1499 "qt_resource_name, qt_resource_data);\n");
1501 writeString(
" return 1;\n");
1502 writeString(
"}\n\n");
1505 writeString(
"#ifdef __clang__\n"
1506 "# pragma clang diagnostic push\n"
1507 "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
1510 writeString(
"namespace {\n"
1511 " struct initializer {\n");
1513 if (m_useNameSpace) {
1514 writeByteArray(
" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources +
")(); }\n"
1515 " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources +
")(); }\n");
1517 writeByteArray(
" initializer() { " + initResources +
"(); }\n"
1518 " ~initializer() { " + cleanResources +
"(); }\n");
1520 writeString(
" } dummy;\n"
1523 writeString(
"#ifdef __clang__\n"
1524 "# pragma clang diagnostic pop\n"
1528 }
else if (m_format ==
Binary) {
1530 char *p = m_out.data();
1534 p[i++] = m_formatVersion;
1536 p[i++] = (m_treeOffset >> 24) & 0xff;
1537 p[i++] = (m_treeOffset >> 16) & 0xff;
1538 p[i++] = (m_treeOffset >> 8) & 0xff;
1539 p[i++] = (m_treeOffset >> 0) & 0xff;
1541 p[i++] = (m_dataOffset >> 24) & 0xff;
1542 p[i++] = (m_dataOffset >> 16) & 0xff;
1543 p[i++] = (m_dataOffset >> 8) & 0xff;
1544 p[i++] = (m_dataOffset >> 0) & 0xff;
1546 p[i++] = (m_namesOffset >> 24) & 0xff;
1547 p[i++] = (m_namesOffset >> 16) & 0xff;
1548 p[i++] = (m_namesOffset >> 8) & 0xff;
1549 p[i++] = (m_namesOffset >> 0) & 0xff;
1551 if (m_formatVersion >= 3) {
1552 p[i++] = (m_overallFlags >> 24) & 0xff;
1553 p[i++] = (m_overallFlags >> 16) & 0xff;
1554 p[i++] = (m_overallFlags >> 8) & 0xff;
1555 p[i++] = (m_overallFlags >> 0) & 0xff;
1558 writeString(
"def qInitResources():\n");
1559 writeString(
" QtCore.qRegisterResourceData(0x");
1560 write2HexDigits(m_formatVersion);
1561 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1562 writeString(
"def qCleanupResources():\n");
1563 writeString(
" QtCore.qUnregisterResourceData(0x");
1564 write2HexDigits(m_formatVersion);
1565 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1566 writeString(
"qInitResources()\n");
IteratorFlag
This enum class describes flags that can be used to configure the behavior of QDirListing.
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)
RCCFileInfo & operator=(RCCFileInfo &&other)=delete
RCCFileInfo & operator=(const RCCFileInfo &)=delete
RCCResourceLibrary::CompressionAlgorithm m_compressAlgo
qint64 writeDataName(RCCResourceLibrary &, qint64 offset)
RCCFileInfo(RCCFileInfo &&)=default
QLocale::Language m_language
qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage)
QString resourceName() const
void writeDataInfo(RCCResourceLibrary &lib)
RCCFileInfo(const RCCFileInfo &)=delete
QLocale::Territory m_territory
QMultiHash< QString, RCCFileInfo * > m_children
RCCResourceLibrary(quint8 formatVersion)
ResourceDataFileMap resourceDataFileMap() const
bool readFiles(bool listMode, QIODevice &errorDevice)
QStringList dataFiles() const
int formatVersion() const
static int parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg)
bool output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
QHash< QString, QString > ResourceDataFileMap
#define qPrintable(string)
#define QStringLiteral(str)
result_type operator()(const RCCFileInfo *left, const RCCFileInfo *right) const