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;
265 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best && !m_noZstd) {
266 m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd;
267 m_compressLevel = 19;
270#ifndef QT_NO_COMPRESS
279 const QString absoluteFilePath = m_fileInfo.absoluteFilePath();
280 QFile file(absoluteFilePath);
281 if (!file.open(QFile::ReadOnly)) {
282 *errorMessage = msgOpenReadFailed(absoluteFilePath, file.errorString());
285 data = file.readAll();
289 const DeduplicationKey key{m_compressAlgo, m_compressLevel, m_compressThreshold,
290 QCryptographicHash::hash(data, QCryptographicHash::Sha256)};
291 const QList<RCCFileInfo *> potentialCandidates = dedupByContent.values(key);
292 for (
const RCCFileInfo *candidate : potentialCandidates) {
294 QFile candidateFile(candidate->m_fileInfo.absoluteFilePath());
295 if (!candidateFile.open(QFile::ReadOnly)) {
296 *errorMessage = msgOpenReadFailed(candidate->m_fileInfo.absoluteFilePath(),
297 candidateFile.errorString());
300 if (data != candidateFile.readAll())
304 m_dataOffset = candidate->m_dataOffset;
305 m_flags = candidate->m_flags;
308 dedupByContent.insert(key,
this);
312 if (data.size() != 0) {
314 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd && !m_noZstd) {
315 qsizetype size = data.size();
316 size = ZSTD_COMPRESSBOUND(size);
318 int compressLevel = m_compressLevel;
319 if (compressLevel < 0)
320 compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK;
322 QByteArray compressed(size, Qt::Uninitialized);
323 char *dst =
const_cast<
char *>(compressed.constData());
324 size_t n = ZSTD_compress(dst, size,
325 data.constData(), data.size(),
327 if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) {
329 if (m_compressLevel < 0) {
331 n = ZSTD_compress(dst, size,
332 data.constData(), data.size(),
333 CONSTANT_ZSTDCOMPRESSLEVEL_STORE);
335 if (ZSTD_isError(n)) {
336 QString msg =
"%1: error: compression with zstd failed: %2\n"_L1
337 .arg(m_name, ZSTD_getErrorName(n));
338 lib.m_errorDevice->write(msg.toUtf8());
339 }
else if (lib.verbose()) {
340 QString msg = QString::fromLatin1(
"%1: note: compressed using zstd (%2 -> %3)\n")
341 .arg(m_name).arg(data.size()).arg(n);
342 lib.m_errorDevice->write(msg.toUtf8());
345 lib.m_overallFlags |= CompressedZstd;
346 m_flags |= CompressedZstd;
347 data = std::move(compressed);
349 }
else if (lib.verbose()) {
350 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
351 lib.m_errorDevice->write(msg.toUtf8());
355#ifndef QT_NO_COMPRESS
358 qCompress(
reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
360 int compressRatio =
int(100.0 * (data.size() - compressed.size()) / data.size());
363 QString msg = QString::fromLatin1(
"%1: note: compressed using zlib (%2 -> %3)\n")
364 .arg(m_name).arg(data.size()).arg(compressed.size());
365 lib.m_errorDevice->write(msg.toUtf8());
367 data =
std::move(compressed);
371 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
372 lib.m_errorDevice->write(msg.toUtf8());
380 lib.writeString(
" // ");
381 lib.writeByteArray(m_fileInfo.fileName().toLocal8Bit());
382 lib.writeString(
"\n ");
386 if (text || binary || pass2 || python)
387 lib.writeNumber4(data.size());
389 lib.writeString(
"\n ");
391 lib.writeString(
"\\\n");
395 const char *p = data.constData();
396 if (text || python) {
397 for (
int i = data.size(), j = 0; --i >= 0; --j) {
401 lib.writeString(
"\n ");
403 lib.writeString(
"\\\n");
407 }
else if (binary || pass2) {
408 lib.writeByteArray(data);
410 offset += data.size();
414 lib.writeString(
"\n ");
416 lib.writeString(
"\\\n");
428 m_nameOffset = offset;
432 lib.writeString(
" // ");
433 lib.writeByteArray(m_name.toLocal8Bit());
434 lib.writeString(
"\n ");
438 lib.writeNumber2(m_name.size());
440 lib.writeString(
"\n ");
442 lib.writeString(
"\\\n");
446 lib.writeNumber4(qt_hash(m_name));
448 lib.writeString(
"\n ");
450 lib.writeString(
"\\\n");
454 const QChar *unicode = m_name.unicode();
455 for (
int i = 0; i < m_name.size(); ++i) {
456 lib.writeNumber2(unicode[i].unicode());
457 if ((text || pass1) && i % 16 == 0)
458 lib.writeString(
"\n ");
459 else if (python && i % 16 == 0)
460 lib.writeString(
"\\\n");
462 offset += m_name.size()*2;
466 lib.writeString(
"\n ");
468 lib.writeString(
"\\\n");
482 TAG_RESOURCE(
"qresource"_L1),
484 ATTRIBUTE_LANG(
"lang"_L1),
485 ATTRIBUTE_PREFIX(
"prefix"_L1),
486 ATTRIBUTE_ALIAS(
"alias"_L1),
487 ATTRIBUTE_EMPTY(
"empty"_L1),
488 ATTRIBUTE_THRESHOLD(
"threshold"_L1),
489 ATTRIBUTE_COMPRESS(
"compress"_L1),
506 m_errorDevice(
nullptr),
507 m_outDevice(
nullptr),
511 m_out.reserve(30 * 1000 * 1000);
528 if (value.compare(
"true"_L1, Qt::CaseInsensitive) == 0)
530 if (value.compare(
"false"_L1, Qt::CaseInsensitive) == 0)
533 *errorMsg = QString::fromLatin1(
"Invalid value for boolean attribute: '%1'").arg(value);
538 const QString &fname, QString currentPath,
bool listMode)
540 Q_ASSERT(m_errorDevice);
541 const QChar slash = u'/';
542 if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
543 currentPath += slash;
545 QXmlStreamReader reader(inputDevice);
546 QStack<RCCXmlTag> tokens;
549 QLocale::Language language = QLocale::c().language();
550 QLocale::Territory territory = QLocale::c().territory();
553 auto compressAlgo = m_compressionAlgo;
554 int compressLevel = m_compressLevel;
555 int compressThreshold = m_compressThreshold;
557 while (!reader.atEnd()) {
558 QXmlStreamReader::TokenType t = reader.readNext();
560 case QXmlStreamReader::StartElement:
561 if (reader.name() == m_strings.TAG_RCC) {
562 if (!tokens.isEmpty())
563 reader.raiseError(
"expected <RCC> tag"_L1);
566 }
else if (reader.name() == m_strings.TAG_LEGAL) {
567 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
568 reader.raiseError(
"unexpected <legal> tag"_L1);
570 m_legal = reader.readElementText().trimmed();
572 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
573 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
574 reader.raiseError(
"unexpected <RESOURCE> tag"_L1);
578 QXmlStreamAttributes attributes = reader.attributes();
579 language = QLocale::c().language();
580 territory = QLocale::c().territory();
582 if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
583 QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
584 QLocale lang = QLocale(attribute);
585 language = lang.language();
586 if (2 == attribute.size()) {
588 territory = QLocale::AnyTerritory;
590 territory = lang.territory();
595 if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
596 prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
597 if (!prefix.startsWith(slash))
598 prefix.prepend(slash);
599 if (!prefix.endsWith(slash))
602 }
else if (reader.name() == m_strings.TAG_FILE) {
603 if (tokens.isEmpty() || tokens.top() !=
ResourceTag) {
604 reader.raiseError(
"unexpected <FILE> tag"_L1);
608 QXmlStreamAttributes attributes = reader.attributes();
610 if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
611 alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
613 compressAlgo = m_compressionAlgo;
614 compressLevel = m_compressLevel;
615 compressThreshold = m_compressThreshold;
618 if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
619 empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
623 if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
624 compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
625 if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
626 QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
631 if (m_compressLevel == -2)
634 if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
635 compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
637 if (!errorString.isEmpty())
638 reader.raiseError(errorString);
641 reader.raiseError(
"unexpected tag: %1"_L1.arg(reader.name().toString()));
645 case QXmlStreamReader::EndElement:
646 if (reader.name() == m_strings.TAG_RCC) {
647 if (!tokens.isEmpty() && tokens.top() ==
RccTag)
650 reader.raiseError(
"unexpected closing tag"_L1);
651 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
652 if (!tokens.isEmpty() && tokens.top() ==
ResourceTag)
655 reader.raiseError(
"unexpected closing tag"_L1);
656 }
else if (reader.name() == m_strings.TAG_FILE) {
657 if (!tokens.isEmpty() && tokens.top() ==
FileTag)
660 reader.raiseError(
"unexpected closing tag"_L1);
664 case QXmlStreamReader::Characters:
665 if (reader.isWhitespace())
667 if (tokens.isEmpty() || tokens.top() !=
FileTag) {
668 reader.raiseError(
"unexpected text"_L1);
670 QString fileName = reader.text().toString();
671 if (fileName.isEmpty()) {
672 const QString msg = QString::fromLatin1(
"RCC: Warning: Null node in XML of '%1'\n").arg(fname);
673 m_errorDevice->write(msg.toUtf8());
679 alias = QDir::cleanPath(alias);
680 while (alias.startsWith(
"../"_L1))
682 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
684 QString absFileName = fileName;
685 if (QDir::isRelativePath(absFileName))
686 absFileName.prepend(currentPath);
687 QFileInfo file(absFileName);
689 if (!alias.endsWith(slash))
692 QStringList filePaths;
693 using F = QDirListing::IteratorFlag;
694 constexpr auto flags = F::FollowDirSymlinks | F::Recursive;
695 for (
const auto &entry : QDirListing(file.filePath(), flags)) {
696 const QString &fileName = entry.fileName();
697 if (fileName ==
"."_L1 || fileName ==
".."_L1)
699 filePaths.emplace_back(entry.filePath());
703 std::sort(filePaths.begin(), filePaths.end());
705 for (
const QString &filePath : filePaths) {
706 QFileInfo child(filePath);
708 addFile(alias + child.fileName(),
709 RCCFileInfo(child.fileName(), child, language, territory,
710 child.isDir() ? RCCFileInfo::Directory
711 : RCCFileInfo::NoFlags,
712 compressAlgo, compressLevel, compressThreshold,
715 m_failedResources.push_back(child.fileName());
717 }
else if (listMode || file.isFile()) {
731 m_failedResources.push_back(absFileName);
732 }
else if (file.exists()) {
733 m_failedResources.push_back(absFileName);
734 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Entry '%2' is neither a file nor a directory\n")
735 .arg(fname, fileName);
736 m_errorDevice->write(msg.toUtf8());
739 m_failedResources.push_back(absFileName);
740 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Cannot find file '%2'\n")
741 .arg(fname, fileName);
742 m_errorDevice->write(msg.toUtf8());
753 if (reader.hasError()) {
754 int errorLine = reader.lineNumber();
755 int errorColumn = reader.columnNumber();
756 QString errorMessage = reader.errorString();
757 QString msg = QString::fromLatin1(
"RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
758 m_errorDevice->write(msg.toUtf8());
762 if (m_root ==
nullptr) {
763 const QString msg = QString::fromLatin1(
"RCC: Warning: No resources in '%1'.\n").arg(fname);
764 m_errorDevice->write(msg.toUtf8());
765 if (!listMode && m_format ==
Binary) {
767 m_root =
new RCCFileInfo{};
777 Q_ASSERT(m_errorDevice);
778 if (file.m_fileInfo.size() > 0xffffffff) {
779 const QString msg = QString::fromLatin1(
"File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
780 m_errorDevice->write(msg.toUtf8());
784 m_root =
new RCCFileInfo{};
789 const QStringList nodes = alias.split(u'/');
790 for (
int i = 1; i < nodes.size()-1; ++i) {
791 const QString node = nodes.at(i);
794 if (!parent->m_children.contains(node)) {
799 parent->m_children.insert(node, s);
802 parent = *parent->m_children.constFind(node);
806 const QString filename = nodes.at(nodes.size()-1);
809 auto cbegin = parent->m_children.constFind(filename);
810 auto cend = parent->m_children.constEnd();
811 for (
auto it = cbegin; it != cend; ++it) {
812 if (it.key() == filename && it.value()->m_language == s->m_language &&
813 it.value()->m_territory == s->m_territory) {
814 for (
const QString &name : std::as_const(m_fileNames)) {
815 qWarning(
"%s: Warning: potential duplicate alias detected: '%s'",
821 parent->m_children.insert(filename, s);
831 m_errorDevice =
nullptr;
832 m_failedResources.clear();
839 m_errorDevice = &errorDevice;
842 const QString msg = QString::fromLatin1(
"Processing %1 files [listMode=%2]\n")
843 .arg(m_fileNames.size()).arg(
static_cast<
int>(listMode));
844 m_errorDevice->write(msg.toUtf8());
846 for (
int i = 0; i < m_fileNames.size(); ++i) {
848 QString fname = m_fileNames.at(i);
850 if (fname ==
"-"_L1) {
851 fname =
"(stdin)"_L1;
852 pwd = QDir::currentPath();
853 fileIn.setFileName(fname);
854 if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
855 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
859 pwd = QFileInfo(fname).path();
860 fileIn.setFileName(fname);
861 if (!fileIn.open(QIODevice::ReadOnly)) {
862 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
867 const QString msg = QString::fromLatin1(
"Interpreting %1\n").arg(fname);
868 m_errorDevice->write(msg.toUtf8());
871 if (!interpretResourceFile(&fileIn, fname,
std::move(pwd), listMode))
880 QStack<RCCFileInfo*> pending;
884 pending.push(m_root);
885 while (!pending.isEmpty()) {
887 for (
auto it = file->m_children.begin();
888 it != file->m_children.end(); ++it) {
893 ret.append(child->m_fileInfo.filePath());
902 const QChar slash = u'/';
903 const auto cend = m_root->m_children.constEnd();
904 for (
auto it = m_root->m_children.constBegin(); it != cend; ++it) {
906 const QString childName = path + slash + child->m_name;
908 resourceDataFileMapRecursion(child, childName, m);
910 m.insert(childName, child->m_fileInfo.filePath());
919 resourceDataFileMapRecursion(m_root, QString(u':'), rc);
925 if (value ==
"best"_L1)
927 if (value ==
"zlib"_L1) {
929 *errorMsg =
"zlib support not compiled in"_L1;
933 }
else if (value ==
"zstd"_L1) {
935 return CompressionAlgorithm::Zstd;
937 *errorMsg =
"Zstandard support not compiled in"_L1;
939 }
else if (value !=
"none"_L1) {
940 *errorMsg = QString::fromLatin1(
"Unknown compression algorithm '%1'").arg(value);
949 int c = level.toInt(&ok);
956 if (c >= 1 && c <= 9)
961 if (c >= 0 && c <= ZSTD_maxCLevel())
968 *errorMsg = QString::fromLatin1(
"invalid compression level '%1'").arg(level);
974 m_errorDevice = &errorDevice;
976 if (m_format ==
Pass2) {
977 const char pattern[] = {
'Q',
'R',
'C',
'_',
'D',
'A',
'T',
'A' };
978 bool foundSignature =
false;
982 for (
int i = 0; i < 8; ) {
983 if (!tempDevice.getChar(&c)) {
986 m_errorDevice->write(
"No data signature found\n");
990 if (c != pattern[i]) {
991 for (
int k = 0; k < i; ++k)
992 outDevice.putChar(pattern[k]);
996 if (c == pattern[i]) {
999 outDevice.putChar(c);
1003 m_outDevice = &outDevice;
1004 quint64 start = outDevice.pos();
1006 quint64 len = outDevice.pos() - start;
1008 tempDevice.seek(tempDevice.pos() + len - 8);
1009 foundSignature =
true;
1015 m_errorDevice->write(
"Outputting code\n");
1016 if (!writeHeader()) {
1017 m_errorDevice->write(
"Could not write header\n");
1021 if (!writeDataBlobs()) {
1022 m_errorDevice->write(
"Could not write data blobs.\n");
1025 if (!writeDataNames()) {
1026 m_errorDevice->write(
"Could not write file names\n");
1029 if (!writeDataStructure()) {
1030 m_errorDevice->write(
"Could not write data tree\n");
1034 if (!writeInitializer()) {
1035 m_errorDevice->write(
"Could not write footer\n");
1038 outDevice.write(m_out.constData(), m_out.size());
1045 char buf[
std::numeric_limits<
int>::digits10 + 2];
1046 int n = snprintf(buf,
sizeof(buf),
"%d", value);
1062 if (tmp >= 32 && tmp < 127 && tmp !=
'"' && tmp !=
'\\') {
1063 writeChar(
char(tmp));
1067 write2HexDigits(tmp);
1076 write2HexDigits(tmp);
1085 writeChar(number >> 8);
1088 writeHex(number >> 8);
1096 m_outDevice->putChar(
char(number >> 24));
1097 m_outDevice->putChar(
char(number >> 16));
1098 m_outDevice->putChar(
char(number >> 8));
1099 m_outDevice->putChar(
char(number));
1101 writeChar(number >> 24);
1102 writeChar(number >> 16);
1103 writeChar(number >> 8);
1106 writeHex(number >> 24);
1107 writeHex(number >> 16);
1108 writeHex(number >> 8);
1116 m_outDevice->putChar(
char(number >> 56));
1117 m_outDevice->putChar(
char(number >> 48));
1118 m_outDevice->putChar(
char(number >> 40));
1119 m_outDevice->putChar(
char(number >> 32));
1120 m_outDevice->putChar(
char(number >> 24));
1121 m_outDevice->putChar(
char(number >> 16));
1122 m_outDevice->putChar(
char(number >> 8));
1123 m_outDevice->putChar(
char(number));
1125 writeChar(number >> 56);
1126 writeChar(number >> 48);
1127 writeChar(number >> 40);
1128 writeChar(number >> 32);
1129 writeChar(number >> 24);
1130 writeChar(number >> 16);
1131 writeChar(number >> 8);
1134 writeHex(number >> 56);
1135 writeHex(number >> 48);
1136 writeHex(number >> 40);
1137 writeHex(number >> 32);
1138 writeHex(number >> 24);
1139 writeHex(number >> 16);
1140 writeHex(number >> 8);
1147 auto writeCopyright = [
this](QByteArrayView prefix) {
1148 const QStringList lines = m_legal.split(u'\n', Qt::SkipEmptyParts);
1149 for (
const QString &line : lines) {
1150 write(prefix.data(), prefix.size());
1151 writeString(line.toUtf8().trimmed());
1158 writeString(
"/****************************************************************************\n");
1159 writeString(
"** Resource object code\n");
1160 writeCopyright(
"** ");
1161 writeString(
"**\n");
1162 writeString(
"** Created by: The Resource Compiler for Qt version ");
1163 writeByteArray(QT_VERSION_STR);
1164 writeString(
"\n**\n");
1165 writeString(
"** WARNING! All changes made in this file will be lost!\n");
1166 writeString(
"*****************************************************************************/\n\n");
1167 writeString(
"#ifdef _MSC_VER\n"
1168 "// disable informational message \"function ... selected for automatic inline expansion\"\n"
1169 "#pragma warning (disable: 4711)\n"
1173 writeString(
"# Resource object code (Python 3)\n");
1174 writeCopyright(
"# ");
1175 writeString(
"# Created by: object code\n");
1176 writeString(
"# Created by: The Resource Compiler for Qt version ");
1177 writeByteArray(QT_VERSION_STR);
1179 writeString(
"# WARNING! All changes made in this file will be lost!\n\n");
1180 writeString(
"from PySide");
1181 writeByteArray(QByteArray::number(QT_VERSION_MAJOR));
1182 writeString(
" import QtCore\n\n");
1185 writeString(
"qres");
1190 if (m_formatVersion >= 3)
1191 writeNumber4(m_overallFlags);
1201 Q_ASSERT(m_errorDevice);
1204 writeString(
"static const unsigned char qt_resource_data[] = {\n");
1207 writeString(
"qt_resource_data = b\"\\\n");
1210 m_dataOffset = m_out.size();
1219 QStack<RCCFileInfo*> pending;
1220 pending.push(m_root);
1223 QString errorMessage;
1224 while (!pending.isEmpty()) {
1226 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1229 pending.push(child);
1231 offset = child->writeDataBlob(*
this, offset,
1232 dedupByContent, &errorMessage);
1234 m_errorDevice->write(errorMessage.toUtf8());
1242 writeString(
"\n};\n\n");
1245 writeString(
"\"\n\n");
1250 writeString(
"\nstatic const unsigned char qt_resource_data[");
1251 writeByteArray(QByteArray::number(offset));
1252 writeString(
"] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n");
1265 writeString(
"static const unsigned char qt_resource_name[] = {\n");
1268 writeString(
"qt_resource_name = b\"\\\n");
1271 m_namesOffset = m_out.size();
1277 QHash<QString,
int> names;
1278 QStack<RCCFileInfo*> pending;
1283 pending.push(m_root);
1285 while (!pending.isEmpty()) {
1287 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1290 pending.push(child);
1291 if (names.contains(child->m_name)) {
1292 child->m_nameOffset = names.value(child->m_name);
1294 names.insert(child->m_name, offset);
1295 offset = child->writeDataName(*
this, offset);
1302 writeString(
"\n};\n\n");
1305 writeString(
"\"\n\n");
1318 return qt_hash(left->m_name) < qt_hash(right->m_name);
1327 writeString(
"static const unsigned char qt_resource_struct[] = {\n");
1330 writeString(
"qt_resource_struct = b\"\\\n");
1333 m_treeOffset = m_out.size();
1339 QStack<RCCFileInfo*> pending;
1345 pending.push(m_root);
1347 while (!pending.isEmpty()) {
1349 file->m_childOffset = offset;
1352 QList<RCCFileInfo*> m_children = file->m_children.values();
1356 for (
int i = 0; i < m_children.size(); ++i) {
1360 pending.push(child);
1365 pending.push(m_root);
1367 while (!pending.isEmpty()) {
1371 QList<RCCFileInfo*> m_children = file->m_children.values();
1375 for (
int i = 0; i < m_children.size(); ++i) {
1379 pending.push(child);
1385 writeString(
"\n};\n\n");
1388 writeString(
"\"\n\n");
1399 if (m_useNameSpace) {
1400 writeString(
"QT_RCC_MANGLE_NAMESPACE(");
1401 writeByteArray(name);
1404 writeByteArray(name);
1410 if (m_useNameSpace) {
1411 writeString(
"QT_RCC_PREPEND_NAMESPACE(");
1412 writeByteArray(name);
1415 writeByteArray(name);
1423 QString initNameStr = m_initName;
1424 if (!initNameStr.isEmpty()) {
1425 initNameStr.prepend(u'_');
1426 auto isAsciiLetterOrNumber = [] (QChar c) ->
bool {
1427 ushort ch = c.unicode();
1428 return (ch >=
'0' && ch <=
'9') ||
1429 (ch >=
'A' && ch <=
'Z') ||
1430 (ch >=
'a' && ch <=
'z') ||
1433 for (QChar &c : initNameStr) {
1434 if (!isAsciiLetterOrNumber(c))
1438 QByteArray initName = initNameStr.toLatin1();
1441 if (m_useNameSpace) {
1442 writeString(
"#ifdef QT_NAMESPACE\n"
1443 "# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name\n"
1444 "# define QT_RCC_MANGLE_NAMESPACE0(x) x\n"
1445 "# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b\n"
1446 "# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)\n"
1447 "# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \\\n"
1448 " QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))\n"
1450 "# define QT_RCC_PREPEND_NAMESPACE(name) name\n"
1451 "# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
1454 writeString(
"#if defined(QT_INLINE_NAMESPACE)\n"
1455 "inline namespace QT_NAMESPACE {\n"
1456 "#elif defined(QT_NAMESPACE)\n"
1457 "namespace QT_NAMESPACE {\n"
1462 writeString(
"bool qRegisterResourceData"
1463 "(int, const unsigned char *, "
1464 "const unsigned char *, const unsigned char *);\n");
1465 writeString(
"bool qUnregisterResourceData"
1466 "(int, const unsigned char *, "
1467 "const unsigned char *, const unsigned char *);\n\n");
1469 if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) {
1471 writeString(
"#if defined(__ELF__) || defined(__APPLE__)\n");
1472 if (m_overallFlags & RCCFileInfo::Compressed) {
1473 writeString(
"static inline unsigned char qResourceFeatureZlib()\n"
1475 " extern const unsigned char qt_resourceFeatureZlib;\n"
1476 " return qt_resourceFeatureZlib;\n"
1479 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1480 writeString(
"static inline unsigned char qResourceFeatureZstd()\n"
1482 " extern const unsigned char qt_resourceFeatureZstd;\n"
1483 " return qt_resourceFeatureZstd;\n"
1486 writeString(
"#else\n");
1487 if (m_overallFlags & RCCFileInfo::Compressed)
1488 writeString(
"unsigned char qResourceFeatureZlib();\n");
1489 if (m_overallFlags & RCCFileInfo::CompressedZstd)
1490 writeString(
"unsigned char qResourceFeatureZstd();\n");
1491 writeString(
"#endif\n\n");
1496 writeString(
"#ifdef QT_NAMESPACE\n}\n#endif\n\n");
1499 initResources += initName;
1502 writeString(
"int ");
1503 writeMangleNamespaceFunction(initResources);
1504 writeString(
"();\n");
1506 writeString(
"int ");
1507 writeMangleNamespaceFunction(initResources);
1508 writeString(
"()\n{\n");
1511 writeString(
" int version = ");
1512 writeDecimal(m_formatVersion);
1513 writeString(
";\n ");
1514 writeAddNamespaceFunction(
"qRegisterResourceData");
1515 writeString(
"\n (version, qt_resource_struct, "
1516 "qt_resource_name, qt_resource_data);\n");
1518 writeString(
" return 1;\n");
1519 writeString(
"}\n\n");
1522 QByteArray cleanResources =
"qCleanupResources";
1523 cleanResources += initName;
1526 writeString(
"int ");
1527 writeMangleNamespaceFunction(cleanResources);
1528 writeString(
"();\n");
1530 writeString(
"int ");
1531 writeMangleNamespaceFunction(cleanResources);
1532 writeString(
"()\n{\n");
1534 writeString(
" int version = ");
1535 writeDecimal(m_formatVersion);
1536 writeString(
";\n ");
1539 if (m_overallFlags & RCCFileInfo::Compressed) {
1540 writeString(
"version += ");
1541 writeAddNamespaceFunction(
"qResourceFeatureZlib()");
1542 writeString(
";\n ");
1544 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1545 writeString(
"version += ");
1546 writeAddNamespaceFunction(
"qResourceFeatureZstd()");
1547 writeString(
";\n ");
1550 writeAddNamespaceFunction(
"qUnregisterResourceData");
1551 writeString(
"\n (version, qt_resource_struct, "
1552 "qt_resource_name, qt_resource_data);\n");
1554 writeString(
" return 1;\n");
1555 writeString(
"}\n\n");
1558 writeString(
"#ifdef __clang__\n"
1559 "# pragma clang diagnostic push\n"
1560 "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
1563 writeString(
"namespace {\n"
1564 " struct initializer {\n");
1566 if (m_useNameSpace) {
1567 writeByteArray(
" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources +
")(); }\n"
1568 " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources +
")(); }\n");
1570 writeByteArray(
" initializer() { " + initResources +
"(); }\n"
1571 " ~initializer() { " + cleanResources +
"(); }\n");
1573 writeString(
" } dummy;\n"
1576 writeString(
"#ifdef __clang__\n"
1577 "# pragma clang diagnostic pop\n"
1581 }
else if (m_format ==
Binary) {
1583 char *p = m_out.data();
1587 p[i++] = m_formatVersion;
1589 p[i++] = (m_treeOffset >> 24) & 0xff;
1590 p[i++] = (m_treeOffset >> 16) & 0xff;
1591 p[i++] = (m_treeOffset >> 8) & 0xff;
1592 p[i++] = (m_treeOffset >> 0) & 0xff;
1594 p[i++] = (m_dataOffset >> 24) & 0xff;
1595 p[i++] = (m_dataOffset >> 16) & 0xff;
1596 p[i++] = (m_dataOffset >> 8) & 0xff;
1597 p[i++] = (m_dataOffset >> 0) & 0xff;
1599 p[i++] = (m_namesOffset >> 24) & 0xff;
1600 p[i++] = (m_namesOffset >> 16) & 0xff;
1601 p[i++] = (m_namesOffset >> 8) & 0xff;
1602 p[i++] = (m_namesOffset >> 0) & 0xff;
1604 if (m_formatVersion >= 3) {
1605 p[i++] = (m_overallFlags >> 24) & 0xff;
1606 p[i++] = (m_overallFlags >> 16) & 0xff;
1607 p[i++] = (m_overallFlags >> 8) & 0xff;
1608 p[i++] = (m_overallFlags >> 0) & 0xff;
1611 writeString(
"def qInitResources():\n");
1612 writeString(
" QtCore.qRegisterResourceData(0x");
1613 write2HexDigits(m_formatVersion);
1614 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1615 writeString(
"def qCleanupResources():\n");
1616 writeString(
" QtCore.qUnregisterResourceData(0x");
1617 write2HexDigits(m_formatVersion);
1618 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1619 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