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