10#include <qbytearray.h>
14#include <qdiriterator.h>
19#include <qxmlstream.h>
29using namespace Qt::StringLiterals;
42 m_out.resize(n + len);
43 memcpy(m_out.data() + n, str, len);
48 if (m_format ==
Pass2) {
49 m_outDevice->write(other);
57 return QString::fromLatin1(
"Unable to open %1 for reading: %2\n").arg(fname, why);
81 RCCFileInfo(
const QString &name,
const QFileInfo &fileInfo, QLocale::Language language,
82 QLocale::Territory territory, uint flags,
84 int compressThreshold,
bool noZstd,
bool isEmpty);
119 QLocale::Territory territory, uint flags,
121 int compressThreshold,
bool noZstd,
bool isEmpty)
137 qDeleteAll(m_children);
142 QString resource = m_name;
144 resource = resource.prepend(p->m_name + u'/');
145 resource.prepend(u':');
156 if (m_language != QLocale::C) {
157 lib.writeString(
" // ");
158 lib.writeByteArray(resourceName().toLocal8Bit());
159 lib.writeString(
" [");
160 lib.writeByteArray(QByteArray::number(m_territory));
161 lib.writeString(
"::");
162 lib.writeByteArray(QByteArray::number(m_language));
163 lib.writeString(
"[\n ");
165 lib.writeString(
" // ");
166 lib.writeByteArray(resourceName().toLocal8Bit());
167 lib.writeString(
"\n ");
174 lib.writeNumber4(m_nameOffset);
180 lib.writeNumber4(m_children.size());
183 lib.writeNumber4(m_childOffset);
186 lib.writeNumber4(m_nameOffset);
192 lib.writeNumber2(m_territory);
193 lib.writeNumber2(m_language);
196 lib.writeNumber4(m_dataOffset);
201 lib.writeString(
"\\\n");
205 const QDateTime lastModified = m_fileInfo.lastModified(QTimeZone::UTC);
206 quint64 lastmod = quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0);
207 static const quint64 sourceDate = 1000 * qgetenv(
"QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong();
209 lastmod = sourceDate;
210 static const quint64 sourceDate2 = 1000 * qgetenv(
"SOURCE_DATE_EPOCH").toULongLong();
211 if (sourceDate2 != 0)
212 lastmod = sourceDate2;
213 lib.writeNumber8(lastmod);
217 lib.writeString(
"\\\n");
222 QString *errorMessage)
231 m_dataOffset = offset;
236 QFile file(m_fileInfo.absoluteFilePath());
237 if (!file.open(QFile::ReadOnly)) {
238 *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
242 data = file.readAll();
246 if (data.size() != 0) {
248 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best && !m_noZstd) {
249 m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd;
250 m_compressLevel = 19;
252 if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd && !m_noZstd) {
253 if (lib.m_zstdCCtx ==
nullptr)
254 lib.m_zstdCCtx = ZSTD_createCCtx();
255 qsizetype size = data.size();
256 size = ZSTD_COMPRESSBOUND(size);
258 int compressLevel = m_compressLevel;
259 if (compressLevel < 0)
260 compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK;
262 QByteArray compressed(size, Qt::Uninitialized);
263 char *dst =
const_cast<
char *>(compressed.constData());
264 size_t n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
265 data.constData(), data.size(),
267 if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) {
269 if (m_compressLevel < 0) {
271 n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size,
272 data.constData(), data.size(),
273 CONSTANT_ZSTDCOMPRESSLEVEL_STORE);
275 if (ZSTD_isError(n)) {
276 QString msg = QString::fromLatin1(
"%1: error: compression with zstd failed: %2\n")
277 .arg(m_name, QString::fromUtf8(ZSTD_getErrorName(n)));
278 lib.m_errorDevice->write(msg.toUtf8());
279 }
else if (lib.verbose()) {
280 QString msg = QString::fromLatin1(
"%1: note: compressed using zstd (%2 -> %3)\n")
281 .arg(m_name).arg(data.size()).arg(n);
282 lib.m_errorDevice->write(msg.toUtf8());
285 lib.m_overallFlags |= CompressedZstd;
286 m_flags |= CompressedZstd;
287 data = std::move(compressed);
289 }
else if (lib.verbose()) {
290 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
291 lib.m_errorDevice->write(msg.toUtf8());
295#ifndef QT_NO_COMPRESS
302 qCompress(
reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
304 int compressRatio =
int(100.0 * (data.size() - compressed.size()) / data.size());
307 QString msg = QString::fromLatin1(
"%1: note: compressed using zlib (%2 -> %3)\n")
308 .arg(m_name).arg(data.size()).arg(compressed.size());
309 lib.m_errorDevice->write(msg.toUtf8());
311 data =
std::move(compressed);
315 QString msg = QString::fromLatin1(
"%1: note: not compressed\n").arg(m_name);
316 lib.m_errorDevice->write(msg.toUtf8());
324 lib.writeString(
" // ");
325 lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
326 lib.writeString(
"\n ");
330 if (text || binary || pass2 || python)
331 lib.writeNumber4(data.size());
333 lib.writeString(
"\n ");
335 lib.writeString(
"\\\n");
339 const char *p = data.constData();
340 if (text || python) {
341 for (
int i = data.size(), j = 0; --i >= 0; --j) {
345 lib.writeString(
"\n ");
347 lib.writeString(
"\\\n");
351 }
else if (binary || pass2) {
352 lib.writeByteArray(data);
354 offset += data.size();
358 lib.writeString(
"\n ");
360 lib.writeString(
"\\\n");
372 m_nameOffset = offset;
376 lib.writeString(
" // ");
377 lib.writeByteArray(m_name.toLocal8Bit());
378 lib.writeString(
"\n ");
382 lib.writeNumber2(m_name.size());
384 lib.writeString(
"\n ");
386 lib.writeString(
"\\\n");
390 lib.writeNumber4(qt_hash(m_name));
392 lib.writeString(
"\n ");
394 lib.writeString(
"\\\n");
398 const QChar *unicode = m_name.unicode();
399 for (
int i = 0; i < m_name.size(); ++i) {
400 lib.writeNumber2(unicode[i].unicode());
401 if ((text || pass1) && i % 16 == 0)
402 lib.writeString(
"\n ");
403 else if (python && i % 16 == 0)
404 lib.writeString(
"\\\n");
406 offset += m_name.size()*2;
410 lib.writeString(
"\n ");
412 lib.writeString(
"\\\n");
426 TAG_RESOURCE(
"qresource"_L1),
428 ATTRIBUTE_LANG(
"lang"_L1),
429 ATTRIBUTE_PREFIX(
"prefix"_L1),
430 ATTRIBUTE_ALIAS(
"alias"_L1),
431 ATTRIBUTE_EMPTY(
"empty"_L1),
432 ATTRIBUTE_THRESHOLD(
"threshold"_L1),
433 ATTRIBUTE_COMPRESS(
"compress"_L1),
450 m_errorDevice(
nullptr),
451 m_outDevice(
nullptr),
455 m_out.reserve(30 * 1000 * 1000);
457 m_zstdCCtx =
nullptr;
465 ZSTD_freeCCtx(m_zstdCCtx);
478 if (value.compare(
"true"_L1, Qt::CaseInsensitive) == 0)
480 if (value.compare(
"false"_L1, Qt::CaseInsensitive) == 0)
483 *errorMsg = QString::fromLatin1(
"Invalid value for boolean attribute: '%1'").arg(value);
488 const QString &fname, QString currentPath,
bool listMode)
490 Q_ASSERT(m_errorDevice);
491 const QChar slash = u'/';
492 if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
493 currentPath += slash;
495 QXmlStreamReader reader(inputDevice);
496 QStack<RCCXmlTag> tokens;
499 QLocale::Language language = QLocale::c().language();
500 QLocale::Territory territory = QLocale::c().territory();
503 auto compressAlgo = m_compressionAlgo;
504 int compressLevel = m_compressLevel;
505 int compressThreshold = m_compressThreshold;
507 while (!reader.atEnd()) {
508 QXmlStreamReader::TokenType t = reader.readNext();
510 case QXmlStreamReader::StartElement:
511 if (reader.name() == m_strings.TAG_RCC) {
512 if (!tokens.isEmpty())
513 reader.raiseError(
"expected <RCC> tag"_L1);
516 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
517 if (tokens.isEmpty() || tokens.top() !=
RccTag) {
518 reader.raiseError(
"unexpected <RESOURCE> tag"_L1);
522 QXmlStreamAttributes attributes = reader.attributes();
523 language = QLocale::c().language();
524 territory = QLocale::c().territory();
526 if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
527 QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
528 QLocale lang = QLocale(attribute);
529 language = lang.language();
530 if (2 == attribute.size()) {
532 territory = QLocale::AnyTerritory;
534 territory = lang.territory();
539 if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
540 prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
541 if (!prefix.startsWith(slash))
542 prefix.prepend(slash);
543 if (!prefix.endsWith(slash))
546 }
else if (reader.name() == m_strings.TAG_FILE) {
547 if (tokens.isEmpty() || tokens.top() !=
ResourceTag) {
548 reader.raiseError(
"unexpected <FILE> tag"_L1);
552 QXmlStreamAttributes attributes = reader.attributes();
554 if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
555 alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
557 compressAlgo = m_compressionAlgo;
558 compressLevel = m_compressLevel;
559 compressThreshold = m_compressThreshold;
562 if (attributes.hasAttribute(m_strings.ATTRIBUTE_EMPTY))
563 empty = parseBoolean(attributes.value(m_strings.ATTRIBUTE_EMPTY), &errorString);
567 if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO))
568 compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString);
569 if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) {
570 QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString();
575 if (m_compressLevel == -2)
578 if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
579 compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
581 if (!errorString.isEmpty())
582 reader.raiseError(errorString);
585 reader.raiseError(
"unexpected tag: %1"_L1.arg(reader.name().toString()));
589 case QXmlStreamReader::EndElement:
590 if (reader.name() == m_strings.TAG_RCC) {
591 if (!tokens.isEmpty() && tokens.top() ==
RccTag)
594 reader.raiseError(
"unexpected closing tag"_L1);
595 }
else if (reader.name() == m_strings.TAG_RESOURCE) {
596 if (!tokens.isEmpty() && tokens.top() ==
ResourceTag)
599 reader.raiseError(
"unexpected closing tag"_L1);
600 }
else if (reader.name() == m_strings.TAG_FILE) {
601 if (!tokens.isEmpty() && tokens.top() ==
FileTag)
604 reader.raiseError(
"unexpected closing tag"_L1);
608 case QXmlStreamReader::Characters:
609 if (reader.isWhitespace())
611 if (tokens.isEmpty() || tokens.top() !=
FileTag) {
612 reader.raiseError(
"unexpected text"_L1);
614 QString fileName = reader.text().toString();
615 if (fileName.isEmpty()) {
616 const QString msg = QString::fromLatin1(
"RCC: Warning: Null node in XML of '%1'\n").arg(fname);
617 m_errorDevice->write(msg.toUtf8());
623 alias = QDir::cleanPath(alias);
624 while (alias.startsWith(
"../"_L1))
626 alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
628 QString absFileName = fileName;
629 if (QDir::isRelativePath(absFileName))
630 absFileName.prepend(currentPath);
631 QFileInfo file(absFileName);
633 QDir dir(file.filePath());
634 if (!alias.endsWith(slash))
637 QStringList filePaths;
638 QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
639 while (it.hasNext()) {
641 if (it.fileName() ==
"."_L1 || it.fileName() ==
".."_L1)
643 filePaths.append(it.filePath());
647 std::sort(filePaths.begin(), filePaths.end());
649 for (
const QString &filePath : filePaths) {
650 QFileInfo child(filePath);
652 addFile(alias + child.fileName(),
653 RCCFileInfo(child.fileName(), child, language, territory,
654 child.isDir() ? RCCFileInfo::Directory
655 : RCCFileInfo::NoFlags,
656 compressAlgo, compressLevel, compressThreshold,
659 m_failedResources.push_back(child.fileName());
661 }
else if (listMode || file.isFile()) {
675 m_failedResources.push_back(absFileName);
676 }
else if (file.exists()) {
677 m_failedResources.push_back(absFileName);
678 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Entry '%2' is neither a file nor a directory\n")
679 .arg(fname, fileName);
680 m_errorDevice->write(msg.toUtf8());
683 m_failedResources.push_back(absFileName);
684 const QString msg = QString::fromLatin1(
"RCC: Error in '%1': Cannot find file '%2'\n")
685 .arg(fname, fileName);
686 m_errorDevice->write(msg.toUtf8());
697 if (reader.hasError()) {
698 int errorLine = reader.lineNumber();
699 int errorColumn = reader.columnNumber();
700 QString errorMessage = reader.errorString();
701 QString msg = QString::fromLatin1(
"RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
702 m_errorDevice->write(msg.toUtf8());
706 if (m_root ==
nullptr) {
707 const QString msg = QString::fromLatin1(
"RCC: Warning: No resources in '%1'.\n").arg(fname);
708 m_errorDevice->write(msg.toUtf8());
709 if (!listMode && m_format ==
Binary) {
711 m_root =
new RCCFileInfo{};
721 Q_ASSERT(m_errorDevice);
722 if (file.m_fileInfo.size() > 0xffffffff) {
723 const QString msg = QString::fromLatin1(
"File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
724 m_errorDevice->write(msg.toUtf8());
728 m_root =
new RCCFileInfo{};
733 const QStringList nodes = alias.split(u'/');
734 for (
int i = 1; i < nodes.size()-1; ++i) {
735 const QString node = nodes.at(i);
738 if (!parent->m_children.contains(node)) {
743 parent->m_children.insert(node, s);
746 parent = *parent->m_children.constFind(node);
750 const QString filename = nodes.at(nodes.size()-1);
753 auto cbegin = parent->m_children.constFind(filename);
754 auto cend = parent->m_children.constEnd();
755 for (
auto it = cbegin; it != cend; ++it) {
756 if (it.key() == filename && it.value()->m_language == s->m_language &&
757 it.value()->m_territory == s->m_territory) {
758 for (
const QString &name : std::as_const(m_fileNames)) {
759 qWarning(
"%s: Warning: potential duplicate alias detected: '%s'",
765 parent->m_children.insert(filename, s);
775 m_errorDevice =
nullptr;
776 m_failedResources.clear();
783 m_errorDevice = &errorDevice;
786 const QString msg = QString::fromLatin1(
"Processing %1 files [listMode=%2]\n")
787 .arg(m_fileNames.size()).arg(
static_cast<
int>(listMode));
788 m_errorDevice->write(msg.toUtf8());
790 for (
int i = 0; i < m_fileNames.size(); ++i) {
792 QString fname = m_fileNames.at(i);
794 if (fname ==
"-"_L1) {
795 fname =
"(stdin)"_L1;
796 pwd = QDir::currentPath();
797 fileIn.setFileName(fname);
798 if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
799 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
803 pwd = QFileInfo(fname).path();
804 fileIn.setFileName(fname);
805 if (!fileIn.open(QIODevice::ReadOnly)) {
806 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
811 const QString msg = QString::fromLatin1(
"Interpreting %1\n").arg(fname);
812 m_errorDevice->write(msg.toUtf8());
815 if (!interpretResourceFile(&fileIn, fname,
std::move(pwd), listMode))
824 QStack<RCCFileInfo*> pending;
828 pending.push(m_root);
829 while (!pending.isEmpty()) {
831 for (
auto it = file->m_children.begin();
832 it != file->m_children.end(); ++it) {
837 ret.append(child->m_fileInfo.filePath());
846 const QChar slash = u'/';
847 const auto cend = m_root->m_children.constEnd();
848 for (
auto it = m_root->m_children.constBegin(); it != cend; ++it) {
850 const QString childName = path + slash + child->m_name;
852 resourceDataFileMapRecursion(child, childName, m);
854 m.insert(childName, child->m_fileInfo.filePath());
863 resourceDataFileMapRecursion(m_root, QString(u':'), rc);
869 if (value ==
"best"_L1)
871 if (value ==
"zlib"_L1) {
873 *errorMsg =
"zlib support not compiled in"_L1;
877 }
else if (value ==
"zstd"_L1) {
879 return CompressionAlgorithm::Zstd;
881 *errorMsg =
"Zstandard support not compiled in"_L1;
883 }
else if (value !=
"none"_L1) {
884 *errorMsg = QString::fromLatin1(
"Unknown compression algorithm '%1'").arg(value);
893 int c = level.toInt(&ok);
900 if (c >= 1 && c <= 9)
905 if (c >= 0 && c <= ZSTD_maxCLevel())
912 *errorMsg = QString::fromLatin1(
"invalid compression level '%1'").arg(level);
918 m_errorDevice = &errorDevice;
920 if (m_format ==
Pass2) {
921 const char pattern[] = {
'Q',
'R',
'C',
'_',
'D',
'A',
'T',
'A' };
922 bool foundSignature =
false;
926 for (
int i = 0; i < 8; ) {
927 if (!tempDevice.getChar(&c)) {
930 m_errorDevice->write(
"No data signature found\n");
934 if (c != pattern[i]) {
935 for (
int k = 0; k < i; ++k)
936 outDevice.putChar(pattern[k]);
940 if (c == pattern[i]) {
943 outDevice.putChar(c);
947 m_outDevice = &outDevice;
948 quint64 start = outDevice.pos();
950 quint64 len = outDevice.pos() - start;
952 tempDevice.seek(tempDevice.pos() + len - 8);
953 foundSignature =
true;
959 m_errorDevice->write(
"Outputting code\n");
960 if (!writeHeader()) {
961 m_errorDevice->write(
"Could not write header\n");
965 if (!writeDataBlobs()) {
966 m_errorDevice->write(
"Could not write data blobs.\n");
969 if (!writeDataNames()) {
970 m_errorDevice->write(
"Could not write file names\n");
973 if (!writeDataStructure()) {
974 m_errorDevice->write(
"Could not write data tree\n");
978 if (!writeInitializer()) {
979 m_errorDevice->write(
"Could not write footer\n");
982 outDevice.write(m_out.constData(), m_out.size());
989 char buf[
std::numeric_limits<
int>::digits10 + 2];
990 int n = snprintf(buf,
sizeof(buf),
"%d", value);
1006 if (tmp >= 32 && tmp < 127 && tmp !=
'"' && tmp !=
'\\') {
1007 writeChar(
char(tmp));
1011 write2HexDigits(tmp);
1020 write2HexDigits(tmp);
1029 writeChar(number >> 8);
1032 writeHex(number >> 8);
1040 m_outDevice->putChar(
char(number >> 24));
1041 m_outDevice->putChar(
char(number >> 16));
1042 m_outDevice->putChar(
char(number >> 8));
1043 m_outDevice->putChar(
char(number));
1045 writeChar(number >> 24);
1046 writeChar(number >> 16);
1047 writeChar(number >> 8);
1050 writeHex(number >> 24);
1051 writeHex(number >> 16);
1052 writeHex(number >> 8);
1060 m_outDevice->putChar(
char(number >> 56));
1061 m_outDevice->putChar(
char(number >> 48));
1062 m_outDevice->putChar(
char(number >> 40));
1063 m_outDevice->putChar(
char(number >> 32));
1064 m_outDevice->putChar(
char(number >> 24));
1065 m_outDevice->putChar(
char(number >> 16));
1066 m_outDevice->putChar(
char(number >> 8));
1067 m_outDevice->putChar(
char(number));
1069 writeChar(number >> 56);
1070 writeChar(number >> 48);
1071 writeChar(number >> 40);
1072 writeChar(number >> 32);
1073 writeChar(number >> 24);
1074 writeChar(number >> 16);
1075 writeChar(number >> 8);
1078 writeHex(number >> 56);
1079 writeHex(number >> 48);
1080 writeHex(number >> 40);
1081 writeHex(number >> 32);
1082 writeHex(number >> 24);
1083 writeHex(number >> 16);
1084 writeHex(number >> 8);
1094 writeString(
"/****************************************************************************\n");
1095 writeString(
"** Resource object code\n");
1096 writeString(
"**\n");
1097 writeString(
"** Created by: The Resource Compiler for Qt version ");
1098 writeByteArray(QT_VERSION_STR);
1099 writeString(
"\n**\n");
1100 writeString(
"** WARNING! All changes made in this file will be lost!\n");
1101 writeString(
"*****************************************************************************/\n\n");
1104 writeString(
"# Resource object code (Python 3)\n");
1105 writeString(
"# Created by: object code\n");
1106 writeString(
"# Created by: The Resource Compiler for Qt version ");
1107 writeByteArray(QT_VERSION_STR);
1109 writeString(
"# WARNING! All changes made in this file will be lost!\n\n");
1110 writeString(
"from PySide");
1111 writeByteArray(QByteArray::number(QT_VERSION_MAJOR));
1112 writeString(
" import QtCore\n\n");
1115 writeString(
"qres");
1120 if (m_formatVersion >= 3)
1121 writeNumber4(m_overallFlags);
1131 Q_ASSERT(m_errorDevice);
1134 writeString(
"static const unsigned char qt_resource_data[] = {\n");
1137 writeString(
"qt_resource_data = b\"\\\n");
1140 m_dataOffset = m_out.size();
1149 QStack<RCCFileInfo*> pending;
1150 pending.push(m_root);
1152 QString errorMessage;
1153 while (!pending.isEmpty()) {
1155 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1158 pending.push(child);
1160 offset = child->writeDataBlob(*
this, offset, &errorMessage);
1162 m_errorDevice->write(errorMessage.toUtf8());
1170 writeString(
"\n};\n\n");
1173 writeString(
"\"\n\n");
1178 writeString(
"\nstatic const unsigned char qt_resource_data[");
1179 writeByteArray(QByteArray::number(offset));
1180 writeString(
"] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n");
1193 writeString(
"static const unsigned char qt_resource_name[] = {\n");
1196 writeString(
"qt_resource_name = b\"\\\n");
1199 m_namesOffset = m_out.size();
1205 QHash<QString,
int> names;
1206 QStack<RCCFileInfo*> pending;
1211 pending.push(m_root);
1213 while (!pending.isEmpty()) {
1215 for (
auto it = file->m_children.cbegin(); it != file->m_children.cend(); ++it) {
1218 pending.push(child);
1219 if (names.contains(child->m_name)) {
1220 child->m_nameOffset = names.value(child->m_name);
1222 names.insert(child->m_name, offset);
1223 offset = child->writeDataName(*
this, offset);
1230 writeString(
"\n};\n\n");
1233 writeString(
"\"\n\n");
1246 return qt_hash(left->m_name) < qt_hash(right->m_name);
1255 writeString(
"static const unsigned char qt_resource_struct[] = {\n");
1258 writeString(
"qt_resource_struct = b\"\\\n");
1261 m_treeOffset = m_out.size();
1267 QStack<RCCFileInfo*> pending;
1273 pending.push(m_root);
1275 while (!pending.isEmpty()) {
1277 file->m_childOffset = offset;
1280 QList<RCCFileInfo*> m_children = file->m_children.values();
1284 for (
int i = 0; i < m_children.size(); ++i) {
1288 pending.push(child);
1293 pending.push(m_root);
1295 while (!pending.isEmpty()) {
1299 QList<RCCFileInfo*> m_children = file->m_children.values();
1303 for (
int i = 0; i < m_children.size(); ++i) {
1307 pending.push(child);
1313 writeString(
"\n};\n\n");
1316 writeString(
"\"\n\n");
1327 if (m_useNameSpace) {
1328 writeString(
"QT_RCC_MANGLE_NAMESPACE(");
1329 writeByteArray(name);
1332 writeByteArray(name);
1338 if (m_useNameSpace) {
1339 writeString(
"QT_RCC_PREPEND_NAMESPACE(");
1340 writeByteArray(name);
1343 writeByteArray(name);
1351 QString initNameStr = m_initName;
1352 if (!initNameStr.isEmpty()) {
1353 initNameStr.prepend(u'_');
1354 auto isAsciiLetterOrNumber = [] (QChar c) ->
bool {
1355 ushort ch = c.unicode();
1356 return (ch >=
'0' && ch <=
'9') ||
1357 (ch >=
'A' && ch <=
'Z') ||
1358 (ch >=
'a' && ch <=
'z') ||
1361 for (QChar &c : initNameStr) {
1362 if (!isAsciiLetterOrNumber(c))
1366 QByteArray initName = initNameStr.toLatin1();
1369 if (m_useNameSpace) {
1370 writeString(
"#ifdef QT_NAMESPACE\n"
1371 "# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name\n"
1372 "# define QT_RCC_MANGLE_NAMESPACE0(x) x\n"
1373 "# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b\n"
1374 "# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)\n"
1375 "# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \\\n"
1376 " QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))\n"
1378 "# define QT_RCC_PREPEND_NAMESPACE(name) name\n"
1379 "# define QT_RCC_MANGLE_NAMESPACE(name) name\n"
1382 writeString(
"#ifdef QT_NAMESPACE\n"
1383 "namespace QT_NAMESPACE {\n"
1388 writeString(
"bool qRegisterResourceData"
1389 "(int, const unsigned char *, "
1390 "const unsigned char *, const unsigned char *);\n");
1391 writeString(
"bool qUnregisterResourceData"
1392 "(int, const unsigned char *, "
1393 "const unsigned char *, const unsigned char *);\n\n");
1395 if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) {
1397 writeString(
"#if defined(__ELF__) || defined(__APPLE__)\n");
1398 if (m_overallFlags & RCCFileInfo::Compressed) {
1399 writeString(
"static inline unsigned char qResourceFeatureZlib()\n"
1401 " extern const unsigned char qt_resourceFeatureZlib;\n"
1402 " return qt_resourceFeatureZlib;\n"
1405 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1406 writeString(
"static inline unsigned char qResourceFeatureZstd()\n"
1408 " extern const unsigned char qt_resourceFeatureZstd;\n"
1409 " return qt_resourceFeatureZstd;\n"
1412 writeString(
"#else\n");
1413 if (m_overallFlags & RCCFileInfo::Compressed)
1414 writeString(
"unsigned char qResourceFeatureZlib();\n");
1415 if (m_overallFlags & RCCFileInfo::CompressedZstd)
1416 writeString(
"unsigned char qResourceFeatureZstd();\n");
1417 writeString(
"#endif\n\n");
1422 writeString(
"#ifdef QT_NAMESPACE\n}\n#endif\n\n");
1425 initResources += initName;
1428 writeString(
"int ");
1429 writeMangleNamespaceFunction(initResources);
1430 writeString(
"();\n");
1432 writeString(
"int ");
1433 writeMangleNamespaceFunction(initResources);
1434 writeString(
"()\n{\n");
1437 writeString(
" int version = ");
1438 writeDecimal(m_formatVersion);
1439 writeString(
";\n ");
1440 writeAddNamespaceFunction(
"qRegisterResourceData");
1441 writeString(
"\n (version, qt_resource_struct, "
1442 "qt_resource_name, qt_resource_data);\n");
1444 writeString(
" return 1;\n");
1445 writeString(
"}\n\n");
1448 QByteArray cleanResources =
"qCleanupResources";
1449 cleanResources += initName;
1452 writeString(
"int ");
1453 writeMangleNamespaceFunction(cleanResources);
1454 writeString(
"();\n");
1456 writeString(
"int ");
1457 writeMangleNamespaceFunction(cleanResources);
1458 writeString(
"()\n{\n");
1460 writeString(
" int version = ");
1461 writeDecimal(m_formatVersion);
1462 writeString(
";\n ");
1465 if (m_overallFlags & RCCFileInfo::Compressed) {
1466 writeString(
"version += ");
1467 writeAddNamespaceFunction(
"qResourceFeatureZlib()");
1468 writeString(
";\n ");
1470 if (m_overallFlags & RCCFileInfo::CompressedZstd) {
1471 writeString(
"version += ");
1472 writeAddNamespaceFunction(
"qResourceFeatureZstd()");
1473 writeString(
";\n ");
1476 writeAddNamespaceFunction(
"qUnregisterResourceData");
1477 writeString(
"\n (version, qt_resource_struct, "
1478 "qt_resource_name, qt_resource_data);\n");
1480 writeString(
" return 1;\n");
1481 writeString(
"}\n\n");
1484 writeString(
"#ifdef __clang__\n"
1485 "# pragma clang diagnostic push\n"
1486 "# pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n"
1489 writeString(
"namespace {\n"
1490 " struct initializer {\n");
1492 if (m_useNameSpace) {
1493 writeByteArray(
" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources +
")(); }\n"
1494 " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources +
")(); }\n");
1496 writeByteArray(
" initializer() { " + initResources +
"(); }\n"
1497 " ~initializer() { " + cleanResources +
"(); }\n");
1499 writeString(
" } dummy;\n"
1502 writeString(
"#ifdef __clang__\n"
1503 "# pragma clang diagnostic pop\n"
1507 }
else if (m_format ==
Binary) {
1509 char *p = m_out.data();
1513 p[i++] = m_formatVersion;
1515 p[i++] = (m_treeOffset >> 24) & 0xff;
1516 p[i++] = (m_treeOffset >> 16) & 0xff;
1517 p[i++] = (m_treeOffset >> 8) & 0xff;
1518 p[i++] = (m_treeOffset >> 0) & 0xff;
1520 p[i++] = (m_dataOffset >> 24) & 0xff;
1521 p[i++] = (m_dataOffset >> 16) & 0xff;
1522 p[i++] = (m_dataOffset >> 8) & 0xff;
1523 p[i++] = (m_dataOffset >> 0) & 0xff;
1525 p[i++] = (m_namesOffset >> 24) & 0xff;
1526 p[i++] = (m_namesOffset >> 16) & 0xff;
1527 p[i++] = (m_namesOffset >> 8) & 0xff;
1528 p[i++] = (m_namesOffset >> 0) & 0xff;
1530 if (m_formatVersion >= 3) {
1531 p[i++] = (m_overallFlags >> 24) & 0xff;
1532 p[i++] = (m_overallFlags >> 16) & 0xff;
1533 p[i++] = (m_overallFlags >> 8) & 0xff;
1534 p[i++] = (m_overallFlags >> 0) & 0xff;
1537 writeString(
"def qInitResources():\n");
1538 writeString(
" QtCore.qRegisterResourceData(0x");
1539 write2HexDigits(m_formatVersion);
1540 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1541 writeString(
"def qCleanupResources():\n");
1542 writeString(
" QtCore.qUnregisterResourceData(0x");
1543 write2HexDigits(m_formatVersion);
1544 writeString(
", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n");
1545 writeString(
"qInitResources()\n");
RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language, QLocale::Territory territory, uint flags, RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel, int compressThreshold, bool noZstd, bool isEmpty)
RCCFileInfo & operator=(RCCFileInfo &&other)=delete
RCCFileInfo & operator=(const RCCFileInfo &)=delete
RCCResourceLibrary::CompressionAlgorithm m_compressAlgo
qint64 writeDataName(RCCResourceLibrary &, qint64 offset)
RCCFileInfo(RCCFileInfo &&)=default
QLocale::Language m_language
qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage)
QString resourceName() const
void writeDataInfo(RCCResourceLibrary &lib)
RCCFileInfo(const RCCFileInfo &)=delete
QLocale::Territory m_territory
QMultiHash< QString, RCCFileInfo * > m_children
RCCResourceLibrary(quint8 formatVersion)
ResourceDataFileMap resourceDataFileMap() const
bool readFiles(bool listMode, QIODevice &errorDevice)
QStringList dataFiles() const
int formatVersion() const
static int parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg)
bool output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
QHash< QString, QString > ResourceDataFileMap
#define qPrintable(string)
#define QStringLiteral(str)
result_type operator()(const RCCFileInfo *left, const RCCFileInfo *right) const