10#include <qcryptographichash.h>
14#include <qdirlisting.h>
19#include <qxmlstream.h>
31using namespace Qt::StringLiterals;
44 m_out.resize(n + len);
45 memcpy(m_out.data() + n, str, len);
50 if (m_format ==
Pass2) {
51 m_outDevice->write(other);
59 return QString::fromLatin1(
"Unable to open %1 for reading: %2\n").arg(fname, why);
83 RCCFileInfo(
const QString &name,
const QFileInfo &fileInfo, QLocale::Language language,
84 QLocale::Territory territory, uint flags,
86 int compressThreshold,
bool noZstd,
bool isEmpty);
104 return compressAlgo == other.compressAlgo &&
105 compressLevel == other.compressLevel &&
106 compressThreshold == other.compressThreshold &&
117 QString *errorMessage);
146 QLocale::Territory territory, uint flags,
148 int compressThreshold,
bool noZstd,
bool isEmpty)
164 qDeleteAll(m_children);
169 QString resource = m_name;
171 resource = resource.prepend(p->m_name + u'/');
172 resource.prepend(u':');
183 if (m_language != QLocale::C) {
184 lib.writeString(
" // ");
185 lib.writeByteArray(resourceName().toLocal8Bit());
186 lib.writeString(
" [");
187 lib.writeByteArray(QByteArray::number(m_territory));
188 lib.writeString(
"::");
189 lib.writeByteArray(QByteArray::number(m_language));
190 lib.writeString(
"[\n ");
192 lib.writeString(
" // ");
193 lib.writeByteArray(resourceName().toLocal8Bit());
194 lib.writeString(
"\n ");
201 lib.writeNumber4(m_nameOffset);
207 lib.writeNumber4(m_children.size());
210 lib.writeNumber4(m_childOffset);
213 lib.writeNumber4(m_nameOffset);
219 lib.writeNumber2(m_territory);
220 lib.writeNumber2(m_language);
223 lib.writeNumber4(m_dataOffset);
228 lib.writeString(
"\\\n");
232 const QDateTime lastModified = m_fileInfo.lastModified(QTimeZone::UTC);
233 quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
234 static const quint64 sourceDate = 1000 * qgetenv(
"QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
236 lastmod = sourceDate;
237 static const quint64 sourceDate2 = 1000 * qgetenv(
"SOURCE_DATE_EPOCH").toULongLong();
238 if (sourceDate2 != 0)
239 lastmod = sourceDate2;
240 lib.writeNumber8(lastmod);
244 lib.writeString(
"\\\n");
251 QString *errorMessage)
260 m_dataOffset = offset;
266 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best && !m_noZstd) {
267 m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd;
268 m_compressLevel = 19;
271#ifndef QT_NO_COMPRESS
280 const QString absoluteFilePath = m_fileInfo.absoluteFilePath();
281 QFile file(absoluteFilePath);
282 if (!file.open(QFile::ReadOnly)) {
283 *errorMessage = msgOpenReadFailed(absoluteFilePath, file.errorString());
286 data = file.readAll();
290 const DeduplicationKey key{m_compressAlgo, m_compressLevel, m_compressThreshold,
291 QCryptographicHash::hash(data, QCryptographicHash::Sha256)};
292 const QList<RCCFileInfo *> potentialCandidates = dedupByContent.values(key);
293 for (
const RCCFileInfo *candidate : potentialCandidates) {
295 QFile candidateFile(candidate->m_fileInfo.absoluteFilePath());
296 if (!candidateFile.open(QFile::ReadOnly)) {
297 *errorMessage = msgOpenReadFailed(candidate->m_fileInfo.absoluteFilePath(),
298 candidateFile.errorString());
301 if (data != candidateFile.readAll())
305 m_dataOffset = candidate->m_dataOffset;
306 m_flags = candidate->m_flags;
309 dedupByContent.insert(key,
this);
313 if (data.size() != 0) {
315 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd && !m_noZstd) {
316 qsizetype size = data.size();
317 size = ZSTD_COMPRESSBOUND(size);
319 int compressLevel = m_compressLevel;
320 if (compressLevel < 0)
321 compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK;
323 QByteArray compressed(size, Qt::Uninitialized);
324 char *dst =
const_cast<
char *>(compressed.constData());
325 size_t n = ZSTD_compress(dst, size,
326 data.constData(), data.size(),
328 if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) {
330 if (m_compressLevel < 0) {
332 n = ZSTD_compress(dst, size,
333 data.constData(), data.size(),
334 CONSTANT_ZSTDCOMPRESSLEVEL_STORE);
336 if (ZSTD_isError(n)) {
337 QString msg =
"%1: error: compression with zstd failed: %2\n"_L1
338 .arg(m_name, ZSTD_getErrorName(n));
339 lib.m_errorDevice->write(msg.toUtf8());
340 }
else if (lib.verbose()) {
341 QString msg = QString::fromLatin1(
"%1: note: compressed using zstd (%2 -> %3)\n")
342 .arg(m_name).arg(data.size()).arg(n);
343 lib.m_errorDevice->write(msg.toUtf8());
346 lib.m_overallFlags |= CompressedZstd;
347 m_flags |= CompressedZstd;
348 data = std::move(compressed);
350 }
else if (lib.verbose()) {
351 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
352 lib.m_errorDevice->write(msg.toUtf8());
356#ifndef QT_NO_COMPRESS
359 qCompress(
reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
361 int compressRatio =
int(100.0 * (data.size() - compressed.size()) / data.size());
364 QString msg = QString::fromLatin1(
"%1: note: compressed using zlib (%2 -> %3)\n")
365 .arg(m_name).arg(data.size()).arg(compressed.size());
366 lib.m_errorDevice->write(msg.toUtf8());
368 data =
std::move(compressed);
372 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
373 lib.m_errorDevice->write(msg.toUtf8());
381 lib.writeString(
" // ");
382 lib.writeByteArray(m_fileInfo.fileName().toLocal8Bit());
383 lib.writeString(
"\n ");
387 if (text || binary || pass2 || python)
388 lib.writeNumber4(data.size());
390 lib.writeString(
"\n ");
392 lib.writeString(
"\\\n");
396 const char *p = data.constData();
397 if (text || python) {
398 for (
int i = data.size(), j = 0; --i >= 0; --j) {
402 lib.writeString(
"\n ");
404 lib.writeString(
"\\\n");
408 }
else if (binary || pass2) {
409 lib.writeByteArray(data);
411 offset += data.size();
415 lib.writeString(
"\n ");
417 lib.writeString(
"\\\n");
429 m_nameOffset = offset;
433 lib.writeString(
" // ");
434 lib.writeByteArray(m_name.toLocal8Bit());
435 lib.writeString(
"\n ");
439 lib.writeNumber2(m_name.size());
441 lib.writeString(
"\n ");
443 lib.writeString(
"\\\n");
447 lib.writeNumber4(qt_hash(m_name));
449 lib.writeString(
"\n ");
451 lib.writeString(
"\\\n");
455 const QChar *unicode = m_name.unicode();
456 for (
int i = 0; i < m_name.size(); ++i) {
457 lib.writeNumber2(unicode[i].unicode());
458 if ((text || pass1) && i % 16 == 0)
459 lib.writeString(
"\n ");
460 else if (python && i % 16 == 0)
461 lib.writeString(
"\\\n");
463 offset += m_name.size()*2;
467 lib.writeString(
"\n ");
469 lib.writeString(
"\\\n");
483 TAG_RESOURCE(
"qresource"_L1),
485 ATTRIBUTE_LANG(
"lang"_L1),
486 ATTRIBUTE_PREFIX(
"prefix"_L1),
487 ATTRIBUTE_ALIAS(
"alias"_L1),
488 ATTRIBUTE_EMPTY(
"empty"_L1),
489 ATTRIBUTE_THRESHOLD(
"threshold"_L1),
490 ATTRIBUTE_COMPRESS(
"compress"_L1),
507 m_errorDevice(
nullptr),
508 m_outDevice(
nullptr),
512 m_out.reserve(30 * 1000 * 1000);
529 if (value.compare(
"true"_L1, Qt::CaseInsensitive) == 0)
531 if (value.compare(
"false"_L1, Qt::CaseInsensitive) == 0)
534 *errorMsg = QString::fromLatin1(
"Invalid value for boolean attribute: '%1'").arg(value);
539 const QString &fname, QString currentPath,
bool listMode)
541 Q_ASSERT(m_errorDevice);
542 const QChar slash = u'/';
543 if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
544 currentPath += slash;
546 QXmlStreamReader reader(inputDevice);
547 QStack<RCCXmlTag> tokens;
550 QLocale::Language language = QLocale::c().language();
551 QLocale::Territory territory = QLocale::c().territory();
554 auto compressAlgo = m_compressionAlgo;
555 int compressLevel = m_compressLevel;
556 int compressThreshold = m_compressThreshold;
558 while (!reader.atEnd()) {
559 QXmlStreamReader::TokenType t = reader.readNext();
561 case QXmlStreamReader::StartElement:
562 if (reader.name() == m_strings.TAG_RCC) {
563 if (!tokens.isEmpty())
564 reader.raiseError(
"expected <RCC> tag"_L1);
567 }
else if (reader.name() == m_strings.TAG_LEGAL) {
568 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
569 reader.raiseError(
"unexpected <legal> tag"_L1);
571 m_legal = reader.readElementText().trimmed();
573 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
574 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
575 reader.raiseError(
"unexpected <RESOURCE> tag"_L1);
579 QXmlStreamAttributes attributes = reader.attributes();
580 language = QLocale::c().language();
581 territory = QLocale::c().territory();
583 if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
584 QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
585 QLocale lang = QLocale(attribute);
586 language = lang.language();
587 if (2 == attribute.size()) {
589 territory = QLocale::AnyTerritory;
591 territory = lang.territory();
596 if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
597 prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
598 if (!prefix.startsWith(slash))
599 prefix.prepend(slash);
600 if (!prefix.endsWith(slash))
603 }
else if (reader.name() == m_strings.TAG_FILE) {
604 if (tokens.isEmpty() || tokens.top() !=
ResourceTag) {
605 reader.raiseError(
"unexpected <FILE> tag"_L1);
609 QXmlStreamAttributes attributes = reader.attributes();
611 if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
612 alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
614 compressAlgo = m_compressionAlgo;
615 compressLevel = m_compressLevel;
616 compressThreshold = m_compressThreshold;
619 if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
620 empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
624 if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
625 compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
626 if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
627 QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
632 if (m_compressLevel == -2)
635 if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
636 compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
638 if (!errorString.isEmpty())
639 reader.raiseError(errorString);
642 reader.raiseError(
"unexpected tag: %1"_L1.arg(reader.name().toString()));
646 case QXmlStreamReader::EndElement:
647 if (reader.name() == m_strings.TAG_RCC) {
648 if (!tokens.isEmpty() && tokens.top() ==
RccTag)
651 reader.raiseError(
"unexpected closing tag"_L1);
652 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
653 if (!tokens.isEmpty() && tokens.top() ==
ResourceTag)
656 reader.raiseError(
"unexpected closing tag"_L1);
657 }
else if (reader.name() == m_strings.TAG_FILE) {
658 if (!tokens.isEmpty() && tokens.top() ==
FileTag)
661 reader.raiseError(
"unexpected closing tag"_L1);
665 case QXmlStreamReader::Characters:
666 if (reader.isWhitespace())
668 if (tokens.isEmpty() || tokens.top() !=
FileTag) {
669 reader.raiseError(
"unexpected text"_L1);
671 QString fileName = reader.text().toString();
672 if (fileName.isEmpty()) {
673 const QString msg = QString::fromLatin1(
"RCC: Warning: Null node in XML of '%1'\n").arg(fname);
674 m_errorDevice->write(msg.toUtf8());
680 alias = QDir::cleanPath(alias);
681 while (alias.startsWith(
"../"_L1))
683 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
685 QString absFileName = fileName;
686 if (QDir::isRelativePath(absFileName))
687 absFileName.prepend(currentPath);
688 QFileInfo file(absFileName);
690 if (!alias.endsWith(slash))
693 QStringList filePaths;
694 using F = QDirListing::IteratorFlag;
695 constexpr auto flags = F::FollowDirSymlinks | F::Recursive;
696 for (
const auto &entry : QDirListing(file.filePath(), flags)) {
697 const QString &fileName = entry.fileName();
698 if (fileName ==
"."_L1 || fileName ==
".."_L1)
700 filePaths.emplace_back(entry.filePath());
704 std::sort(filePaths.begin(), filePaths.end());
706 for (
const QString &filePath : filePaths) {
707 QFileInfo child(filePath);
709 addFile(alias + child.fileName(),
710 RCCFileInfo(child.fileName(), child, language, territory,
711 child.isDir() ? RCCFileInfo::Directory
712 : RCCFileInfo::NoFlags,
713 compressAlgo, compressLevel, compressThreshold,
716 m_failedResources.push_back(child.fileName());
718 }
else if (listMode || file.isFile()) {
732 m_failedResources.push_back(absFileName);
733 }
else if (file.exists()) {
734 m_failedResources.push_back(absFileName);
735 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Entry '%2' is neither a file nor a directory\n")
736 .arg(fname, fileName);
737 m_errorDevice->write(msg.toUtf8());
740 m_failedResources.push_back(absFileName);
741 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Cannot find file '%2'\n")
742 .arg(fname, fileName);
743 m_errorDevice->write(msg.toUtf8());
754 if (reader.hasError()) {
755 int errorLine = reader.lineNumber();
756 int errorColumn = reader.columnNumber();
757 QString errorMessage = reader.errorString();
758 QString msg = QString::fromLatin1(
"RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
759 m_errorDevice->write(msg.toUtf8());
763 if (m_root ==
nullptr) {
764 const QString msg = QString::fromLatin1(
"RCC: Warning: No resources in '%1'.\n").arg(fname);
765 m_errorDevice->write(msg.toUtf8());
766 if (!listMode && m_format ==
Binary) {
768 m_root =
new RCCFileInfo{};
778 Q_ASSERT(m_errorDevice);
779 if (file.m_fileInfo.size() > 0xffffffff) {
780 const QString msg = QString::fromLatin1(
"File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
781 m_errorDevice->write(msg.toUtf8());
785 m_root =
new RCCFileInfo{};
790 const QStringList nodes = alias.split(u'/');
791 for (
int i = 1; i < nodes.size()-1; ++i) {
792 const QString node = nodes.at(i);
795 if (!parent->m_children.contains(node)) {
800 parent->m_children.insert(node, s);
803 parent = *parent->m_children.constFind(node);
807 const QString filename = nodes.at(nodes.size()-1);
810 auto cbegin = parent->m_children.constFind(filename);
811 auto cend = parent->m_children.constEnd();
812 for (
auto it = cbegin; it != cend; ++it) {
813 if (it.key() == filename && it.value()->m_language == s->m_language &&
814 it.value()->m_territory == s->m_territory) {
815 for (
const QString &name : std::as_const(m_fileNames)) {
816 qWarning(
"%s: Warning: potential duplicate alias detected: '%s'",
822 parent->m_children.insert(filename, s);
832 m_errorDevice =
nullptr;
833 m_failedResources.clear();
840 m_errorDevice = &errorDevice;
843 const QString msg = QString::fromLatin1(
"Processing %1 files [listMode=%2]\n")
844 .arg(m_fileNames.size()).arg(
static_cast<
int>(listMode));
845 m_errorDevice->write(msg.toUtf8());
847 for (
int i = 0; i < m_fileNames.size(); ++i) {
849 QString fname = m_fileNames.at(i);
851 if (fname ==
"-"_L1) {
852 fname =
"(stdin)"_L1;
853 pwd = QDir::currentPath();
854 fileIn.setFileName(fname);
855 if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
856 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
860 pwd = QFileInfo(fname).path();
861 fileIn.setFileName(fname);
862 if (!fileIn.open(QIODevice::ReadOnly)) {
863 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
868 const QString msg = QString::fromLatin1(
"Interpreting %1\n").arg(fname);
869 m_errorDevice->write(msg.toUtf8());
872 if (!interpretResourceFile(&fileIn, fname,
std::move(pwd), listMode))
881 QStack<RCCFileInfo*> pending;
885 pending.push(m_root);
886 while (!pending.isEmpty()) {
888 for (
auto it = file->m_children.begin();
889 it != file->m_children.end(); ++it) {
894 ret.append(child->m_fileInfo.filePath());
903 const QChar slash = u'/';
904 const auto cend = m_root->m_children.constEnd();
905 for (
auto it = m_root->m_children.constBegin(); it != cend; ++it) {
907 const QString childName = path + slash + child->m_name;
909 resourceDataFileMapRecursion(child, childName, m);
911 m.insert(childName, child->m_fileInfo.filePath());
920 resourceDataFileMapRecursion(m_root, QString(u':'), rc);
926 if (value ==
"best"_L1)
928 if (value ==
"zlib"_L1) {
930 *errorMsg =
"zlib support not compiled in"_L1;
934 }
else if (value ==
"zstd"_L1) {
936 return CompressionAlgorithm::Zstd;
938 *errorMsg =
"Zstandard support not compiled in"_L1;
940 }
else if (value !=
"none"_L1) {
941 *errorMsg = QString::fromLatin1(
"Unknown compression algorithm '%1'").arg(value);
950 int c = level.toInt(&ok);
957 if (c >= 1 && c <= 9)
962 if (c >= 0 && c <= ZSTD_maxCLevel())
969 *errorMsg = QString::fromLatin1(
"invalid compression level '%1'").arg(level);
975 m_errorDevice = &errorDevice;
977 if (m_format ==
Pass2) {
978 const char pattern[] = {
'Q',
'R',
'C',
'_',
'D',
'A',
'T',
'A' };
979 bool foundSignature =
false;
983 for (
int i = 0; i < 8; ) {
984 if (!tempDevice.getChar(&c)) {
987 m_errorDevice->write(
"No data signature found\n");
991 if (c != pattern[i]) {
992 for (
int k = 0; k < i; ++k)
993 outDevice.putChar(pattern[k]);
997 if (c == pattern[i]) {
1000 outDevice.putChar(c);
1004 m_outDevice = &outDevice;
1005 quint64 start = outDevice.pos();
1007 quint64 len = outDevice.pos() - start;
1009 tempDevice.seek(tempDevice.pos() + len - 8);
1010 foundSignature =
true;
1016 m_errorDevice->write(
"Outputting code\n");
1017 if (!writeHeader()) {
1018 m_errorDevice->write(
"Could not write header\n");
1022 if (!writeDataBlobs()) {
1023 m_errorDevice->write(
"Could not write data blobs.\n");
1026 if (!writeDataNames()) {
1027 m_errorDevice->write(
"Could not write file names\n");
1030 if (!writeDataStructure()) {
1031 m_errorDevice->write(
"Could not write data tree\n");
1035 if (!writeInitializer()) {
1036 m_errorDevice->write(
"Could not write footer\n");
1039 outDevice.write(m_out.constData(), m_out.size());
1046 char buf[
std::numeric_limits<
int>::digits10 + 2];
1047 int n = snprintf(buf,
sizeof(buf),
"%d", value);
1063 if (tmp >= 32 && tmp < 127 && tmp !=
'"' && tmp !=
'\\') {
1064 writeChar(
char(tmp));
1068 write2HexDigits(tmp);
1077 write2HexDigits(tmp);
1086 writeChar(number >> 8);
1089 writeHex(number >> 8);
1097 m_outDevice->putChar(
char(number >> 24));
1098 m_outDevice->putChar(
char(number >> 16));
1099 m_outDevice->putChar(
char(number >> 8));
1100 m_outDevice->putChar(
char(number));
1102 writeChar(number >> 24);
1103 writeChar(number >> 16);
1104 writeChar(number >> 8);
1107 writeHex(number >> 24);
1108 writeHex(number >> 16);
1109 writeHex(number >> 8);
1117 m_outDevice->putChar(
char(number >> 56));
1118 m_outDevice->putChar(
char(number >> 48));
1119 m_outDevice->putChar(
char(number >> 40));
1120 m_outDevice->putChar(
char(number >> 32));
1121 m_outDevice->putChar(
char(number >> 24));
1122 m_outDevice->putChar(
char(number >> 16));
1123 m_outDevice->putChar(
char(number >> 8));
1124 m_outDevice->putChar(
char(number));
1126 writeChar(number >> 56);
1127 writeChar(number >> 48);
1128 writeChar(number >> 40);
1129 writeChar(number >> 32);
1130 writeChar(number >> 24);
1131 writeChar(number >> 16);
1132 writeChar(number >> 8);
1135 writeHex(number >> 56);
1136 writeHex(number >> 48);
1137 writeHex(number >> 40);
1138 writeHex(number >> 32);
1139 writeHex(number >> 24);
1140 writeHex(number >> 16);
1141 writeHex(number >> 8);
1148 auto writeCopyright = [
this](QByteArrayView prefix) {
1149 const QStringList lines = m_legal.split(u'\n', Qt::SkipEmptyParts);
1150 for (
const QString &line : lines) {
1151 write(prefix.data(), prefix.size());
1152 writeString(line.toUtf8().trimmed());
1159 writeString(
"/****************************************************************************\n");
1160 writeString(
"** Resource object code\n");
1161 writeCopyright(
"** ");
1162 writeString(
"**\n");
1163 writeString(
"** Created by: The Resource Compiler for Qt version ");
1164 writeByteArray(QT_VERSION_STR);
1165 writeString(
"\n**\n");
1166 writeString(
"** WARNING! All changes made in this file will be lost!\n");
1167 writeString(
"*****************************************************************************/\n\n");
1168 writeString(
"#ifdef _MSC_VER\n"
1169 "// disable informational message \"function ... selected for automatic inline expansion\"\n"
1170 "#pragma warning (disable: 4711)\n"
1174 writeString(
"# Resource object code (Python 3)\n");
1175 writeCopyright(
"# ");
1176 writeString(
"# Created by: object code\n");
1177 writeString(
"# Created by: The Resource Compiler for Qt version ");
1178 writeByteArray(QT_VERSION_STR);
1180 writeString(
"# WARNING! All changes made in this file will be lost!\n\n");
1181 writeString(
"from PySide");
1182 writeByteArray(QByteArray::number(QT_VERSION_MAJOR));
1183 writeString(
" import QtCore\n\n");
1186 writeString(
"qres");
1191 if (m_formatVersion >= 3)
1192 writeNumber4(m_overallFlags);
1202 Q_ASSERT(m_errorDevice);
1205 writeString(
"static const unsigned char qt_resource_data[] = {\n");
1208 writeString(
"qt_resource_data = b\"\\\n");
1211 m_dataOffset = m_out.size();
1220 QStack<RCCFileInfo*> pending;
1221 pending.push(m_root);
1224 QString errorMessage;
1225 while (!pending.isEmpty()) {
1227 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1230 pending.push(child);
1232 offset = child->writeDataBlob(*
this, offset,
1233 dedupByContent, &errorMessage);
1235 m_errorDevice->write(errorMessage.toUtf8());
1243 writeString(
"\n};\n\n");
1246 writeString(
"\"\n\n");
1251 writeString(
"\nstatic const unsigned char qt_resource_data[");
1252 writeByteArray(QByteArray::number(offset));
1253 writeString(
"] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n");
1266 writeString(
"static const unsigned char qt_resource_name[] = {\n");
1269 writeString(
"qt_resource_name = b\"\\\n");
1272 m_namesOffset = m_out.size();
1278 QHash<QString,
int> names;
1279 QStack<RCCFileInfo*> pending;
1284 pending.push(m_root);
1286 while (!pending.isEmpty()) {
1288 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1291 pending.push(child);
1292 if (names.contains(child->m_name)) {
1293 child->m_nameOffset = names.value(child->m_name);
1295 names.insert(child->m_name, offset);
1296 offset = child->writeDataName(*
this, offset);
1303 writeString(
"\n};\n\n");
1306 writeString(
"\"\n\n");
1319 return qt_hash(left->m_name) < qt_hash(right->m_name);
1328 writeString(
"static const unsigned char qt_resource_struct[] = {\n");
1331 writeString(
"qt_resource_struct = b\"\\\n");
1334 m_treeOffset = m_out.size();
1340 QStack<RCCFileInfo*> pending;
1346 pending.push(m_root);
1348 while (!pending.isEmpty()) {
1350 file->m_childOffset = offset;
1353 QList<RCCFileInfo*> m_children = file->m_children.values();
1357 for (
int i = 0; i < m_children.size(); ++i) {
1361 pending.push(child);
1366 pending.push(m_root);
1368 while (!pending.isEmpty()) {
1372 QList<RCCFileInfo*> m_children = file->m_children.values();
1376 for (
int i = 0; i < m_children.size(); ++i) {
1380 pending.push(child);
1386 writeString(
"\n};\n\n");
1389 writeString(
"\"\n\n");
1400 if (m_useNameSpace) {
1401 writeString(
"QT_RCC_MANGLE_NAMESPACE(");
1402 writeByteArray(name);
1405 writeByteArray(name);
1411 if (m_useNameSpace) {
1412 writeString(
"QT_RCC_PREPEND_NAMESPACE(");
1413 writeByteArray(name);
1416 writeByteArray(name);
1424 QString initNameStr = m_initName;
1425 if (!initNameStr.isEmpty()) {
1426 initNameStr.prepend(u'_');
1427 auto isAsciiLetterOrNumber = [] (QChar c) ->
bool {
1428 ushort ch = c.unicode();
1429 return (ch >=
'0' && ch <=
'9') ||
1430 (ch >=
'A' && ch <=
'Z') ||
1431 (ch >=
'a' && ch <=
'z') ||
1434 for (QChar &c : initNameStr) {
1435 if (!isAsciiLetterOrNumber(c))
1439 QByteArray initName = initNameStr.toLatin1();
1442 if (m_useNameSpace) {
1443 writeString(
"#ifdef QT_NAMESPACE\n"
1444 "# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name\n"
1445 "# define QT_RCC_MANGLE_NAMESPACE0(x) x\n"
1446 "# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b\n"
1447 "# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)\n"
1448 "# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \\\n"
1449 " QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))\n"
1451 "# define QT_RCC_PREPEND_NAMESPACE(name) name\n"
1452 "# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
1455 writeString(
"#if defined(QT_INLINE_NAMESPACE)\n"
1456 "inline namespace QT_NAMESPACE {\n"
1457 "#elif defined(QT_NAMESPACE)\n"
1458 "namespace QT_NAMESPACE {\n"
1463 writeString(
"bool qRegisterResourceData"
1464 "(int, const unsigned char *, "
1465 "const unsigned char *, const unsigned char *);\n");
1466 writeString(
"bool qUnregisterResourceData"
1467 "(int, const unsigned char *, "
1468 "const unsigned char *, const unsigned char *);\n\n");
1470 if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) {
1472 writeString(
"#if defined(__ELF__) || defined(__APPLE__)\n");
1473 if (m_overallFlags & RCCFileInfo::Compressed) {
1474 writeString(
"static inline unsigned char qResourceFeatureZlib()\n"
1476 " extern const unsigned char qt_resourceFeatureZlib;\n"
1477 " return qt_resourceFeatureZlib;\n"
1480 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1481 writeString(
"static inline unsigned char qResourceFeatureZstd()\n"
1483 " extern const unsigned char qt_resourceFeatureZstd;\n"
1484 " return qt_resourceFeatureZstd;\n"
1487 writeString(
"#else\n");
1488 if (m_overallFlags & RCCFileInfo::Compressed)
1489 writeString(
"unsigned char qResourceFeatureZlib();\n");
1490 if (m_overallFlags & RCCFileInfo::CompressedZstd)
1491 writeString(
"unsigned char qResourceFeatureZstd();\n");
1492 writeString(
"#endif\n\n");
1497 writeString(
"#ifdef QT_NAMESPACE\n}\n#endif\n\n");
1500 initResources += initName;
1503 writeString(
"int ");
1504 writeMangleNamespaceFunction(initResources);
1505 writeString(
"();\n");
1507 writeString(
"int ");
1508 writeMangleNamespaceFunction(initResources);
1509 writeString(
"()\n{\n");
1512 writeString(
" int version = ");
1513 writeDecimal(m_formatVersion);
1514 writeString(
";\n ");
1515 writeAddNamespaceFunction(
"qRegisterResourceData");
1516 writeString(
"\n (version, qt_resource_struct, "
1517 "qt_resource_name, qt_resource_data);\n");
1519 writeString(
" return 1;\n");
1520 writeString(
"}\n\n");
1523 QByteArray cleanResources =
"qCleanupResources";
1524 cleanResources += initName;
1527 writeString(
"int ");
1528 writeMangleNamespaceFunction(cleanResources);
1529 writeString(
"();\n");
1531 writeString(
"int ");
1532 writeMangleNamespaceFunction(cleanResources);
1533 writeString(
"()\n{\n");
1535 writeString(
" int version = ");
1536 writeDecimal(m_formatVersion);
1537 writeString(
";\n ");
1540 if (m_overallFlags & RCCFileInfo::Compressed) {
1541 writeString(
"version += ");
1542 writeAddNamespaceFunction(
"qResourceFeatureZlib()");
1543 writeString(
";\n ");
1545 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1546 writeString(
"version += ");
1547 writeAddNamespaceFunction(
"qResourceFeatureZstd()");
1548 writeString(
";\n ");
1551 writeAddNamespaceFunction(
"qUnregisterResourceData");
1552 writeString(
"\n (version, qt_resource_struct, "
1553 "qt_resource_name, qt_resource_data);\n");
1555 writeString(
" return 1;\n");
1556 writeString(
"}\n\n");
1559 writeString(
"#ifdef __clang__\n"
1560 "# pragma clang diagnostic push\n"
1561 "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
1564 writeString(
"namespace {\n"
1565 " struct initializer {\n");
1567 if (m_useNameSpace) {
1568 writeByteArray(
" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources +
")(); }\n"
1569 " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources +
")(); }\n");
1571 writeByteArray(
" initializer() { " + initResources +
"(); }\n"
1572 " ~initializer() { " + cleanResources +
"(); }\n");
1574 writeString(
" } dummy;\n"
1577 writeString(
"#ifdef __clang__\n"
1578 "# pragma clang diagnostic pop\n"
1582 }
else if (m_format ==
Binary) {
1584 char *p = m_out.data();
1588 p[i++] = m_formatVersion;
1590 p[i++] = (m_treeOffset >> 24) & 0xff;
1591 p[i++] = (m_treeOffset >> 16) & 0xff;
1592 p[i++] = (m_treeOffset >> 8) & 0xff;
1593 p[i++] = (m_treeOffset >> 0) & 0xff;
1595 p[i++] = (m_dataOffset >> 24) & 0xff;
1596 p[i++] = (m_dataOffset >> 16) & 0xff;
1597 p[i++] = (m_dataOffset >> 8) & 0xff;
1598 p[i++] = (m_dataOffset >> 0) & 0xff;
1600 p[i++] = (m_namesOffset >> 24) & 0xff;
1601 p[i++] = (m_namesOffset >> 16) & 0xff;
1602 p[i++] = (m_namesOffset >> 8) & 0xff;
1603 p[i++] = (m_namesOffset >> 0) & 0xff;
1605 if (m_formatVersion >= 3) {
1606 p[i++] = (m_overallFlags >> 24) & 0xff;
1607 p[i++] = (m_overallFlags >> 16) & 0xff;
1608 p[i++] = (m_overallFlags >> 8) & 0xff;
1609 p[i++] = (m_overallFlags >> 0) & 0xff;
1612 writeString(
"def qInitResources():\n");
1613 writeString(
" QtCore.qRegisterResourceData(0x");
1614 write2HexDigits(m_formatVersion);
1615 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1616 writeString(
"def qCleanupResources():\n");
1617 writeString(
" QtCore.qUnregisterResourceData(0x");
1618 write2HexDigits(m_formatVersion);
1619 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1620 writeString(
"qInitResources()\n");
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)
#define QStringLiteral(str)
bool operator==(const DeduplicationKey &other) const
RCCResourceLibrary::CompressionAlgorithm compressAlgo
result_type operator()(const RCCFileInfo *left, const RCCFileInfo *right) const