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

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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