src/corelib/io/qresource.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtCore module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qresource.h"
       
    43 #include "qresource_p.h"
       
    44 #include "qresource_iterator_p.h"
       
    45 #include "qset.h"
       
    46 #include "qhash.h"
       
    47 #include "qmutex.h"
       
    48 #include "qdebug.h"
       
    49 #include "qlocale.h"
       
    50 #include "qglobal.h"
       
    51 #include "qvector.h"
       
    52 #include "qdatetime.h"
       
    53 #include "qbytearray.h"
       
    54 #include "qstringlist.h"
       
    55 #include <qshareddata.h>
       
    56 #include <qplatformdefs.h>
       
    57 #include "private/qabstractfileengine_p.h"
       
    58 
       
    59 #ifdef Q_OS_UNIX
       
    60 # include "private/qcore_unix_p.h"
       
    61 #endif
       
    62 
       
    63 //#define DEBUG_RESOURCE_MATCH
       
    64 
       
    65 #if defined(Q_OS_VXWORKS)
       
    66 #  if defined(m_data)
       
    67 #    undef m_data
       
    68 #  endif
       
    69 #  if defined(m_len)
       
    70 #    undef m_len
       
    71 #  endif
       
    72 #endif
       
    73 
       
    74 QT_BEGIN_NAMESPACE
       
    75 
       
    76 
       
    77 class QStringSplitter
       
    78 {
       
    79 public:
       
    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
       
   108 class 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;
       
   120 public:
       
   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 
       
   140 protected:
       
   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 
       
   148 static QString cleanPath(const QString &_path)
       
   149 {
       
   150     QString path = QDir::cleanPath(_path);
       
   151     // QDir::cleanPath does not remove two trailing slashes under _Windows_
       
   152     // due to support for UNC paths. Remove those manually.
       
   153     if (path.startsWith(QLatin1String("//")))
       
   154         path.remove(0, 1);
       
   155     return path;
       
   156 }
       
   157 
       
   158 Q_DECLARE_TYPEINFO(QResourceRoot, Q_MOVABLE_TYPE);
       
   159 
       
   160 Q_GLOBAL_STATIC_WITH_ARGS(QMutex, resourceMutex, (QMutex::Recursive))
       
   161 
       
   162 typedef QList<QResourceRoot*> ResourceList;
       
   163 Q_GLOBAL_STATIC(ResourceList, resourceList)
       
   164 
       
   165 Q_GLOBAL_STATIC(QStringList, resourceSearchPaths)
       
   166 
       
   167 /*!
       
   168     \class QResource
       
   169     \brief The QResource class provides an interface for reading directly from resources.
       
   170 
       
   171     \ingroup io
       
   172 
       
   173     \reentrant
       
   174     \since 4.2
       
   175 
       
   176     QResource is an object that represents a set of data (and possibly
       
   177     children) relating to a single resource entity. QResource gives direct
       
   178     access to the bytes in their raw format. In this way direct access
       
   179     allows reading data without buffer copying or indirection. Indirection
       
   180     is often useful when interacting with the resource entity as if it is a
       
   181     file, this can be achieved with QFile. The data and children behind a
       
   182     QResource are normally compiled into an application/library, but it is
       
   183     also possible to load a resource at runtime. When loaded at run time
       
   184     the resource file will be loaded as one big set of data and then given
       
   185     out in pieces via references into the resource tree.
       
   186 
       
   187     A QResource can either be loaded with an absolute path, either treated
       
   188     as a file system rooted with a \c{/} character, or in resource notation
       
   189     rooted with a \c{:} character. A relative resource can also be opened
       
   190     which will be found 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 
       
   226 class QResourcePrivate {
       
   227 public:
       
   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 
       
   250 void
       
   251 QResourcePrivate::clear()
       
   252 {
       
   253     absoluteFilePath.clear();
       
   254     compressed = 0;
       
   255     data = 0;
       
   256     size = 0;
       
   257     children.clear();
       
   258     container = 0;
       
   259     for(int i = 0; i < related.size(); ++i) {
       
   260         QResourceRoot *root = related.at(i);
       
   261         if(!root->ref.deref())
       
   262             delete root;
       
   263     }
       
   264     related.clear();
       
   265 }
       
   266 
       
   267 bool
       
   268 QResourcePrivate::load(const QString &file)
       
   269 {
       
   270     related.clear();
       
   271     QMutexLocker lock(resourceMutex());
       
   272     const ResourceList *list = resourceList();
       
   273     QString cleaned = cleanPath(file);
       
   274     for(int i = 0; i < list->size(); ++i) {
       
   275         QResourceRoot *res = list->at(i);
       
   276         const int node = res->findNode(cleaned);
       
   277         if(node != -1) {
       
   278             if(related.isEmpty()) {
       
   279                 container = res->isContainer(node);
       
   280                 if(!container) {
       
   281                     data = res->data(node, &size);
       
   282                     compressed = res->isCompressed(node);
       
   283                 } else {
       
   284                     data = 0;
       
   285                     size = 0;
       
   286                     compressed = 0;
       
   287                 }
       
   288             } else if(res->isContainer(node) != container) {
       
   289                 qWarning("QResourceInfo: Resource [%s] has both data and children!", file.toLatin1().constData());
       
   290             }
       
   291             res->ref.ref();
       
   292             related.append(res);
       
   293         } else if(res->mappingRootSubdir(file)) {
       
   294             container = true;
       
   295             data = 0;
       
   296             size = 0;
       
   297             compressed = 0;
       
   298             res->ref.ref();
       
   299             related.append(res);
       
   300         }
       
   301     }
       
   302     return !related.isEmpty();
       
   303 }
       
   304 
       
   305 void
       
   306 QResourcePrivate::ensureInitialized() const
       
   307 {
       
   308     if(!related.isEmpty())
       
   309         return;
       
   310     QResourcePrivate *that = const_cast<QResourcePrivate *>(this);
       
   311     if(fileName == QLatin1String(":"))
       
   312         that->fileName += QLatin1Char('/');
       
   313     that->absoluteFilePath = fileName;
       
   314     if(!that->absoluteFilePath.startsWith(QLatin1Char(':')))
       
   315         that->absoluteFilePath.prepend(QLatin1Char(':'));
       
   316 
       
   317     QString path = fileName;
       
   318     if(path.startsWith(QLatin1Char(':')))
       
   319         path = path.mid(1);
       
   320 
       
   321     if(path.startsWith(QLatin1Char('/'))) {
       
   322         that->load(path);
       
   323     } else {
       
   324         QMutexLocker lock(resourceMutex());
       
   325         QStringList searchPaths = *resourceSearchPaths();
       
   326         searchPaths << QLatin1String("");
       
   327         for(int i = 0; i < searchPaths.size(); ++i) {
       
   328             const QString searchPath(searchPaths.at(i) + QLatin1Char('/') + path);
       
   329             if(that->load(searchPath)) {
       
   330                 that->absoluteFilePath = QLatin1Char(':') + searchPath;
       
   331                 break;
       
   332             }
       
   333         }
       
   334     }
       
   335 }
       
   336 
       
   337 void
       
   338 QResourcePrivate::ensureChildren() const
       
   339 {
       
   340     ensureInitialized();
       
   341     if(!children.isEmpty() || !container || related.isEmpty())
       
   342         return;
       
   343 
       
   344     QString path = absoluteFilePath, k;
       
   345     if(path.startsWith(QLatin1Char(':')))
       
   346         path = path.mid(1);
       
   347     QSet<QString> kids;
       
   348     QString cleaned = cleanPath(path);
       
   349     for(int i = 0; i < related.size(); ++i) {
       
   350         QResourceRoot *res = related.at(i);
       
   351         if(res->mappingRootSubdir(path, &k) && !k.isEmpty()) {
       
   352             if(!kids.contains(k)) {
       
   353                 children += k;
       
   354                 kids.insert(k);
       
   355             }
       
   356         } else {
       
   357             const int node = res->findNode(cleaned);
       
   358             if(node != -1) {
       
   359                 QStringList related_children = res->children(node);
       
   360                 for(int kid = 0; kid < related_children.size(); ++kid) {
       
   361                     k = related_children.at(kid);
       
   362                     if(!kids.contains(k)) {
       
   363                         children += k;
       
   364                         kids.insert(k);
       
   365                     }
       
   366                 }
       
   367             }
       
   368         }
       
   369     }
       
   370 }
       
   371 
       
   372 /*!
       
   373     Constructs a QResource pointing to \a file. \a locale is used to
       
   374     load a specific localization of a resource data.
       
   375 
       
   376     \sa QFileInfo, searchPaths(), setFileName(), setLocale()
       
   377 */
       
   378 
       
   379 QResource::QResource(const QString &file, const QLocale &locale) : d_ptr(new QResourcePrivate(this))
       
   380 {
       
   381     Q_D(QResource);
       
   382     d->fileName = file;
       
   383     d->locale = locale;
       
   384 }
       
   385 
       
   386 /*!
       
   387     Releases the resources of the QResource object.
       
   388 */
       
   389 QResource::~QResource()
       
   390 {
       
   391 }
       
   392 
       
   393 /*!
       
   394     Sets a QResource to only load the localization of resource to for \a
       
   395     locale. If a resource for the specific locale is not found then the
       
   396     C locale is used.
       
   397 
       
   398     \sa setFileName()
       
   399 */
       
   400 
       
   401 void QResource::setLocale(const QLocale &locale)
       
   402 {
       
   403     Q_D(QResource);
       
   404     d->clear();
       
   405     d->locale = locale;
       
   406 }
       
   407 
       
   408 /*!
       
   409     Returns the locale used to locate the data for the QResource.
       
   410 */
       
   411 
       
   412 QLocale QResource::locale() const
       
   413 {
       
   414     Q_D(const QResource);
       
   415     return d->locale;
       
   416 }
       
   417 
       
   418 /*!
       
   419     Sets a QResource to point to \a file. \a file can either be absolute,
       
   420     in which case it is opened directly, if relative then the file will be
       
   421     tried to be found in searchPaths().
       
   422 
       
   423     \sa absoluteFilePath()
       
   424 */
       
   425 
       
   426 void QResource::setFileName(const QString &file)
       
   427 {
       
   428     Q_D(QResource);
       
   429     d->clear();
       
   430     d->fileName = file;
       
   431 }
       
   432 
       
   433 /*!
       
   434     Returns the full path to the file that this QResource represents as it
       
   435     was passed.
       
   436 
       
   437     \sa absoluteFilePath()
       
   438 */
       
   439 
       
   440 QString QResource::fileName() const
       
   441 {
       
   442     Q_D(const QResource);
       
   443     d->ensureInitialized();
       
   444     return d->fileName;
       
   445 }
       
   446 
       
   447 /*!
       
   448     Returns the real path that this QResource represents, if the resource
       
   449     was found via the searchPaths() it will be indicated in the path.
       
   450 
       
   451     \sa fileName()
       
   452 */
       
   453 
       
   454 QString QResource::absoluteFilePath() const
       
   455 {
       
   456     Q_D(const QResource);
       
   457     d->ensureInitialized();
       
   458     return d->absoluteFilePath;
       
   459 }
       
   460 
       
   461 /*!
       
   462     Returns true if the resource really exists in the resource hierarchy,
       
   463     false otherwise.
       
   464 
       
   465 */
       
   466 
       
   467 bool QResource::isValid() const
       
   468 {
       
   469     Q_D(const QResource);
       
   470     d->ensureInitialized();
       
   471     return !d->related.isEmpty();
       
   472 }
       
   473 
       
   474 /*!
       
   475     \fn bool QResource::isFile() const
       
   476 
       
   477     Returns true if the resource represents a file and thus has data
       
   478     backing it, false if it represents a directory.
       
   479 
       
   480     \sa isDir()
       
   481 */
       
   482 
       
   483 
       
   484 /*!
       
   485     Returns true if the resource represents a file and the data backing it
       
   486     is in a compressed format, false otherwise.
       
   487 
       
   488     \sa data(), isFile()
       
   489 */
       
   490 
       
   491 bool QResource::isCompressed() const
       
   492 {
       
   493     Q_D(const QResource);
       
   494     d->ensureInitialized();
       
   495     return d->compressed;
       
   496 }
       
   497 
       
   498 /*!
       
   499     Returns the size of the data backing the resource.
       
   500 
       
   501     \sa data(), isFile()
       
   502 */
       
   503 
       
   504 qint64 QResource::size() const
       
   505 {
       
   506     Q_D(const QResource);
       
   507     d->ensureInitialized();
       
   508     return d->size;
       
   509 }
       
   510 
       
   511 /*!
       
   512     Returns direct access to a read only segment of data that this resource
       
   513     represents. If the resource is compressed the data returns is
       
   514     compressed and qUncompress() must be used to access the data. If the
       
   515     resource is a directory 0 is returned.
       
   516 
       
   517     \sa size(), isCompressed(), isFile()
       
   518 */
       
   519 
       
   520 const uchar *QResource::data() const
       
   521 {
       
   522     Q_D(const QResource);
       
   523     d->ensureInitialized();
       
   524     return d->data;
       
   525 }
       
   526 
       
   527 /*!
       
   528     Returns true if the resource represents a directory and thus may have
       
   529     children() in it, false if it represents a file.
       
   530 
       
   531     \sa isFile()
       
   532 */
       
   533 
       
   534 bool QResource::isDir() const
       
   535 {
       
   536     Q_D(const QResource);
       
   537     d->ensureInitialized();
       
   538     return d->container;
       
   539 }
       
   540 
       
   541 /*!
       
   542     Returns a list of all resources in this directory, if the resource
       
   543     represents a file the list will be empty.
       
   544 
       
   545     \sa isDir()
       
   546 */
       
   547 
       
   548 QStringList 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 
       
   568 void
       
   569 QResource::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 
       
   587 QStringList
       
   588 QResource::searchPaths()
       
   589 {
       
   590     QMutexLocker lock(resourceMutex());
       
   591     return *resourceSearchPaths();
       
   592 }
       
   593 
       
   594 inline 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 }
       
   605 inline 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 
       
   629 int 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 }
       
   759 short 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 }
       
   766 const 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 }
       
   791 QStringList 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 }
       
   812 bool 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 
       
   835 Q_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 
       
   858 Q_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 
       
   880 class QDynamicBufferResourceRoot: public QResourceRoot
       
   881 {
       
   882     QString root;
       
   883     const uchar *buffer;
       
   884 
       
   885 public:
       
   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
       
   936 QT_BEGIN_INCLUDE_NAMESPACE
       
   937 #include <sys/mman.h>
       
   938 #include <errno.h>
       
   939 QT_END_INCLUDE_NAMESPACE
       
   940 #endif
       
   941 
       
   942 
       
   943 
       
   944 class 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 
       
   951 public:
       
   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 
       
  1037 static 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 
       
  1058 bool
       
  1059 QResource::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 
       
  1090 bool
       
  1091 QResource::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 
       
  1129 bool
       
  1130 QResource::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 
       
  1161 bool
       
  1162 QResource::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
       
  1186 class QResourceFileEngineHandler : public QAbstractFileEngineHandler
       
  1187 {
       
  1188 public:
       
  1189     QResourceFileEngineHandler() { }
       
  1190     ~QResourceFileEngineHandler() { }
       
  1191     QAbstractFileEngine *create(const QString &path) const;
       
  1192 };
       
  1193 QAbstractFileEngine *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
       
  1201 class QResourceFileEnginePrivate : public QAbstractFileEnginePrivate
       
  1202 {
       
  1203 protected:
       
  1204     Q_DECLARE_PUBLIC(QResourceFileEngine)
       
  1205 private:
       
  1206     uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
       
  1207     bool unmap(uchar *ptr);
       
  1208     qint64 offset;
       
  1209     QResource resource;
       
  1210     QByteArray uncompressed;
       
  1211 protected:
       
  1212     QResourceFileEnginePrivate() : offset(0) { }
       
  1213 };
       
  1214 
       
  1215 bool QResourceFileEngine::mkdir(const QString &, bool) const
       
  1216 {
       
  1217     return false;
       
  1218 }
       
  1219 
       
  1220 bool QResourceFileEngine::rmdir(const QString &, bool) const
       
  1221 {
       
  1222     return false;
       
  1223 }
       
  1224 
       
  1225 bool QResourceFileEngine::setSize(qint64)
       
  1226 {
       
  1227     return false;
       
  1228 }
       
  1229 
       
  1230 QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
       
  1231 {
       
  1232     return QAbstractFileEngine::entryList(filters, filterNames);
       
  1233 }
       
  1234 
       
  1235 bool QResourceFileEngine::caseSensitive() const
       
  1236 {
       
  1237     return true;
       
  1238 }
       
  1239 
       
  1240 QResourceFileEngine::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 
       
  1254 QResourceFileEngine::~QResourceFileEngine()
       
  1255 {
       
  1256 }
       
  1257 
       
  1258 void QResourceFileEngine::setFileName(const QString &file)
       
  1259 {
       
  1260     Q_D(QResourceFileEngine);
       
  1261     d->resource.setFileName(file);
       
  1262 }
       
  1263 
       
  1264 bool 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 
       
  1278 bool QResourceFileEngine::close()
       
  1279 {
       
  1280     Q_D(QResourceFileEngine);
       
  1281     d->offset = 0;
       
  1282     d->uncompressed.clear();
       
  1283     return true;
       
  1284 }
       
  1285 
       
  1286 bool QResourceFileEngine::flush()
       
  1287 {
       
  1288     return false;
       
  1289 }
       
  1290 
       
  1291 qint64 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 
       
  1306 qint64 QResourceFileEngine::write(const char *, qint64)
       
  1307 {
       
  1308     return -1;
       
  1309 }
       
  1310 
       
  1311 bool QResourceFileEngine::remove()
       
  1312 {
       
  1313     return false;
       
  1314 }
       
  1315 
       
  1316 bool QResourceFileEngine::copy(const QString &)
       
  1317 {
       
  1318     return false;
       
  1319 }
       
  1320 
       
  1321 bool QResourceFileEngine::rename(const QString &)
       
  1322 {
       
  1323     return false;
       
  1324 }
       
  1325 
       
  1326 bool QResourceFileEngine::link(const QString &)
       
  1327 {
       
  1328     return false;
       
  1329 }
       
  1330 
       
  1331 qint64 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 
       
  1341 qint64 QResourceFileEngine::pos() const
       
  1342 {
       
  1343     Q_D(const QResourceFileEngine);
       
  1344     return d->offset;
       
  1345 }
       
  1346 
       
  1347 bool QResourceFileEngine::atEnd() const
       
  1348 {
       
  1349     Q_D(const QResourceFileEngine);
       
  1350     if(!d->resource.isValid())
       
  1351         return true;
       
  1352     return d->offset == size();
       
  1353 }
       
  1354 
       
  1355 bool 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 
       
  1367 bool QResourceFileEngine::isSequential() const
       
  1368 {
       
  1369     return false;
       
  1370 }
       
  1371 
       
  1372 QAbstractFileEngine::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 
       
  1395 bool QResourceFileEngine::setPermissions(uint)
       
  1396 {
       
  1397     return false;
       
  1398 }
       
  1399 
       
  1400 QString 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 
       
  1425 bool QResourceFileEngine::isRelativePath() const
       
  1426 {
       
  1427     return false;
       
  1428 }
       
  1429 
       
  1430 uint QResourceFileEngine::ownerId(FileOwner) const
       
  1431 {
       
  1432     static const uint nobodyID = (uint) -2;
       
  1433     return nobodyID;
       
  1434 }
       
  1435 
       
  1436 QString QResourceFileEngine::owner(FileOwner) const
       
  1437 {
       
  1438     return QString();
       
  1439 }
       
  1440 
       
  1441 QDateTime QResourceFileEngine::fileTime(FileTime) const
       
  1442 {
       
  1443     return QDateTime();
       
  1444 }
       
  1445 
       
  1446 /*!
       
  1447     \internal
       
  1448 */
       
  1449 QAbstractFileEngine::Iterator *QResourceFileEngine::beginEntryList(QDir::Filters filters,
       
  1450                                                                    const QStringList &filterNames)
       
  1451 {
       
  1452     return new QResourceFileEngineIterator(filters, filterNames);
       
  1453 }
       
  1454 
       
  1455 /*!
       
  1456     \internal
       
  1457 */
       
  1458 QAbstractFileEngine::Iterator *QResourceFileEngine::endEntryList()
       
  1459 {
       
  1460     return 0;
       
  1461 }
       
  1462 
       
  1463 bool 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 
       
  1479 bool QResourceFileEngine::supportsExtension(Extension extension) const
       
  1480 {
       
  1481     return (extension == UnMapExtension || extension == MapExtension);
       
  1482 }
       
  1483 
       
  1484 uchar *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 
       
  1496 bool QResourceFileEnginePrivate::unmap(uchar *ptr)
       
  1497 {
       
  1498     Q_UNUSED(ptr);
       
  1499     return true;
       
  1500 }
       
  1501 
       
  1502 //Initialization and cleanup
       
  1503 Q_GLOBAL_STATIC(QResourceFileEngineHandler, resource_file_handler)
       
  1504 
       
  1505 static int qt_force_resource_init() { resource_file_handler(); return 1; }
       
  1506 Q_CORE_EXPORT void qInitResourceIO() { resource_file_handler(); }
       
  1507 static int qt_forced_resource_init = qt_force_resource_init();
       
  1508 Q_CONSTRUCTOR_FUNCTION(qt_force_resource_init)
       
  1509 
       
  1510 QT_END_NAMESPACE