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

Last change on this file since 613 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 44.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 through the 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 Adds \a path to the search paths searched in to find resources that are
559 not specified with an absolute path. The \a path must be an absolute
560 path (start with \c{/}).
561
562 The default search path is to search only in the root (\c{:/}). The last
563 path added will be consulted first upon next QResource creation.
564
565 Use QDir::addSearchPath() with a prefix instead.
566*/
567
568void
569QResource::addSearchPath(const QString &path)
570{
571 if (!path.startsWith(QLatin1Char('/'))) {
572 qWarning("QResource::addResourceSearchPath: Search paths must be absolute (start with /) [%s]",
573 path.toLocal8Bit().data());
574 return;
575 }
576 QMutexLocker lock(resourceMutex());
577 resourceSearchPaths()->prepend(path);
578}
579
580/*!
581 Returns the current search path list. This list is consulted when
582 creating a relative resource.
583
584 \sa QDir::addSearchPath() QDir::setSearchPaths()
585*/
586
587QStringList
588QResource::searchPaths()
589{
590 QMutexLocker lock(resourceMutex());
591 return *resourceSearchPaths();
592}
593
594inline int QResourceRoot::hash(int node) const
595{
596 if(!node) //root
597 return 0;
598 const int offset = findOffset(node);
599 int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
600 (tree[offset+2] << 8) + (tree[offset+3] << 0);
601 name_offset += 2; //jump past name length
602 return (names[name_offset+0] << 24) + (names[name_offset+1] << 16) +
603 (names[name_offset+2] << 8) + (names[name_offset+3] << 0);
604}
605inline QString QResourceRoot::name(int node) const
606{
607 if(!node) // root
608 return QString();
609 const int offset = findOffset(node);
610
611 QString ret;
612 int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
613 (tree[offset+2] << 8) + (tree[offset+3] << 0);
614 const short name_length = (names[name_offset+0] << 8) +
615 (names[name_offset+1] << 0);
616 name_offset += 2;
617 name_offset += 4; //jump past hash
618
619 ret.resize(name_length);
620 QChar *strData = ret.data();
621 for(int i = 0; i < name_length*2; i+=2) {
622 QChar c(names[name_offset+i+1], names[name_offset+i]);
623 *strData = c;
624 ++strData;
625 }
626 return ret;
627}
628
629int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
630{
631 QString path = _path;
632 {
633 QString root = mappingRoot();
634 if(!root.isEmpty()) {
635 if(root == path) {
636 path = QLatin1Char('/');
637 } else {
638 if(!root.endsWith(QLatin1Char('/')))
639 root += QLatin1Char('/');
640 if(path.size() >= root.size() && path.startsWith(root))
641 path = path.mid(root.length()-1);
642 if(path.isEmpty())
643 path = QLatin1Char('/');
644 }
645 }
646 }
647#ifdef DEBUG_RESOURCE_MATCH
648 qDebug() << "!!!!" << "START" << path << locale.country() << locale.language();
649#endif
650
651 if(path == QLatin1String("/"))
652 return 0;
653
654 //the root node is always first
655 int child_count = (tree[6] << 24) + (tree[7] << 16) +
656 (tree[8] << 8) + (tree[9] << 0);
657 int child = (tree[10] << 24) + (tree[11] << 16) +
658 (tree[12] << 8) + (tree[13] << 0);
659
660 //now iterate up the tree
661 int node = -1;
662
663 QStringSplitter splitter(path);
664 while (child_count && splitter.hasNext()) {
665 QStringRef segment = splitter.next();
666
667#ifdef DEBUG_RESOURCE_MATCH
668 qDebug() << " CHILDREN" << segment;
669 for(int j = 0; j < child_count; ++j) {
670 qDebug() << " " << child+j << " :: " << name(child+j);
671 }
672#endif
673 const int h = qHash(segment);
674
675 //do the binary search for the hash
676 int l = 0, r = child_count-1;
677 int sub_node = (l+r+1)/2;
678 while(r != l) {
679 const int sub_node_hash = hash(child+sub_node);
680 if(h == sub_node_hash)
681 break;
682 else if(h < sub_node_hash)
683 r = sub_node - 1;
684 else
685 l = sub_node;
686 sub_node = (l + r + 1) / 2;
687 }
688 sub_node += child;
689
690 //now do the "harder" compares
691 bool found = false;
692 if(hash(sub_node) == h) {
693 while(sub_node > child && hash(sub_node-1) == h) //backup for collisions
694 --sub_node;
695 for(; sub_node < child+child_count && hash(sub_node) == h; ++sub_node) { //here we go...
696 if(name(sub_node) == segment) {
697 found = true;
698 int offset = findOffset(sub_node);
699#ifdef DEBUG_RESOURCE_MATCH
700 qDebug() << " TRY" << sub_node << name(sub_node) << offset;
701#endif
702 offset += 4; //jump past name
703
704 const short flags = (tree[offset+0] << 8) +
705 (tree[offset+1] << 0);
706 offset += 2;
707
708 if(!splitter.hasNext()) {
709 if(!(flags & Directory)) {
710 const short country = (tree[offset+0] << 8) +
711 (tree[offset+1] << 0);
712 offset += 2;
713
714 const short language = (tree[offset+0] << 8) +
715 (tree[offset+1] << 0);
716 offset += 2;
717#ifdef DEBUG_RESOURCE_MATCH
718 qDebug() << " " << "LOCALE" << country << language;
719#endif
720 if(country == locale.country() && language == locale.language()) {
721#ifdef DEBUG_RESOURCE_MATCH
722 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
723#endif
724 return sub_node;
725 } else if((country == QLocale::AnyCountry && language == locale.language()) ||
726 (country == QLocale::AnyCountry && language == QLocale::C && node == -1)) {
727 node = sub_node;
728 }
729 continue;
730 } else {
731#ifdef DEBUG_RESOURCE_MATCH
732 qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
733#endif
734
735 return sub_node;
736 }
737 }
738
739 if(!(flags & Directory))
740 return -1;
741
742 child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
743 (tree[offset+2] << 8) + (tree[offset+3] << 0);
744 offset += 4;
745 child = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
746 (tree[offset+2] << 8) + (tree[offset+3] << 0);
747 break;
748 }
749 }
750 }
751 if(!found)
752 break;
753 }
754#ifdef DEBUG_RESOURCE_MATCH
755 qDebug() << "!!!!" << "FINISHED" << __LINE__ << node;
756#endif
757 return node;
758}
759short QResourceRoot::flags(int node) const
760{
761 if(node == -1)
762 return 0;
763 const int offset = findOffset(node) + 4; //jump past name
764 return (tree[offset+0] << 8) + (tree[offset+1] << 0);
765}
766const uchar *QResourceRoot::data(int node, qint64 *size) const
767{
768 if(node == -1) {
769 *size = 0;
770 return 0;
771 }
772 int offset = findOffset(node) + 4; //jump past name
773
774 const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
775 offset += 2;
776
777 offset += 4; //jump past locale
778
779 if(!(flags & Directory)) {
780 const int data_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
781 (tree[offset+2] << 8) + (tree[offset+3] << 0);
782 const uint data_length = (payloads[data_offset+0] << 24) + (payloads[data_offset+1] << 16) +
783 (payloads[data_offset+2] << 8) + (payloads[data_offset+3] << 0);
784 const uchar *ret = payloads+data_offset+4;
785 *size = data_length;
786 return ret;
787 }
788 *size = 0;
789 return 0;
790}
791QStringList QResourceRoot::children(int node) const
792{
793 if(node == -1)
794 return QStringList();
795 int offset = findOffset(node) + 4; //jump past name
796
797 const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
798 offset += 2;
799
800 QStringList ret;
801 if(flags & Directory) {
802 const int child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
803 (tree[offset+2] << 8) + (tree[offset+3] << 0);
804 offset += 4;
805 const int child_off = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
806 (tree[offset+2] << 8) + (tree[offset+3] << 0);
807 for(int i = child_off; i < child_off+child_count; ++i)
808 ret << name(i);
809 }
810 return ret;
811}
812bool QResourceRoot::mappingRootSubdir(const QString &path, QString *match) const
813{
814 const QString root = mappingRoot();
815 if(!root.isEmpty()) {
816 const QStringList root_segments = root.split(QLatin1Char('/'), QString::SkipEmptyParts),
817 path_segments = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
818 if(path_segments.size() <= root_segments.size()) {
819 int matched = 0;
820 for(int i = 0; i < path_segments.size(); ++i) {
821 if(root_segments[i] != path_segments[i])
822 break;
823 ++matched;
824 }
825 if(matched == path_segments.size()) {
826 if(match && root_segments.size() > matched)
827 *match = root_segments.at(matched);
828 return true;
829 }
830 }
831 }
832 return false;
833}
834
835Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
836 const unsigned char *name, const unsigned char *data)
837{
838 QMutexLocker lock(resourceMutex());
839 if(version == 0x01 && resourceList()) {
840 bool found = false;
841 QResourceRoot res(tree, name, data);
842 for(int i = 0; i < resourceList()->size(); ++i) {
843 if(*resourceList()->at(i) == res) {
844 found = true;
845 break;
846 }
847 }
848 if(!found) {
849 QResourceRoot *root = new QResourceRoot(tree, name, data);
850 root->ref.ref();
851 resourceList()->append(root);
852 }
853 return true;
854 }
855 return false;
856}
857
858Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tree,
859 const unsigned char *name, const unsigned char *data)
860{
861 QMutexLocker lock(resourceMutex());
862 if(version == 0x01 && resourceList()) {
863 QResourceRoot res(tree, name, data);
864 for(int i = 0; i < resourceList()->size(); ) {
865 if(*resourceList()->at(i) == res) {
866 QResourceRoot *root = resourceList()->takeAt(i);
867 if(!root->ref.deref())
868 delete root;
869 } else {
870 ++i;
871 }
872 }
873 return true;
874 }
875 return false;
876}
877
878//run time resource creation
879
880class QDynamicBufferResourceRoot: public QResourceRoot
881{
882 QString root;
883 const uchar *buffer;
884
885public:
886 inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(0) { }
887 inline ~QDynamicBufferResourceRoot() { }
888 inline const uchar *mappingBuffer() const { return buffer; }
889 virtual QString mappingRoot() const { return root; }
890 virtual ResourceRootType type() const { return Resource_Buffer; }
891
892 bool registerSelf(const uchar *b) {
893 //setup the data now
894 int offset = 0;
895
896 //magic number
897 if(b[offset+0] != 'q' || b[offset+1] != 'r' ||
898 b[offset+2] != 'e' || b[offset+3] != 's') {
899 return false;
900 }
901 offset += 4;
902
903 const int version = (b[offset+0] << 24) + (b[offset+1] << 16) +
904 (b[offset+2] << 8) + (b[offset+3] << 0);
905 offset += 4;
906
907 const int tree_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
908 (b[offset+2] << 8) + (b[offset+3] << 0);
909 offset += 4;
910
911 const int data_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
912 (b[offset+2] << 8) + (b[offset+3] << 0);
913 offset += 4;
914
915 const int name_offset = (b[offset+0] << 24) + (b[offset+1] << 16) +
916 (b[offset+2] << 8) + (b[offset+3] << 0);
917 offset += 4;
918
919 if(version == 0x01) {
920 buffer = b;
921 setSource(b+tree_offset, b+name_offset, b+data_offset);
922 return true;
923 }
924 return false;
925 }
926};
927
928#if defined(Q_OS_UNIX)
929#define QT_USE_MMAP
930#endif
931
932// most of the headers below are already included in qplatformdefs.h
933// also this lacks Large File support but that's probably irrelevant
934#if defined(QT_USE_MMAP)
935// for mmap
936QT_BEGIN_INCLUDE_NAMESPACE
937#include <sys/mman.h>
938#include <errno.h>
939QT_END_INCLUDE_NAMESPACE
940#endif
941
942
943
944class QDynamicFileResourceRoot: public QDynamicBufferResourceRoot
945{
946 QString fileName;
947 // for mmap'ed files, this is what needs to be unmapped.
948 uchar *unmapPointer;
949 unsigned int unmapLength;
950
951public:
952 inline QDynamicFileResourceRoot(const QString &_root) : QDynamicBufferResourceRoot(_root), unmapPointer(0), unmapLength(0) { }
953 ~QDynamicFileResourceRoot() {
954#if defined(QT_USE_MMAP)
955 if (unmapPointer) {
956 munmap((char*)unmapPointer, unmapLength);
957 unmapPointer = 0;
958 unmapLength = 0;
959 } else
960#endif
961 {
962 delete [] (uchar *)mappingBuffer();
963 }
964 }
965 QString mappingFile() const { return fileName; }
966 virtual ResourceRootType type() const { return Resource_File; }
967
968 bool registerSelf(const QString &f) {
969 bool fromMM = false;
970 uchar *data = 0;
971 unsigned int data_len = 0;
972
973#ifdef QT_USE_MMAP
974
975#ifndef MAP_FILE
976#define MAP_FILE 0
977#endif
978#ifndef MAP_FAILED
979#define MAP_FAILED -1
980#endif
981
982 int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY,
983#if defined(Q_OS_WIN)
984 _S_IREAD | _S_IWRITE
985#else
986 0666
987#endif
988 );
989 if (fd >= 0) {
990 QT_STATBUF st;
991 if (!QT_FSTAT(fd, &st)) {
992 uchar *ptr;
993 ptr = reinterpret_cast<uchar *>(
994 mmap(0, st.st_size, // any address, whole file
995 PROT_READ, // read-only memory
996 MAP_FILE | MAP_PRIVATE, // swap-backed map from file
997 fd, 0)); // from offset 0 of fd
998 if (ptr && ptr != reinterpret_cast<uchar *>(MAP_FAILED)) {
999 data = ptr;
1000 data_len = st.st_size;
1001 fromMM = true;
1002 }
1003 }
1004 ::close(fd);
1005 }
1006#endif // QT_USE_MMAP
1007 if(!data) {
1008 QFile file(f);
1009 if (!file.exists())
1010 return false;
1011 data_len = file.size();
1012 data = new uchar[data_len];
1013
1014 bool ok = false;
1015 if (file.open(QIODevice::ReadOnly))
1016 ok = (data_len == (uint)file.read((char*)data, data_len));
1017 if (!ok) {
1018 delete [] data;
1019 data = 0;
1020 data_len = 0;
1021 return false;
1022 }
1023 fromMM = false;
1024 }
1025 if(data && QDynamicBufferResourceRoot::registerSelf(data)) {
1026 if(fromMM) {
1027 unmapPointer = data;
1028 unmapLength = data_len;
1029 }
1030 fileName = f;
1031 return true;
1032 }
1033 return false;
1034 }
1035};
1036
1037static QString qt_resource_fixResourceRoot(QString r) {
1038 if(!r.isEmpty()) {
1039 if(r.startsWith(QLatin1Char(':')))
1040 r = r.mid(1);
1041 if(!r.isEmpty())
1042 r = QDir::cleanPath(r);
1043 }
1044 return r;
1045}
1046
1047
1048/*!
1049 \fn bool QResource::registerResource(const QString &rccFileName, const QString &mapRoot)
1050
1051 Registers the resource with the given \a rccFileName at the location in the
1052 resource tree specified by \a mapRoot, and returns true if the file is
1053 successfully opened; otherwise returns false.
1054
1055 \sa unregisterResource()
1056*/
1057
1058bool
1059QResource::registerResource(const QString &rccFilename, const QString &resourceRoot)
1060{
1061 QString r = qt_resource_fixResourceRoot(resourceRoot);
1062 if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
1063 qWarning("QDir::registerResource: Registering a resource [%s] must be rooted in an absolute path (start with /) [%s]",
1064 rccFilename.toLocal8Bit().data(), resourceRoot.toLocal8Bit().data());
1065 return false;
1066 }
1067
1068 QDynamicFileResourceRoot *root = new QDynamicFileResourceRoot(r);
1069 if(root->registerSelf(rccFilename)) {
1070 root->ref.ref();
1071 QMutexLocker lock(resourceMutex());
1072 resourceList()->append(root);
1073 return true;
1074 }
1075 delete root;
1076 return false;
1077}
1078
1079/*!
1080 \fn bool QResource::unregisterResource(const QString &rccFileName, const QString &mapRoot)
1081
1082 Unregisters the resource with the given \a rccFileName at the location in
1083 the resource tree specified by \a mapRoot, and returns true if the
1084 resource is successfully unloaded and no references exist for the
1085 resource; otherwise returns false.
1086
1087 \sa registerResource()
1088*/
1089
1090bool
1091QResource::unregisterResource(const QString &rccFilename, const QString &resourceRoot)
1092{
1093 QString r = qt_resource_fixResourceRoot(resourceRoot);
1094
1095 QMutexLocker lock(resourceMutex());
1096 ResourceList *list = resourceList();
1097 for(int i = 0; i < list->size(); ++i) {
1098 QResourceRoot *res = list->at(i);
1099 if(res->type() == QResourceRoot::Resource_File) {
1100 QDynamicFileResourceRoot *root = reinterpret_cast<QDynamicFileResourceRoot*>(res);
1101 if(root->mappingFile() == rccFilename && root->mappingRoot() == r) {
1102 resourceList()->removeAt(i);
1103 if(!root->ref.deref()) {
1104 delete root;
1105 return true;
1106 }
1107 return false;
1108 }
1109 }
1110 }
1111 return false;
1112}
1113
1114
1115/*!
1116 \fn bool QResource::registerResource(const uchar *rccData, const QString &mapRoot)
1117 \since 4.3
1118
1119 Registers the resource with the given \a rccData at the location in the
1120 resource tree specified by \a mapRoot, and returns true if the file is
1121 successfully opened; otherwise returns false.
1122
1123 \warning The data must remain valid throughout the life of any QFile
1124 that may reference the resource data.
1125
1126 \sa unregisterResource()
1127*/
1128
1129bool
1130QResource::registerResource(const uchar *rccData, const QString &resourceRoot)
1131{
1132 QString r = qt_resource_fixResourceRoot(resourceRoot);
1133 if(!r.isEmpty() && r[0] != QLatin1Char('/')) {
1134 qWarning("QDir::registerResource: Registering a resource [%p] must be rooted in an absolute path (start with /) [%s]",
1135 rccData, resourceRoot.toLocal8Bit().data());
1136 return false;
1137 }
1138
1139 QDynamicBufferResourceRoot *root = new QDynamicBufferResourceRoot(r);
1140 if(root->registerSelf(rccData)) {
1141 root->ref.ref();
1142 QMutexLocker lock(resourceMutex());
1143 resourceList()->append(root);
1144 return true;
1145 }
1146 delete root;
1147 return false;
1148}
1149
1150/*!
1151 \fn bool QResource::unregisterResource(const uchar *rccData, const QString &mapRoot)
1152 \since 4.3
1153
1154 Unregisters the resource with the given \a rccData at the location in the
1155 resource tree specified by \a mapRoot, and returns true if the resource is
1156 successfully unloaded and no references exist into the resource; otherwise returns false.
1157
1158 \sa registerResource()
1159*/
1160
1161bool
1162QResource::unregisterResource(const uchar *rccData, const QString &resourceRoot)
1163{
1164 QString r = qt_resource_fixResourceRoot(resourceRoot);
1165
1166 QMutexLocker lock(resourceMutex());
1167 ResourceList *list = resourceList();
1168 for(int i = 0; i < list->size(); ++i) {
1169 QResourceRoot *res = list->at(i);
1170 if(res->type() == QResourceRoot::Resource_Buffer) {
1171 QDynamicBufferResourceRoot *root = reinterpret_cast<QDynamicBufferResourceRoot*>(res);
1172 if(root->mappingBuffer() == rccData && root->mappingRoot() == r) {
1173 resourceList()->removeAt(i);
1174 if(!root->ref.deref()) {
1175 delete root;
1176 return true;
1177 }
1178 return false;
1179 }
1180 }
1181 }
1182 return false;
1183}
1184
1185//file type handler
1186class QResourceFileEngineHandler : public QAbstractFileEngineHandler
1187{
1188public:
1189 QResourceFileEngineHandler() { }
1190 ~QResourceFileEngineHandler() { }
1191 QAbstractFileEngine *create(const QString &path) const;
1192};
1193QAbstractFileEngine *QResourceFileEngineHandler::create(const QString &path) const
1194{
1195 if (path.size() > 0 && path.startsWith(QLatin1Char(':')))
1196 return new QResourceFileEngine(path);
1197 return 0;
1198}
1199
1200//resource engine
1201class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
1202{
1203protected:
1204 Q_DECLARE_PUBLIC(QResourceFileEngine)
1205private:
1206 uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
1207 bool unmap(uchar *ptr);
1208 qint64 offset;
1209 QResource resource;
1210 QByteArray uncompressed;
1211protected:
1212 QResourceFileEnginePrivate() : offset(0) { }
1213};
1214
1215bool QResourceFileEngine::mkdir(const QString &, bool) const
1216{
1217 return false;
1218}
1219
1220bool QResourceFileEngine::rmdir(const QString &, bool) const
1221{
1222 return false;
1223}
1224
1225bool QResourceFileEngine::setSize(qint64)
1226{
1227 return false;
1228}
1229
1230QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
1231{
1232 return QAbstractFileEngine::entryList(filters, filterNames);
1233}
1234
1235bool QResourceFileEngine::caseSensitive() const
1236{
1237 return true;
1238}
1239
1240QResourceFileEngine::QResourceFileEngine(const QString &file) :
1241 QAbstractFileEngine(*new QResourceFileEnginePrivate)
1242{
1243 Q_D(QResourceFileEngine);
1244 d->resource.setFileName(file);
1245 if(d->resource.isCompressed() && d->resource.size()) {
1246#ifndef QT_NO_COMPRESS
1247 d->uncompressed = qUncompress(d->resource.data(), d->resource.size());
1248#else
1249 Q_ASSERT(!"QResourceFileEngine::open: Qt built without support for compression");
1250#endif
1251 }
1252}
1253
1254QResourceFileEngine::~QResourceFileEngine()
1255{
1256}
1257
1258void QResourceFileEngine::setFileName(const QString &file)
1259{
1260 Q_D(QResourceFileEngine);
1261 d->resource.setFileName(file);
1262}
1263
1264bool QResourceFileEngine::open(QIODevice::OpenMode flags)
1265{
1266 Q_D(QResourceFileEngine);
1267 if (d->resource.fileName().isEmpty()) {
1268 qWarning("QResourceFileEngine::open: Missing file name");
1269 return false;
1270 }
1271 if(flags & QIODevice::WriteOnly)
1272 return false;
1273 if(!d->resource.isValid())
1274 return false;
1275 return true;
1276}
1277
1278bool QResourceFileEngine::close()
1279{
1280 Q_D(QResourceFileEngine);
1281 d->offset = 0;
1282 d->uncompressed.clear();
1283 return true;
1284}
1285
1286bool QResourceFileEngine::flush()
1287{
1288 return false;
1289}
1290
1291qint64 QResourceFileEngine::read(char *data, qint64 len)
1292{
1293 Q_D(QResourceFileEngine);
1294 if(len > size()-d->offset)
1295 len = size()-d->offset;
1296 if(len <= 0)
1297 return 0;
1298 if(d->resource.isCompressed())
1299 memcpy(data, d->uncompressed.constData()+d->offset, len);
1300 else
1301 memcpy(data, d->resource.data()+d->offset, len);
1302 d->offset += len;
1303 return len;
1304}
1305
1306qint64 QResourceFileEngine::write(const char *, qint64)
1307{
1308 return -1;
1309}
1310
1311bool QResourceFileEngine::remove()
1312{
1313 return false;
1314}
1315
1316bool QResourceFileEngine::copy(const QString &)
1317{
1318 return false;
1319}
1320
1321bool QResourceFileEngine::rename(const QString &)
1322{
1323 return false;
1324}
1325
1326bool QResourceFileEngine::link(const QString &)
1327{
1328 return false;
1329}
1330
1331qint64 QResourceFileEngine::size() const
1332{
1333 Q_D(const QResourceFileEngine);
1334 if(!d->resource.isValid())
1335 return 0;
1336 if(d->resource.isCompressed())
1337 return d->uncompressed.size();
1338 return d->resource.size();
1339}
1340
1341qint64 QResourceFileEngine::pos() const
1342{
1343 Q_D(const QResourceFileEngine);
1344 return d->offset;
1345}
1346
1347bool QResourceFileEngine::atEnd() const
1348{
1349 Q_D(const QResourceFileEngine);
1350 if(!d->resource.isValid())
1351 return true;
1352 return d->offset == size();
1353}
1354
1355bool QResourceFileEngine::seek(qint64 pos)
1356{
1357 Q_D(QResourceFileEngine);
1358 if(!d->resource.isValid())
1359 return false;
1360
1361 if(d->offset > size())
1362 return false;
1363 d->offset = pos;
1364 return true;
1365}
1366
1367bool QResourceFileEngine::isSequential() const
1368{
1369 return false;
1370}
1371
1372QAbstractFileEngine::FileFlags QResourceFileEngine::fileFlags(QAbstractFileEngine::FileFlags type) const
1373{
1374 Q_D(const QResourceFileEngine);
1375 QAbstractFileEngine::FileFlags ret = 0;
1376 if(!d->resource.isValid())
1377 return ret;
1378
1379 if(type & PermsMask)
1380 ret |= QAbstractFileEngine::FileFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm);
1381 if(type & TypesMask) {
1382 if(d->resource.isDir())
1383 ret |= DirectoryType;
1384 else
1385 ret |= FileType;
1386 }
1387 if(type & FlagsMask) {
1388 ret |= ExistsFlag;
1389 if(d->resource.absoluteFilePath() == QLatin1String(":/"))
1390 ret |= RootFlag;
1391 }
1392 return ret;
1393}
1394
1395bool QResourceFileEngine::setPermissions(uint)
1396{
1397 return false;
1398}
1399
1400QString QResourceFileEngine::fileName(FileName file) const
1401{
1402 Q_D(const QResourceFileEngine);
1403 if(file == BaseName) {
1404 int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
1405 if (slash == -1)
1406 return d->resource.fileName();
1407 return d->resource.fileName().mid(slash + 1);
1408 } else if(file == PathName || file == AbsolutePathName) {
1409 const QString path = (file == AbsolutePathName) ? d->resource.absoluteFilePath() : d->resource.fileName();
1410 const int slash = path.lastIndexOf(QLatin1Char('/'));
1411 if (slash != -1)
1412 return path.left(slash);
1413 } else if(file == CanonicalName || file == CanonicalPathName) {
1414 const QString absoluteFilePath = d->resource.absoluteFilePath();
1415 if(file == CanonicalPathName) {
1416 const int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
1417 if (slash != -1)
1418 return absoluteFilePath.left(slash);
1419 }
1420 return absoluteFilePath;
1421 }
1422 return d->resource.fileName();
1423}
1424
1425bool QResourceFileEngine::isRelativePath() const
1426{
1427 return false;
1428}
1429
1430uint QResourceFileEngine::ownerId(FileOwner) const
1431{
1432 static const uint nobodyID = (uint) -2;
1433 return nobodyID;
1434}
1435
1436QString QResourceFileEngine::owner(FileOwner) const
1437{
1438 return QString();
1439}
1440
1441QDateTime QResourceFileEngine::fileTime(FileTime) const
1442{
1443 return QDateTime();
1444}
1445
1446/*!
1447 \internal
1448*/
1449QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
1450 const QStringList &filterNames)
1451{
1452 return new QResourceFileEngineIterator(filters, filterNames);
1453}
1454
1455/*!
1456 \internal
1457*/
1458QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
1459{
1460 return 0;
1461}
1462
1463bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
1464{
1465 Q_D(QResourceFileEngine);
1466 if (extension == MapExtension) {
1467 const MapExtensionOption *options = (MapExtensionOption*)(option);
1468 MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
1469 returnValue->address = d->map(options->offset, options->size, options->flags);
1470 return (returnValue->address != 0);
1471 }
1472 if (extension == UnMapExtension) {
1473 UnMapExtensionOption *options = (UnMapExtensionOption*)option;
1474 return d->unmap(options->address);
1475 }
1476 return false;
1477}
1478
1479bool QResourceFileEngine::supportsExtension(Extension extension) const
1480{
1481 return (extension == UnMapExtension || extension == MapExtension);
1482}
1483
1484uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
1485{
1486 Q_Q(QResourceFileEngine);
1487 Q_UNUSED(flags);
1488 if (offset < 0 || size <= 0 || !resource.isValid() || offset + size > resource.size()) {
1489 q->setError(QFile::UnspecifiedError, QString());
1490 return 0;
1491 }
1492 uchar *address = const_cast<uchar *>(resource.data());
1493 return (address + offset);
1494}
1495
1496bool QResourceFileEnginePrivate::unmap(uchar *ptr)
1497{
1498 Q_UNUSED(ptr);
1499 return true;
1500}
1501
1502//Initialization and cleanup
1503Q_GLOBAL_STATIC(QResourceFileEngineHandler, resource_file_handler)
1504
1505static int qt_force_resource_init() { resource_file_handler(); return 1; }
1506Q_CORE_EXPORT void qInitResourceIO() { resource_file_handler(); }
1507static int qt_forced_resource_init = qt_force_resource_init();
1508Q_CONSTRUCTOR_FUNCTION(qt_force_resource_init)
1509
1510QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.