source: trunk/src/corelib/io/qresource.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 44.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
74QT_BEGIN_NAMESPACE
75
76
77class QStringSplitter
78{
79public:
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
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
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
108class QResourceRoot
109{
110 enum Flags
111 {
112 Compressed = 0x01,
113 Directory = 0x02
114 };
115 const uchar *tree, *names, *payloads;
116 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;
120public:
121 mutable QAtomicInt ref;
122
123 inline QResourceRoot(): tree(0), names(0), payloads(0) {}
124 inline QResourceRoot(const uchar *t, const uchar *n, const uchar *d) { setSource(t, n, d); }
125 virtual ~QResourceRoot() { }
126 int findNode(const QString &path, const QLocale &locale=QLocale()) const;
127 inline bool isContainer(int node) const { return flags(node) & Directory; }
128 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;
131 virtual QString mappingRoot() const { return QString(); }
132 bool mappingRootSubdir(const QString &path, QString *match=0) const;
133 inline bool operator==(const QResourceRoot &other) const
134 { return tree == other.tree && names == other.names && payloads == other.payloads; }
135 inline bool operator!=(const QResourceRoot &other) const
136 { return !operator==(other); }
137 enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer };
138 virtual ResourceRootType type() const { return Resource_Builtin; }
139
140protected:
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
148static 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
158Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE);
159
160Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive))
161
162typedef QList<QResourceRoot*> ResourceList;
163Q_GLOBAL_STATIC(ResourceList, resourceList)
164
165Q_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
226class QResourcePrivate {
227public:
228 inline QResourcePrivate(QResource *_q) : q_ptr(_q) { clear(); }
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
250void
251QResourcePrivate::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
267bool
268QResourcePrivate::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
305void
306QResourcePrivate::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
337void
338QResourcePrivate::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, searchPaths(), setFileName(), setLocale()
377*/
378
379QResource::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*/
389QResource::~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
401void 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
412QLocale 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 searchPaths().
422
423 \sa absoluteFilePath()
424*/
425
426void 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
440QString 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 searchPaths() it will be indicated in the path.
450
451 \sa fileName()
452*/
453
454QString 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
467bool 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
491bool 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
504qint64 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
520const 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
534bool 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
548QStringList 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*/
567void
568QResource::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
590QStringList
591QResource::searchPaths()
592{
593 QMutexLocker lock(resourceMutex());
594 return *resourceSearchPaths();
595}
596
597inline 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}
608inline 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
632int 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}
762short 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}
769const 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}
794QStringList 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}
815bool 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
838Q_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
861Q_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
883class QDynamicBufferResourceRoot: public QResourceRoot
884{
885 QString root;
886 const uchar *buffer;
887
888public:
889 inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(0) { }
890 inline ~QDynamicBufferResourceRoot() { }
891 inline const uchar *mappingBuffer() const { return buffer; }
892 virtual QString mappingRoot() const { return root; }
893 virtual ResourceRootType type() const { return Resource_Buffer; }
894
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)
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
939QT_BEGIN_INCLUDE_NAMESPACE
940#include <sys/mman.h>
941#include <errno.h>
942QT_END_INCLUDE_NAMESPACE
943#endif
944
945
946
947class 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
954public:
955 inline QDynamicFileResourceRoot(const QString &_root) : QDynamicBufferResourceRoot(_root), unmapPointer(0), unmapLength(0) { }
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 }
968 QString mappingFile() const { return fileName; }
969 virtual ResourceRootType type() const { return Resource_File; }
970
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
1040static 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
1061bool
1062QResource::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
1093bool
1094QResource::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
1132bool
1133QResource::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
1164bool
1165QResource::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//file type handler
1189class QResourceFileEngineHandler : public QAbstractFileEngineHandler
1190{
1191public:
1192 QResourceFileEngineHandler() { }
1193 ~QResourceFileEngineHandler() { }
1194 QAbstractFileEngine *create(const QString &path) const;
1195};
1196QAbstractFileEngine *QResourceFileEngineHandler::create(const QString &path) const
1197{
1198 if (path.size() > 0 && path.startsWith(QLatin1Char(':')))
1199 return new QResourceFileEngine(path);
1200 return 0;
1201}
1202
1203//resource engine
1204class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
1205{
1206protected:
1207 Q_DECLARE_PUBLIC(QResourceFileEngine)
1208private:
1209 uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1210 bool unmap(uchar *ptr);
1211 qint64 offset;
1212 QResource resource;
1213 QByteArray uncompressed;
1214protected:
1215 QResourceFileEnginePrivate() : offset(0) { }
1216};
1217
1218bool QResourceFileEngine::mkdir(const QString &, bool) const
1219{
1220 return false;
1221}
1222
1223bool QResourceFileEngine::rmdir(const QString &, bool) const
1224{
1225 return false;
1226}
1227
1228bool QResourceFileEngine::setSize(qint64)
1229{
1230 return false;
1231}
1232
1233QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
1234{
1235 return QAbstractFileEngine::entryList(filters, filterNames);
1236}
1237
1238bool QResourceFileEngine::caseSensitive() const
1239{
1240 return true;
1241}
1242
1243QResourceFileEngine::QResourceFileEngine(const QString &file) :
1244 QAbstractFileEngine(*new QResourceFileEnginePrivate)
1245{
1246 Q_D(QResourceFileEngine);
1247 d->resource.setFileName(file);
1248 if(d->resource.isCompressed() && d->resource.size()) {
1249#ifndef QT_NO_COMPRESS
1250 d->uncompressed = qUncompress(d->resource.data(), d->resource.size());
1251#else
1252 Q_ASSERT(!"QResourceFileEngine::open: Qt built without support for compression");
1253#endif
1254 }
1255}
1256
1257QResourceFileEngine::~QResourceFileEngine()
1258{
1259}
1260
1261void QResourceFileEngine::setFileName(const QString &file)
1262{
1263 Q_D(QResourceFileEngine);
1264 d->resource.setFileName(file);
1265}
1266
1267bool QResourceFileEngine::open(QIODevice::OpenMode flags)
1268{
1269 Q_D(QResourceFileEngine);
1270 if (d->resource.fileName().isEmpty()) {
1271 qWarning("QResourceFileEngine::open: Missing file name");
1272 return false;
1273 }
1274 if(flags & QIODevice::WriteOnly)
1275 return false;
1276 if(!d->resource.isValid())
1277 return false;
1278 return true;
1279}
1280
1281bool QResourceFileEngine::close()
1282{
1283 Q_D(QResourceFileEngine);
1284 d->offset = 0;
1285 d->uncompressed.clear();
1286 return true;
1287}
1288
1289bool QResourceFileEngine::flush()
1290{
1291 return true;
1292}
1293
1294qint64 QResourceFileEngine::read(char *data, qint64 len)
1295{
1296 Q_D(QResourceFileEngine);
1297 if(len > size()-d->offset)
1298 len = size()-d->offset;
1299 if(len <= 0)
1300 return 0;
1301 if(d->resource.isCompressed())
1302 memcpy(data, d->uncompressed.constData()+d->offset, len);
1303 else
1304 memcpy(data, d->resource.data()+d->offset, len);
1305 d->offset += len;
1306 return len;
1307}
1308
1309qint64 QResourceFileEngine::write(const char *, qint64)
1310{
1311 return -1;
1312}
1313
1314bool QResourceFileEngine::remove()
1315{
1316 return false;
1317}
1318
1319bool QResourceFileEngine::copy(const QString &)
1320{
1321 return false;
1322}
1323
1324bool QResourceFileEngine::rename(const QString &)
1325{
1326 return false;
1327}
1328
1329bool QResourceFileEngine::link(const QString &)
1330{
1331 return false;
1332}
1333
1334qint64 QResourceFileEngine::size() const
1335{
1336 Q_D(const QResourceFileEngine);
1337 if(!d->resource.isValid())
1338 return 0;
1339 if(d->resource.isCompressed())
1340 return d->uncompressed.size();
1341 return d->resource.size();
1342}
1343
1344qint64 QResourceFileEngine::pos() const
1345{
1346 Q_D(const QResourceFileEngine);
1347 return d->offset;
1348}
1349
1350bool QResourceFileEngine::atEnd() const
1351{
1352 Q_D(const QResourceFileEngine);
1353 if(!d->resource.isValid())
1354 return true;
1355 return d->offset == size();
1356}
1357
1358bool QResourceFileEngine::seek(qint64 pos)
1359{
1360 Q_D(QResourceFileEngine);
1361 if(!d->resource.isValid())
1362 return false;
1363
1364 if(d->offset > size())
1365 return false;
1366 d->offset = pos;
1367 return true;
1368}
1369
1370bool QResourceFileEngine::isSequential() const
1371{
1372 return false;
1373}
1374
1375QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1376{
1377 Q_D(const QResourceFileEngine);
1378 QAbstractFileEngine::FileFlags ret = 0;
1379 if(!d->resource.isValid())
1380 return ret;
1381
1382 if(type & PermsMask)
1383 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm);
1384 if(type & TypesMask) {
1385 if(d->resource.isDir())
1386 ret |= DirectoryType;
1387 else
1388 ret |= FileType;
1389 }
1390 if(type & FlagsMask) {
1391 ret |= ExistsFlag;
1392 if(d->resource.absoluteFilePath() == QLatin1String(":/"))
1393 ret |= RootFlag;
1394 }
1395 return ret;
1396}
1397
1398bool QResourceFileEngine::setPermissions(uint)
1399{
1400 return false;
1401}
1402
1403QString QResourceFileEngine::fileName(FileName file) const
1404{
1405 Q_D(const QResourceFileEngine);
1406 if(file == BaseName) {
1407 int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
1408 if (slash == -1)
1409 return d->resource.fileName();
1410 return d->resource.fileName().mid(slash + 1);
1411 } else if(file == PathName || file == AbsolutePathName) {
1412 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath() : d->resource.fileName();
1413 const int slash = path.lastIndexOf(QLatin1Char('/'));
1414 if (slash == -1)
1415 return QLatin1String(":");
1416 else if (slash <= 1)
1417 return QLatin1String(":/");
1418 return path.left(slash);
1419
1420 } else if(file == CanonicalName || file == CanonicalPathName) {
1421 const QString absoluteFilePath = d->resource.absoluteFilePath();
1422 if(file == CanonicalPathName) {
1423 const int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
1424 if (slash != -1)
1425 return absoluteFilePath.left(slash);
1426 }
1427 return absoluteFilePath;
1428 }
1429 return d->resource.fileName();
1430}
1431
1432bool QResourceFileEngine::isRelativePath() const
1433{
1434 return false;
1435}
1436
1437uint QResourceFileEngine::ownerId(FileOwner) const
1438{
1439 static const uint nobodyID = (uint) -2;
1440 return nobodyID;
1441}
1442
1443QString QResourceFileEngine::owner(FileOwner) const
1444{
1445 return QString();
1446}
1447
1448QDateTime QResourceFileEngine::fileTime(FileTime) const
1449{
1450 return QDateTime();
1451}
1452
1453/*!
1454 \internal
1455*/
1456QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
1457 const QStringList &filterNames)
1458{
1459 return new QResourceFileEngineIterator(filters, filterNames);
1460}
1461
1462/*!
1463 \internal
1464*/
1465QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
1466{
1467 return 0;
1468}
1469
1470bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
1471{
1472 Q_D(QResourceFileEngine);
1473 if (extension == MapExtension) {
1474 const MapExtensionOption *options = (MapExtensionOption*)(option);
1475 MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
1476 returnValue->address = d->map(options->offset, options->size, options->flags);
1477 return (returnValue->address != 0);
1478 }
1479 if (extension == UnMapExtension) {
1480 UnMapExtensionOption *options = (UnMapExtensionOption*)option;
1481 return d->unmap(options->address);
1482 }
1483 return false;
1484}
1485
1486bool QResourceFileEngine::supportsExtension(Extension extension) const
1487{
1488 return (extension == UnMapExtension || extension == MapExtension);
1489}
1490
1491uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1492{
1493 Q_Q(QResourceFileEngine);
1494 Q_UNUSED(flags);
1495 if (offset < 0 || size <= 0 || !resource.isValid() || offset + size > resource.size()) {
1496 q->setError(QFile::UnspecifiedError, QString());
1497 return 0;
1498 }
1499 uchar *address = const_cast<uchar *>(resource.data());
1500 return (address + offset);
1501}
1502
1503bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1504{
1505 Q_UNUSED(ptr);
1506 return true;
1507}
1508
1509//Initialization and cleanup
1510Q_GLOBAL_STATIC(QResourceFileEngineHandler, resource_file_handler)
1511
1512static int qt_force_resource_init() { resource_file_handler(); return 1; }
1513Q_CORE_EXPORT void qInitResourceIO() { resource_file_handler(); }
1514static int qt_forced_resource_init = qt_force_resource_init();
1515Q_CONSTRUCTOR_FUNCTION(qt_force_resource_init)
1516
1517QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.