9#include <qcryptographichash.h>
13#include <qdirlisting.h>
18#include <qxmlstream.h>
30using namespace Qt::StringLiterals;
43 m_out.resize(n + len);
44 memcpy(m_out.data() + n, str, len);
49 if (m_format ==
Pass2) {
50 m_outDevice->write(other);
58 return QString::fromLatin1(
"Unable to open %1 for reading: %2\n").arg(fname, why);
82 RCCFileInfo(
const QString &name,
const QFileInfo &fileInfo, QLocale::Language language,
83 QLocale::Territory territory, uint flags,
85 int compressThreshold,
bool noZstd,
bool isEmpty);
103 return compressAlgo == other.compressAlgo &&
104 compressLevel == other.compressLevel &&
105 compressThreshold == other.compressThreshold &&
116 QString *errorMessage);
145 QLocale::Territory territory, uint flags,
147 int compressThreshold,
bool noZstd,
bool isEmpty)
163 qDeleteAll(m_children);
168 QString resource = m_name;
170 resource = resource.prepend(p->m_name + u'/');
171 resource.prepend(u':');
182 if (m_language != QLocale::C) {
183 lib.writeString(
" // ");
184 lib.writeByteArray(resourceName().toLocal8Bit());
185 lib.writeString(
" [");
186 lib.writeByteArray(QByteArray::number(m_territory));
187 lib.writeString(
"::");
188 lib.writeByteArray(QByteArray::number(m_language));
189 lib.writeString(
"[\n ");
191 lib.writeString(
" // ");
192 lib.writeByteArray(resourceName().toLocal8Bit());
193 lib.writeString(
"\n ");
200 lib.writeNumber4(m_nameOffset);
206 lib.writeNumber4(m_children.size());
209 lib.writeNumber4(m_childOffset);
212 lib.writeNumber4(m_nameOffset);
218 lib.writeNumber2(m_territory);
219 lib.writeNumber2(m_language);
222 lib.writeNumber4(m_dataOffset);
227 lib.writeString(
"\\\n");
231 const QDateTime lastModified = m_fileInfo.lastModified(QTimeZone::UTC);
232 quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
233 static const quint64 sourceDate = 1000 * qgetenv(
"QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
235 lastmod = sourceDate;
236 static const quint64 sourceDate2 = 1000 * qgetenv(
"SOURCE_DATE_EPOCH").toULongLong();
237 if (sourceDate2 != 0)
238 lastmod = sourceDate2;
239 lib.writeNumber8(lastmod);
243 lib.writeString(
"\\\n");
250 QString *errorMessage)
259 m_dataOffset = offset;
264 const QString absoluteFilePath = m_fileInfo.absoluteFilePath();
265 QFile file(absoluteFilePath);
266 if (!file.open(QFile::ReadOnly)) {
267 *errorMessage = msgOpenReadFailed(absoluteFilePath, file.errorString());
270 data = file.readAll();
274 const QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha256);
276 const QList<RCCFileInfo *> potentialCandidates = dedupByContent.values(key);
277 for (
const RCCFileInfo *candidate : potentialCandidates) {
279 QFile candidateFile(candidate->m_fileInfo.absoluteFilePath());
280 if (!candidateFile.open(QFile::ReadOnly)) {
281 *errorMessage = msgOpenReadFailed(candidate->m_fileInfo.absoluteFilePath(),
282 candidateFile.errorString());
285 if (data != candidateFile.readAll()) {
290 m_dataOffset = candidate->m_dataOffset;
291 m_flags = candidate->m_flags;
294 dedupByContent.insert(key,
this);
298 if (data.size() != 0) {
300 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best && !m_noZstd) {
301 m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd;
302 m_compressLevel = 19;
304 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd && !m_noZstd) {
305 if (lib.m_zstdCCtx ==
nullptr)
306 lib.m_zstdCCtx = ZSTD_createCCtx();
307 qsizetype size = data.size();
308 size = ZSTD_COMPRESSBOUND(size);
310 int compressLevel = m_compressLevel;
311 if (compressLevel < 0)
312 compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK;
314 QByteArray compressed(size, Qt::Uninitialized);
315 char *dst =
const_cast<
char *>(compressed.constData());
316 size_t n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
317 data.constData(), data.size(),
319 if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) {
321 if (m_compressLevel < 0) {
323 n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
324 data.constData(), data.size(),
325 CONSTANT_ZSTDCOMPRESSLEVEL_STORE);
327 if (ZSTD_isError(n)) {
328 QString msg = QString::fromLatin1(
"%1: error: compression with zstd failed: %2\n")
329 .arg(m_name, QString::fromUtf8(ZSTD_getErrorName(n)));
330 lib.m_errorDevice->write(msg.toUtf8());
331 }
else if (lib.verbose()) {
332 QString msg = QString::fromLatin1(
"%1: note: compressed using zstd (%2 -> %3)\n")
333 .arg(m_name).arg(data.size()).arg(n);
334 lib.m_errorDevice->write(msg.toUtf8());
337 lib.m_overallFlags |= CompressedZstd;
338 m_flags |= CompressedZstd;
339 data = std::move(compressed);
341 }
else if (lib.verbose()) {
342 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
343 lib.m_errorDevice->write(msg.toUtf8());
347#ifndef QT_NO_COMPRESS
354 qCompress(
reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
356 int compressRatio =
int(100.0 * (data.size() - compressed.size()) / data.size());
359 QString msg = QString::fromLatin1(
"%1: note: compressed using zlib (%2 -> %3)\n")
360 .arg(m_name).arg(data.size()).arg(compressed.size());
361 lib.m_errorDevice->write(msg.toUtf8());
367 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
368 lib.m_errorDevice->write(msg.toUtf8());
376 lib.writeString(
" // ");
377 lib.writeByteArray(m_fileInfo.fileName().toLocal8Bit());
378 lib.writeString(
"\n ");
382 if (text || binary || pass2 || python)
383 lib.writeNumber4(data.size());
385 lib.writeString(
"\n ");
387 lib.writeString(
"\\\n");
391 const char *p = data.constData();
392 if (text || python) {
393 for (
int i = data.size(), j = 0; --i >= 0; --j) {
397 lib.writeString(
"\n ");
399 lib.writeString(
"\\\n");
403 }
else if (binary || pass2) {
404 lib.writeByteArray(data);
406 offset += data.size();
410 lib.writeString(
"\n ");
412 lib.writeString(
"\\\n");
424 m_nameOffset = offset;
428 lib.writeString(
" // ");
429 lib.writeByteArray(m_name.toLocal8Bit());
430 lib.writeString(
"\n ");
434 lib.writeNumber2(m_name.size());
436 lib.writeString(
"\n ");
438 lib.writeString(
"\\\n");
442 lib.writeNumber4(qt_hash(m_name));
444 lib.writeString(
"\n ");
446 lib.writeString(
"\\\n");
450 const QChar *unicode = m_name.unicode();
451 for (
int i = 0; i < m_name.size(); ++i) {
452 lib.writeNumber2(unicode[i].unicode());
453 if ((text || pass1) && i % 16 == 0)
454 lib.writeString(
"\n ");
455 else if (python && i % 16 == 0)
456 lib.writeString(
"\\\n");
458 offset += m_name.size()*2;
462 lib.writeString(
"\n ");
464 lib.writeString(
"\\\n");
478 TAG_RESOURCE(
"qresource"_L1),
480 ATTRIBUTE_LANG(
"lang"_L1),
481 ATTRIBUTE_PREFIX(
"prefix"_L1),
482 ATTRIBUTE_ALIAS(
"alias"_L1),
483 ATTRIBUTE_EMPTY(
"empty"_L1),
484 ATTRIBUTE_THRESHOLD(
"threshold"_L1),
485 ATTRIBUTE_COMPRESS(
"compress"_L1),
486 ATTRIBUTE_COMPRESSALGO(QStringLiteral(
"compression-algorithm"))
502 m_errorDevice(
nullptr),
503 m_outDevice(
nullptr),
507 m_out.reserve(30 * 1000 * 1000);
509 m_zstdCCtx =
nullptr;
517 ZSTD_freeCCtx(m_zstdCCtx);
530 if (value.compare(
"true"_L1, Qt::CaseInsensitive) == 0)
532 if (value.compare(
"false"_L1, Qt::CaseInsensitive) == 0)
535 *errorMsg = QString::fromLatin1(
"Invalid value for boolean attribute: '%1'").arg(value);
540 const QString &fname, QString currentPath,
bool listMode)
542 Q_ASSERT(m_errorDevice);
543 const QChar slash = u'/';
544 if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
545 currentPath += slash;
547 QXmlStreamReader reader(inputDevice);
548 QStack<RCCXmlTag> tokens;
551 QLocale::Language language = QLocale::c().language();
552 QLocale::Territory territory = QLocale::c().territory();
555 auto compressAlgo = m_compressionAlgo;
556 int compressLevel = m_compressLevel;
557 int compressThreshold = m_compressThreshold;
559 while (!reader.atEnd()) {
560 QXmlStreamReader::TokenType t = reader.readNext();
562 case QXmlStreamReader::StartElement:
563 if (reader.name() == m_strings.TAG_RCC) {
564 if (!tokens.isEmpty())
565 reader.raiseError(
"expected <RCC> tag"_L1);
568 }
else if (reader.name() == m_strings.TAG_LEGAL) {
569 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
570 reader.raiseError(
"unexpected <legal> tag"_L1);
572 m_legal = reader.readElementText().trimmed();
574 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
575 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
576 reader.raiseError(
"unexpected <RESOURCE> tag"_L1);
580 QXmlStreamAttributes attributes = reader.attributes();
581 language = QLocale::c().language();
582 territory = QLocale::c().territory();
584 if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
585 QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
586 QLocale lang = QLocale(attribute);
587 language = lang.language();
588 if (2 == attribute.size()) {
590 territory = QLocale::AnyTerritory;
592 territory = lang.territory();
597 if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
598 prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
599 if (!prefix.startsWith(slash))
600 prefix.prepend(slash);
601 if (!prefix.endsWith(slash))
604 }
else if (reader.name() == m_strings.TAG_FILE) {
605 if (tokens.isEmpty() || tokens.top() !=
ResourceTag) {
606 reader.raiseError(
"unexpected <FILE> tag"_L1);
610 QXmlStreamAttributes attributes = reader.attributes();
612 if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
613 alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
615 compressAlgo = m_compressionAlgo;
616 compressLevel = m_compressLevel;
617 compressThreshold = m_compressThreshold;
620 if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
621 empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
625 if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
626 compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
627 if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
628 QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
633 if (m_compressLevel == -2)
636 if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
637 compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
639 if (!errorString.isEmpty())
640 reader.raiseError(errorString);
643 reader.raiseError(
"unexpected tag: %1"_L1.arg(reader.name().toString()));
647 case QXmlStreamReader::EndElement:
648 if (reader.name() == m_strings.TAG_RCC) {
649 if (!tokens.isEmpty() && tokens.top() ==
RccTag)
652 reader.raiseError(
"unexpected closing tag"_L1);
653 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
654 if (!tokens.isEmpty() && tokens.top() ==
ResourceTag)
657 reader.raiseError(
"unexpected closing tag"_L1);
658 }
else if (reader.name() == m_strings.TAG_FILE) {
659 if (!tokens.isEmpty() && tokens.top() ==
FileTag)
662 reader.raiseError(
"unexpected closing tag"_L1);
666 case QXmlStreamReader::Characters:
667 if (reader.isWhitespace())
669 if (tokens.isEmpty() || tokens.top() !=
FileTag) {
670 reader.raiseError(
"unexpected text"_L1);
672 QString fileName = reader.text().toString();
673 if (fileName.isEmpty()) {
674 const QString msg = QString::fromLatin1(
"RCC: Warning: Null node in XML of '%1'\n").arg(fname);
675 m_errorDevice->write(msg.toUtf8());
681 alias = QDir::cleanPath(alias);
682 while (alias.startsWith(
"../"_L1))
684 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
686 QString absFileName = fileName;
687 if (QDir::isRelativePath(absFileName))
688 absFileName.prepend(currentPath);
689 QFileInfo file(absFileName);
691 if (!alias.endsWith(slash))
694 QStringList filePaths;
697 for (
const auto &entry : QDirListing(file.filePath(), flags)) {
698 const QString &fileName = entry.fileName();
699 if (fileName ==
"."_L1 || fileName ==
".."_L1)
701 filePaths.emplace_back(entry.filePath());
705 std::sort(filePaths.begin(), filePaths.end());
707 for (
const QString &filePath : filePaths) {
708 QFileInfo child(filePath);
710 addFile(alias + child.fileName(),
711 RCCFileInfo(child.fileName(), child, language, territory,
712 child.isDir() ? RCCFileInfo::Directory
713 : RCCFileInfo::NoFlags,
714 compressAlgo, compressLevel, compressThreshold,
717 m_failedResources.push_back(child.fileName());
719 }
else if (listMode || file.isFile()) {
733 m_failedResources.push_back(absFileName);
734 }
else if (file.exists()) {
735 m_failedResources.push_back(absFileName);
736 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Entry '%2' is neither a file nor a directory\n")
737 .arg(fname, fileName);
738 m_errorDevice->write(msg.toUtf8());
741 m_failedResources.push_back(absFileName);
742 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Cannot find file '%2'\n")
743 .arg(fname, fileName);
744 m_errorDevice->write(msg.toUtf8());
755 if (reader.hasError()) {
756 int errorLine = reader.lineNumber();
757 int errorColumn = reader.columnNumber();
758 QString errorMessage = reader.errorString();
759 QString msg = QString::fromLatin1(
"RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
760 m_errorDevice->write(msg.toUtf8());
764 if (m_root ==
nullptr) {
765 const QString msg = QString::fromLatin1(
"RCC: Warning: No resources in '%1'.\n").arg(fname);
766 m_errorDevice->write(msg.toUtf8());
767 if (!listMode && m_format ==
Binary) {
769 m_root =
new RCCFileInfo{};
779 Q_ASSERT(m_errorDevice);
780 if (file.m_fileInfo.size() > 0xffffffff) {
781 const QString msg = QString::fromLatin1(
"File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
782 m_errorDevice->write(msg.toUtf8());
786 m_root =
new RCCFileInfo{};
791 const QStringList nodes = alias.split(u'/');
792 for (
int i = 1; i < nodes.size()-1; ++i) {
793 const QString node = nodes.at(i);
796 if (!parent->m_children.contains(node)) {
801 parent->m_children.insert(node, s);
804 parent = *parent->m_children.constFind(node);
808 const QString filename = nodes.at(nodes.size()-1);
811 auto cbegin = parent->m_children.constFind(filename);
812 auto cend = parent->m_children.constEnd();
813 for (
auto it = cbegin; it != cend; ++it) {
814 if (it.key() == filename && it.value()->m_language == s->m_language &&
815 it.value()->m_territory == s->m_territory) {
816 for (
const QString &name : std::as_const(m_fileNames)) {
817 qWarning(
"%s: Warning: potential duplicate alias detected: '%s'",
823 parent->m_children.insert(filename, s);
833 m_errorDevice =
nullptr;
834 m_failedResources.clear();
841 m_errorDevice = &errorDevice;
844 const QString msg = QString::fromLatin1(
"Processing %1 files [listMode=%2]\n")
845 .arg(m_fileNames.size()).arg(
static_cast<
int>(listMode));
846 m_errorDevice->write(msg.toUtf8());
848 for (
int i = 0; i < m_fileNames.size(); ++i) {
850 QString fname = m_fileNames.at(i);
852 if (fname ==
"-"_L1) {
853 fname =
"(stdin)"_L1;
854 pwd = QDir::currentPath();
855 fileIn.setFileName(fname);
856 if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
857 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
861 pwd = QFileInfo(fname).path();
862 fileIn.setFileName(fname);
863 if (!fileIn.open(QIODevice::ReadOnly)) {
864 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
869 const QString msg = QString::fromLatin1(
"Interpreting %1\n").arg(fname);
870 m_errorDevice->write(msg.toUtf8());
873 if (!interpretResourceFile(&fileIn, fname, pwd, listMode))
882 QStack<RCCFileInfo*> pending;
886 pending.push(m_root);
887 while (!pending.isEmpty()) {
889 for (
auto it = file->m_children.begin();
890 it != file->m_children.end(); ++it) {
895 ret.append(child->m_fileInfo.filePath());
904 const QChar slash = u'/';
905 const auto cend = m_root->m_children.constEnd();
906 for (
auto it = m_root->m_children.constBegin(); it != cend; ++it) {
908 const QString childName = path + slash + child->m_name;
910 resourceDataFileMapRecursion(child, childName, m);
912 m.insert(childName, child->m_fileInfo.filePath());
921 resourceDataFileMapRecursion(m_root, QString(u':'), rc);
927 if (value ==
"best"_L1)
929 if (value ==
"zlib"_L1) {
931 *errorMsg =
"zlib support not compiled in"_L1;
935 }
else if (value ==
"zstd"_L1) {
937 return CompressionAlgorithm::Zstd;
939 *errorMsg =
"Zstandard support not compiled in"_L1;
941 }
else if (value !=
"none"_L1) {
942 *errorMsg = QString::fromLatin1(
"Unknown compression algorithm '%1'").arg(value);
951 int c = level.toInt(&ok);
958 if (c >= 1 && c <= 9)
963 if (c >= 0 && c <= ZSTD_maxCLevel())
970 *errorMsg = QString::fromLatin1(
"invalid compression level '%1'").arg(level);
976 m_errorDevice = &errorDevice;
978 if (m_format ==
Pass2) {
979 const char pattern[] = {
'Q',
'R',
'C',
'_',
'D',
'A',
'T',
'A' };
980 bool foundSignature =
false;
984 for (
int i = 0; i < 8; ) {
985 if (!tempDevice.getChar(&c)) {
988 m_errorDevice->write(
"No data signature found\n");
992 if (c != pattern[i]) {
993 for (
int k = 0; k < i; ++k)
994 outDevice.putChar(pattern[k]);
998 if (c == pattern[i]) {
1001 outDevice.putChar(c);
1005 m_outDevice = &outDevice;
1006 quint64 start = outDevice.pos();
1008 quint64 len = outDevice.pos() - start;
1010 tempDevice.seek(tempDevice.pos() + len - 8);
1011 foundSignature =
true;
1017 m_errorDevice->write(
"Outputting code\n");
1018 if (!writeHeader()) {
1019 m_errorDevice->write(
"Could not write header\n");
1023 if (!writeDataBlobs()) {
1024 m_errorDevice->write(
"Could not write data blobs.\n");
1027 if (!writeDataNames()) {
1028 m_errorDevice->write(
"Could not write file names\n");
1031 if (!writeDataStructure()) {
1032 m_errorDevice->write(
"Could not write data tree\n");
1036 if (!writeInitializer()) {
1037 m_errorDevice->write(
"Could not write footer\n");
1040 outDevice.write(m_out.constData(), m_out.size());
1047 char buf[
std::numeric_limits<
int>::digits10 + 2];
1048 int n = snprintf(buf,
sizeof(buf),
"%d", value);
1064 if (tmp >= 32 && tmp < 127 && tmp !=
'"' && tmp !=
'\\') {
1065 writeChar(
char(tmp));
1069 write2HexDigits(tmp);
1078 write2HexDigits(tmp);
1087 writeChar(number >> 8);
1090 writeHex(number >> 8);
1098 m_outDevice->putChar(
char(number >> 24));
1099 m_outDevice->putChar(
char(number >> 16));
1100 m_outDevice->putChar(
char(number >> 8));
1101 m_outDevice->putChar(
char(number));
1103 writeChar(number >> 24);
1104 writeChar(number >> 16);
1105 writeChar(number >> 8);
1108 writeHex(number >> 24);
1109 writeHex(number >> 16);
1110 writeHex(number >> 8);
1118 m_outDevice->putChar(
char(number >> 56));
1119 m_outDevice->putChar(
char(number >> 48));
1120 m_outDevice->putChar(
char(number >> 40));
1121 m_outDevice->putChar(
char(number >> 32));
1122 m_outDevice->putChar(
char(number >> 24));
1123 m_outDevice->putChar(
char(number >> 16));
1124 m_outDevice->putChar(
char(number >> 8));
1125 m_outDevice->putChar(
char(number));
1127 writeChar(number >> 56);
1128 writeChar(number >> 48);
1129 writeChar(number >> 40);
1130 writeChar(number >> 32);
1131 writeChar(number >> 24);
1132 writeChar(number >> 16);
1133 writeChar(number >> 8);
1136 writeHex(number >> 56);
1137 writeHex(number >> 48);
1138 writeHex(number >> 40);
1139 writeHex(number >> 32);
1140 writeHex(number >> 24);
1141 writeHex(number >> 16);
1142 writeHex(number >> 8);
1149 auto writeCopyright = [
this](QByteArrayView prefix) {
1150 const QStringList lines = m_legal.split(u'\n', Qt::SkipEmptyParts);
1151 for (
const QString &line : lines) {
1152 write(prefix.data(), prefix.size());
1153 writeString(line.toUtf8().trimmed());
1160 writeString(
"/****************************************************************************\n");
1161 writeString(
"** Resource object code\n");
1162 writeCopyright(
"** ");
1163 writeString(
"**\n");
1164 writeString(
"** Created by: The Resource Compiler for Qt version ");
1165 writeByteArray(QT_VERSION_STR);
1166 writeString(
"\n**\n");
1167 writeString(
"** WARNING! All changes made in this file will be lost!\n");
1168 writeString(
"*****************************************************************************/\n\n");
1169 writeString(
"#ifdef _MSC_VER\n"
1170 "// disable informational message \"function ... selected for automatic inline expansion\"\n"
1171 "#pragma warning (disable: 4711)\n"
1175 writeString(
"# Resource object code (Python 3)\n");
1176 writeCopyright(
"# ");
1177 writeString(
"# Created by: object code\n");
1178 writeString(
"# Created by: The Resource Compiler for Qt version ");
1179 writeByteArray(QT_VERSION_STR);
1181 writeString(
"# WARNING! All changes made in this file will be lost!\n\n");
1182 writeString(
"from PySide");
1183 writeByteArray(QByteArray::number(QT_VERSION_MAJOR));
1184 writeString(
" import QtCore\n\n");
1187 writeString(
"qres");
1192 if (m_formatVersion >= 3)
1193 writeNumber4(m_overallFlags);
1203 Q_ASSERT(m_errorDevice);
1206 writeString(
"static const unsigned char qt_resource_data[] = {\n");
1209 writeString(
"qt_resource_data = b\"\\\n");
1212 m_dataOffset = m_out.size();
1221 QStack<RCCFileInfo*> pending;
1222 pending.push(m_root);
1225 QString errorMessage;
1226 while (!pending.isEmpty()) {
1228 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1231 pending.push(child);
1233 offset = child->writeDataBlob(*
this, offset,
1234 dedupByContent, &errorMessage);
1236 m_errorDevice->write(errorMessage.toUtf8());
1244 writeString(
"\n};\n\n");
1247 writeString(
"\"\n\n");
1252 writeString(
"\nstatic const unsigned char qt_resource_data[");
1253 writeByteArray(QByteArray::number(offset));
1254 writeString(
"] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n");
1267 writeString(
"static const unsigned char qt_resource_name[] = {\n");
1270 writeString(
"qt_resource_name = b\"\\\n");
1273 m_namesOffset = m_out.size();
1279 QHash<QString,
int> names;
1280 QStack<RCCFileInfo*> pending;
1285 pending.push(m_root);
1287 while (!pending.isEmpty()) {
1289 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1292 pending.push(child);
1293 if (names.contains(child->m_name)) {
1294 child->m_nameOffset = names.value(child->m_name);
1296 names.insert(child->m_name, offset);
1297 offset = child->writeDataName(*
this, offset);
1304 writeString(
"\n};\n\n");
1307 writeString(
"\"\n\n");
1320 return qt_hash(left->m_name) < qt_hash(right->m_name);
1329 writeString(
"static const unsigned char qt_resource_struct[] = {\n");
1332 writeString(
"qt_resource_struct = b\"\\\n");
1335 m_treeOffset = m_out.size();
1341 QStack<RCCFileInfo*> pending;
1347 pending.push(m_root);
1349 while (!pending.isEmpty()) {
1351 file->m_childOffset = offset;
1354 QList<RCCFileInfo*> m_children = file->m_children.values();
1358 for (
int i = 0; i < m_children.size(); ++i) {
1362 pending.push(child);
1367 pending.push(m_root);
1369 while (!pending.isEmpty()) {
1373 QList<RCCFileInfo*> m_children = file->m_children.values();
1377 for (
int i = 0; i < m_children.size(); ++i) {
1381 pending.push(child);
1387 writeString(
"\n};\n\n");
1390 writeString(
"\"\n\n");
1401 if (m_useNameSpace) {
1402 writeString(
"QT_RCC_MANGLE_NAMESPACE(");
1403 writeByteArray(name);
1406 writeByteArray(name);
1412 if (m_useNameSpace) {
1413 writeString(
"QT_RCC_PREPEND_NAMESPACE(");
1414 writeByteArray(name);
1417 writeByteArray(name);
1425 QString initNameStr = m_initName;
1426 if (!initNameStr.isEmpty()) {
1427 initNameStr.prepend(u'_');
1428 auto isAsciiLetterOrNumber = [] (QChar c) ->
bool {
1429 ushort ch = c.unicode();
1430 return (ch >=
'0' && ch <=
'9') ||
1431 (ch >=
'A' && ch <=
'Z') ||
1432 (ch >=
'a' && ch <=
'z') ||
1435 for (QChar &c : initNameStr) {
1436 if (!isAsciiLetterOrNumber(c))
1440 QByteArray initName = initNameStr.toLatin1();
1443 if (m_useNameSpace) {
1444 writeString(
"#ifdef QT_NAMESPACE\n"
1445 "# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name\n"
1446 "# define QT_RCC_MANGLE_NAMESPACE0(x) x\n"
1447 "# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b\n"
1448 "# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)\n"
1449 "# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \\\n"
1450 " QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))\n"
1452 "# define QT_RCC_PREPEND_NAMESPACE(name) name\n"
1453 "# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
1456 writeString(
"#if defined(QT_INLINE_NAMESPACE)\n"
1457 "inline namespace QT_NAMESPACE {\n"
1458 "#elif defined(QT_NAMESPACE)\n"
1459 "namespace QT_NAMESPACE {\n"
1464 writeString(
"bool qRegisterResourceData"
1465 "(int, const unsigned char *, "
1466 "const unsigned char *, const unsigned char *);\n");
1467 writeString(
"bool qUnregisterResourceData"
1468 "(int, const unsigned char *, "
1469 "const unsigned char *, const unsigned char *);\n\n");
1471 if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) {
1473 writeString(
"#if defined(__ELF__) || defined(__APPLE__)\n");
1474 if (m_overallFlags & RCCFileInfo::Compressed) {
1475 writeString(
"static inline unsigned char qResourceFeatureZlib()\n"
1477 " extern const unsigned char qt_resourceFeatureZlib;\n"
1478 " return qt_resourceFeatureZlib;\n"
1481 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1482 writeString(
"static inline unsigned char qResourceFeatureZstd()\n"
1484 " extern const unsigned char qt_resourceFeatureZstd;\n"
1485 " return qt_resourceFeatureZstd;\n"
1488 writeString(
"#else\n");
1489 if (m_overallFlags & RCCFileInfo::Compressed)
1490 writeString(
"unsigned char qResourceFeatureZlib();\n");
1491 if (m_overallFlags & RCCFileInfo::CompressedZstd)
1492 writeString(
"unsigned char qResourceFeatureZstd();\n");
1493 writeString(
"#endif\n\n");
1498 writeString(
"#ifdef QT_NAMESPACE\n}\n#endif\n\n");
1501 initResources += initName;
1504 writeString(
"int ");
1505 writeMangleNamespaceFunction(initResources);
1506 writeString(
"();\n");
1508 writeString(
"int ");
1509 writeMangleNamespaceFunction(initResources);
1510 writeString(
"()\n{\n");
1513 writeString(
" int version = ");
1514 writeDecimal(m_formatVersion);
1515 writeString(
";\n ");
1516 writeAddNamespaceFunction(
"qRegisterResourceData");
1517 writeString(
"\n (version, qt_resource_struct, "
1518 "qt_resource_name, qt_resource_data);\n");
1520 writeString(
" return 1;\n");
1521 writeString(
"}\n\n");
1524 QByteArray cleanResources =
"qCleanupResources";
1525 cleanResources += initName;
1528 writeString(
"int ");
1529 writeMangleNamespaceFunction(cleanResources);
1530 writeString(
"();\n");
1532 writeString(
"int ");
1533 writeMangleNamespaceFunction(cleanResources);
1534 writeString(
"()\n{\n");
1536 writeString(
" int version = ");
1537 writeDecimal(m_formatVersion);
1538 writeString(
";\n ");
1541 if (m_overallFlags & RCCFileInfo::Compressed) {
1542 writeString(
"version += ");
1543 writeAddNamespaceFunction(
"qResourceFeatureZlib()");
1544 writeString(
";\n ");
1546 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1547 writeString(
"version += ");
1548 writeAddNamespaceFunction(
"qResourceFeatureZstd()");
1549 writeString(
";\n ");
1552 writeAddNamespaceFunction(
"qUnregisterResourceData");
1553 writeString(
"\n (version, qt_resource_struct, "
1554 "qt_resource_name, qt_resource_data);\n");
1556 writeString(
" return 1;\n");
1557 writeString(
"}\n\n");
1560 writeString(
"#ifdef __clang__\n"
1561 "# pragma clang diagnostic push\n"
1562 "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
1565 writeString(
"namespace {\n"
1566 " struct initializer {\n");
1568 if (m_useNameSpace) {
1569 writeByteArray(
" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources +
")(); }\n"
1570 " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources +
")(); }\n");
1572 writeByteArray(
" initializer() { " + initResources +
"(); }\n"
1573 " ~initializer() { " + cleanResources +
"(); }\n");
1575 writeString(
" } dummy;\n"
1578 writeString(
"#ifdef __clang__\n"
1579 "# pragma clang diagnostic pop\n"
1583 }
else if (m_format ==
Binary) {
1585 char *p = m_out.data();
1589 p[i++] = m_formatVersion;
1591 p[i++] = (m_treeOffset >> 24) & 0xff;
1592 p[i++] = (m_treeOffset >> 16) & 0xff;
1593 p[i++] = (m_treeOffset >> 8) & 0xff;
1594 p[i++] = (m_treeOffset >> 0) & 0xff;
1596 p[i++] = (m_dataOffset >> 24) & 0xff;
1597 p[i++] = (m_dataOffset >> 16) & 0xff;
1598 p[i++] = (m_dataOffset >> 8) & 0xff;
1599 p[i++] = (m_dataOffset >> 0) & 0xff;
1601 p[i++] = (m_namesOffset >> 24) & 0xff;
1602 p[i++] = (m_namesOffset >> 16) & 0xff;
1603 p[i++] = (m_namesOffset >> 8) & 0xff;
1604 p[i++] = (m_namesOffset >> 0) & 0xff;
1606 if (m_formatVersion >= 3) {
1607 p[i++] = (m_overallFlags >> 24) & 0xff;
1608 p[i++] = (m_overallFlags >> 16) & 0xff;
1609 p[i++] = (m_overallFlags >> 8) & 0xff;
1610 p[i++] = (m_overallFlags >> 0) & 0xff;
1613 writeString(
"def qInitResources():\n");
1614 writeString(
" QtCore.qRegisterResourceData(0x");
1615 write2HexDigits(m_formatVersion);
1616 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1617 writeString(
"def qCleanupResources():\n");
1618 writeString(
" QtCore.qUnregisterResourceData(0x");
1619 write2HexDigits(m_formatVersion);
1620 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1621 writeString(
"qInitResources()\n");
IteratorFlag
This enum class describes flags that can be used to configure the behavior of QDirListing.
QMultiHash< DeduplicationKey, RCCFileInfo * > DeduplicationMultiHash
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
QString resourceName() const
void writeDataInfo(RCCResourceLibrary &lib)
RCCFileInfo(const RCCFileInfo &)=delete
QLocale::Territory m_territory
QMultiHash< QString, RCCFileInfo * > m_children
qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, DeduplicationMultiHash &dedupByContent, QString *errorMessage)
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)
bool operator==(const DeduplicationKey &other) const
RCCResourceLibrary::CompressionAlgorithm compressAlgo
result_type operator()(const RCCFileInfo *left, const RCCFileInfo *right) const