1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Copyright (C) 2020 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qresource.h"
42#include "qresource_p.h"
43#include "qresource_iterator_p.h"
44#include "qset.h"
45#include <private/qlocking_p.h>
46#include "qdebug.h"
47#include "qlocale.h"
48#include "qglobal.h"
49#include "qlist.h"
50#include "qdatetime.h"
51#include "qbytearray.h"
52#include "qstringlist.h"
53#include "qendian.h"
54#include <qshareddata.h>
55#include <qplatformdefs.h>
56#include <qendian.h>
57#include "private/qabstractfileengine_p.h"
58#include "private/qduplicatetracker_p.h"
59#include "private/qnumeric_p.h"
60#include "private/qsimd_p.h"
61#include "private/qtools_p.h"
62#include "private/qsystemerror_p.h"
63
64#ifndef QT_NO_COMPRESS
65# include <zconf.h>
66# include <zlib.h>
67#endif
68#if QT_CONFIG(zstd)
69# include <zstd.h>
70#endif
71
72#if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY)
73# define QT_USE_MMAP
74# include <sys/mman.h>
75#endif
76
77//#define DEBUG_RESOURCE_MATCH
78
79QT_BEGIN_NAMESPACE
80
81// Symbols used by code generated by RCC.
82// They cause compilation errors if the RCC content couldn't
83// be interpreted by this QtCore version.
84#if defined(__ELF__) || defined(__APPLE__) // same as RCC generates
85# define RCC_FEATURE_SYMBOL(feature) \
86 extern Q_CORE_EXPORT const quint8 qt_resourceFeature ## feature; \
87 const quint8 qt_resourceFeature ## feature = 0;
88#else
89# define RCC_FEATURE_SYMBOL(feature) \
90 Q_CORE_EXPORT quint8 qResourceFeature ## feature() { return 0; }
91#endif
92
93#ifndef QT_NO_COMPRESS
94RCC_FEATURE_SYMBOL(Zlib)
95#endif
96#if QT_CONFIG(zstd)
97RCC_FEATURE_SYMBOL(Zstd)
98#endif
99
100#undef RCC_FEATURE_SYMBOL
101
102class QStringSplitter
103{
104public:
105 explicit QStringSplitter(QStringView sv)
106 : m_data(sv.data()), m_len(sv.size())
107 {
108 }
109
110 inline bool hasNext()
111 {
112 while (m_pos < m_len && m_data[m_pos] == m_splitChar)
113 ++m_pos;
114 return m_pos < m_len;
115 }
116
117 inline QStringView next()
118 {
119 int start = m_pos;
120 while (m_pos < m_len && m_data[m_pos] != m_splitChar)
121 ++m_pos;
122 return QStringView(m_data + start, m_pos - start);
123 }
124
125 const QChar *m_data;
126 qsizetype m_len;
127 qsizetype m_pos = 0;
128 QChar m_splitChar = QLatin1Char('/');
129};
130
131// resource glue
132class QResourceRoot
133{
134public:
135 enum Flags {
136 // must match rcc.h
137 Compressed = 0x01,
138 Directory = 0x02,
139 CompressedZstd = 0x04
140 };
141
142private:
143 const uchar *tree, *names, *payloads;
144 int version;
145 inline int findOffset(int node) const { return node * (14 + (version >= 0x02 ? 8 : 0)); } //sizeof each tree element
146 uint hash(int node) const;
147 QString name(int node) const;
148 short flags(int node) const;
149public:
150 mutable QAtomicInt ref;
151
152 inline QResourceRoot(): tree(nullptr), names(nullptr), payloads(nullptr), version(0) {}
153 inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); }
154 virtual ~QResourceRoot() { }
155 int findNode(const QString &path, const QLocale &locale=QLocale()) const;
156 inline bool isContainer(int node) const { return flags(node) & Directory; }
157 QResource::Compression compressionAlgo(int node)
158 {
159 uint compressionFlags = flags(node) & (Compressed | CompressedZstd);
160 if (compressionFlags == Compressed)
161 return QResource::ZlibCompression;
162 if (compressionFlags == CompressedZstd)
163 return QResource::ZstdCompression;
164 return QResource::NoCompression;
165 }
166 const uchar *data(int node, qint64 *size) const;
167 quint64 lastModified(int node) const;
168 QStringList children(int node) const;
169 virtual QString mappingRoot() const { return QString(); }
170 bool mappingRootSubdir(const QString &path, QString *match = nullptr) const;
171 inline bool operator==(const QResourceRoot &other) const
172 { return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
173 inline bool operator!=(const QResourceRoot &other) const
174 { return !operator==(other); }
175 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
176 virtual ResourceRootType type() const { return Resource_Builtin; }
177
178protected:
179 inline void setSource(int v, const uchar *t, const uchar *n, const uchar *d) {
180 tree = t;
181 names = n;
182 payloads = d;
183 version = v;
184 }
185};
186
187static QString cleanPath(const QString &_path)
188{
189 QString path = QDir::cleanPath(_path);
190 // QDir::cleanPath does not remove two trailing slashes under _Windows_
191 // due to support for UNC paths. Remove those manually.
192 if (path.startsWith(QLatin1String("//")))
193 path.remove(0, 1);
194 return path;
195}
196
197Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE);
198
199typedef QList<QResourceRoot*> ResourceList;
200struct QResourceGlobalData
201{
202 QRecursiveMutex resourceMutex;
203 ResourceList resourceList;
204 QStringList resourceSearchPaths;
205};
206Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
207
208static inline QRecursiveMutex &resourceMutex()
209{ return resourceGlobalData->resourceMutex; }
210
211static inline ResourceList *resourceList()
212{ return &resourceGlobalData->resourceList; }
213
214static inline QStringList *resourceSearchPaths()
215{ return &resourceGlobalData->resourceSearchPaths; }
216
217/*!
218 \class QResource
219 \inmodule QtCore
220 \brief The QResource class provides an interface for reading directly from resources.
221
222 \ingroup io
223
224 \reentrant
225 \since 4.2
226
227 QResource is an object that represents a set of data (and possibly
228 children) relating to a single resource entity. QResource gives direct
229 access to the bytes in their raw format. In this way direct access
230 allows reading data without buffer copying or indirection. Indirection
231 is often useful when interacting with the resource entity as if it is a
232 file, this can be achieved with QFile. The data and children behind a
233 QResource are normally compiled into an application/library, but it is
234 also possible to load a resource at runtime. When loaded at run time
235 the resource file will be loaded as one big set of data and then given
236 out in pieces via references into the resource tree.
237
238 A QResource can either be loaded with an absolute path, either treated
239 as a file system rooted with a \c{/} character, or in resource notation
240 rooted with a \c{:} character. A relative resource can also be opened
241 which will be found in the list of paths returned by QDir::searchPaths().
242
243 A QResource that is representing a file will have data backing it, this
244 data can possibly be compressed, in which case qUncompress() must be
245 used to access the real data; this happens implicitly when accessed
246 through a QFile. A QResource that is representing a directory will have
247 only children and no data.
248
249 \section1 Dynamic Resource Loading
250
251 A resource can be left out of an application's binary and loaded when
252 it is needed at run-time by using the registerResource() function. The
253 resource file passed into registerResource() must be a binary resource
254 as created by rcc. Further information about binary resources can be
255 found in \l{The Qt Resource System} documentation.
256
257 This can often be useful when loading a large set of application icons
258 that may change based on a setting, or that can be edited by a user and
259 later recreated. The resource is immediately loaded into memory, either
260 as a result of a single file read operation, or as a memory mapped file.
261
262 This approach can prove to be a significant performance gain as only a
263 single file will be loaded, and pieces of data will be given out via the
264 path requested in setFileName().
265
266 The unregisterResource() function removes a reference to a particular
267 file. If there are QResource objects that currently reference resources related
268 to the unregistered file, they will continue to be valid but the resource
269 file itself will be removed from the resource roots, and thus no further
270 QResource can be created pointing into this resource data. The resource
271 itself will be unmapped from memory when the last QResource that points
272 to it is destroyed.
273
274 \sa {The Qt Resource System}, QFile, QDir, QFileInfo
275*/
276
277/*!
278 \enum QResource::Compression
279 \since 5.13
280
281 This enum is used by compressionAlgorithm() to indicate which algorithm the
282 RCC tool used to compress the payload.
283
284 \value NoCompression Contents are not compressed
285 \value ZlibCompression Contents are compressed using \l{https://zlib.net}{zlib} and can
286 be decompressed using the qUncompress() function.
287 \value ZstdCompression Contents are compressed using \l{https://zstd.net}{zstd}. To
288 decompress, use the \c{ZSTD_decompress} function from the zstd
289 library.
290
291 \sa compressionAlgorithm()
292*/
293
294class QResourcePrivate {
295public:
296 inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
297 inline ~QResourcePrivate() { clear(); }
298
299 void ensureInitialized() const;
300 void ensureChildren() const;
301 qint64 uncompressedSize() const Q_DECL_PURE_FUNCTION;
302 qsizetype decompress(char *buffer, qsizetype bufferSize) const;
303
304 bool load(const QString &file);
305 void clear();
306
307 QLocale locale;
308 QString fileName, absoluteFilePath;
309 QList<QResourceRoot *> related;
310 mutable qint64 size;
311 mutable quint64 lastModified;
312 mutable const uchar *data;
313 mutable QStringList children;
314 mutable quint8 compressionAlgo;
315 bool container;
316 /* 2 or 6 padding bytes */
317
318 QResource *q_ptr;
319 Q_DECLARE_PUBLIC(QResource)
320};
321
322void QResourcePrivate::clear()
323{
324 absoluteFilePath.clear();
325 compressionAlgo = QResource::NoCompression;
326 data = nullptr;
327 size = 0;
328 children.clear();
329 lastModified = 0;
330 container = 0;
331 for (int i = 0; i < related.size(); ++i) {
332 QResourceRoot *root = related.at(i);
333 if (!root->ref.deref())
334 delete root;
335 }
336 related.clear();
337}
338
339bool QResourcePrivate::load(const QString &file)
340{
341 related.clear();
342 const auto locker = qt_scoped_lock(resourceMutex());
343 const ResourceList *list = resourceList();
344 QString cleaned = cleanPath(file);
345 for (int i = 0; i < list->size(); ++i) {
346 QResourceRoot *res = list->at(i);
347 const int node = res->findNode(cleaned, locale);
348 if (node != -1) {
349 if (related.isEmpty()) {
350 container = res->isContainer(node);
351 if (!container) {
352 data = res->data(node, &size);
353 compressionAlgo = res->compressionAlgo(node);
354 } else {
355 data = nullptr;
356 size = 0;
357 compressionAlgo = QResource::NoCompression;
358 }
359 lastModified = res->lastModified(node);
360 } else if (res->isContainer(node) != container) {
361 qWarning("QResourceInfo: Resource [%s] has both data and children!",
362 file.toLatin1().constData());
363 }
364 res->ref.ref();
365 related.append(res);
366 } else if (res->mappingRootSubdir(file)) {
367 container = true;
368 data = nullptr;
369 size = 0;
370 compressionAlgo = QResource::NoCompression;
371 lastModified = 0;
372 res->ref.ref();
373 related.append(res);
374 }
375 }
376 return !related.isEmpty();
377}
378
379void QResourcePrivate::ensureInitialized() const
380{
381 if (!related.isEmpty())
382 return;
383 QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
384 if (fileName == QLatin1String(":"))
385 that->fileName += QLatin1Char('/');
386 that->absoluteFilePath = fileName;
387 if (!that->absoluteFilePath.startsWith(QLatin1Char(':')))
388 that->absoluteFilePath.prepend(QLatin1Char(':'));
389
390 QStringView path(fileName);
391 if (path.startsWith(QLatin1Char(':')))
392 path = path.mid(1);
393
394 if (path.startsWith(QLatin1Char('/'))) {
395 that->load(path.toString());
396 } else {
397 const auto locker = qt_scoped_lock(resourceMutex());
398 QStringList searchPaths = *resourceSearchPaths();
399 searchPaths << QLatin1String("");
400 for (int i = 0; i < searchPaths.size(); ++i) {
401 const QString searchPath(searchPaths.at(i) + QLatin1Char('/') + path);
402 if (that->load(searchPath)) {
403 that->absoluteFilePath = QLatin1Char(':') + searchPath;
404 break;
405 }
406 }
407 }
408}
409
410void QResourcePrivate::ensureChildren() const
411{
412 ensureInitialized();
413 if (!children.isEmpty() || !container || related.isEmpty())
414 return;
415
416 QString path = absoluteFilePath, k;
417 if (path.startsWith(QLatin1Char(':')))
418 path = path.mid(1);
419 QDuplicateTracker<QString> kids;
420 kids.reserve(related.size());
421 QString cleaned = cleanPath(path);
422 for (int i = 0; i < related.size(); ++i) {
423 QResourceRoot *res = related.at(i);
424 if (res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
425 if (!kids.hasSeen(k))
426 children += k;
427 } else {
428 const int node = res->findNode(cleaned);
429 if (node != -1) {
430 QStringList related_children = res->children(node);
431 for (int kid = 0; kid < related_children.size(); ++kid) {
432 k = related_children.at(kid);
433 if (!kids.hasSeen(k))
434 children += k;
435 }
436 }
437 }
438 }
439}
440
441qint64 QResourcePrivate::uncompressedSize() const
442{
443 switch (compressionAlgo) {
444 case QResource::NoCompression:
445 return size;
446
447 case QResource::ZlibCompression:
448#ifndef QT_NO_COMPRESS
449 if (size_t(size) >= sizeof(quint32))
450 return qFromBigEndian<quint32>(data);
451#else
452 Q_ASSERT(!"QResource: Qt built without support for Zlib compression");
453 Q_UNREACHABLE();
454#endif
455 break;
456
457 case QResource::ZstdCompression: {
458#if QT_CONFIG(zstd)
459 size_t n = ZSTD_getFrameContentSize(data, size);
460 return ZSTD_isError(n) ? -1 : qint64(n);
461#else
462 // This should not happen because we've refused to load such resource
463 Q_ASSERT(!"QResource: Qt built without support for Zstd compression");
464 Q_UNREACHABLE();
465#endif
466 }
467 }
468 return -1;
469}
470
471qsizetype QResourcePrivate::decompress(char *buffer, qsizetype bufferSize) const
472{
473 Q_ASSERT(data);
474
475 switch (compressionAlgo) {
476 case QResource::NoCompression:
477 Q_UNREACHABLE();
478 break;
479
480 case QResource::ZlibCompression: {
481#ifndef QT_NO_COMPRESS
482 uLong len = uLong(bufferSize);
483 int res = ::uncompress(reinterpret_cast<Bytef *>(buffer), &len, data + sizeof(quint32),
484 uLong(size - sizeof(quint32)));
485 if (res != Z_OK) {
486 qWarning("QResource: error decompressing zlib content (%d)", res);
487 return -1;
488 }
489 return len;
490#else
491 Q_UNREACHABLE();
492#endif
493 }
494
495 case QResource::ZstdCompression: {
496#if QT_CONFIG(zstd)
497 size_t usize = ZSTD_decompress(buffer, bufferSize, data, size);
498 if (ZSTD_isError(usize)) {
499 qWarning("QResource: error decompressing zstd content: %s", ZSTD_getErrorName(usize));
500 return -1;
501 }
502 return usize;
503#else
504 Q_UNREACHABLE();
505#endif
506 }
507 }
508
509 return -1;
510}
511
512/*!
513 Constructs a QResource pointing to \a file. \a locale is used to
514 load a specific localization of a resource data.
515
516 \sa QFileInfo, QDir::searchPaths(), setFileName(), setLocale()
517*/
518
519QResource::QResource(const QString &file, const QLocale &locale) : d_ptr(new QResourcePrivate(this))
520{
521 Q_D(QResource);
522 d->fileName = file;
523 d->locale = locale;
524}
525
526/*!
527 Releases the resources of the QResource object.
528*/
529QResource::~QResource()
530{
531}
532
533/*!
534 Sets a QResource to only load the localization of resource to for \a
535 locale. If a resource for the specific locale is not found then the
536 C locale is used.
537
538 \sa setFileName()
539*/
540
541void QResource::setLocale(const QLocale &locale)
542{
543 Q_D(QResource);
544 d->clear();
545 d->locale = locale;
546}
547
548/*!
549 Returns the locale used to locate the data for the QResource.
550*/
551
552QLocale QResource::locale() const
553{
554 Q_D(const QResource);
555 return d->locale;
556}
557
558/*!
559 Sets a QResource to point to \a file. \a file can either be absolute,
560 in which case it is opened directly, if relative then the file will be
561 tried to be found in QDir::searchPaths().
562
563 \sa absoluteFilePath()
564*/
565
566void QResource::setFileName(const QString &file)
567{
568 Q_D(QResource);
569 d->clear();
570 d->fileName = file;
571}
572
573/*!
574 Returns the full path to the file that this QResource represents as it
575 was passed.
576
577 \sa absoluteFilePath()
578*/
579
580QString QResource::fileName() const
581{
582 Q_D(const QResource);
583 d->ensureInitialized();
584 return d->fileName;
585}
586
587/*!
588 Returns the real path that this QResource represents, if the resource
589 was found via the QDir::searchPaths() it will be indicated in the path.
590
591 \sa fileName()
592*/
593
594QString QResource::absoluteFilePath() const
595{
596 Q_D(const QResource);
597 d->ensureInitialized();
598 return d->absoluteFilePath;
599}
600
601/*!
602 Returns \c true if the resource really exists in the resource hierarchy,
603 false otherwise.
604
605*/
606
607bool QResource::isValid() const
608{
609 Q_D(const QResource);
610 d->ensureInitialized();
611 return !d->related.isEmpty();
612}
613
614/*!
615 \fn bool QResource::isFile() const
616
617 Returns \c true if the resource represents a file and thus has data
618 backing it, false if it represents a directory.
619
620 \sa isDir()
621*/
622
623/*!
624 \since 5.13
625
626 Returns the compression type that this resource is compressed with, if any.
627 If it is not compressed, this function returns QResource::NoCompression.
628
629 If this function returns QResource::ZlibCompression, you may decompress the
630 data using the qUncompress() function. Up until Qt 5.13, this was the only
631 possible compression algorithm.
632
633 If this function returns QResource::ZstdCompression, you need to use the
634 Zstandard library functios (\c{<zstd.h> header). Qt does not provide a
635 wrapper.
636
637 See \l{http://facebook.github.io/zstd/zstd_manual.html}{Zstandard manual}.
638
639 \sa data(), isFile()
640*/
641QResource::Compression QResource::compressionAlgorithm() const
642{
643 Q_D(const QResource);
644 d->ensureInitialized();
645 return Compression(d->compressionAlgo);
646}
647
648/*!
649 Returns the size of the stored data backing the resource.
650
651 If the resource is compressed, this function returns the size of the
652 compressed data. See uncompressedSize() for the uncompressed size.
653
654 \sa data(), uncompressedSize(), isFile()
655*/
656
657qint64 QResource::size() const
658{
659 Q_D(const QResource);
660 d->ensureInitialized();
661 return d->size;
662}
663
664/*!
665 \since 5.15
666
667 Returns the size of the data in this resource. If the data was not
668 compressed, this function returns the same as size(). If it was, then this
669 function extracts the size of the original uncompressed data from the
670 stored stream.
671
672 \sa size(), uncompressedData(), isFile()
673*/
674qint64 QResource::uncompressedSize() const
675{
676 Q_D(const QResource);
677 d->ensureInitialized();
678 return d->uncompressedSize();
679}
680
681/*!
682 Returns direct access to a segment of read-only data, that this resource
683 represents. If the resource is compressed, the data returned is also
684 compressed. The caller must then decompress the data or use
685 uncompressedData(). If the resource is a directory, \c nullptr is returned.
686
687 \sa uncompressedData(), size(), isFile()
688*/
689
690const uchar *QResource::data() const
691{
692 Q_D(const QResource);
693 d->ensureInitialized();
694 return d->data;
695}
696
697/*!
698 \since 5.15
699
700 Returns the resource data, decompressing it first, if the data was stored
701 compressed. If the resource is a directory or an error occurs while
702 decompressing, a null QByteArray is returned.
703
704 \note If the data was compressed, this function will decompress every time
705 it is called. The result is not cached between calls.
706
707 \sa uncompressedSize(), size(), compressionAlgorithm(), isFile()
708*/
709
710QByteArray QResource::uncompressedData() const
711{
712 Q_D(const QResource);
713 qint64 n = uncompressedSize();
714 if (n < 0)
715 return QByteArray();
716 if (n > std::numeric_limits<QByteArray::size_type>::max()) {
717 qWarning("QResource: compressed content does not fit into a QByteArray; use QFile instead");
718 return QByteArray();
719 }
720 if (d->compressionAlgo == NoCompression)
721 return QByteArray::fromRawData(reinterpret_cast<const char *>(d->data), n);
722
723 // decompress
724 QByteArray result(n, Qt::Uninitialized);
725 n = d->decompress(result.data(), n);
726 if (n < 0)
727 result.clear();
728 else
729 result.truncate(n);
730 return result;
731}
732
733/*!
734 \since 5.8
735
736 Returns the date and time when the file was last modified before
737 packaging into a resource.
738*/
739QDateTime QResource::lastModified() const
740{
741 Q_D(const QResource);
742 d->ensureInitialized();
743 return d->lastModified ? QDateTime::fromMSecsSinceEpoch(d->lastModified) : QDateTime();
744}
745
746/*!
747 Returns \c true if the resource represents a directory and thus may have
748 children() in it, false if it represents a file.
749
750 \sa isFile()
751*/
752
753bool QResource::isDir() const
754{
755 Q_D(const QResource);
756 d->ensureInitialized();
757 return d->container;
758}
759
760/*!
761 Returns a list of all resources in this directory, if the resource
762 represents a file the list will be empty.
763
764 \sa isDir()
765*/
766
767QStringList QResource::children() const
768{
769 Q_D(const QResource);
770 d->ensureChildren();
771 return d->children;
772}
773
774inline uint QResourceRoot::hash(int node) const
775{
776 if (!node) // root
777 return 0;
778 const int offset = findOffset(node);
779 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
780 name_offset += 2; // jump past name length
781 return qFromBigEndian<quint32>(names + name_offset);
782}
783inline QString QResourceRoot::name(int node) const
784{
785 if (!node) // root
786 return QString();
787 const int offset = findOffset(node);
788
789 QString ret;
790 qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
791 quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
792 name_offset += 2;
793 name_offset += 4; // jump past hash
794
795 ret.resize(name_length);
796 QChar *strData = ret.data();
797 qFromBigEndian<ushort>(names + name_offset, name_length, strData);
798 return ret;
799}
800
801int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
802{
803 QString path = _path;
804 {
805 QString root = mappingRoot();
806 if (!root.isEmpty()) {
807 if (root == path) {
808 path = QLatin1Char('/');
809 } else {
810 if (!root.endsWith(QLatin1Char('/')))
811 root += QLatin1Char('/');
812 if (path.size() >= root.size() && path.startsWith(root))
813 path = path.mid(root.length() - 1);
814 if (path.isEmpty())
815 path = QLatin1Char('/');
816 }
817 }
818 }
819#ifdef DEBUG_RESOURCE_MATCH
820 qDebug() << "!!!!" << "START" << path << locale.country() << locale.language();
821#endif
822
823 if (path == QLatin1String("/"))
824 return 0;
825
826 // the root node is always first
827 qint32 child_count = qFromBigEndian<qint32>(tree + 6);
828 qint32 child = qFromBigEndian<qint32>(tree + 10);
829
830 // now iterate up the tree
831 int node = -1;
832
833 QStringSplitter splitter(path);
834 while (child_count && splitter.hasNext()) {
835 QStringView segment = splitter.next();
836
837#ifdef DEBUG_RESOURCE_MATCH
838 qDebug() << " CHILDREN" << segment;
839 for (int j = 0; j < child_count; ++j) {
840 qDebug() << " " << child + j << " :: " << name(child + j);
841 }
842#endif
843 const uint h = qt_hash(segment);
844
845 // do the binary search for the hash
846 int l = 0, r = child_count - 1;
847 int sub_node = (l + r + 1) / 2;
848 while (r != l) {
849 const uint sub_node_hash = hash(child + sub_node);
850 if (h == sub_node_hash)
851 break;
852 else if (h < sub_node_hash)
853 r = sub_node - 1;
854 else
855 l = sub_node;
856 sub_node = (l + r + 1) / 2;
857 }
858 sub_node += child;
859
860 // now do the "harder" compares
861 bool found = false;
862 if (hash(sub_node) == h) {
863 while (sub_node > child && hash(sub_node - 1) == h) // backup for collisions
864 --sub_node;
865 for (; sub_node < child + child_count && hash(sub_node) == h;
866 ++sub_node) { // here we go...
867 if (name(sub_node) == segment) {
868 found = true;
869 int offset = findOffset(sub_node);
870#ifdef DEBUG_RESOURCE_MATCH
871 qDebug() << " TRY" << sub_node << name(sub_node) << offset;
872#endif
873 offset += 4; // jump past name
874
875 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
876 offset += 2;
877
878 if (!splitter.hasNext()) {
879 if (!(flags & Directory)) {
880 const qint16 country = qFromBigEndian<qint16>(tree + offset);
881 offset += 2;
882
883 const qint16 language = qFromBigEndian<qint16>(tree + offset);
884 offset += 2;
885#ifdef DEBUG_RESOURCE_MATCH
886 qDebug() << " " << "LOCALE" << country << language;
887#endif
888 if (country == locale.country() && language == locale.language()) {
889#ifdef DEBUG_RESOURCE_MATCH
890 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
891#endif
892 return sub_node;
893 } else if ((country == QLocale::AnyCountry
894 && language == locale.language())
895 || (country == QLocale::AnyCountry && language == QLocale::C
896 && node == -1)) {
897 node = sub_node;
898 }
899 continue;
900 } else {
901#ifdef DEBUG_RESOURCE_MATCH
902 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
903#endif
904
905 return sub_node;
906 }
907 }
908
909 if (!(flags & Directory))
910 return -1;
911
912 child_count = qFromBigEndian<qint32>(tree + offset);
913 offset += 4;
914 child = qFromBigEndian<qint32>(tree + offset);
915 break;
916 }
917 }
918 }
919 if (!found)
920 break;
921 }
922#ifdef DEBUG_RESOURCE_MATCH
923 qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
924#endif
925 return node;
926}
927short QResourceRoot::flags(int node) const
928{
929 if (node == -1)
930 return 0;
931 const int offset = findOffset(node) + 4; // jump past name
932 return qFromBigEndian<qint16>(tree + offset);
933}
934const uchar *QResourceRoot::data(int node, qint64 *size) const
935{
936 if (node == -1) {
937 *size = 0;
938 return nullptr;
939 }
940 int offset = findOffset(node) + 4; // jump past name
941
942 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
943 offset += 2;
944
945 offset += 4; // jump past locale
946
947 if (!(flags & Directory)) {
948 const qint32 data_offset = qFromBigEndian<qint32>(tree + offset);
949 const quint32 data_length = qFromBigEndian<quint32>(payloads + data_offset);
950 const uchar *ret = payloads + data_offset + 4;
951 *size = data_length;
952 return ret;
953 }
954 *size = 0;
955 return nullptr;
956}
957
958quint64 QResourceRoot::lastModified(int node) const
959{
960 if (node == -1 || version < 0x02)
961 return 0;
962
963 const int offset = findOffset(node) + 14;
964
965 return qFromBigEndian<quint64>(tree + offset);
966}
967
968QStringList QResourceRoot::children(int node) const
969{
970 if (node == -1)
971 return QStringList();
972 int offset = findOffset(node) + 4; // jump past name
973
974 const qint16 flags = qFromBigEndian<qint16>(tree + offset);
975 offset += 2;
976
977 QStringList ret;
978 if (flags & Directory) {
979 const qint32 child_count = qFromBigEndian<qint32>(tree + offset);
980 offset += 4;
981 const qint32 child_off = qFromBigEndian<qint32>(tree + offset);
982 ret.reserve(child_count);
983 for (int i = child_off; i < child_off + child_count; ++i)
984 ret << name(i);
985 }
986 return ret;
987}
988bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
989{
990 const QString root = mappingRoot();
991 if (root.isEmpty())
992 return false;
993
994 QStringSplitter rootIt(root);
995 QStringSplitter pathIt(path);
996 while (rootIt.hasNext()) {
997 if (pathIt.hasNext()) {
998 if (rootIt.next() != pathIt.next()) // mismatch
999 return false;
1000 } else {
1001 // end of path, but not of root:
1002 if (match)
1003 *match = rootIt.next().toString();
1004 return true;
1005 }
1006 }
1007 // end of root
1008 return !pathIt.hasNext();
1009}
1010
1011Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
1012 const unsigned char *name, const unsigned char *data)
1013{
1014 if (resourceGlobalData.isDestroyed())
1015 return false;
1016 const auto locker = qt_scoped_lock(resourceMutex());
1017 ResourceList *list = resourceList();
1018 if (version >= 0x01 && version <= 0x3) {
1019 bool found = false;
1020 QResourceRoot res(version, tree, name, data);
1021 for (int i = 0; i < list->size(); ++i) {
1022 if (*list->at(i) == res) {
1023 found = true;
1024 break;
1025 }
1026 }
1027 if (!found) {
1028 QResourceRoot *root = new QResourceRoot(version, tree, name, data);
1029 root->ref.ref();
1030 list->append(root);
1031 }
1032 return true;
1033 }
1034 return false;
1035}
1036
1037Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
1038 const unsigned char *name, const unsigned char *data)
1039{
1040 if (resourceGlobalData.isDestroyed())
1041 return false;
1042
1043 const auto locker = qt_scoped_lock(resourceMutex());
1044 if (version >= 0x01 && version <= 0x3) {
1045 QResourceRoot res(version, tree, name, data);
1046 ResourceList *list = resourceList();
1047 for (int i = 0; i < list->size();) {
1048 if (*list->at(i) == res) {
1049 QResourceRoot *root = list->takeAt(i);
1050 if (!root->ref.deref())
1051 delete root;
1052 } else {
1053 ++i;
1054 }
1055 }
1056 return true;
1057 }
1058 return false;
1059}
1060
1061// run time resource creation
1062
1063class QDynamicBufferResourceRoot : public QResourceRoot
1064{
1065 QString root;
1066 const uchar *buffer;
1067
1068public:
1069 inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(nullptr) { }
1070 inline ~QDynamicBufferResourceRoot() { }
1071 inline const uchar *mappingBuffer() const { return buffer; }
1072 QString mappingRoot() const override { return root; }
1073 ResourceRootType type() const override { return Resource_Buffer; }
1074
1075 // size == -1 means "unknown"
1076 bool registerSelf(const uchar *b, qsizetype size)
1077 {
1078 // 5 int "pointers"
1079 if (size >= 0 && size < 20)
1080 return false;
1081
1082 // setup the data now
1083 int offset = 0;
1084
1085 // magic number
1086 if (b[offset + 0] != 'q' || b[offset + 1] != 'r' || b[offset + 2] != 'e'
1087 || b[offset + 3] != 's') {
1088 return false;
1089 }
1090 offset += 4;
1091
1092 const int version = qFromBigEndian<qint32>(b + offset);
1093 offset += 4;
1094
1095 const int tree_offset = qFromBigEndian<qint32>(b + offset);
1096 offset += 4;
1097
1098 const int data_offset = qFromBigEndian<qint32>(b + offset);
1099 offset += 4;
1100
1101 const int name_offset = qFromBigEndian<qint32>(b + offset);
1102 offset += 4;
1103
1104 quint32 file_flags = 0;
1105 if (version >= 3) {
1106 file_flags = qFromBigEndian<qint32>(b + offset);
1107 offset += 4;
1108 }
1109
1110 // Some sanity checking for sizes. This is _not_ a security measure.
1111 if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size))
1112 return false;
1113
1114 // And some sanity checking for features
1115 quint32 acceptableFlags = 0;
1116#ifndef QT_NO_COMPRESS
1117 acceptableFlags |= Compressed;
1118#endif
1119 if (QT_CONFIG(zstd))
1120 acceptableFlags |= CompressedZstd;
1121 if (file_flags & ~acceptableFlags)
1122 return false;
1123
1124 if (version >= 0x01 && version <= 0x03) {
1125 buffer = b;
1126 setSource(version, b + tree_offset, b + name_offset, b + data_offset);
1127 return true;
1128 }
1129 return false;
1130 }
1131};
1132
1133class QDynamicFileResourceRoot : public QDynamicBufferResourceRoot
1134{
1135 QString fileName;
1136 // for mmap'ed files, this is what needs to be unmapped.
1137 uchar *unmapPointer;
1138 qsizetype unmapLength;
1139
1140public:
1141 QDynamicFileResourceRoot(const QString &_root)
1142 : QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0)
1143 { }
1144 ~QDynamicFileResourceRoot() {
1145#if defined(QT_USE_MMAP)
1146 if (unmapPointer) {
1147 munmap(reinterpret_cast<char *>(unmapPointer), unmapLength);
1148 unmapPointer = nullptr;
1149 unmapLength = 0;
1150 } else
1151#endif
1152 {
1153 delete[] mappingBuffer();
1154 }
1155 }
1156 QString mappingFile() const { return fileName; }
1157 ResourceRootType type() const override { return Resource_File; }
1158
1159 bool registerSelf(const QString &f);
1160};
1161
1162#ifndef MAP_FILE
1163# define MAP_FILE 0
1164#endif
1165#ifndef MAP_FAILED
1166# define MAP_FAILED reinterpret_cast<void *>(-1)
1167#endif
1168
1169bool QDynamicFileResourceRoot::registerSelf(const QString &f)
1170{
1171 bool fromMM = false;
1172 uchar *data = nullptr;
1173 qsizetype data_len = 0;
1174
1175#if defined(QT_USE_MMAP)
1176 int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY);
1177 if (fd >= 0) {
1178 QT_STATBUF st;
1179 if (!QT_FSTAT(fd, &st) && st.st_size <= std::numeric_limits<qsizetype>::max()) {
1180 int protection = PROT_READ; // read-only memory
1181 int flags = MAP_FILE | MAP_PRIVATE; // swap-backed map from file
1182 void *ptr = QT_MMAP(nullptr, st.st_size, // any address, whole file
1183 protection, flags,
1184 fd, 0); // from offset 0 of fd
1185 if (ptr != MAP_FAILED) {
1186 data = static_cast<uchar *>(ptr);
1187 data_len = st.st_size;
1188 fromMM = true;
1189 }
1190 }
1191 QT_CLOSE(fd);
1192 }
1193#endif // QT_USE_MMAP
1194 if (!data) {
1195 QFile file(f);
1196 bool ok = false;
1197 if (file.open(QIODevice::ReadOnly)) {
1198 qint64 fsize = file.size();
1199 if (fsize <= std::numeric_limits<qsizetype>::max()) {
1200 data_len = file.size();
1201 data = new uchar[data_len];
1202 ok = (data_len == file.read(reinterpret_cast<char *>(data), data_len));
1203 }
1204 }
1205 if (!ok) {
1206 delete[] data;
1207 data = nullptr;
1208 data_len = 0;
1209 return false;
1210 }
1211 fromMM = false;
1212 }
1213 if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
1214 if (fromMM) {
1215 unmapPointer = data;
1216 unmapLength = data_len;
1217 }
1218 fileName = f;
1219 return true;
1220 }
1221 return false;
1222}
1223
1224static QString qt_resource_fixResourceRoot(QString r)
1225{
1226 if (!r.isEmpty()) {
1227 if (r.startsWith(QLatin1Char(':')))
1228 r = r.mid(1);
1229 if (!r.isEmpty())
1230 r = QDir::cleanPath(r);
1231 }
1232 return r;
1233}
1234
1235/*!
1236 \fn bool QResource::registerResource(const QString &rccFileName, const QString &mapRoot)
1237
1238 Registers the resource with the given \a rccFileName at the location in the
1239 resource tree specified by \a mapRoot, and returns \c true if the file is
1240 successfully opened; otherwise returns \c false.
1241
1242 \sa unregisterResource()
1243*/
1244
1245bool QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
1246{
1247 QString r = qt_resource_fixResourceRoot(resourceRoot);
1248 if (!r.isEmpty() && r[0] != QLatin1Char('/')) {
1249 qWarning("QDir::registerResource: Registering a resource [%ls] must be rooted in an "
1250 "absolute path (start with /) [%ls]",
1251 qUtf16Printable(rccFilename), qUtf16Printable(resourceRoot));
1252 return false;
1253 }
1254
1255 QDynamicFileResourceRoot *root = new QDynamicFileResourceRoot(r);
1256 if (root->registerSelf(rccFilename)) {
1257 root->ref.ref();
1258 const auto locker = qt_scoped_lock(resourceMutex());
1259 resourceList()->append(root);
1260 return true;
1261 }
1262 delete root;
1263 return false;
1264}
1265
1266/*!
1267 \fn bool QResource::unregisterResource(const QString &rccFileName, const QString &mapRoot)
1268
1269 Unregisters the resource with the given \a rccFileName at the location in
1270 the resource tree specified by \a mapRoot, and returns \c true if the
1271 resource is successfully unloaded and no references exist for the
1272 resource; otherwise returns \c false.
1273
1274 \sa registerResource()
1275*/
1276
1277bool QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
1278{
1279 QString r = qt_resource_fixResourceRoot(resourceRoot);
1280
1281 const auto locker = qt_scoped_lock(resourceMutex());
1282 ResourceList *list = resourceList();
1283 for (int i = 0; i < list->size(); ++i) {
1284 QResourceRoot *res = list->at(i);
1285 if (res->type() == QResourceRoot::Resource_File) {
1286 QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot *>(res);
1287 if (root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1288 list->removeAt(i);
1289 if (!root->ref.deref()) {
1290 delete root;
1291 return true;
1292 }
1293 return false;
1294 }
1295 }
1296 }
1297 return false;
1298}
1299
1300/*!
1301 \fn bool QResource::registerResource(const uchar *rccData, const QString &mapRoot)
1302 \since 4.3
1303
1304 Registers the resource with the given \a rccData at the location in the
1305 resource tree specified by \a mapRoot, and returns \c true if the file is
1306 successfully opened; otherwise returns \c false.
1307
1308 \warning The data must remain valid throughout the life of any QFile
1309 that may reference the resource data.
1310
1311 \sa unregisterResource()
1312*/
1313
1314bool QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
1315{
1316 QString r = qt_resource_fixResourceRoot(resourceRoot);
1317 if (!r.isEmpty() && r[0] != QLatin1Char('/')) {
1318 qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an "
1319 "absolute path (start with /) [%ls]",
1320 rccData, qUtf16Printable(resourceRoot));
1321 return false;
1322 }
1323
1324 QDynamicBufferResourceRoot *root = new QDynamicBufferResourceRoot(r);
1325 if (root->registerSelf(rccData, -1)) {
1326 root->ref.ref();
1327 const auto locker = qt_scoped_lock(resourceMutex());
1328 resourceList()->append(root);
1329 return true;
1330 }
1331 delete root;
1332 return false;
1333}
1334
1335/*!
1336 \fn bool QResource::unregisterResource(const uchar *rccData, const QString &mapRoot)
1337 \since 4.3
1338
1339 Unregisters the resource with the given \a rccData at the location in the
1340 resource tree specified by \a mapRoot, and returns \c true if the resource is
1341 successfully unloaded and no references exist into the resource; otherwise returns \c false.
1342
1343 \sa registerResource()
1344*/
1345
1346bool QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
1347{
1348 QString r = qt_resource_fixResourceRoot(resourceRoot);
1349
1350 const auto locker = qt_scoped_lock(resourceMutex());
1351 ResourceList *list = resourceList();
1352 for (int i = 0; i < list->size(); ++i) {
1353 QResourceRoot *res = list->at(i);
1354 if (res->type() == QResourceRoot::Resource_Buffer) {
1355 QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot *>(res);
1356 if (root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1357 list->removeAt(i);
1358 if (!root->ref.deref()) {
1359 delete root;
1360 return true;
1361 }
1362 return false;
1363 }
1364 }
1365 }
1366 return false;
1367}
1368
1369#if !defined(QT_BOOTSTRAPPED)
1370// resource engine
1371class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
1372{
1373protected:
1374 Q_DECLARE_PUBLIC(QResourceFileEngine)
1375private:
1376 uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1377 bool unmap(uchar *ptr);
1378 void uncompress() const;
1379 qint64 offset;
1380 QResource resource;
1381 mutable QByteArray uncompressed;
1382protected:
1383 QResourceFileEnginePrivate() : offset(0) { }
1384};
1385
1386bool QResourceFileEngine::mkdir(const QString &, bool) const
1387{
1388 return false;
1389}
1390
1391bool QResourceFileEngine::rmdir(const QString &, bool) const
1392{
1393 return false;
1394}
1395
1396bool QResourceFileEngine::setSize(qint64)
1397{
1398 return false;
1399}
1400
1401QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
1402{
1403 return QAbstractFileEngine::entryList(filters, filterNames);
1404}
1405
1406bool QResourceFileEngine::caseSensitive() const
1407{
1408 return true;
1409}
1410
1411QResourceFileEngine::QResourceFileEngine(const QString &file) :
1412 QAbstractFileEngine(*new QResourceFileEnginePrivate)
1413{
1414 Q_D(QResourceFileEngine);
1415 d->resource.setFileName(file);
1416}
1417
1418QResourceFileEngine::~QResourceFileEngine()
1419{
1420}
1421
1422void QResourceFileEngine::setFileName(const QString &file)
1423{
1424 Q_D(QResourceFileEngine);
1425 d->resource.setFileName(file);
1426}
1427
1428bool QResourceFileEngine::open(QIODevice::OpenMode flags)
1429{
1430 Q_D(QResourceFileEngine);
1431 if (d->resource.fileName().isEmpty()) {
1432 qWarning("QResourceFileEngine::open: Missing file name");
1433 return false;
1434 }
1435 if (flags & QIODevice::WriteOnly)
1436 return false;
1437 if (d->resource.compressionAlgorithm() != QResource::NoCompression) {
1438 d->uncompress();
1439 if (d->uncompressed.isNull()) {
1440 d->errorString = QSystemError::stdString(EIO);
1441 return false;
1442 }
1443 }
1444 if (!d->resource.isValid()) {
1445 d->errorString = QSystemError::stdString(ENOENT);
1446 return false;
1447 }
1448 return true;
1449}
1450
1451bool QResourceFileEngine::close()
1452{
1453 Q_D(QResourceFileEngine);
1454 d->offset = 0;
1455 return true;
1456}
1457
1458bool QResourceFileEngine::flush()
1459{
1460 return true;
1461}
1462
1463qint64 QResourceFileEngine::read(char *data, qint64 len)
1464{
1465 Q_D(QResourceFileEngine);
1466 if (len > size() - d->offset)
1467 len = size() - d->offset;
1468 if (len <= 0)
1469 return 0;
1470 if (!d->uncompressed.isNull())
1471 memcpy(data, d->uncompressed.constData() + d->offset, len);
1472 else
1473 memcpy(data, d->resource.data() + d->offset, len);
1474 d->offset += len;
1475 return len;
1476}
1477
1478qint64 QResourceFileEngine::write(const char *, qint64)
1479{
1480 return -1;
1481}
1482
1483bool QResourceFileEngine::remove()
1484{
1485 return false;
1486}
1487
1488bool QResourceFileEngine::copy(const QString &)
1489{
1490 return false;
1491}
1492
1493bool QResourceFileEngine::rename(const QString &)
1494{
1495 return false;
1496}
1497
1498bool QResourceFileEngine::link(const QString &)
1499{
1500 return false;
1501}
1502
1503qint64 QResourceFileEngine::size() const
1504{
1505 Q_D(const QResourceFileEngine);
1506 return d->resource.isValid() ? d->resource.uncompressedSize() : 0;
1507}
1508
1509qint64 QResourceFileEngine::pos() const
1510{
1511 Q_D(const QResourceFileEngine);
1512 return d->offset;
1513}
1514
1515bool QResourceFileEngine::atEnd() const
1516{
1517 Q_D(const QResourceFileEngine);
1518 if (!d->resource.isValid())
1519 return true;
1520 return d->offset == size();
1521}
1522
1523bool QResourceFileEngine::seek(qint64 pos)
1524{
1525 Q_D(QResourceFileEngine);
1526 if (!d->resource.isValid())
1527 return false;
1528
1529 if (d->offset > size())
1530 return false;
1531 d->offset = pos;
1532 return true;
1533}
1534
1535bool QResourceFileEngine::isSequential() const
1536{
1537 return false;
1538}
1539
1540QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1541{
1542 Q_D(const QResourceFileEngine);
1543 QAbstractFileEngine::FileFlags ret;
1544 if (!d->resource.isValid())
1545 return ret;
1546
1547 if (type & PermsMask)
1548 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm | ReadUserPerm | ReadGroupPerm
1549 | ReadOtherPerm);
1550 if (type & TypesMask) {
1551 if (d->resource.isDir())
1552 ret |= DirectoryType;
1553 else
1554 ret |= FileType;
1555 }
1556 if (type & FlagsMask) {
1557 ret |= ExistsFlag;
1558 if (d->resource.absoluteFilePath() == QLatin1String(":/"))
1559 ret |= RootFlag;
1560 }
1561 return ret;
1562}
1563
1564bool QResourceFileEngine::setPermissions(uint)
1565{
1566 return false;
1567}
1568
1569QString QResourceFileEngine::fileName(FileName file) const
1570{
1571 Q_D(const QResourceFileEngine);
1572 if (file == BaseName) {
1573 int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
1574 if (slash == -1)
1575 return d->resource.fileName();
1576 return d->resource.fileName().mid(slash + 1);
1577 } else if (file == PathName || file == AbsolutePathName) {
1578 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath()
1579 : d->resource.fileName();
1580 const int slash = path.lastIndexOf(QLatin1Char('/'));
1581 if (slash == -1)
1582 return QLatin1String(":");
1583 else if (slash <= 1)
1584 return QLatin1String(":/");
1585 return path.left(slash);
1586
1587 } else if (file == CanonicalName || file == CanonicalPathName) {
1588 const QString absoluteFilePath = d->resource.absoluteFilePath();
1589 if (file == CanonicalPathName) {
1590 const int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
1591 if (slash != -1)
1592 return absoluteFilePath.left(slash);
1593 }
1594 return absoluteFilePath;
1595 }
1596 return d->resource.fileName();
1597}
1598
1599bool QResourceFileEngine::isRelativePath() const
1600{
1601 return false;
1602}
1603
1604uint QResourceFileEngine::ownerId(FileOwner) const
1605{
1606 static const uint nobodyID = static_cast<uint>(-2);
1607 return nobodyID;
1608}
1609
1610QString QResourceFileEngine::owner(FileOwner) const
1611{
1612 return QString();
1613}
1614
1615QDateTime QResourceFileEngine::fileTime(FileTime time) const
1616{
1617 Q_D(const QResourceFileEngine);
1618 if (time == ModificationTime)
1619 return d->resource.lastModified();
1620 return QDateTime();
1621}
1622
1623/*!
1624 \internal
1625*/
1626QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
1627 const QStringList &filterNames)
1628{
1629 return new QResourceFileEngineIterator(filters, filterNames);
1630}
1631
1632/*!
1633 \internal
1634*/
1635QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
1636{
1637 return nullptr;
1638}
1639
1640bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
1641{
1642 Q_D(QResourceFileEngine);
1643 if (extension == MapExtension) {
1644 const auto *options = static_cast<const MapExtensionOption *>(option);
1645 auto *returnValue = static_cast<MapExtensionReturn *>(output);
1646 returnValue->address = d->map(options->offset, options->size, options->flags);
1647 return (returnValue->address != nullptr);
1648 }
1649 if (extension == UnMapExtension) {
1650 const auto *options = static_cast<const UnMapExtensionOption *>(option);
1651 return d->unmap(options->address);
1652 }
1653 return false;
1654}
1655
1656bool QResourceFileEngine::supportsExtension(Extension extension) const
1657{
1658 return (extension == UnMapExtension || extension == MapExtension);
1659}
1660
1661uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1662{
1663 Q_Q(QResourceFileEngine);
1664 Q_UNUSED(flags);
1665
1666 qint64 max = resource.uncompressedSize();
1667 qint64 end;
1668 if (offset < 0 || size <= 0 || !resource.isValid() ||
1669 add_overflow(offset, size, &end) || end > max) {
1670 q->setError(QFile::UnspecifiedError, QString());
1671 return nullptr;
1672 }
1673
1674 const uchar *address = resource.data();
1675 if (resource.compressionAlgorithm() != QResource::NoCompression) {
1676 uncompress();
1677 if (uncompressed.isNull())
1678 return nullptr;
1679 address = reinterpret_cast<const uchar *>(uncompressed.constData());
1680 }
1681
1682 return const_cast<uchar *>(address) + offset;
1683}
1684
1685bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1686{
1687 Q_UNUSED(ptr);
1688 return true;
1689}
1690
1691void QResourceFileEnginePrivate::uncompress() const
1692{
1693 if (resource.compressionAlgorithm() == QResource::NoCompression
1694 || !uncompressed.isEmpty() || resource.size() == 0)
1695 return; // nothing to do
1696 uncompressed = resource.uncompressedData();
1697}
1698
1699#endif // !defined(QT_BOOTSTRAPPED)
1700
1701QT_END_NAMESPACE
1702