1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the core module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
33
#include <qdatetime.h>
34
#include <qbytearray.h>
35
#include <qstringlist.h>
37
#include <private/qfileengine_p.h>
39
#include "qresource_p.h"
49
const uchar *tree, *names, *payloads;
50
int findNode(const QString &path) const;
51
inline int findOffset(int node) const { return node * 14; } //sizeof each tree element
52
inline int hash(int offset) const;
53
inline QString name(int offset) const;
55
inline QResource(): tree(0), names(0), payloads(0) {}
56
inline QResource(const uchar *t, const uchar *n, const uchar *d)
57
: tree(t), names(n), payloads(d) {}
58
bool isContainer(const QString &path) const;
59
bool exists(const QString &path) const;
60
QByteArray data(const QString &path) const;
61
QStringList children(const QString &path) const;
62
inline bool operator==(const QResource &other) const
63
{ return tree == other.tree && names == other.names && payloads == other.payloads; }
64
inline bool operator!=(const QResource &other) const
65
{ return !operator==(other); }
68
Q_DECLARE_TYPEINFO(QResource, Q_MOVABLE_TYPE);
70
inline int QResource::hash(int node) const
74
const int offset = findOffset(node);
75
int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
76
(tree[offset+2] << 8) + (tree[offset+3] << 0);
77
name_offset += 2; //jump past name length
78
return (names[name_offset+0] << 24) + (names[name_offset+1] << 16) +
79
(names[name_offset+2] << 8) + (names[name_offset+3] << 0);
81
inline QString QResource::name(int node) const
85
const int offset = findOffset(node);
88
int name_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
89
(tree[offset+2] << 8) + (tree[offset+3] << 0);
90
const short name_length = (names[name_offset+0] << 8) +
91
(names[name_offset+1] << 0);
93
name_offset += 4; //jump past hash
94
for(int i = 0; i < name_length*2; i+=2)
95
ret += QChar(names[name_offset+i+1], names[name_offset+i]);
98
int QResource::findNode(const QString &path) const
100
if(path == QLatin1String("/"))
103
//the root node is always first
104
int child_count = (tree[6] << 24) + (tree[7] << 16) +
105
(tree[8] << 8) + (tree[9] << 0);
106
int child = (tree[10] << 24) + (tree[11] << 16) +
107
(tree[12] << 8) + (tree[13] << 0);
109
//now iterate up the tree
112
QStringList segments = path.split('/', QString::SkipEmptyParts);
113
for(int i = 0; child_count && i < segments.size(); ++i) {
114
const QString &segment = segments[i];
115
const int h = qHash(segment);
117
//do the binary search for the hash
118
int l = 0, r = child_count-1;
119
int sub_node = (l+r+1)/2;
121
const int sub_node_hash = hash(child+sub_node);
122
if(h == sub_node_hash)
124
else if(h < sub_node_hash)
128
sub_node = (l + r + 1) / 2;
132
//now do the "harder" compares
134
if(hash(sub_node) == h) {
135
while(sub_node > child && hash(sub_node-1) == h) //backup for collisions
137
for(; sub_node < child+child_count && hash(sub_node) == h; ++sub_node) { //here we go...
138
if(name(sub_node) == segment) {
140
int offset = findOffset(sub_node) + 4; //jump past name
142
const short flags = (tree[offset+0] << 8) +
143
(tree[offset+1] << 0);
146
if(i == segments.size()-1) {
147
if(!(flags & Directory)) {
148
const short country = (tree[offset+0] << 8) +
149
(tree[offset+1] << 0);
152
const short language = (tree[offset+0] << 8) +
153
(tree[offset+1] << 0);
156
if(country == locale.country() && language == locale.language())
158
else if((country == QLocale::AnyCountry && language == locale.language()) ||
159
(country == QLocale::AnyCountry && language == QLocale::C && node == -1))
167
if(!(flags & Directory))
170
child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
171
(tree[offset+2] << 8) + (tree[offset+3] << 0);
173
child = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
174
(tree[offset+2] << 8) + (tree[offset+3] << 0);
184
bool QResource::isContainer(const QString &path) const
186
int node = findNode(path);
189
const int offset = findOffset(node) + 4; //jump past name
190
const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
191
return flags & Directory;
193
bool QResource::exists(const QString &path) const
195
return findNode(path) != -1;
197
QByteArray QResource::data(const QString &path) const
199
const int node = findNode(path);
202
int offset = findOffset(node) + 4; //jump past name
204
const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
207
offset += 4; //jump past locale
210
if(!(flags & Directory)) {
211
const int data_offset = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
212
(tree[offset+2] << 8) + (tree[offset+3] << 0);
213
const uint data_length = (payloads[data_offset+0] << 24) + (payloads[data_offset+1] << 16) +
214
(payloads[data_offset+2] << 8) + (payloads[data_offset+3] << 0);
215
const uchar *data = payloads+data_offset+4;
216
if(flags & Compressed)
217
ret = qUncompress(data, data_length);
219
ret = QByteArray((char*)data, data_length);
223
QStringList QResource::children(const QString &path) const
225
int node = findNode(path);
227
return QStringList();
228
int offset = findOffset(node) + 4; //jump past name
230
const short flags = (tree[offset+0] << 8) + (tree[offset+1] << 0);
234
if(flags & Directory) {
235
const int child_count = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
236
(tree[offset+2] << 8) + (tree[offset+3] << 0);
238
const int child_off = (tree[offset+0] << 24) + (tree[offset+1] << 16) +
239
(tree[offset+2] << 8) + (tree[offset+3] << 0);
240
for(int i = child_off; i < child_off+child_count; ++i)
246
Q_GLOBAL_STATIC(QStringList, qt_resource_search_paths)
247
bool qt_resource_add_search_path(const QString &path)
249
if(path[0] != QLatin1Char('/')) {
250
qWarning("QDir::addResourceSearchPath: Search paths must be absolute (start with /) [%s]",
251
path.toLocal8Bit().data());
254
qt_resource_search_paths()->prepend(path);
258
typedef QVector<QResource> ResourceList;
259
Q_GLOBAL_STATIC(ResourceList, resourceList)
263
QString file, searchFile;
264
ResourceList related;
267
mutable uint hasData : 1;
268
mutable QByteArray mData;
270
mutable uint hasChildren : 1;
271
mutable QStringList mChildren;
273
inline void clear() {
276
hasData = hasChildren = 0;
280
bool loadResource(const QString &);
282
QResourceInfo() { clear(); }
283
QResourceInfo(const QString &f) { setFileName(f); }
285
void setFileName(const QString &f);
286
QString fileName() const { return file; }
287
QString searchFileName() const { return searchFile; }
289
bool exists() const { return !related.isEmpty(); }
290
bool isContainer() const { return container; }
291
QByteArray data() const;
292
QStringList children() const;
295
QResourceInfo::loadResource(const QString &path)
297
const ResourceList *list = resourceList();
298
for(int i = 0; i < list->size(); ++i) {
299
QResource res = list->at(i);
300
if(res.exists(path)) {
301
if(related.isEmpty())
302
container = res.isContainer(path);
303
else if(res.isContainer(path) != container)
304
qWarning("Resource [%s] has both data and children!", file.toLatin1().constData());
308
return !related.isEmpty();
311
QResourceInfo::setFileName(const QString &f)
317
if(file == QLatin1String(":"))
322
if(path.startsWith(QLatin1String(":")))
324
if(path[0] == QLatin1Char('/')) {
328
QStringList searchPaths = *qt_resource_search_paths();
329
searchPaths << QLatin1String("");
330
for(int i = 0; i < searchPaths.size(); ++i) {
331
const QString searchPath(searchPaths.at(i) + "/" + path);
332
if(loadResource(searchPath)) {
333
searchFile = ":" + searchPath;
339
QByteArray QResourceInfo::data() const
341
if(container || related.isEmpty())
346
QString path = searchFile;
347
if(path.startsWith(":"))
349
mData = related.at(0).data(path);
354
QStringList QResourceInfo::children() const
356
if(!container || related.isEmpty())
357
return QStringList();
361
QString path = searchFile;
362
if(path.startsWith(":"))
365
for(int i = 0; i < related.size(); ++i) {
366
QStringList related_children = related.at(i).children(path);
367
for(int kid = 0; kid < related_children.size(); ++kid) {
368
QString k = related_children.at(kid);
369
if(!kids.contains(k)) {
379
Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree,
380
const unsigned char *name, const unsigned char *data)
382
if(version == 0x01) {
383
QResource res(tree, name, data);
384
if (!resourceList()->contains(res))
385
resourceList()->append(res);
392
class QResourceFileEngineHandler : public QFileEngineHandler
395
QResourceFileEngineHandler() { }
396
~QResourceFileEngineHandler() { }
397
QFileEngine *createFileEngine(const QString &path);
399
QFileEngine *QResourceFileEngineHandler::createFileEngine(const QString &path)
401
if (path.size() > 0 && path.startsWith(QLatin1String(":")))
402
return new QResourceFileEngine(path);
407
class QResourceFileEnginePrivate : public QFileEnginePrivate
410
Q_DECLARE_PUBLIC(QResourceFileEngine)
413
QResourceInfo resource;
415
QResourceFileEnginePrivate() : offset(0) { }
418
bool QResourceFileEngine::mkdir(const QString &, bool) const
423
bool QResourceFileEngine::rmdir(const QString &, bool) const
428
bool QResourceFileEngine::setSize(qint64)
433
QStringList QResourceFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
435
Q_D(const QResourceFileEngine);
437
const bool doDirs = (filters & QDir::Dirs) != 0;
438
const bool doFiles = (filters & QDir::Files) != 0;
439
const bool doReadable = (filters & QDir::Readable) != 0;
442
if((!doDirs && !doFiles) || ((filters & QDir::PermissionMask) && !doReadable))
444
if(!d->resource.exists() || !d->resource.isContainer())
445
return ret; // cannot read the "directory"
447
QStringList entries = d->resource.children();
448
for(int i = 0; i < entries.size(); i++) {
449
QResourceInfo entry(d->resource.fileName() + "/" + entries[i]);
451
if(!(filters & QDir::AllDirs && entry.isContainer())) {
452
bool matched = false;
453
for(QStringList::ConstIterator sit = filterNames.begin(); sit != filterNames.end(); ++sit) {
455
(filters & QDir::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive,
457
if (rx.exactMatch(entries[i])) {
466
if ((doDirs && entry.isContainer()) ||
467
(doFiles && !entry.isContainer()))
468
ret.append(entries[i]);
473
bool QResourceFileEngine::caseSensitive() const
478
QResourceFileEngine::QResourceFileEngine(const QString &file) :
479
QFileEngine(*new QResourceFileEnginePrivate)
481
Q_D(QResourceFileEngine);
482
d->resource.setFileName(file);
485
QResourceFileEngine::~QResourceFileEngine()
489
void QResourceFileEngine::setFileName(const QString &file)
491
Q_D(QResourceFileEngine);
492
d->resource.setFileName(file);
495
bool QResourceFileEngine::open(int flags)
497
Q_D(QResourceFileEngine);
498
if (d->resource.fileName().isEmpty()) {
499
qWarning("QFSFileEngine::open: No file name specified");
502
if(flags & QIODevice::WriteOnly)
504
if(!d->resource.exists())
509
bool QResourceFileEngine::close()
511
Q_D(QResourceFileEngine);
516
void QResourceFileEngine::flush()
521
qint64 QResourceFileEngine::read(char *data, qint64 len)
523
Q_D(QResourceFileEngine);
524
if(len > d->resource.data().size()-d->offset)
525
len = d->resource.data().size()-d->offset;
528
memcpy(data, d->resource.data().constData()+d->offset, len);
533
qint64 QResourceFileEngine::write(const char *, qint64)
538
int QResourceFileEngine::ungetch(int)
543
bool QResourceFileEngine::remove()
548
bool QResourceFileEngine::copy(const QString &)
553
bool QResourceFileEngine::rename(const QString &)
558
bool QResourceFileEngine::link(const QString &)
563
qint64 QResourceFileEngine::size() const
565
Q_D(const QResourceFileEngine);
566
if(!d->resource.exists())
568
return d->resource.data().size();
571
qint64 QResourceFileEngine::at() const
573
Q_D(const QResourceFileEngine);
577
bool QResourceFileEngine::atEnd() const
579
Q_D(const QResourceFileEngine);
580
if(!d->resource.exists())
582
return d->offset == d->resource.data().size();
585
bool QResourceFileEngine::seek(qint64 pos)
587
Q_D(QResourceFileEngine);
588
if(!d->resource.exists())
591
if(d->offset > d->resource.data().size())
597
bool QResourceFileEngine::isSequential() const
602
QFileEngine::FileFlags QResourceFileEngine::fileFlags(QFileEngine::FileFlags type) const
604
Q_D(const QResourceFileEngine);
605
QFileEngine::FileFlags ret = 0;
606
if(!d->resource.exists())
609
ret |= QFileEngine::FileFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm);
610
if(type & TypesMask) {
611
if(d->resource.isContainer())
612
ret |= DirectoryType;
616
if(type & FlagsMask) {
618
if(d->resource.fileName() == QLatin1String(":/"))
624
bool QResourceFileEngine::chmod(uint)
629
QString QResourceFileEngine::fileName(FileName file) const
631
Q_D(const QResourceFileEngine);
632
if(file == BaseName) {
633
int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
635
return d->resource.fileName();
636
return d->resource.fileName().mid(slash + 1);
637
} else if(file == PathName || file == AbsolutePathName) {
638
const int slash = d->resource.fileName().lastIndexOf(QLatin1Char('/'));
640
return d->resource.fileName().left(slash);
641
} else if(file == CanonicalName || file == CanonicalPathName) {
642
const QString canonicalPath = d->resource.searchFileName();
643
if(file == CanonicalPathName) {
644
const int slash = canonicalPath.lastIndexOf(QLatin1Char('/'));
646
return canonicalPath.left(slash);
648
return canonicalPath;
650
return d->resource.fileName();
653
bool QResourceFileEngine::isRelativePath() const
658
uint QResourceFileEngine::ownerId(FileOwner) const
660
static const uint nobodyID = (uint) -2;
664
QString QResourceFileEngine::owner(FileOwner) const
669
QDateTime QResourceFileEngine::fileTime(FileTime) const
674
QFileEngine::Type QResourceFileEngine::type() const
676
return QFileEngine::Resource;
679
//Initialization and cleanup
680
Q_GLOBAL_STATIC(QResourceFileEngineHandler, resource_file_handler)
682
static int qt_force_resource_init() { resource_file_handler(); return 1; }
683
Q_CORE_EXPORT void qInitResourceIO() { resource_file_handler(); }
684
static int qt_forced_resource_init = qt_force_resource_init();
685
Q_CONSTRUCTOR_FUNCTION(qt_force_resource_init)