1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qqmlbundle_p.h"
43 #include <QtCore/QtCore>
47 static const unsigned char qmlBundleHeaderData[] = { 255, 'q', 'm', 'l', 'd', 'i', 'r', 255 };
48 static const unsigned int qmlBundleHeaderLength = 8;
53 QString QQmlBundle::FileEntry::fileName() const
55 return QString((QChar *)&data[0], fileNameLength / sizeof(QChar));
58 bool QQmlBundle::FileEntry::isFileName(const QString &fileName) const
60 return fileName.length() * sizeof(QChar) == (unsigned)fileNameLength &&
61 0 == ::memcmp(fileName.constData(), &data[0], fileNameLength);
64 const char *QQmlBundle::FileEntry::contents() const {
65 return &data[fileNameLength];
68 quint32 QQmlBundle::FileEntry::fileSize() const
70 return size - (sizeof(FileEntry) + fileNameLength);
77 QQmlBundle::QQmlBundle(const QString &fileName)
86 QQmlBundle::~QQmlBundle()
91 bool QQmlBundle::open(QIODevice::OpenMode mode)
97 bufferSize = file.size();
98 buffer = file.map(0, bufferSize);
100 if (bufferSize == 0 ||
101 (bufferSize >= 8 && 0 == ::memcmp(buffer, qmlBundleHeaderData, qmlBundleHeaderLength))) {
103 headerWritten = false;
113 void QQmlBundle::close()
117 headerWritten = false;
123 QList<const QQmlBundle::FileEntry *> QQmlBundle::files() const
125 QList<const FileEntry *> files;
126 const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
127 const char *end = (const char *) buffer + bufferSize;
130 const Entry *cmd = (const Entry *) ptr;
132 switch (static_cast<Entry::Kind>(cmd->kind)) {
134 const FileEntry *f = reinterpret_cast<const FileEntry *>(cmd);
145 return QList<const FileEntry *>();
149 Q_ASSERT(ptr <= end); // throw an error
154 void QQmlBundle::remove(const FileEntry *entry)
156 Q_ASSERT(entry->kind == Entry::File); // ### throw an error
157 Q_ASSERT(file.isWritable());
158 const_cast<FileEntry *>(entry)->kind = Entry::Skip;
161 int QQmlBundle::bundleHeaderLength()
163 return qmlBundleHeaderLength;
166 bool QQmlBundle::isBundleHeader(const char *data, int size)
168 if ((unsigned int)size < qmlBundleHeaderLength)
171 return 0 == ::memcmp(data, qmlBundleHeaderData, qmlBundleHeaderLength);
175 // find a some empty space we can use to insert new entries.
177 const QQmlBundle::Entry *QQmlBundle::findInsertPoint(quint32 size, qint32 *offset)
179 const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
180 const char *end = (const char *) buffer + bufferSize;
183 const Entry *cmd = (const Entry *) ptr;
185 if (cmd->kind == Entry::Skip && size + sizeof(RawEntry) < cmd->size) {
186 *offset = ptr - ((const char *) buffer + qmlBundleHeaderLength);
191 Q_ASSERT(ptr <= end); // throw an error
197 const QQmlBundle::FileEntry *QQmlBundle::find(const QString &fileName) const
199 const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
200 const char *end = (const char *) buffer + bufferSize;
203 const Entry *cmd = (const Entry *) ptr;
205 if (cmd->kind == Entry::File) {
206 const FileEntry *fileEntry = static_cast<const FileEntry *>(cmd);
208 if (fileEntry->isFileName(fileName))
213 Q_ASSERT(ptr <= end); // throw an error
219 const QQmlBundle::FileEntry *QQmlBundle::link(const FileEntry *entry, const QString &linkName) const
221 const char *ptr = (const char *) buffer + entry->link;
223 while (ptr != (const char *)buffer) {
224 const Entry *cmd = (const Entry *) ptr;
225 Q_ASSERT(cmd->kind == Entry::Link);
227 const FileEntry *fileEntry = static_cast<const FileEntry *>(cmd);
228 if (fileEntry->fileName() == linkName)
231 ptr = (const char *) buffer + fileEntry->link;
237 const QQmlBundle::FileEntry *QQmlBundle::find(const QChar *fileName, int length) const
239 return find(QString::fromRawData(fileName, length));
242 bool QQmlBundle::add(const QString &name, const QString &fileName)
244 if (!file.isWritable())
246 else if (find(fileName))
249 QFile inputFile(fileName);
250 if (!inputFile.open(QFile::ReadOnly))
253 // ### use best-fit algorithm
255 file.seek(file.size());
258 const quint32 inputFileSize = inputFile.size();
260 cmd.kind = Entry::File;
262 cmd.size = sizeof(FileEntry) + name.length() * sizeof(QChar) + inputFileSize;
263 cmd.fileNameLength = name.length() * sizeof(QChar);
265 if (bufferSize == 0 && headerWritten == false) {
266 file.write((const char *)qmlBundleHeaderData, qmlBundleHeaderLength);
267 headerWritten = true;
270 file.write((const char *) &cmd, sizeof(FileEntry));
271 file.write((const char *) name.constData(), name.length() * sizeof(QChar));
273 uchar *source = inputFile.map(0, inputFileSize);
274 file.write((const char *) source, inputFileSize);
275 inputFile.unmap(source);
279 bool QQmlBundle::add(const QString &fileName)
281 return add(fileName, fileName);
284 bool QQmlBundle::addMetaLink(const QString &fileName,
285 const QString &linkName,
286 const QByteArray &data)
288 if (!file.isWritable())
291 const FileEntry *fileEntry = find(fileName);
295 // ### use best-fit algorithm
297 file.seek(file.size());
301 const quint32 inputFileSize = data.size();
303 cmd.kind = Entry::Link;
304 cmd.link = fileEntry->link;
305 cmd.size = sizeof(FileEntry) + linkName.length() * sizeof(QChar) + inputFileSize;
306 cmd.fileNameLength = linkName.length() * sizeof(QChar);
308 if (bufferSize == 0 && headerWritten == false) {
309 file.write((const char *)qmlBundleHeaderData, qmlBundleHeaderLength);
310 headerWritten = true;
313 const_cast<FileEntry *>(fileEntry)->link = file.size();
315 file.write((const char *) &cmd, sizeof(FileEntry));
316 file.write((const char *) linkName.constData(), linkName.length() * sizeof(QChar));
317 file.write((const char *) data.constData(), inputFileSize);