Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qresource.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// Copyright (C) 2020 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qresource.h"
6#include "qresource_p.h"
8#include "qset.h"
9#include <private/qlocking_p.h>
10#include "qdebug.h"
11#include "qlocale.h"
12#include "qglobal.h"
13#include "qlist.h"
14#include "qdatetime.h"
15#include "qbytearray.h"
16#include "qstringlist.h"
17#include "qendian.h"
18#include <qshareddata.h>
19#include <qplatformdefs.h>
20#include <qendian.h>
21#include "private/qabstractfileengine_p.h"
22#include "private/qduplicatetracker_p.h"
23#include "private/qnumeric_p.h"
24#include "private/qsimd_p.h"
25#include "private/qtools_p.h"
26#include "private/qsystemerror_p.h"
27
28#ifndef QT_NO_COMPRESS
29# include <zconf.h>
30# include <zlib.h>
31#endif
32#if QT_CONFIG(zstd)
33# include <zstd.h>
34#endif
35
36#if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY)
37# define QT_USE_MMAP
38# include <sys/mman.h>
39#endif
40
41//#define DEBUG_RESOURCE_MATCH
42
44
45using namespace Qt::StringLiterals;
46
47// Symbols used by code generated by RCC.
48// They cause compilation errors if the RCC content couldn't
49// be interpreted by this QtCore version.
50#if defined(__ELF__) || defined(__APPLE__) // same as RCC generates
51# define RCC_FEATURE_SYMBOL(feature) \
52 extern Q_CORE_EXPORT const quint8 qt_resourceFeature ## feature; \
53 const quint8 qt_resourceFeature ## feature = 0;
54#else
55# define RCC_FEATURE_SYMBOL(feature) \
56 Q_CORE_EXPORT quint8 qResourceFeature ## feature() { return 0; }
57#endif
58
59#ifndef QT_NO_COMPRESS
61#endif
62#if QT_CONFIG(zstd)
64#endif
65
66#undef RCC_FEATURE_SYMBOL
67
69{
70public:
72 : m_data(sv.data()), m_len(sv.size())
73 {
74 }
75
76 inline bool hasNext()
77 {
78 while (m_pos < m_len && m_data[m_pos] == m_splitChar)
79 ++m_pos;
80 return m_pos < m_len;
81 }
82
84 {
85 const qsizetype start = m_pos;
86 while (m_pos < m_len && m_data[m_pos] != m_splitChar)
87 ++m_pos;
88 return QStringView(m_data + start, m_pos - start);
89 }
90
91 const QChar *m_data;
95};
96
97// resource glue
99{
100public:
101 enum Flags {
102 // must match rcc.h
104 Directory = 0x02,
105 CompressedZstd = 0x04
106 };
107
108private:
109 const uchar *tree, *names, *payloads;
110 int version;
111 inline int findOffset(int node) const { return node * (14 + (version >= 0x02 ? 8 : 0)); } //sizeof each tree element
112 uint hash(int node) const;
113 QString name(int node) const;
114 short flags(int node) const;
115public:
117
118 inline QResourceRoot(): tree(nullptr), names(nullptr), payloads(nullptr), version(0) {}
119 inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); }
120 virtual ~QResourceRoot() { }
121 int findNode(const QString &path, const QLocale &locale=QLocale()) const;
122 inline bool isContainer(int node) const { return flags(node) & Directory; }
124 {
125 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
126 if (compressionFlags == Compressed)
128 if (compressionFlags == CompressedZstd)
131 }
132 const uchar *data(int node, qint64 *size) const;
133 quint64 lastModified(int node) const;
134 QStringList children(int node) const;
135 virtual QString mappingRoot() const { return QString(); }
136 bool mappingRootSubdir(const QString &path, QString *match = nullptr) const;
137 inline bool operator==(const QResourceRoot &other) const
138 { return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
139 inline bool operator!=(const QResourceRoot &other) const
140 { return !operator==(other); }
142 virtual ResourceRootType type() const { return Resource_Builtin; }
143
144protected:
145 inline void setSource(int v, const uchar *t, const uchar *n, const uchar *d) {
146 tree = t;
147 names = n;
148 payloads = d;
149 version = v;
150 }
151};
152
153static QString cleanPath(const QString &_path)
154{
156 // QDir::cleanPath does not remove two trailing slashes under _Windows_
157 // due to support for UNC paths. Remove those manually.
158 if (path.startsWith("//"_L1))
159 path.remove(0, 1);
160 return path;
161}
162
164
167{
170};
171Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
172
174{ return resourceGlobalData->resourceMutex; }
175
177{ return &resourceGlobalData->resourceList; }
178
257public:
258 inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
259 inline ~QResourcePrivate() { clear(); }
260
261 void ensureInitialized() const;
262 void ensureChildren() const;
264 qsizetype decompress(char *buffer, qsizetype bufferSize) const;
265
266 bool load(const QString &file);
267 void clear();
268
272 mutable qint64 size;
274 mutable const uchar *data;
278 /* 2 or 6 padding bytes */
279
281 Q_DECLARE_PUBLIC(QResource)
282};
283
285{
288 data = nullptr;
289 size = 0;
290 children.clear();
291 lastModified = 0;
292 container = 0;
293 for (int i = 0; i < related.size(); ++i) {
294 QResourceRoot *root = related.at(i);
295 if (!root->ref.deref())
296 delete root;
297 }
298 related.clear();
299}
300
302{
303 related.clear();
304 const auto locker = qt_scoped_lock(resourceMutex());
305 const ResourceList *list = resourceList();
307 for (int i = 0; i < list->size(); ++i) {
309 const int node = res->findNode(cleaned, locale);
310 if (node != -1) {
311 if (related.isEmpty()) {
312 container = res->isContainer(node);
313 if (!container) {
314 data = res->data(node, &size);
315 compressionAlgo = res->compressionAlgo(node);
316 } else {
317 data = nullptr;
318 size = 0;
320 }
321 lastModified = res->lastModified(node);
322 } else if (res->isContainer(node) != container) {
323 qWarning("QResourceInfo: Resource [%s] has both data and children!",
324 file.toLatin1().constData());
325 }
326 res->ref.ref();
328 } else if (res->mappingRootSubdir(file)) {
329 container = true;
330 data = nullptr;
331 size = 0;
333 lastModified = 0;
334 res->ref.ref();
336 }
337 }
338 return !related.isEmpty();
339}
340
342{
343 if (!related.isEmpty())
344 return;
345 QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
346 if (fileName == ":"_L1)
347 that->fileName += u'/';
349 if (!that->absoluteFilePath.startsWith(u':'))
350 that->absoluteFilePath.prepend(u':');
351
353 if (path.startsWith(u':'))
354 path = path.mid(1);
355
356 if (path.startsWith(u'/')) {
357 that->load(path.toString());
358 } else {
359 // Should we search QDir::searchPath() before falling back to root ?
360 const QString searchPath(u'/' + path);
361 if (that->load(searchPath))
362 that->absoluteFilePath = u':' + searchPath;
363 }
364}
365
367{
369 if (!children.isEmpty() || !container || related.isEmpty())
370 return;
371
373 if (path.startsWith(u':'))
374 path = path.mid(1);
377 for (int i = 0; i < related.size(); ++i) {
379 if (res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
380 if (!kids.hasSeen(k))
381 children += k;
382 } else {
383 const int node = res->findNode(cleaned);
384 if (node != -1) {
385 QStringList related_children = res->children(node);
386 for (int kid = 0; kid < related_children.size(); ++kid) {
387 k = related_children.at(kid);
388 if (!kids.hasSeen(k))
389 children += k;
390 }
391 }
392 }
393 }
394}
395
397{
398 switch (compressionAlgo) {
400 return size;
401
403#ifndef QT_NO_COMPRESS
404 if (size_t(size) >= sizeof(quint32))
405 return qFromBigEndian<quint32>(data);
406#else
407 Q_ASSERT(!"QResource: Qt built without support for Zlib compression");
408 Q_UNREACHABLE();
409#endif
410 break;
411
413#if QT_CONFIG(zstd)
414 size_t n = ZSTD_getFrameContentSize(data, size);
415 return ZSTD_isError(n) ? -1 : qint64(n);
416#else
417 // This should not happen because we've refused to load such resource
418 Q_ASSERT(!"QResource: Qt built without support for Zstd compression");
419 Q_UNREACHABLE();
420#endif
421 }
422 }
423 return -1;
424}
425
427{
428 Q_ASSERT(data);
429#if defined(QT_NO_COMPRESS) && !QT_CONFIG(zstd)
431 Q_UNUSED(bufferSize);
432#endif
433
434 switch (compressionAlgo) {
436 Q_UNREACHABLE();
437 break;
438
440#ifndef QT_NO_COMPRESS
441 uLong len = uLong(bufferSize);
442 int res = ::uncompress(reinterpret_cast<Bytef *>(buffer), &len, data + sizeof(quint32),
443 uLong(size - sizeof(quint32)));
444 if (res != Z_OK) {
445 qWarning("QResource: error decompressing zlib content (%d)", res);
446 return -1;
447 }
448 return len;
449#else
450 Q_UNREACHABLE();
451#endif
452 }
453
455#if QT_CONFIG(zstd)
456 size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
457 if (ZSTD_isError(usize)) {
458 qWarning("QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
459 return -1;
460 }
461 return usize;
462#else
463 Q_UNREACHABLE();
464#endif
465 }
466 }
467
468 return -1;
469}
470
479{
480 Q_D(QResource);
481 d->fileName = file;
482 d->locale = locale;
483}
484
489{
490}
491
500void QResource::setLocale(const QLocale &locale)
501{
502 Q_D(QResource);
503 d->clear();
504 d->locale = locale;
505}
506
512{
513 Q_D(const QResource);
514 return d->locale;
515}
516
526{
527 Q_D(QResource);
528 d->clear();
529 d->fileName = file;
530}
531
540{
541 Q_D(const QResource);
542 d->ensureInitialized();
543 return d->fileName;
544}
545
554{
555 Q_D(const QResource);
556 d->ensureInitialized();
557 return d->absoluteFilePath;
558}
559
567{
568 Q_D(const QResource);
569 d->ensureInitialized();
570 return !d->related.isEmpty();
571}
572
601{
602 Q_D(const QResource);
603 d->ensureInitialized();
604 return Compression(d->compressionAlgo);
605}
606
617{
618 Q_D(const QResource);
619 d->ensureInitialized();
620 return d->size;
621}
622
634{
635 Q_D(const QResource);
636 d->ensureInitialized();
637 return d->uncompressedSize();
638}
639
649const uchar *QResource::data() const
650{
651 Q_D(const QResource);
652 d->ensureInitialized();
653 return d->data;
654}
655
670{
671 Q_D(const QResource);
673 if (n < 0)
674 return QByteArray();
675 if (n > std::numeric_limits<QByteArray::size_type>::max()) {
676 qWarning("QResource: compressed content does not fit into a QByteArray; use QFile instead");
677 return QByteArray();
678 }
679 if (d->compressionAlgo == NoCompression)
680 return QByteArray::fromRawData(reinterpret_cast<const char *>(d->data), n);
681
682 // decompress
684 n = d->decompress(result.data(), n);
685 if (n < 0)
686 result.clear();
687 else
688 result.truncate(n);
689 return result;
690}
691
699{
700 Q_D(const QResource);
701 d->ensureInitialized();
702 return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
703}
704
713{
714 Q_D(const QResource);
715 d->ensureInitialized();
716 return d->container;
717}
718
727{
728 Q_D(const QResource);
729 d->ensureChildren();
730 return d->children;
731}
732
733inline uint QResourceRoot::hash(int node) const
734{
735 if (!node) // root
736 return 0;
737 const int offset = findOffset(node);
738 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
739 name_offset += 2; // jump past name length
740 return qFromBigEndian<quint32>(names + name_offset);
741}
742inline QString QResourceRoot::name(int node) const
743{
744 if (!node) // root
745 return QString();
746 const int offset = findOffset(node);
747
748 QString ret;
749 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
750 quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
751 name_offset += 2;
752 name_offset += 4; // jump past hash
753
754 ret.resize(name_length);
755 QChar *strData = ret.data();
756 qFromBigEndian<char16_t>(names + name_offset, name_length, strData);
757 return ret;
758}
759
760int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
761{
762 QString path = _path;
763 {
764 QString root = mappingRoot();
765 if (!root.isEmpty()) {
766 if (root == path) {
767 path = u'/';
768 } else {
769 if (!root.endsWith(u'/'))
770 root += u'/';
771 if (path.size() >= root.size() && path.startsWith(root))
772 path = path.mid(root.size() - 1);
773 if (path.isEmpty())
774 path = u'/';
775 }
776 }
777 }
778#ifdef DEBUG_RESOURCE_MATCH
779 qDebug() << "!!!!" << "START" << path << locale.territory() << locale.language();
780#endif
781
782 if (path == "/"_L1)
783 return 0;
784
785 // the root node is always first
786 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
787 qint32 child = qFromBigEndian<qint32>(tree + 10);
788
789 // now iterate up the tree
790 int node = -1;
791
792 QStringSplitter splitter(path);
793 while (child_count && splitter.hasNext()) {
794 QStringView segment = splitter.next();
795
796#ifdef DEBUG_RESOURCE_MATCH
797 qDebug() << " CHILDREN" << segment;
798 for (int j = 0; j < child_count; ++j) {
799 qDebug() << " " << child + j << " :: " << name(child + j);
800 }
801#endif
802 const uint h = qt_hash(segment);
803
804 // do the binary search for the hash
805 int l = 0, r = child_count - 1;
806 int sub_node = (l + r + 1) / 2;
807 while (r != l) {
808 const uint sub_node_hash = hash(child + sub_node);
809 if (h == sub_node_hash)
810 break;
811 else if (h < sub_node_hash)
812 r = sub_node - 1;
813 else
814 l = sub_node;
815 sub_node = (l + r + 1) / 2;
816 }
817 sub_node += child;
818
819 // now do the "harder" compares
820 bool found = false;
821 if (hash(sub_node) == h) {
822 while (sub_node > child && hash(sub_node - 1) == h) // backup for collisions
823 --sub_node;
824 for (; sub_node < child + child_count && hash(sub_node) == h;
825 ++sub_node) { // here we go...
826 if (name(sub_node) == segment) {
827 found = true;
828 int offset = findOffset(sub_node);
829#ifdef DEBUG_RESOURCE_MATCH
830 qDebug() << " TRY" << sub_node << name(sub_node) << offset;
831#endif
832 offset += 4; // jump past name
833
834 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
835 offset += 2;
836
837 if (!splitter.hasNext()) {
838 if (!(flags & Directory)) {
839 const qint16 territory = qFromBigEndian<qint16>(tree + offset);
840 offset += 2;
841
842 const qint16 language = qFromBigEndian<qint16>(tree + offset);
843 offset += 2;
844#ifdef DEBUG_RESOURCE_MATCH
845 qDebug() << " " << "LOCALE" << country << language;
846#endif
847 if (territory == locale.territory() && language == locale.language()) {
848#ifdef DEBUG_RESOURCE_MATCH
849 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
850#endif
851 return sub_node;
852 } else if ((territory == QLocale::AnyTerritory
853 && language == locale.language())
854 || (territory == QLocale::AnyTerritory
855 && language == QLocale::C
856 && node == -1)) {
857 node = sub_node;
858 }
859 continue;
860 } else {
861#ifdef DEBUG_RESOURCE_MATCH
862 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
863#endif
864
865 return sub_node;
866 }
867 }
868
869 if (!(flags & Directory))
870 return -1;
871
872 child_count = qFromBigEndian<qint32>(tree + offset);
873 offset += 4;
874 child = qFromBigEndian<qint32>(tree + offset);
875 break;
876 }
877 }
878 }
879 if (!found)
880 break;
881 }
882#ifdef DEBUG_RESOURCE_MATCH
883 qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
884#endif
885 return node;
886}
887short QResourceRoot::flags(int node) const
888{
889 if (node == -1)
890 return 0;
891 const int offset = findOffset(node) + 4; // jump past name
892 return qFromBigEndian<qint16>(tree + offset);
893}
894const uchar *QResourceRoot::data(int node, qint64 *size) const
895{
896 if (node == -1) {
897 *size = 0;
898 return nullptr;
899 }
900 int offset = findOffset(node) + 4; // jump past name
901
902 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
903 offset += 2;
904
905 offset += 4; // jump past locale
906
907 if (!(flags & Directory)) {
908 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
909 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
910 const uchar *ret = payloads + data_offset + 4;
911 *size = data_length;
912 return ret;
913 }
914 *size = 0;
915 return nullptr;
916}
917
919{
920 if (node == -1 || version < 0x02)
921 return 0;
922
923 const int offset = findOffset(node) + 14;
924
925 return qFromBigEndian<quint64>(tree + offset);
926}
927
929{
930 if (node == -1)
931 return QStringList();
932 int offset = findOffset(node) + 4; // jump past name
933
934 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
935 offset += 2;
936
938 if (flags & Directory) {
939 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
940 offset += 4;
941 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
942 ret.reserve(child_count);
943 for (int i = child_off; i < child_off + child_count; ++i)
944 ret << name(i);
945 }
946 return ret;
947}
949{
950 const QString root = mappingRoot();
951 if (root.isEmpty())
952 return false;
953
954 QStringSplitter rootIt(root);
955 QStringSplitter pathIt(path);
956 while (rootIt.hasNext()) {
957 if (pathIt.hasNext()) {
958 if (rootIt.next() != pathIt.next()) // mismatch
959 return false;
960 } else {
961 // end of path, but not of root:
962 if (match)
963 *match = rootIt.next().toString();
964 return true;
965 }
966 }
967 // end of root
968 return !pathIt.hasNext();
969}
970
971Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
972 const unsigned char *name, const unsigned char *data)
973{
974 if (resourceGlobalData.isDestroyed())
975 return false;
976 const auto locker = qt_scoped_lock(resourceMutex());
978 if (version >= 0x01 && version <= 0x3) {
979 bool found = false;
980 QResourceRoot res(version, tree, name, data);
981 for (int i = 0; i < list->size(); ++i) {
982 if (*list->at(i) == res) {
983 found = true;
984 break;
985 }
986 }
987 if (!found) {
988 QResourceRoot *root = new QResourceRoot(version, tree, name, data);
989 root->ref.ref();
990 list->append(root);
991 }
992 return true;
993 }
994 return false;
995}
996
997Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
998 const unsigned char *name, const unsigned char *data)
999{
1000 if (resourceGlobalData.isDestroyed())
1001 return false;
1002
1003 const auto locker = qt_scoped_lock(resourceMutex());
1004 if (version >= 0x01 && version <= 0x3) {
1005 QResourceRoot res(version, tree, name, data);
1007 for (int i = 0; i < list->size();) {
1008 if (*list->at(i) == res) {
1009 QResourceRoot *root = list->takeAt(i);
1010 if (!root->ref.deref())
1011 delete root;
1012 } else {
1013 ++i;
1014 }
1015 }
1016 return true;
1017 }
1018 return false;
1019}
1020
1021// run time resource creation
1022
1024{
1025 QString root;
1026 const uchar *buffer;
1027
1028public:
1029 inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(nullptr) { }
1031 inline const uchar *mappingBuffer() const { return buffer; }
1032 QString mappingRoot() const override { return root; }
1033 ResourceRootType type() const override { return Resource_Buffer; }
1034
1035 // size == -1 means "unknown"
1037 {
1038 // 5 int "pointers"
1039 if (size >= 0 && size < 20)
1040 return false;
1041
1042 // setup the data now
1043 int offset = 0;
1044
1045 // magic number
1046 if (b[offset + 0] != 'q' || b[offset + 1] != 'r' || b[offset + 2] != 'e'
1047 || b[offset + 3] != 's') {
1048 return false;
1049 }
1050 offset += 4;
1051
1052 const int version = qFromBigEndian<qint32>(b + offset);
1053 offset += 4;
1054
1055 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1056 offset += 4;
1057
1058 const int data_offset = qFromBigEndian<qint32>(b + offset);
1059 offset += 4;
1060
1061 const int name_offset = qFromBigEndian<qint32>(b + offset);
1062 offset += 4;
1063
1064 quint32 file_flags = 0;
1065 if (version >= 3) {
1066 file_flags = qFromBigEndian<qint32>(b + offset);
1067 offset += 4;
1068 }
1069
1070 // Some sanity checking for sizes. This is _not_ a security measure.
1071 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1072 return false;
1073
1074 // And some sanity checking for features
1075 quint32 acceptableFlags = 0;
1076#ifndef QT_NO_COMPRESS
1077 acceptableFlags |= Compressed;
1078#endif
1079 if (QT_CONFIG(zstd))
1080 acceptableFlags |= CompressedZstd;
1081 if (file_flags & ~acceptableFlags)
1082 return false;
1083
1084 if (version >= 0x01 && version <= 0x03) {
1085 buffer = b;
1086 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1087 return true;
1088 }
1089 return false;
1090 }
1091};
1092
1094{
1095 QString fileName;
1096 // for mmap'ed files, this is what needs to be unmapped.
1097 uchar *unmapPointer;
1098 qsizetype unmapLength;
1099
1100public:
1102 : QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0)
1103 { }
1105#if defined(QT_USE_MMAP)
1106 if (unmapPointer) {
1107 munmap(reinterpret_cast<char *>(unmapPointer), unmapLength);
1108 unmapPointer = nullptr;
1109 unmapLength = 0;
1110 } else
1111#endif
1112 {
1113 delete[] mappingBuffer();
1114 }
1115 }
1116 QString mappingFile() const { return fileName; }
1117 ResourceRootType type() const override { return Resource_File; }
1118
1119 bool registerSelf(const QString &f);
1120};
1121
1122#ifndef MAP_FILE
1123# define MAP_FILE 0
1124#endif
1125#ifndef MAP_FAILED
1126# define MAP_FAILED reinterpret_cast<void *>(-1)
1127#endif
1128
1130{
1131 bool fromMM = false;
1132 uchar *data = nullptr;
1133 qsizetype data_len = 0;
1134
1135#if defined(QT_USE_MMAP)
1136 int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY);
1137 if (fd >= 0) {
1138 QT_STATBUF st;
1139 if (!QT_FSTAT(fd, &st) && st.st_size <= std::numeric_limits<qsizetype>::max()) {
1140 int protection = PROT_READ; // read-only memory
1141 int flags = MAP_FILE | MAP_PRIVATE; // swap-backed map from file
1142 void *ptr = QT_MMAP(nullptr, st.st_size, // any address, whole file
1143 protection, flags,
1144 fd, 0); // from offset 0 of fd
1145 if (ptr != MAP_FAILED) {
1146 data = static_cast<uchar *>(ptr);
1147 data_len = st.st_size;
1148 fromMM = true;
1149 }
1150 }
1151 QT_CLOSE(fd);
1152 }
1153#endif // QT_USE_MMAP
1154 if (!data) {
1155 QFile file(f);
1156 bool ok = false;
1158 qint64 fsize = file.size();
1159 if (fsize <= std::numeric_limits<qsizetype>::max()) {
1160 data_len = file.size();
1161 data = new uchar[data_len];
1162 ok = (data_len == file.read(reinterpret_cast<char *>(data), data_len));
1163 }
1164 }
1165 if (!ok) {
1166 delete[] data;
1167 data = nullptr;
1168 data_len = 0;
1169 return false;
1170 }
1171 fromMM = false;
1172 }
1174 if (fromMM) {
1175 unmapPointer = data;
1176 unmapLength = data_len;
1177 }
1178 fileName = f;
1179 return true;
1180 }
1181 return false;
1182}
1183
1185{
1186 if (!r.isEmpty()) {
1187 if (r.startsWith(u':'))
1188 r = r.mid(1);
1189 if (!r.isEmpty())
1190 r = QDir::cleanPath(r);
1191 }
1192 return r;
1193}
1194
1205bool QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
1206{
1207 QString r = qt_resource_fixResourceRoot(resourceRoot);
1208 if (!r.isEmpty() && r[0] != u'/') {
1209 qWarning("QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1210 "absolute path (start with /) [%ls]",
1211 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1212 return false;
1213 }
1214
1216 if (root->registerSelf(rccFilename)) {
1217 root->ref.ref();
1218 const auto locker = qt_scoped_lock(resourceMutex());
1219 resourceList()->append(root);
1220 return true;
1221 }
1222 delete root;
1223 return false;
1224}
1225
1237bool QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
1238{
1239 QString r = qt_resource_fixResourceRoot(resourceRoot);
1240
1241 const auto locker = qt_scoped_lock(resourceMutex());
1243 for (int i = 0; i < list->size(); ++i) {
1244 QResourceRoot *res = list->at(i);
1245 if (res->type() == QResourceRoot::Resource_File) {
1246 QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot *>(res);
1247 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1248 list->removeAt(i);
1249 if (!root->ref.deref()) {
1250 delete root;
1251 return true;
1252 }
1253 return false;
1254 }
1255 }
1256 }
1257 return false;
1258}
1259
1274bool QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
1275{
1276 QString r = qt_resource_fixResourceRoot(resourceRoot);
1277 if (!r.isEmpty() && r[0] != u'/') {
1278 qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an "
1279 "absolute path (start with /) [%ls]",
1280 rccData, qUtf16Printable(resourceRoot));
1281 return false;
1282 }
1283
1285 if (root->registerSelf(rccData, -1)) {
1286 root->ref.ref();
1287 const auto locker = qt_scoped_lock(resourceMutex());
1288 resourceList()->append(root);
1289 return true;
1290 }
1291 delete root;
1292 return false;
1293}
1294
1306bool QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
1307{
1308 QString r = qt_resource_fixResourceRoot(resourceRoot);
1309
1310 const auto locker = qt_scoped_lock(resourceMutex());
1312 for (int i = 0; i < list->size(); ++i) {
1313 QResourceRoot *res = list->at(i);
1314 if (res->type() == QResourceRoot::Resource_Buffer) {
1315 QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1316 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1317 list->removeAt(i);
1318 if (!root->ref.deref()) {
1319 delete root;
1320 return true;
1321 }
1322 return false;
1323 }
1324 }
1325 }
1326 return false;
1327}
1328
1329#if !defined(QT_BOOTSTRAPPED)
1330// resource engine
1332{
1333protected:
1334 Q_DECLARE_PUBLIC(QResourceFileEngine)
1335private:
1336 uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1337 bool unmap(uchar *ptr);
1338 void uncompress() const;
1339 qint64 offset;
1340 QResource resource;
1341 mutable QByteArray uncompressed;
1342protected:
1344};
1345
1347{
1348 return true;
1349}
1350
1353{
1355 d->resource.setFileName(file);
1356}
1357
1359{
1360}
1361
1363{
1365 d->resource.setFileName(file);
1366}
1367
1368bool QResourceFileEngine::open(QIODevice::OpenMode flags,
1369 std::optional<QFile::Permissions> permissions)
1370{
1371 Q_UNUSED(permissions);
1372
1374 if (d->resource.fileName().isEmpty()) {
1375 qWarning("QResourceFileEngine::open: Missing file name");
1376 return false;
1377 }
1379 return false;
1380 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1381 d->uncompress();
1382 if (d->uncompressed.isNull()) {
1383 d->errorString = QSystemError::stdString(EIO);
1384 return false;
1385 }
1386 }
1387 if (!d->resource.isValid()) {
1388 d->errorString = QSystemError::stdString(ENOENT);
1389 return false;
1390 }
1391 return true;
1392}
1393
1395{
1397 d->offset = 0;
1398 return true;
1399}
1400
1402{
1403 return true;
1404}
1405
1407{
1409 if (len > size() - d->offset)
1410 len = size() - d->offset;
1411 if (len <= 0)
1412 return 0;
1413 if (!d->uncompressed.isNull())
1414 memcpy(data, d->uncompressed.constData() + d->offset, len);
1415 else
1416 memcpy(data, d->resource.data() + d->offset, len);
1417 d->offset += len;
1418 return len;
1419}
1420
1422{
1423 Q_D(const QResourceFileEngine);
1424 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1425}
1426
1428{
1429 Q_D(const QResourceFileEngine);
1430 return d->offset;
1431}
1432
1434{
1435 Q_D(const QResourceFileEngine);
1436 if (!d->resource.isValid())
1437 return true;
1438 return d->offset == size();
1439}
1440
1442{
1444 if (!d->resource.isValid())
1445 return false;
1446
1447 if (d->offset > size())
1448 return false;
1449 d->offset = pos;
1450 return true;
1451}
1452
1453QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1454{
1455 Q_D(const QResourceFileEngine);
1456 QAbstractFileEngine::FileFlags ret;
1457 if (!d->resource.isValid())
1458 return ret;
1459
1460 if (type & PermsMask)
1461 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1462 | ReadOtherPerm);
1463 if (type & TypesMask) {
1464 if (d->resource.isDir())
1465 ret |= DirectoryType;
1466 else
1467 ret |= FileType;
1468 }
1469 if (type & FlagsMask) {
1470 ret |= ExistsFlag;
1471 if (d->resource.absoluteFilePath() == ":/"_L1)
1472 ret |= RootFlag;
1473 }
1474 return ret;
1475}
1476
1478{
1479 Q_D(const QResourceFileEngine);
1480 if (file == BaseName) {
1481 const qsizetype slash = d->resource.fileName().lastIndexOf(u'/');
1482 if (slash == -1)
1483 return d->resource.fileName();
1484 return d->resource.fileName().mid(slash + 1);
1485 } else if (file == PathName || file == AbsolutePathName) {
1486 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1487 : d->resource.fileName();
1488 const qsizetype slash = path.lastIndexOf(u'/');
1489 if (slash == -1)
1490 return ":"_L1;
1491 else if (slash <= 1)
1492 return ":/"_L1;
1493 return path.left(slash);
1494
1495 } else if (file == CanonicalName || file == CanonicalPathName) {
1496 const QString absoluteFilePath = d->resource.absoluteFilePath();
1497 if (file == CanonicalPathName) {
1498 const qsizetype slash = absoluteFilePath.lastIndexOf(u'/');
1499 if (slash != -1)
1500 return absoluteFilePath.left(slash);
1501 }
1502 return absoluteFilePath;
1503 }
1504 return d->resource.fileName();
1505}
1506
1508{
1509 static const uint nobodyID = static_cast<uint>(-2);
1510 return nobodyID;
1511}
1512
1514{
1515 Q_D(const QResourceFileEngine);
1516 if (time == ModificationTime)
1517 return d->resource.lastModified();
1518 return QDateTime();
1519}
1520
1525 const QStringList &filterNames)
1526{
1527 return new QResourceFileEngineIterator(filters, filterNames);
1528}
1529
1534{
1535 return nullptr;
1536}
1537
1539{
1541 if (extension == MapExtension) {
1542 const auto *options = static_cast<const MapExtensionOption *>(option);
1543 auto *returnValue = static_cast<MapExtensionReturn *>(output);
1544 returnValue->address = d->map(options->offset, options->size, options->flags);
1545 return (returnValue->address != nullptr);
1546 }
1547 if (extension == UnMapExtension) {
1548 const auto *options = static_cast<const UnMapExtensionOption *>(option);
1549 return d->unmap(options->address);
1550 }
1551 return false;
1552}
1553
1555{
1557}
1558
1559uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1560{
1562 Q_UNUSED(flags);
1563
1564 qint64 max = resource.uncompressedSize();
1565 qint64 end;
1566 if (offset < 0 || size <= 0 || !resource.isValid() ||
1567 qAddOverflow(offset, size, &end) || end > max) {
1568 q->setError(QFile::UnspecifiedError, QString());
1569 return nullptr;
1570 }
1571
1572 const uchar *address = resource.data();
1574 uncompress();
1575 if (uncompressed.isNull())
1576 return nullptr;
1577 address = reinterpret_cast<const uchar *>(uncompressed.constData());
1578 }
1579
1580 return const_cast<uchar *>(address) + offset;
1581}
1582
1583bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1584{
1585 Q_UNUSED(ptr);
1586 return true;
1587}
1588
1589void QResourceFileEnginePrivate::uncompress() const
1590{
1592 || !uncompressed.isEmpty() || resource.size() == 0)
1593 return; // nothing to do
1594 uncompressed = resource.uncompressedData();
1595}
1596
1597#endif // !defined(QT_BOOTSTRAPPED)
1598
The QAbstractFileEngineIterator class provides an iterator interface for custom file engines.
\inmodule QtCore \reentrant
FileOwner
\value OwnerUser The user who owns the file.
FileTime
These are used by the fileTime() function.
FileName
These values are used to request a file name in a particular format.
\inmodule QtCore
Definition qatomic.h:112
bool ref() noexcept
bool deref() noexcept
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
\inmodule QtCore
Definition qchar.h:48
\inmodule QtCore\reentrant
Definition qdatetime.h:257
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2395
bool hasSeen(const T &s)
bool registerSelf(const uchar *b, qsizetype size)
QString mappingRoot() const override
QDynamicBufferResourceRoot(const QString &_root)
ResourceRootType type() const override
const uchar * mappingBuffer() const
ResourceRootType type() const override
bool registerSelf(const QString &f)
QDynamicFileResourceRoot(const QString &_root)
QString mappingFile() const
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
qint64 size() const override
\reimp
Definition qfile.cpp:1156
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
void removeAt(qsizetype i)
Definition qlist.h:573
T takeAt(qsizetype i)
Definition qlist.h:592
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
@ AnyTerritory
Definition qlocale.h:558
\inmodule QtCore
Definition qmutex.h:313
bool caseSensitive() const override
Should return true if the underlying file system is case-sensitive; otherwise return false.
bool flush() override
Flushes the open file, returning true if successful; otherwise returns false.
Iterator * beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
qint64 read(char *data, qint64 maxlen) override
Reads a number of characters from the file into data.
bool close() override
Closes the file, returning true if successful; otherwise returns false.
bool open(QIODevice::OpenMode flags, std::optional< QFile::Permissions > permissions) override
Opens the file in the specified mode.
void setFileName(const QString &file) override
Sets the file engine's file name to file.
bool seek(qint64) override
Sets the file position to the given offset.
qint64 pos() const override
Returns the current file position.
QString fileName(QAbstractFileEngine::FileName file) const override
Return the file engine's current file name in the format specified by file.
QResourceFileEngine(const QString &path)
qint64 size() const override
Returns the size of the file.
FileFlags fileFlags(FileFlags type) const override
This function should return the set of OR'd flags that are true for the file engine's file,...
QDateTime fileTime(FileTime time) const override
If time is BirthTime, return when the file was born (created).
bool extension(Extension extension, const ExtensionOption *option=nullptr, ExtensionReturn *output=nullptr) override
Iterator * endEntryList() override
virtual bool atEnd() const
uint ownerId(FileOwner) const override
If owner is OwnerUser return the ID of the user who owns the file.
bool supportsExtension(Extension extension) const override
void ensureChildren() const
qint64 uncompressedSize() const Q_DECL_PURE_FUNCTION
QList< QResourceRoot * > related
QResource * q_ptr
void ensureInitialized() const
QResourcePrivate(QResource *_q)
QString absoluteFilePath
QStringList children
qsizetype decompress(char *buffer, qsizetype bufferSize) const
bool load(const QString &file)
QResource::Compression compressionAlgo(int node)
bool operator==(const QResourceRoot &other) const
QAtomicInt ref
void setSource(int v, const uchar *t, const uchar *n, const uchar *d)
bool operator!=(const QResourceRoot &other) const
virtual QString mappingRoot() const
bool isContainer(int node) const
int findNode(const QString &path, const QLocale &locale=QLocale()) const
virtual ~QResourceRoot()
virtual ResourceRootType type() const
quint64 lastModified(int node) const
QStringList children(int node) const
const uchar * data(int node, qint64 *size) const
bool mappingRootSubdir(const QString &path, QString *match=nullptr) const
QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d)
\inmodule QtCore
Definition qresource.h:20
qint64 uncompressedSize() const
void setFileName(const QString &file)
Sets a QResource to point to file.
QResource(const QString &file=QString(), const QLocale &locale=QLocale())
Constructs a QResource pointing to file.
QStringList children() const
Returns a list of all resources in this directory, if the resource represents a file the list will be...
qint64 size() const
Returns the size of the stored data backing the resource.
const uchar * data() const
Returns direct access to a segment of read-only data, that this resource represents.
@ ZlibCompression
Definition qresource.h:24
@ ZstdCompression
Definition qresource.h:25
@ NoCompression
Definition qresource.h:23
static bool registerResource(const QString &rccFilename, const QString &resourceRoot=QString())
Registers the resource with the given rccFileName at the location in the resource tree specified by m...
QLocale locale() const
Returns the locale used to locate the data for the QResource.
QDateTime lastModified() const
void setLocale(const QLocale &locale)
Sets a QResource to only load the localization of resource to for locale.
QString absoluteFilePath() const
Returns the real path that this QResource represents, if the resource was found via the QDir::searchP...
QString fileName() const
Returns the full path to the file that this QResource represents as it was passed.
bool isValid() const
Returns true if the resource really exists in the resource hierarchy, false otherwise.
~QResource()
Releases the resources of the QResource object.
static bool unregisterResource(const QString &rccFilename, const QString &resourceRoot=QString())
Unregisters the resource with the given rccFileName at the location in the resource tree specified by...
QByteArray uncompressedData() const
bool isDir() const
Returns true if the resource represents a directory and thus may have children() in it,...
Compression compressionAlgorithm() const
\inmodule QtCore
QStringSplitter(QStringView sv)
Definition qresource.cpp:71
QStringView next()
Definition qresource.cpp:83
const QChar * m_data
Definition qresource.cpp:91
qsizetype m_pos
Definition qresource.cpp:93
qsizetype m_len
Definition qresource.cpp:92
\inmodule QtCore
Definition qstringview.h:76
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1014
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
QString & prepend(QChar c)
Definition qstring.h:411
static Q_CORE_EXPORT QString stdString(int errorCode=-1)
void extension()
[6]
Definition dialogs.cpp:230
#define this
Definition dialogs.cpp:9
QHash< int, QWidget * > hash
[35multi]
QMap< QString, QString > map
[6]
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
static QByteArray cleaned(const QByteArray &input)
#define Q_DECL_PURE_FUNCTION
#define QT_OPEN
#define QT_CLOSE
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
uint qt_hash(QStringView key, uint chained) noexcept
Definition qhash.cpp:1146
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
return ret
static ControlElement< T > * ptr(QWidget *widget)
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLuint name
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
GLuint res
GLuint segment
GLuint GLuint * names
GLenum GLsizei len
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLuint GLenum option
GLint fsize
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static ResourceList * resourceList()
#define MAP_FILE
#define RCC_FEATURE_SYMBOL(feature)
Definition qresource.cpp:55
static QRecursiveMutex & resourceMutex()
Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data)
#define MAP_FAILED
static QString qt_resource_fixResourceRoot(QString r)
QList< QResourceRoot * > ResourceList
static QString cleanPath(const QString &_path)
Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data)
#define qUtf16Printable(string)
Definition qstring.h:1403
static QString absoluteFilePath(const Options *options, const QString &relativeFileName)
Definition main.cpp:1791
#define QT_CONFIG(feature)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:145
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:163
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
short qint16
Definition qtypes.h:42
unsigned short quint16
Definition qtypes.h:43
int qint32
Definition qtypes.h:44
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
unsigned char quint8
Definition qtypes.h:41
QT_BEGIN_NAMESPACE typedef uchar * output
QList< int > list
[14]
QFile file
[0]
QObject::connect nullptr
QSharedPointer< T > other(t)
[5]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QLayoutItem * child
[0]
ResourceList resourceList
QRecursiveMutex resourceMutex