1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qresource.h"
43 #include "qresource_p.h"
44 #include "qresource_iterator_p.h"
45 #include "qset.h"
46 #include "qhash.h"
47 #include "qmutex.h"
48 #include "qdebug.h"
49 #include "qlocale.h"
50 #include "qglobal.h"
51 #include "qvector.h"
52 #include "qdatetime.h"
53 #include "qbytearray.h"
54 #include "qstringlist.h"
55 #include <qshareddata.h>
56 #include <qplatformdefs.h>
57 #include "private/qabstractfileengine_p.h"
58 
59 #ifdef Q_OS_UNIX
60 # include "private/qcore_unix_p.h"
61 #endif
62 
63 //#define DEBUG_RESOURCE_MATCH
64 
65 #if defined(Q_OS_VXWORKS)
66 #  if defined(m_data)
67 #    undef m_data
68 #  endif
69 #  if defined(m_len)
70 #    undef m_len
71 #  endif
72 #endif
73 
74 QT_BEGIN_NAMESPACE
75 
76 
77 class QStringSplitter
78 {
79 public:
QStringSplitter(const QString & s)80     QStringSplitter(const QString &s)
81         : m_string(s), m_data(m_string.constData()), m_len(s.length()), m_pos(0)
82     {
83         m_splitChar = QLatin1Char('/');
84     }
85 
hasNext()86     inline bool hasNext() {
87         while (m_pos < m_len && m_data[m_pos] == m_splitChar)
88             ++m_pos;
89         return m_pos < m_len;
90     }
91 
next()92     inline QStringRef next() {
93         int start = m_pos;
94         while (m_pos < m_len && m_data[m_pos] != m_splitChar)
95             ++m_pos;
96         return QStringRef(&m_string, start, m_pos - start);
97     }
98 
99     QString m_string;
100     const QChar *m_data;
101     QChar m_splitChar;
102     int m_len;
103     int m_pos;
104 };
105 
106 
107 //resource glue
108 class QResourceRoot
109 {
110     enum Flags
111     {
112         Compressed = 0x01,
113         Directory = 0x02
114     };
115     const uchar *tree, *names, *payloads;
findOffset(int node) const116     inline int findOffset(int node) const { return node * 14; } //sizeof each tree element
117     int hash(int node) const;
118     QString name(int node) const;
119     short flags(int node) const;
120 public:
121     mutable QAtomicInt ref;
122 
QResourceRoot()123     inline QResourceRoot(): tree(0), names(0), payloads(0) {}
QResourceRoot(const uchar * t,const uchar * n,const uchar * d)124     inline QResourceRoot(const uchar *t, const uchar *n, const uchar *d) { setSource(t, n, d); }
~QResourceRoot()125     virtual ~QResourceRoot() { }
126     int findNode(const QString &path, const QLocale &locale=QLocale()) const;
isContainer(int node) const127     inline bool isContainer(int node) const { return flags(node) & Directory; }
isCompressed(int node) const128     inline bool isCompressed(int node) const { return flags(node) & Compressed; }
129     const uchar *data(int node, qint64 *size) const;
130     QStringList children(int node) const;
mappingRoot() const131     virtual QString mappingRoot() const { return QString(); }
132     bool mappingRootSubdir(const QString &path, QString *match=0) const;
operator ==(const QResourceRoot & other) const133     inline bool operator==(const QResourceRoot &other) const
134     { return tree == other.tree && names == other.names && payloads == other.payloads; }
operator !=(const QResourceRoot & other) const135     inline bool operator!=(const QResourceRoot &other) const
136     { return !operator==(other); }
137     enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
type() const138     virtual ResourceRootType type() const { return Resource_Builtin; }
139 
140 protected:
setSource(const uchar * t,const uchar * n,const uchar * d)141     inline void setSource(const uchar *t, const uchar *n, const uchar *d) {
142         tree = t;
143         names = n;
144         payloads = d;
145     }
146 };
147 
cleanPath(const QString & _path)148 static QString cleanPath(const QString &_path)
149 {
150     QString path = QDir::cleanPath(_path);
151     // QDir::cleanPath does not remove two trailing slashes under _Windows_
152     // due to support for UNC paths. Remove those manually.
153     if (path.startsWith(QLatin1String("//")))
154         path.remove(0, 1);
155     return path;
156 }
157 
158 Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE);
159 
160 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive))
161 
162 typedef QList<QResourceRoot*> ResourceList;
163 Q_GLOBAL_STATIC(ResourceList, resourceList)
164 
165 Q_GLOBAL_STATIC(QStringList, resourceSearchPaths)
166 
167 /*!
168     \class QResource
169     \brief The QResource class provides an interface for reading directly from resources.
170 
171     \ingroup io
172 
173     \reentrant
174     \since 4.2
175 
176     QResource is an object that represents a set of data (and possibly
177     children) relating to a single resource entity. QResource gives direct
178     access to the bytes in their raw format. In this way direct access
179     allows reading data without buffer copying or indirection. Indirection
180     is often useful when interacting with the resource entity as if it is a
181     file, this can be achieved with QFile. The data and children behind a
182     QResource are normally compiled into an application/library, but it is
183     also possible to load a resource at runtime. When loaded at run time
184     the resource file will be loaded as one big set of data and then given
185     out in pieces via references into the resource tree.
186 
187     A QResource can either be loaded with an absolute path, either treated
188     as a file system rooted with a \c{/} character, or in resource notation
189     rooted with a \c{:} character. A relative resource can also be opened
190     which will be found in the list of paths returned by QDir::searchPaths().
191 
192     A QResource that is representing a file will have data backing it, this
193     data can possibly be compressed, in which case qUncompress() must be
194     used to access the real data; this happens implicitly when accessed
195     through a QFile. A QResource that is representing a directory will have
196     only children and no data.
197 
198     \section1 Dynamic Resource Loading
199 
200     A resource can be left out of an application's binary and loaded when
201     it is needed at run-time by using the registerResource() function. The
202     resource file passed into registerResource() must be a binary resource
203     as created by rcc. Further information about binary resources can be
204     found in \l{The Qt Resource System} documentation.
205 
206     This can often be useful when loading a large set of application icons
207     that may change based on a setting, or that can be edited by a user and
208     later recreated. The resource is immediately loaded into memory, either
209     as a result of a single file read operation, or as a memory mapped file.
210 
211     This approach can prove to be a significant performance gain as only a
212     single file will be loaded, and pieces of data will be given out via the
213     path requested in setFileName().
214 
215     The unregisterResource() function removes a reference to a particular
216     file. If there are QResources that currently reference resources related
217     to the unregistered file, they will continue to be valid but the resource
218     file itself will be removed from the resource roots, and thus no further
219     QResource can be created pointing into this resource data. The resource
220     itself will be unmapped from memory when the last QResource that points
221     to it is destroyed.
222 
223     \sa {The Qt Resource System}, QFile, QDir, QFileInfo
224 */
225 
226 class QResourcePrivate {
227 public:
QResourcePrivate(QResource * _q)228     inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
~QResourcePrivate()229     inline ~QResourcePrivate() { clear(); }
230 
231     void ensureInitialized() const;
232     void ensureChildren() const;
233 
234     bool load(const QString &file);
235     void clear();
236 
237     QLocale locale;
238     QString fileName, absoluteFilePath;
239     QList<QResourceRoot*> related;
240     uint container : 1;
241     mutable uint compressed : 1;
242     mutable qint64 size;
243     mutable const uchar *data;
244     mutable QStringList children;
245 
246     QResource *q_ptr;
247     Q_DECLARE_PUBLIC(QResource)
248 };
249 
250 void
clear()251 QResourcePrivate::clear()
252 {
253     absoluteFilePath.clear();
254     compressed = 0;
255     data = 0;
256     size = 0;
257     children.clear();
258     container = 0;
259     for(int i = 0; i < related.size(); ++i) {
260         QResourceRoot *root = related.at(i);
261         if(!root->ref.deref())
262             delete root;
263     }
264     related.clear();
265 }
266 
267 bool
load(const QString & file)268 QResourcePrivate::load(const QString &file)
269 {
270     related.clear();
271     QMutexLocker lock(resourceMutex());
272     const ResourceList *list = resourceList();
273     QString cleaned = cleanPath(file);
274     for(int i = 0; i < list->size(); ++i) {
275         QResourceRoot *res = list->at(i);
276         const int node = res->findNode(cleaned, locale);
277         if(node != -1) {
278             if(related.isEmpty()) {
279                 container = res->isContainer(node);
280                 if(!container) {
281                     data = res->data(node, &size);
282                     compressed = res->isCompressed(node);
283                 } else {
284                     data = 0;
285                     size = 0;
286                     compressed = 0;
287                 }
288             } else if(res->isContainer(node) != container) {
289                 qWarning("QResourceInfo: Resource [%s] has both data and children!", file.toLatin1().constData());
290             }
291             res->ref.ref();
292             related.append(res);
293         } else if(res->mappingRootSubdir(file)) {
294             container = true;
295             data = 0;
296             size = 0;
297             compressed = 0;
298             res->ref.ref();
299             related.append(res);
300         }
301     }
302     return !related.isEmpty();
303 }
304 
305 void
ensureInitialized() const306 QResourcePrivate::ensureInitialized() const
307 {
308     if(!related.isEmpty())
309         return;
310     QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
311     if(fileName == QLatin1String(":"))
312         that->fileName += QLatin1Char('/');
313     that->absoluteFilePath = fileName;
314     if(!that->absoluteFilePath.startsWith(QLatin1Char(':')))
315         that->absoluteFilePath.prepend(QLatin1Char(':'));
316 
317     QString path = fileName;
318     if(path.startsWith(QLatin1Char(':')))
319         path = path.mid(1);
320 
321     if(path.startsWith(QLatin1Char('/'))) {
322         that->load(path);
323     } else {
324         QMutexLocker lock(resourceMutex());
325         QStringList searchPaths = *resourceSearchPaths();
326         searchPaths << QLatin1String("");
327         for(int i = 0; i < searchPaths.size(); ++i) {
328             const QString searchPath(searchPaths.at(i) + QLatin1Char('/') + path);
329             if(that->load(searchPath)) {
330                 that->absoluteFilePath = QLatin1Char(':') + searchPath;
331                 break;
332             }
333         }
334     }
335 }
336 
337 void
ensureChildren() const338 QResourcePrivate::ensureChildren() const
339 {
340     ensureInitialized();
341     if(!children.isEmpty() || !container || related.isEmpty())
342         return;
343 
344     QString path = absoluteFilePath, k;
345     if(path.startsWith(QLatin1Char(':')))
346         path = path.mid(1);
347     QSet<QString> kids;
348     QString cleaned = cleanPath(path);
349     for(int i = 0; i < related.size(); ++i) {
350         QResourceRoot *res = related.at(i);
351         if(res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
352             if(!kids.contains(k)) {
353                 children += k;
354                 kids.insert(k);
355             }
356         } else {
357             const int node = res->findNode(cleaned);
358             if(node != -1) {
359                 QStringList related_children = res->children(node);
360                 for(int kid = 0; kid < related_children.size(); ++kid) {
361                     k = related_children.at(kid);
362                     if(!kids.contains(k)) {
363                         children += k;
364                         kids.insert(k);
365                     }
366                 }
367             }
368         }
369     }
370 }
371 
372 /*!
373     Constructs a QResource pointing to \a file. \a locale is used to
374     load a specific localization of a resource data.
375 
376     \sa QFileInfo, QDir::searchPaths(), setFileName(), setLocale()
377 */
378 
QResource(const QString & file,const QLocale & locale)379 QResource::QResource(const QString &file, const QLocale &locale) : d_ptr(new QResourcePrivate(this))
380 {
381     Q_D(QResource);
382     d->fileName = file;
383     d->locale = locale;
384 }
385 
386 /*!
387     Releases the resources of the QResource object.
388 */
~QResource()389 QResource::~QResource()
390 {
391 }
392 
393 /*!
394     Sets a QResource to only load the localization of resource to for \a
395     locale. If a resource for the specific locale is not found then the
396     C locale is used.
397 
398     \sa setFileName()
399 */
400 
setLocale(const QLocale & locale)401 void QResource::setLocale(const QLocale &locale)
402 {
403     Q_D(QResource);
404     d->clear();
405     d->locale = locale;
406 }
407 
408 /*!
409     Returns the locale used to locate the data for the QResource.
410 */
411 
locale() const412 QLocale QResource::locale() const
413 {
414     Q_D(const QResource);
415     return d->locale;
416 }
417 
418 /*!
419     Sets a QResource to point to \a file. \a file can either be absolute,
420     in which case it is opened directly, if relative then the file will be
421     tried to be found in QDir::searchPaths().
422 
423     \sa absoluteFilePath()
424 */
425 
setFileName(const QString & file)426 void QResource::setFileName(const QString &file)
427 {
428     Q_D(QResource);
429     d->clear();
430     d->fileName = file;
431 }
432 
433 /*!
434     Returns the full path to the file that this QResource represents as it
435     was passed.
436 
437     \sa absoluteFilePath()
438 */
439 
fileName() const440 QString QResource::fileName() const
441 {
442     Q_D(const QResource);
443     d->ensureInitialized();
444     return d->fileName;
445 }
446 
447 /*!
448     Returns the real path that this QResource represents, if the resource
449     was found via the QDir::searchPaths() it will be indicated in the path.
450 
451     \sa fileName()
452 */
453 
absoluteFilePath() const454 QString QResource::absoluteFilePath() const
455 {
456     Q_D(const QResource);
457     d->ensureInitialized();
458     return d->absoluteFilePath;
459 }
460 
461 /*!
462     Returns true if the resource really exists in the resource hierarchy,
463     false otherwise.
464 
465 */
466 
isValid() const467 bool QResource::isValid() const
468 {
469     Q_D(const QResource);
470     d->ensureInitialized();
471     return !d->related.isEmpty();
472 }
473 
474 /*!
475     \fn bool QResource::isFile() const
476 
477     Returns true if the resource represents a file and thus has data
478     backing it, false if it represents a directory.
479 
480     \sa isDir()
481 */
482 
483 
484 /*!
485     Returns true if the resource represents a file and the data backing it
486     is in a compressed format, false otherwise.
487 
488     \sa data(), isFile()
489 */
490 
isCompressed() const491 bool QResource::isCompressed() const
492 {
493     Q_D(const QResource);
494     d->ensureInitialized();
495     return d->compressed;
496 }
497 
498 /*!
499     Returns the size of the data backing the resource.
500 
501     \sa data(), isFile()
502 */
503 
size() const504 qint64 QResource::size() const
505 {
506     Q_D(const QResource);
507     d->ensureInitialized();
508     return d->size;
509 }
510 
511 /*!
512     Returns direct access to a read only segment of data that this resource
513     represents. If the resource is compressed the data returns is
514     compressed and qUncompress() must be used to access the data. If the
515     resource is a directory 0 is returned.
516 
517     \sa size(), isCompressed(), isFile()
518 */
519 
data() const520 const uchar *QResource::data() const
521 {
522     Q_D(const QResource);
523     d->ensureInitialized();
524     return d->data;
525 }
526 
527 /*!
528     Returns true if the resource represents a directory and thus may have
529     children() in it, false if it represents a file.
530 
531     \sa isFile()
532 */
533 
isDir() const534 bool QResource::isDir() const
535 {
536     Q_D(const QResource);
537     d->ensureInitialized();
538     return d->container;
539 }
540 
541 /*!
542     Returns a list of all resources in this directory, if the resource
543     represents a file the list will be empty.
544 
545     \sa isDir()
546 */
547 
children() const548 QStringList QResource::children() const
549 {
550     Q_D(const QResource);
551     d->ensureChildren();
552     return d->children;
553 }
554 
555 /*!
556   \obsolete
557 
558   Use QDir::addSearchPath() with a prefix instead.
559 
560   Adds \a path to the search paths searched in to find resources that are
561   not specified with an absolute path. The \a path must be an absolute
562   path (start with \c{/}).
563 
564   The default search path is to search only in the root (\c{:/}). The last
565   path added will be consulted first upon next QResource creation.
566 */
567 void
addSearchPath(const QString & path)568 QResource::addSearchPath(const QString &path)
569 {
570     if (!path.startsWith(QLatin1Char('/'))) {
571         qWarning("QResource::addResourceSearchPath: Search paths must be absolute (start with /) [%s]",
572                  path.toLocal8Bit().data());
573         return;
574     }
575     QMutexLocker lock(resourceMutex());
576     resourceSearchPaths()->prepend(path);
577 }
578 
579 /*!
580   \obsolete
581 
582   Use QDir::searchPaths() instead.
583 
584   Returns the current search path list. This list is consulted when
585   creating a relative resource.
586 
587   \sa QDir::addSearchPath() QDir::setSearchPaths()
588 */
589 
590 QStringList
searchPaths()591 QResource::searchPaths()
592 {
593     QMutexLocker lock(resourceMutex());
594     return *resourceSearchPaths();
595 }
596 
hash(int node) const597 inline int QResourceRoot::hash(int node) const
598 {
599     if(!node) //root
600         return 0;
601     const int offset = findOffset(node);
602     int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
603                       (tree[offset+2] << 8) + (tree[offset+3] << 0);
604     name_offset += 2; //jump past name length
605     return (names[name_offset+0] << 24) + (names[name_offset+1] << 16) +
606            (names[name_offset+2] << 8) + (names[name_offset+3] << 0);
607 }
name(int node) const608 inline QString QResourceRoot::name(int node) const
609 {
610     if(!node) // root
611         return QString();
612     const int offset = findOffset(node);
613 
614     QString ret;
615     int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
616                       (tree[offset+2] << 8) + (tree[offset+3] << 0);
617     const short name_length = (names[name_offset+0] << 8) +
618                               (names[name_offset+1] << 0);
619     name_offset += 2;
620     name_offset += 4; //jump past hash
621 
622     ret.resize(name_length);
623     QChar *strData = ret.data();
624     for(int i = 0; i < name_length*2; i+=2) {
625         QChar c(names[name_offset+i+1], names[name_offset+i]);
626         *strData = c;
627         ++strData;
628     }
629     return ret;
630 }
631 
findNode(const QString & _path,const QLocale & locale) const632 int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
633 {
634     QString path = _path;
635     {
636         QString root = mappingRoot();
637         if(!root.isEmpty()) {
638             if(root == path) {
639                 path = QLatin1Char('/');
640             } else {
641                 if(!root.endsWith(QLatin1Char('/')))
642                     root += QLatin1Char('/');
643                 if(path.size() >= root.size() && path.startsWith(root))
644                     path = path.mid(root.length()-1);
645                 if(path.isEmpty())
646                     path = QLatin1Char('/');
647             }
648         }
649     }
650 #ifdef DEBUG_RESOURCE_MATCH
651     qDebug() << "!!!!" << "START" << path << locale.country() << locale.language();
652 #endif
653 
654     if(path == QLatin1String("/"))
655         return 0;
656 
657     //the root node is always first
658     int child_count = (tree[6] << 24) + (tree[7] << 16) +
659                       (tree[8] << 8) + (tree[9] << 0);
660     int child       = (tree[10] << 24) + (tree[11] << 16) +
661                       (tree[12] << 8) + (tree[13] << 0);
662 
663     //now iterate up the tree
664     int node = -1;
665 
666     QStringSplitter splitter(path);
667     while (child_count && splitter.hasNext()) {
668         QStringRef segment = splitter.next();
669 
670 #ifdef DEBUG_RESOURCE_MATCH
671         qDebug() << "  CHILDREN" << segment;
672         for(int j = 0; j < child_count; ++j) {
673             qDebug() << "   " << child+j << " :: " << name(child+j);
674         }
675 #endif
676         const int h = qHash(segment);
677 
678         //do the binary search for the hash
679         int l = 0, r = child_count-1;
680         int sub_node = (l+r+1)/2;
681         while(r != l) {
682             const int sub_node_hash = hash(child+sub_node);
683             if(h == sub_node_hash)
684                 break;
685             else if(h < sub_node_hash)
686                 r = sub_node - 1;
687             else
688                 l = sub_node;
689             sub_node = (l + r + 1) / 2;
690         }
691         sub_node += child;
692 
693         //now do the "harder" compares
694         bool found = false;
695         if(hash(sub_node) == h) {
696             while(sub_node > child && hash(sub_node-1) == h) //backup for collisions
697                 --sub_node;
698             for(; sub_node < child+child_count && hash(sub_node) == h; ++sub_node) { //here we go...
699                 if(name(sub_node) == segment) {
700                     found = true;
701                     int offset = findOffset(sub_node);
702 #ifdef DEBUG_RESOURCE_MATCH
703                     qDebug() << "  TRY" << sub_node << name(sub_node) << offset;
704 #endif
705                     offset += 4;  //jump past name
706 
707                     const short flags = (tree[offset+0] << 8) +
708                                         (tree[offset+1] << 0);
709                     offset += 2;
710 
711                     if(!splitter.hasNext()) {
712                         if(!(flags & Directory)) {
713                             const short country = (tree[offset+0] << 8) +
714                                                   (tree[offset+1] << 0);
715                             offset += 2;
716 
717                             const short language = (tree[offset+0] << 8) +
718                                                    (tree[offset+1] << 0);
719                             offset += 2;
720 #ifdef DEBUG_RESOURCE_MATCH
721                             qDebug() << "    " << "LOCALE" << country << language;
722 #endif
723                             if(country == locale.country() && language == locale.language()) {
724 #ifdef DEBUG_RESOURCE_MATCH
725                                 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
726 #endif
727                                 return sub_node;
728                             } else if((country == QLocale::AnyCountry && language == locale.language()) ||
729                                       (country == QLocale::AnyCountry && language == QLocale::C && node == -1)) {
730                                 node = sub_node;
731                             }
732                             continue;
733                         } else {
734 #ifdef DEBUG_RESOURCE_MATCH
735                             qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
736 #endif
737 
738                             return sub_node;
739                         }
740                     }
741 
742                     if(!(flags & Directory))
743                         return -1;
744 
745                     child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
746                                   (tree[offset+2] << 8) + (tree[offset+3] << 0);
747                     offset += 4;
748                     child = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
749                             (tree[offset+2] << 8) + (tree[offset+3] << 0);
750                     break;
751                 }
752             }
753         }
754         if(!found)
755             break;
756     }
757 #ifdef DEBUG_RESOURCE_MATCH
758     qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
759 #endif
760     return node;
761 }
flags(int node) const762 short QResourceRoot::flags(int node) const
763 {
764     if(node == -1)
765         return 0;
766     const int offset = findOffset(node) + 4; //jump past name
767     return (tree[offset+0] << 8) + (tree[offset+1] << 0);
768 }
data(int node,qint64 * size) const769 const uchar *QResourceRoot::data(int node, qint64 *size) const
770 {
771     if(node == -1) {
772         *size = 0;
773         return 0;
774     }
775     int offset = findOffset(node) + 4; //jump past name
776 
777     const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
778     offset += 2;
779 
780     offset += 4; //jump past locale
781 
782     if(!(flags & Directory)) {
783         const int data_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
784                                 (tree[offset+2] << 8) + (tree[offset+3] << 0);
785         const uint data_length = (payloads[data_offset+0] << 24) + (payloads[data_offset+1] << 16) +
786                                  (payloads[data_offset+2] << 8) + (payloads[data_offset+3] << 0);
787         const uchar *ret = payloads+data_offset+4;
788         *size = data_length;
789         return ret;
790     }
791     *size = 0;
792     return 0;
793 }
children(int node) const794 QStringList QResourceRoot::children(int node) const
795 {
796     if(node == -1)
797         return QStringList();
798     int offset = findOffset(node) + 4; //jump past name
799 
800     const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
801     offset += 2;
802 
803     QStringList ret;
804     if(flags & Directory) {
805         const int child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
806                                 (tree[offset+2] << 8) + (tree[offset+3] << 0);
807         offset += 4;
808         const int child_off = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
809                               (tree[offset+2] << 8) + (tree[offset+3] << 0);
810         for(int i = child_off; i < child_off+child_count; ++i)
811             ret << name(i);
812     }
813     return ret;
814 }
mappingRootSubdir(const QString & path,QString * match) const815 bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
816 {
817     const QString root = mappingRoot();
818     if(!root.isEmpty()) {
819         const QStringList root_segments = root.split(QLatin1Char('/'), QString::SkipEmptyParts),
820                           path_segments = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
821         if(path_segments.size() <= root_segments.size()) {
822             int matched = 0;
823             for(int i = 0; i < path_segments.size(); ++i) {
824                 if(root_segments[i] != path_segments[i])
825                     break;
826                 ++matched;
827             }
828             if(matched == path_segments.size()) {
829                 if(match && root_segments.size() > matched)
830                     *match = root_segments.at(matched);
831                 return true;
832             }
833         }
834     }
835     return false;
836 }
837 
qRegisterResourceData(int version,const unsigned char * tree,const unsigned char * name,const unsigned char * data)838 Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
839                                          const unsigned char *name, const unsigned char *data)
840 {
841     QMutexLocker lock(resourceMutex());
842     if(version == 0x01 && resourceList()) {
843         bool found = false;
844         QResourceRoot res(tree, name, data);
845         for(int i = 0; i < resourceList()->size(); ++i) {
846             if(*resourceList()->at(i) == res) {
847                 found = true;
848                 break;
849             }
850         }
851         if(!found) {
852             QResourceRoot *root = new QResourceRoot(tree, name, data);
853             root->ref.ref();
854             resourceList()->append(root);
855         }
856         return true;
857     }
858     return false;
859 }
860 
qUnregisterResourceData(int version,const unsigned char * tree,const unsigned char * name,const unsigned char * data)861 Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
862                                            const unsigned char *name, const unsigned char *data)
863 {
864     QMutexLocker lock(resourceMutex());
865     if(version == 0x01 && resourceList()) {
866         QResourceRoot res(tree, name, data);
867         for(int i = 0; i < resourceList()->size(); ) {
868             if(*resourceList()->at(i) == res) {
869                 QResourceRoot *root = resourceList()->takeAt(i);
870                 if(!root->ref.deref())
871                     delete root;
872             } else {
873                 ++i;
874             }
875         }
876         return true;
877     }
878     return false;
879 }
880 
881 //run time resource creation
882 
883 class QDynamicBufferResourceRoot: public QResourceRoot
884 {
885     QString root;
886     const uchar *buffer;
887 
888 public:
QDynamicBufferResourceRoot(const QString & _root)889     inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(0) { }
~QDynamicBufferResourceRoot()890     inline ~QDynamicBufferResourceRoot() { }
mappingBuffer() const891     inline const uchar *mappingBuffer() const { return buffer; }
mappingRoot() const892     virtual QString mappingRoot() const { return root; }
type() const893     virtual ResourceRootType type() const { return Resource_Buffer; }
894 
registerSelf(const uchar * b)895     bool registerSelf(const uchar *b) {
896         //setup the data now
897         int offset = 0;
898 
899         //magic number
900         if(b[offset+0] != 'q' || b[offset+1] != 'r' ||
901            b[offset+2] != 'e' || b[offset+3] != 's') {
902             return false;
903         }
904         offset += 4;
905 
906         const int version = (b[offset+0] << 24) + (b[offset+1] << 16) +
907                          (b[offset+2] << 8) + (b[offset+3] << 0);
908         offset += 4;
909 
910         const int tree_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
911                                 (b[offset+2] << 8) + (b[offset+3] << 0);
912         offset += 4;
913 
914         const int data_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
915                                 (b[offset+2] << 8) + (b[offset+3] << 0);
916         offset += 4;
917 
918         const int name_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
919                                 (b[offset+2] << 8) + (b[offset+3] << 0);
920         offset += 4;
921 
922         if(version == 0x01) {
923             buffer = b;
924             setSource(b+tree_offset, b+name_offset, b+data_offset);
925             return true;
926         }
927         return false;
928     }
929 };
930 
931 #if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined (Q_OS_NACL) && !defined(Q_OS_INTEGRITY)
932 #define QT_USE_MMAP
933 #endif
934 
935 // most of the headers below are already included in qplatformdefs.h
936 // also this lacks Large File support but that's probably irrelevant
937 #if defined(QT_USE_MMAP)
938 // for mmap
939 QT_BEGIN_INCLUDE_NAMESPACE
940 #include <sys/mman.h>
941 #include <errno.h>
942 QT_END_INCLUDE_NAMESPACE
943 #endif
944 
945 
946 
947 class QDynamicFileResourceRoot: public QDynamicBufferResourceRoot
948 {
949     QString fileName;
950     // for mmap'ed files, this is what needs to be unmapped.
951     uchar *unmapPointer;
952     unsigned int unmapLength;
953 
954 public:
QDynamicFileResourceRoot(const QString & _root)955     inline QDynamicFileResourceRoot(const QString &_root) : QDynamicBufferResourceRoot(_root), unmapPointer(0), unmapLength(0) { }
~QDynamicFileResourceRoot()956     ~QDynamicFileResourceRoot() {
957 #if defined(QT_USE_MMAP)
958         if (unmapPointer) {
959             munmap((char*)unmapPointer, unmapLength);
960             unmapPointer = 0;
961             unmapLength = 0;
962         } else
963 #endif
964         {
965             delete [] (uchar *)mappingBuffer();
966         }
967     }
mappingFile() const968     QString mappingFile() const { return fileName; }
type() const969     virtual ResourceRootType type() const { return Resource_File; }
970 
registerSelf(const QString & f)971     bool registerSelf(const QString &f) {
972         bool fromMM = false;
973         uchar *data = 0;
974         unsigned int data_len = 0;
975 
976 #ifdef QT_USE_MMAP
977 
978 #ifndef MAP_FILE
979 #define MAP_FILE 0
980 #endif
981 #ifndef MAP_FAILED
982 #define MAP_FAILED -1
983 #endif
984 
985         int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY,
986 #if defined(Q_OS_WIN)
987                          _S_IREAD | _S_IWRITE
988 #else
989                          0666
990 #endif
991             );
992         if (fd >= 0) {
993             QT_STATBUF st;
994             if (!QT_FSTAT(fd, &st)) {
995                 uchar *ptr;
996                 ptr = reinterpret_cast<uchar *>(
997                     mmap(0, st.st_size,             // any address, whole file
998                          PROT_READ,                 // read-only memory
999                          MAP_FILE | MAP_PRIVATE,    // swap-backed map from file
1000                          fd, 0));                   // from offset 0 of fd
1001                 if (ptr && ptr != reinterpret_cast<uchar *>(MAP_FAILED)) {
1002                     data = ptr;
1003                     data_len = st.st_size;
1004                     fromMM = true;
1005                 }
1006             }
1007             ::close(fd);
1008         }
1009 #endif // QT_USE_MMAP
1010         if(!data) {
1011             QFile file(f);
1012             if (!file.exists())
1013                 return false;
1014             data_len = file.size();
1015             data = new uchar[data_len];
1016 
1017             bool ok = false;
1018             if (file.open(QIODevice::ReadOnly))
1019                 ok = (data_len == (uint)file.read((char*)data, data_len));
1020             if (!ok) {
1021                 delete [] data;
1022                 data = 0;
1023                 data_len = 0;
1024                 return false;
1025             }
1026             fromMM = false;
1027         }
1028         if(data && QDynamicBufferResourceRoot::registerSelf(data)) {
1029             if(fromMM) {
1030                 unmapPointer = data;
1031                 unmapLength = data_len;
1032             }
1033             fileName = f;
1034             return true;
1035         }
1036         return false;
1037     }
1038 };
1039 
qt_resource_fixResourceRoot(QString r)1040 static QString qt_resource_fixResourceRoot(QString r) {
1041     if(!r.isEmpty()) {
1042         if(r.startsWith(QLatin1Char(':')))
1043             r = r.mid(1);
1044         if(!r.isEmpty())
1045             r = QDir::cleanPath(r);
1046     }
1047     return r;
1048 }
1049 
1050 
1051 /*!
1052    \fn bool QResource::registerResource(const QString &rccFileName, const QString &mapRoot)
1053 
1054    Registers the resource with the given \a rccFileName at the location in the
1055    resource tree specified by \a mapRoot, and returns true if the file is
1056    successfully opened; otherwise returns false.
1057 
1058    \sa unregisterResource()
1059 */
1060 
1061 bool
registerResource(const QString & rccFilename,const QString & resourceRoot)1062 QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
1063 {
1064     QString r = qt_resource_fixResourceRoot(resourceRoot);
1065     if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
1066         qWarning("QDir::registerResource: Registering a resource [%s] must be rooted in an absolute path (start with /) [%s]",
1067                  rccFilename.toLocal8Bit().data(), resourceRoot.toLocal8Bit().data());
1068         return false;
1069     }
1070 
1071     QDynamicFileResourceRoot *root = new QDynamicFileResourceRoot(r);
1072     if(root->registerSelf(rccFilename)) {
1073         root->ref.ref();
1074         QMutexLocker lock(resourceMutex());
1075         resourceList()->append(root);
1076         return true;
1077     }
1078     delete root;
1079     return false;
1080 }
1081 
1082 /*!
1083   \fn bool QResource::unregisterResource(const QString &rccFileName, const QString &mapRoot)
1084 
1085   Unregisters the resource with the given \a rccFileName at the location in
1086   the resource tree specified by \a mapRoot, and returns true if the
1087   resource is successfully unloaded and no references exist for the
1088   resource; otherwise returns false.
1089 
1090   \sa registerResource()
1091 */
1092 
1093 bool
unregisterResource(const QString & rccFilename,const QString & resourceRoot)1094 QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
1095 {
1096     QString r = qt_resource_fixResourceRoot(resourceRoot);
1097 
1098     QMutexLocker lock(resourceMutex());
1099     ResourceList *list = resourceList();
1100     for(int i = 0; i < list->size(); ++i) {
1101         QResourceRoot *res = list->at(i);
1102         if(res->type() == QResourceRoot::Resource_File) {
1103 	    QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot*>(res);
1104 	    if(root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1105                 resourceList()->removeAt(i);
1106                 if(!root->ref.deref()) {
1107                     delete root;
1108                     return true;
1109                 }
1110                 return false;
1111             }
1112 	}
1113     }
1114     return false;
1115 }
1116 
1117 
1118 /*!
1119    \fn bool QResource::registerResource(const uchar *rccData, const QString &mapRoot)
1120    \since 4.3
1121 
1122    Registers the resource with the given \a rccData at the location in the
1123    resource tree specified by \a mapRoot, and returns true if the file is
1124    successfully opened; otherwise returns false.
1125 
1126    \warning The data must remain valid throughout the life of any QFile
1127    that may reference the resource data.
1128 
1129    \sa unregisterResource()
1130 */
1131 
1132 bool
registerResource(const uchar * rccData,const QString & resourceRoot)1133 QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
1134 {
1135     QString r = qt_resource_fixResourceRoot(resourceRoot);
1136     if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
1137         qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an absolute path (start with /) [%s]",
1138                  rccData, resourceRoot.toLocal8Bit().data());
1139         return false;
1140     }
1141 
1142     QDynamicBufferResourceRoot *root = new QDynamicBufferResourceRoot(r);
1143     if(root->registerSelf(rccData)) {
1144         root->ref.ref();
1145         QMutexLocker lock(resourceMutex());
1146         resourceList()->append(root);
1147         return true;
1148     }
1149     delete root;
1150     return false;
1151 }
1152 
1153 /*!
1154   \fn bool QResource::unregisterResource(const uchar *rccData, const QString &mapRoot)
1155   \since 4.3
1156 
1157   Unregisters the resource with the given \a rccData at the location in the
1158   resource tree specified by \a mapRoot, and returns true if the resource is
1159   successfully unloaded and no references exist into the resource; otherwise returns false.
1160 
1161   \sa registerResource()
1162 */
1163 
1164 bool
unregisterResource(const uchar * rccData,const QString & resourceRoot)1165 QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
1166 {
1167     QString r = qt_resource_fixResourceRoot(resourceRoot);
1168 
1169     QMutexLocker lock(resourceMutex());
1170     ResourceList *list = resourceList();
1171     for(int i = 0; i < list->size(); ++i) {
1172         QResourceRoot *res = list->at(i);
1173         if(res->type() == QResourceRoot::Resource_Buffer) {
1174 	    QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot*>(res);
1175 	    if(root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1176                 resourceList()->removeAt(i);
1177                 if(!root->ref.deref()) {
1178                     delete root;
1179                     return true;
1180                 }
1181 		return false;
1182             }
1183 	}
1184     }
1185     return false;
1186 }
1187 
1188 //resource engine
1189 class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
1190 {
1191 protected:
1192     Q_DECLARE_PUBLIC(QResourceFileEngine)
1193 private:
1194     uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1195     bool unmap(uchar *ptr);
1196     qint64 offset;
1197     QResource resource;
1198     QByteArray uncompressed;
1199 protected:
QResourceFileEnginePrivate()1200     QResourceFileEnginePrivate() : offset(0) { }
1201 };
1202 
mkdir(const QString &,bool) const1203 bool QResourceFileEngine::mkdir(const QString &, bool) const
1204 {
1205     return false;
1206 }
1207 
rmdir(const QString &,bool) const1208 bool QResourceFileEngine::rmdir(const QString &, bool) const
1209 {
1210     return false;
1211 }
1212 
setSize(qint64)1213 bool QResourceFileEngine::setSize(qint64)
1214 {
1215     return false;
1216 }
1217 
entryList(QDir::Filters filters,const QStringList & filterNames) const1218 QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
1219 {
1220     return QAbstractFileEngine::entryList(filters, filterNames);
1221 }
1222 
caseSensitive() const1223 bool QResourceFileEngine::caseSensitive() const
1224 {
1225     return true;
1226 }
1227 
QResourceFileEngine(const QString & file)1228 QResourceFileEngine::QResourceFileEngine(const QString &file) :
1229     QAbstractFileEngine(*new QResourceFileEnginePrivate)
1230 {
1231     Q_D(QResourceFileEngine);
1232     d->resource.setFileName(file);
1233     if(d->resource.isCompressed() && d->resource.size()) {
1234 #ifndef QT_NO_COMPRESS
1235         d->uncompressed = qUncompress(d->resource.data(), d->resource.size());
1236 #else
1237         Q_ASSERT(!"QResourceFileEngine::open: Qt built without support for compression");
1238 #endif
1239     }
1240 }
1241 
~QResourceFileEngine()1242 QResourceFileEngine::~QResourceFileEngine()
1243 {
1244 }
1245 
setFileName(const QString & file)1246 void QResourceFileEngine::setFileName(const QString &file)
1247 {
1248     Q_D(QResourceFileEngine);
1249     d->resource.setFileName(file);
1250 }
1251 
open(QIODevice::OpenMode flags)1252 bool QResourceFileEngine::open(QIODevice::OpenMode flags)
1253 {
1254     Q_D(QResourceFileEngine);
1255     if (d->resource.fileName().isEmpty()) {
1256         qWarning("QResourceFileEngine::open: Missing file name");
1257         return false;
1258     }
1259     if(flags & QIODevice::WriteOnly)
1260         return false;
1261     if(!d->resource.isValid())
1262        return false;
1263     return true;
1264 }
1265 
close()1266 bool QResourceFileEngine::close()
1267 {
1268     Q_D(QResourceFileEngine);
1269     d->offset = 0;
1270     d->uncompressed.clear();
1271     return true;
1272 }
1273 
flush()1274 bool QResourceFileEngine::flush()
1275 {
1276     return true;
1277 }
1278 
read(char * data,qint64 len)1279 qint64 QResourceFileEngine::read(char *data, qint64 len)
1280 {
1281     Q_D(QResourceFileEngine);
1282     if(len > size()-d->offset)
1283         len = size()-d->offset;
1284     if(len <= 0)
1285         return 0;
1286     if(d->resource.isCompressed())
1287         memcpy(data, d->uncompressed.constData()+d->offset, len);
1288     else
1289         memcpy(data, d->resource.data()+d->offset, len);
1290     d->offset += len;
1291     return len;
1292 }
1293 
write(const char *,qint64)1294 qint64 QResourceFileEngine::write(const char *, qint64)
1295 {
1296     return -1;
1297 }
1298 
remove()1299 bool QResourceFileEngine::remove()
1300 {
1301     return false;
1302 }
1303 
copy(const QString &)1304 bool QResourceFileEngine::copy(const QString &)
1305 {
1306     return false;
1307 }
1308 
rename(const QString &)1309 bool QResourceFileEngine::rename(const QString &)
1310 {
1311     return false;
1312 }
1313 
link(const QString &)1314 bool QResourceFileEngine::link(const QString &)
1315 {
1316     return false;
1317 }
1318 
size() const1319 qint64 QResourceFileEngine::size() const
1320 {
1321     Q_D(const QResourceFileEngine);
1322     if(!d->resource.isValid())
1323         return 0;
1324     if(d->resource.isCompressed())
1325         return d->uncompressed.size();
1326     return d->resource.size();
1327 }
1328 
pos() const1329 qint64 QResourceFileEngine::pos() const
1330 {
1331     Q_D(const QResourceFileEngine);
1332     return d->offset;
1333 }
1334 
atEnd() const1335 bool QResourceFileEngine::atEnd() const
1336 {
1337     Q_D(const QResourceFileEngine);
1338     if(!d->resource.isValid())
1339         return true;
1340     return d->offset == size();
1341 }
1342 
seek(qint64 pos)1343 bool QResourceFileEngine::seek(qint64 pos)
1344 {
1345     Q_D(QResourceFileEngine);
1346     if(!d->resource.isValid())
1347         return false;
1348 
1349     if(d->offset > size())
1350         return false;
1351     d->offset = pos;
1352     return true;
1353 }
1354 
isSequential() const1355 bool QResourceFileEngine::isSequential() const
1356 {
1357     return false;
1358 }
1359 
fileFlags(QAbstractFileEngine::FileFlags type) const1360 QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1361 {
1362     Q_D(const QResourceFileEngine);
1363     QAbstractFileEngine::FileFlags ret = 0;
1364     if(!d->resource.isValid())
1365         return ret;
1366 
1367     if(type & PermsMask)
1368         ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm);
1369     if(type & TypesMask) {
1370         if(d->resource.isDir())
1371             ret |= DirectoryType;
1372         else
1373             ret |= FileType;
1374     }
1375     if(type & FlagsMask) {
1376         ret |= ExistsFlag;
1377         if(d->resource.absoluteFilePath() == QLatin1String(":/"))
1378             ret |= RootFlag;
1379     }
1380     return ret;
1381 }
1382 
setPermissions(uint)1383 bool QResourceFileEngine::setPermissions(uint)
1384 {
1385     return false;
1386 }
1387 
fileName(FileName file) const1388 QString QResourceFileEngine::fileName(FileName file) const
1389 {
1390     Q_D(const QResourceFileEngine);
1391     if(file == BaseName) {
1392 	int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
1393 	if (slash == -1)
1394 	    return d->resource.fileName();
1395 	return d->resource.fileName().mid(slash + 1);
1396     } else if(file == PathName || file == AbsolutePathName) {
1397         const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath() : d->resource.fileName();
1398 	const int slash = path.lastIndexOf(QLatin1Char('/'));
1399         if (slash == -1)
1400             return QLatin1String(":");
1401         else if (slash <= 1)
1402             return QLatin1String(":/");
1403         return path.left(slash);
1404 
1405     } else if(file == CanonicalName || file == CanonicalPathName) {
1406         const QString absoluteFilePath = d->resource.absoluteFilePath();
1407         if(file == CanonicalPathName) {
1408             const int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
1409             if (slash != -1)
1410                 return absoluteFilePath.left(slash);
1411         }
1412         return absoluteFilePath;
1413     }
1414     return d->resource.fileName();
1415 }
1416 
isRelativePath() const1417 bool QResourceFileEngine::isRelativePath() const
1418 {
1419     return false;
1420 }
1421 
ownerId(FileOwner) const1422 uint QResourceFileEngine::ownerId(FileOwner) const
1423 {
1424     static const uint nobodyID = (uint) -2;
1425     return nobodyID;
1426 }
1427 
owner(FileOwner) const1428 QString QResourceFileEngine::owner(FileOwner) const
1429 {
1430     return QString();
1431 }
1432 
fileTime(FileTime) const1433 QDateTime QResourceFileEngine::fileTime(FileTime) const
1434 {
1435     return QDateTime();
1436 }
1437 
1438 /*!
1439     \internal
1440 */
beginEntryList(QDir::Filters filters,const QStringList & filterNames)1441 QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
1442                                                                    const QStringList &filterNames)
1443 {
1444     return new QResourceFileEngineIterator(filters, filterNames);
1445 }
1446 
1447 /*!
1448     \internal
1449 */
endEntryList()1450 QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
1451 {
1452     return 0;
1453 }
1454 
extension(Extension extension,const ExtensionOption * option,ExtensionReturn * output)1455 bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
1456 {
1457     Q_D(QResourceFileEngine);
1458     if (extension == MapExtension) {
1459         const MapExtensionOption *options = (MapExtensionOption*)(option);
1460         MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
1461         returnValue->address = d->map(options->offset, options->size, options->flags);
1462         return (returnValue->address != 0);
1463     }
1464     if (extension == UnMapExtension) {
1465         UnMapExtensionOption *options = (UnMapExtensionOption*)option;
1466         return d->unmap(options->address);
1467     }
1468     return false;
1469 }
1470 
supportsExtension(Extension extension) const1471 bool QResourceFileEngine::supportsExtension(Extension extension) const
1472 {
1473     return (extension == UnMapExtension || extension == MapExtension);
1474 }
1475 
map(qint64 offset,qint64 size,QFile::MemoryMapFlags flags)1476 uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1477 {
1478     Q_Q(QResourceFileEngine);
1479     Q_UNUSED(flags);
1480     if (offset < 0 || size <= 0 || !resource.isValid() || offset + size > resource.size()) {
1481         q->setError(QFile::UnspecifiedError, QString());
1482         return 0;
1483     }
1484     uchar *address = const_cast<uchar *>(resource.data());
1485     return (address + offset);
1486 }
1487 
unmap(uchar * ptr)1488 bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1489 {
1490     Q_UNUSED(ptr);
1491     return true;
1492 }
1493 
qInitResourceIO()1494 Q_CORE_EXPORT void qInitResourceIO() { } // ### Qt 5: remove
1495 
1496 QT_END_NAMESPACE
1497