src/tools/rcc/rcc.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 tools applications 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 "rcc.h"
       
    43 
       
    44 #include <QtCore/QByteArray>
       
    45 #include <QtCore/QDateTime>
       
    46 #include <QtCore/QDebug>
       
    47 #include <QtCore/QDir>
       
    48 #include <QtCore/QDirIterator>
       
    49 #include <QtCore/QFile>
       
    50 #include <QtCore/QIODevice>
       
    51 #include <QtCore/QLocale>
       
    52 #include <QtCore/QStack>
       
    53 
       
    54 #include <QtXml/QDomDocument>
       
    55 
       
    56 QT_BEGIN_NAMESPACE
       
    57 
       
    58 enum {
       
    59     CONSTANT_USENAMESPACE = 1,
       
    60     CONSTANT_COMPRESSLEVEL_DEFAULT = -1,
       
    61     CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70
       
    62 };
       
    63 
       
    64 
       
    65 #define writeString(s) write(s, sizeof(s))
       
    66 
       
    67 void RCCResourceLibrary::write(const char *str, int len)
       
    68 {
       
    69     --len; // trailing \0 on string literals...
       
    70     int n = m_out.size();
       
    71     m_out.resize(n + len);
       
    72     memcpy(m_out.data() + n, str, len);
       
    73 }
       
    74 
       
    75 void RCCResourceLibrary::writeByteArray(const QByteArray &other)
       
    76 {
       
    77     m_out.append(other);
       
    78 }
       
    79 
       
    80 static inline QString msgOpenReadFailed(const QString &fname, const QString &why)
       
    81 {
       
    82     return QString::fromUtf8("Unable to open %1 for reading: %2\n").arg(fname).arg(why);
       
    83 }
       
    84 
       
    85 
       
    86 ///////////////////////////////////////////////////////////
       
    87 //
       
    88 // RCCFileInfo
       
    89 //
       
    90 ///////////////////////////////////////////////////////////
       
    91 
       
    92 class RCCFileInfo
       
    93 {
       
    94 public:
       
    95     enum Flags
       
    96     {
       
    97         NoFlags = 0x00,
       
    98         Compressed = 0x01,
       
    99         Directory = 0x02
       
   100     };
       
   101 
       
   102     RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(),
       
   103                 QLocale::Language language = QLocale::C,
       
   104                 QLocale::Country country = QLocale::AnyCountry,
       
   105                 uint flags = NoFlags,
       
   106                 int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT,
       
   107                 int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT);
       
   108     ~RCCFileInfo();
       
   109 
       
   110     QString resourceName() const;
       
   111 
       
   112 public:
       
   113     qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage);
       
   114     qint64 writeDataName(RCCResourceLibrary &, qint64 offset);
       
   115     void writeDataInfo(RCCResourceLibrary &lib);
       
   116 
       
   117     int m_flags;
       
   118     QString m_name;
       
   119     QLocale::Language m_language;
       
   120     QLocale::Country m_country;
       
   121     QFileInfo m_fileInfo;
       
   122     RCCFileInfo *m_parent;
       
   123     QHash<QString, RCCFileInfo*> m_children;
       
   124     int m_compressLevel;
       
   125     int m_compressThreshold;
       
   126 
       
   127     qint64 m_nameOffset;
       
   128     qint64 m_dataOffset;
       
   129     qint64 m_childOffset;
       
   130 };
       
   131 
       
   132 RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
       
   133     QLocale::Language language, QLocale::Country country, uint flags,
       
   134     int compressLevel, int compressThreshold)
       
   135 {
       
   136     m_name = name;
       
   137     m_fileInfo = fileInfo;
       
   138     m_language = language;
       
   139     m_country = country;
       
   140     m_flags = flags;
       
   141     m_parent = 0;
       
   142     m_nameOffset = 0;
       
   143     m_dataOffset = 0;
       
   144     m_childOffset = 0;
       
   145     m_compressLevel = compressLevel;
       
   146     m_compressThreshold = compressThreshold;
       
   147 }
       
   148 
       
   149 RCCFileInfo::~RCCFileInfo()
       
   150 {
       
   151     qDeleteAll(m_children);
       
   152 }
       
   153 
       
   154 QString RCCFileInfo::resourceName() const
       
   155 {
       
   156     QString resource = m_name;
       
   157     for (RCCFileInfo *p = m_parent; p; p = p->m_parent)
       
   158         resource = resource.prepend(p->m_name + QLatin1Char('/'));
       
   159     return QLatin1Char(':') + resource;
       
   160 }
       
   161 
       
   162 void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
       
   163 {
       
   164     const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
       
   165     //some info
       
   166     if (text) {
       
   167         if (m_language != QLocale::C) {
       
   168             lib.writeString("  // ");
       
   169             lib.writeByteArray(resourceName().toLocal8Bit());
       
   170             lib.writeString(" [");
       
   171             lib.writeByteArray(QByteArray::number(m_country));
       
   172             lib.writeString("::");
       
   173             lib.writeByteArray(QByteArray::number(m_language));
       
   174             lib.writeString("[\n  ");
       
   175         } else {
       
   176             lib.writeString("  // ");
       
   177             lib.writeByteArray(resourceName().toLocal8Bit());
       
   178             lib.writeString("\n  ");
       
   179         }
       
   180     }
       
   181 
       
   182     //pointer data
       
   183     if (m_flags & RCCFileInfo::Directory) {
       
   184         // name offset
       
   185         lib.writeNumber4(m_nameOffset);
       
   186 
       
   187         // flags
       
   188         lib.writeNumber2(m_flags);
       
   189 
       
   190         // child count
       
   191         lib.writeNumber4(m_children.size());
       
   192 
       
   193         // first child offset
       
   194         lib.writeNumber4(m_childOffset);
       
   195     } else {
       
   196         // name offset
       
   197         lib.writeNumber4(m_nameOffset);
       
   198 
       
   199         // flags
       
   200         lib.writeNumber2(m_flags);
       
   201 
       
   202         // locale
       
   203         lib.writeNumber2(m_country);
       
   204         lib.writeNumber2(m_language);
       
   205 
       
   206         //data offset
       
   207         lib.writeNumber4(m_dataOffset);
       
   208     }
       
   209     if (text)
       
   210         lib.writeChar('\n');
       
   211 }
       
   212 
       
   213 qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
       
   214     QString *errorMessage)
       
   215 {
       
   216     const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
       
   217 
       
   218     //capture the offset
       
   219     m_dataOffset = offset;
       
   220 
       
   221     //find the data to be written
       
   222     QFile file(m_fileInfo.absoluteFilePath());
       
   223     if (!file.open(QFile::ReadOnly)) {
       
   224         *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
       
   225         return 0;
       
   226     }
       
   227     QByteArray data = file.readAll();
       
   228 
       
   229 #ifndef QT_NO_COMPRESS
       
   230     // Check if compression is useful for this file
       
   231     if (m_compressLevel != 0 && data.size() != 0) {
       
   232         QByteArray compressed =
       
   233             qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
       
   234 
       
   235         int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
       
   236         if (compressRatio >= m_compressThreshold) {
       
   237             data = compressed;
       
   238             m_flags |= Compressed;
       
   239         }
       
   240     }
       
   241 #endif // QT_NO_COMPRESS
       
   242 
       
   243     // some info
       
   244     if (text) {
       
   245         lib.writeString("  // ");
       
   246         lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
       
   247         lib.writeString("\n  ");
       
   248     }
       
   249 
       
   250     // write the length
       
   251 
       
   252     lib.writeNumber4(data.size());
       
   253     if (text)
       
   254         lib.writeString("\n  ");
       
   255     offset += 4;
       
   256 
       
   257     // write the payload
       
   258     const char *p = data.constData();
       
   259     if (text) {
       
   260         for (int i = data.size(), j = 0; --i >= 0; --j) {
       
   261             lib.writeHex(*p++);
       
   262             if (j == 0) {
       
   263                 lib.writeString("\n  ");
       
   264                 j = 16;
       
   265             }
       
   266         }
       
   267     } else {
       
   268         for (int i = data.size(); --i >= 0; )
       
   269            lib.writeChar(*p++);
       
   270     }
       
   271     offset += data.size();
       
   272 
       
   273     // done
       
   274     if (text)
       
   275         lib.writeString("\n  ");
       
   276     return offset;
       
   277 }
       
   278 
       
   279 qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
       
   280 {
       
   281     const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
       
   282 
       
   283     // capture the offset
       
   284     m_nameOffset = offset;
       
   285 
       
   286     // some info
       
   287     if (text) {
       
   288         lib.writeString("  // ");
       
   289         lib.writeByteArray(m_name.toLocal8Bit());
       
   290         lib.writeString("\n  ");
       
   291     }
       
   292 
       
   293     // write the length
       
   294     lib.writeNumber2(m_name.length());
       
   295     if (text)
       
   296         lib.writeString("\n  ");
       
   297     offset += 2;
       
   298 
       
   299     // write the hash
       
   300     lib.writeNumber4(qHash(m_name));
       
   301     if (text)
       
   302         lib.writeString("\n  ");
       
   303     offset += 4;
       
   304 
       
   305     // write the m_name
       
   306     const QChar *unicode = m_name.unicode();
       
   307     for (int i = 0; i < m_name.length(); ++i) {
       
   308         lib.writeNumber2(unicode[i].unicode());
       
   309         if (text && i % 16 == 0)
       
   310             lib.writeString("\n  ");
       
   311     }
       
   312     offset += m_name.length()*2;
       
   313 
       
   314     // done
       
   315     if (text)
       
   316         lib.writeString("\n  ");
       
   317     return offset;
       
   318 }
       
   319 
       
   320 
       
   321 ///////////////////////////////////////////////////////////
       
   322 //
       
   323 // RCCResourceLibrary
       
   324 //
       
   325 ///////////////////////////////////////////////////////////
       
   326 
       
   327 RCCResourceLibrary::Strings::Strings() :
       
   328    TAG_RCC(QLatin1String("RCC")),
       
   329    TAG_RESOURCE(QLatin1String("qresource")),
       
   330    TAG_FILE(QLatin1String("file")),
       
   331    ATTRIBUTE_LANG(QLatin1String("lang")),
       
   332    ATTRIBUTE_PREFIX(QLatin1String("prefix")),
       
   333    ATTRIBUTE_ALIAS(QLatin1String("alias")),
       
   334    ATTRIBUTE_THRESHOLD(QLatin1String("threshold")),
       
   335    ATTRIBUTE_COMPRESS(QLatin1String("compress"))
       
   336 {
       
   337 }
       
   338 
       
   339 RCCResourceLibrary::RCCResourceLibrary()
       
   340   : m_root(0),
       
   341     m_format(C_Code),
       
   342     m_verbose(false),
       
   343     m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
       
   344     m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
       
   345     m_treeOffset(0),
       
   346     m_namesOffset(0),
       
   347     m_dataOffset(0),
       
   348     m_useNameSpace(CONSTANT_USENAMESPACE),
       
   349     m_errorDevice(0)
       
   350 {
       
   351     m_out.reserve(30 * 1000 * 1000);
       
   352 }
       
   353 
       
   354 RCCResourceLibrary::~RCCResourceLibrary()
       
   355 {
       
   356     delete m_root;
       
   357 }
       
   358 
       
   359 bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
       
   360     const QString &fname, QString currentPath, bool ignoreErrors)
       
   361 {
       
   362     Q_ASSERT(m_errorDevice);
       
   363     const QChar slash = QLatin1Char('/');
       
   364     if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
       
   365         currentPath += slash;
       
   366 
       
   367     QDomDocument document;
       
   368     {
       
   369         QString errorMsg;
       
   370         int errorLine = 0;
       
   371         int errorColumn = 0;
       
   372         if (!document.setContent(inputDevice, &errorMsg, &errorLine, &errorColumn)) {
       
   373             if (ignoreErrors)
       
   374                 return true;
       
   375             const QString msg = QString::fromUtf8("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMsg);
       
   376             m_errorDevice->write(msg.toUtf8());
       
   377             return false;
       
   378         }
       
   379     }
       
   380 
       
   381     QDomElement domRoot = document.firstChildElement(m_strings.TAG_RCC).toElement();
       
   382     if (!domRoot.isNull() && domRoot.tagName() == m_strings.TAG_RCC) {
       
   383         for (QDomNode node = domRoot.firstChild(); !node.isNull(); node = node.nextSibling()) {
       
   384             if (!node.isElement())
       
   385                 continue;
       
   386 
       
   387             QDomElement child = node.toElement();
       
   388             if (!child.isNull() && child.tagName() == m_strings.TAG_RESOURCE) {
       
   389                 QLocale::Language language = QLocale::c().language();
       
   390                 QLocale::Country country = QLocale::c().country();
       
   391 
       
   392                 if (child.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
       
   393                     QString attribute = child.attribute(m_strings.ATTRIBUTE_LANG);
       
   394                     QLocale lang = QLocale(attribute);
       
   395                     language = lang.language();
       
   396                     if (2 == attribute.length()) {
       
   397                         // Language only
       
   398                         country = QLocale::AnyCountry;
       
   399                     } else {
       
   400                         country = lang.country();
       
   401                     }
       
   402                 }
       
   403 
       
   404                 QString prefix;
       
   405                 if (child.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
       
   406                     prefix = child.attribute(m_strings.ATTRIBUTE_PREFIX);
       
   407                 if (!prefix.startsWith(slash))
       
   408                     prefix.prepend(slash);
       
   409                 if (!prefix.endsWith(slash))
       
   410                     prefix += slash;
       
   411 
       
   412                 for (QDomNode res = child.firstChild(); !res.isNull(); res = res.nextSibling()) {
       
   413                     if (res.isElement() && res.toElement().tagName() == m_strings.TAG_FILE) {
       
   414 
       
   415                         QString fileName(res.firstChild().toText().data());
       
   416                         if (fileName.isEmpty()) {
       
   417                             const QString msg = QString::fromUtf8("RCC: Warning: Null node in XML of '%1'\n").arg(fname);
       
   418                             m_errorDevice->write(msg.toUtf8());
       
   419                         }
       
   420                         QString alias;
       
   421                         if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_ALIAS))
       
   422                             alias = res.toElement().attribute(m_strings.ATTRIBUTE_ALIAS);
       
   423                         else
       
   424                             alias = fileName;
       
   425 
       
   426                         int compressLevel = m_compressLevel;
       
   427                         if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_COMPRESS))
       
   428                             compressLevel = res.toElement().attribute(m_strings.ATTRIBUTE_COMPRESS).toInt();
       
   429                         int compressThreshold = m_compressThreshold;
       
   430                         if (res.toElement().hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
       
   431                             compressThreshold = res.toElement().attribute(m_strings.ATTRIBUTE_THRESHOLD).toInt();
       
   432 
       
   433                         // Special case for -no-compress. Overrides all other settings.
       
   434                         if (m_compressLevel == -2)
       
   435                             compressLevel = 0;
       
   436 
       
   437                         alias = QDir::cleanPath(alias);
       
   438                         while (alias.startsWith(QLatin1String("../")))
       
   439                             alias.remove(0, 3);
       
   440                         alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
       
   441 
       
   442                         QString absFileName = fileName;
       
   443                         if (QDir::isRelativePath(absFileName))
       
   444                             absFileName.prepend(currentPath);
       
   445                         QFileInfo file(absFileName);
       
   446                         if (!file.exists()) {
       
   447                             m_failedResources.push_back(absFileName);
       
   448                             const QString msg = QString::fromUtf8("RCC: Error in '%1': Cannot find file '%2'\n").arg(fname).arg(fileName);
       
   449                             m_errorDevice->write(msg.toUtf8());
       
   450                             if (ignoreErrors)
       
   451                                 continue;
       
   452                             else
       
   453                                 return false;
       
   454                         } else if (file.isFile()) {
       
   455                             const bool arc = addFile(alias, RCCFileInfo(alias.section(slash, -1), file, language, country,
       
   456                                                                         RCCFileInfo::NoFlags, compressLevel, compressThreshold));
       
   457                             if (!arc)
       
   458                                 m_failedResources.push_back(absFileName);
       
   459                         } else {
       
   460                             QDir dir;
       
   461                             if (file.isDir()) {
       
   462                                 dir.setPath(file.filePath());
       
   463                             } else {
       
   464                                 dir.setPath(file.path());
       
   465                                 dir.setNameFilters(QStringList(file.fileName()));
       
   466                                 if (alias.endsWith(file.fileName()))
       
   467                                     alias = alias.left(alias.length()-file.fileName().length());
       
   468                             }
       
   469                             if (!alias.endsWith(slash))
       
   470                                 alias += slash;
       
   471                             QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
       
   472                             while (it.hasNext()) {
       
   473                                 it.next();
       
   474                                 QFileInfo child(it.fileInfo());
       
   475                                 if (child.fileName() != QLatin1String(".") && child.fileName() != QLatin1String("..")) {
       
   476                                     const bool arc = addFile(alias + child.fileName(),
       
   477                                                              RCCFileInfo(child.fileName(), child, language, country,
       
   478                                                              RCCFileInfo::NoFlags, compressLevel, compressThreshold));
       
   479                                     if (!arc)
       
   480                                         m_failedResources.push_back(child.fileName());
       
   481                                 }
       
   482                             }
       
   483                         }
       
   484                     }
       
   485                 }
       
   486             }
       
   487         }
       
   488     }
       
   489     if (m_root == 0) {
       
   490         const QString msg = QString::fromUtf8("RCC: Warning: No resources in '%1'.\n").arg(fname);
       
   491         m_errorDevice->write(msg.toUtf8());
       
   492         if (!ignoreErrors && m_format == Binary) {
       
   493             // create dummy entry, otherwise loading qith QResource will crash
       
   494             m_root = new RCCFileInfo(QString(), QFileInfo(),
       
   495                     QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
       
   496         }
       
   497     }
       
   498 
       
   499     return true;
       
   500 }
       
   501 
       
   502 bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
       
   503 {
       
   504     Q_ASSERT(m_errorDevice);
       
   505     if (file.m_fileInfo.size() > 0xffffffff) {
       
   506         const QString msg = QString::fromUtf8("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
       
   507         m_errorDevice->write(msg.toUtf8());
       
   508         return false;
       
   509     }
       
   510     if (!m_root)
       
   511         m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
       
   512 
       
   513     RCCFileInfo *parent = m_root;
       
   514     const QStringList nodes = alias.split(QLatin1Char('/'));
       
   515     for (int i = 1; i < nodes.size()-1; ++i) {
       
   516         const QString node = nodes.at(i);
       
   517         if (node.isEmpty())
       
   518             continue;
       
   519         if (!parent->m_children.contains(node)) {
       
   520             RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
       
   521             s->m_parent = parent;
       
   522             parent->m_children.insert(node, s);
       
   523             parent = s;
       
   524         } else {
       
   525             parent = parent->m_children[node];
       
   526         }
       
   527     }
       
   528 
       
   529     const QString filename = nodes.at(nodes.size()-1);
       
   530     RCCFileInfo *s = new RCCFileInfo(file);
       
   531     s->m_parent = parent;
       
   532     parent->m_children.insertMulti(filename, s);
       
   533     return true;
       
   534 }
       
   535 
       
   536 void RCCResourceLibrary::reset()
       
   537 {
       
   538      if (m_root) {
       
   539         delete m_root;
       
   540         m_root = 0;
       
   541     }
       
   542     m_errorDevice = 0;
       
   543     m_failedResources.clear();
       
   544 }
       
   545 
       
   546 
       
   547 bool RCCResourceLibrary::readFiles(bool ignoreErrors, QIODevice &errorDevice)
       
   548 {
       
   549     reset();
       
   550     m_errorDevice = &errorDevice;
       
   551     //read in data
       
   552     if (m_verbose) {
       
   553         const QString msg = QString::fromUtf8("Processing %1 files [%2]\n")
       
   554             .arg(m_fileNames.size()).arg(static_cast<int>(ignoreErrors));
       
   555         m_errorDevice->write(msg.toUtf8());
       
   556     }
       
   557     for (int i = 0; i < m_fileNames.size(); ++i) {
       
   558         QFile fileIn;
       
   559         QString fname = m_fileNames.at(i);
       
   560         QString pwd;
       
   561         if (fname == QLatin1String("-")) {
       
   562             fname = QLatin1String("(stdin)");
       
   563             pwd = QDir::currentPath();
       
   564             fileIn.setFileName(fname);
       
   565             if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
       
   566                 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
       
   567                 return false;
       
   568             }
       
   569         } else {
       
   570             pwd = QFileInfo(fname).path();
       
   571             fileIn.setFileName(fname);
       
   572             if (!fileIn.open(QIODevice::ReadOnly)) {
       
   573                 m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
       
   574                 return false;
       
   575             }
       
   576         }
       
   577         if (m_verbose) {
       
   578             const QString msg = QString::fromUtf8("Interpreting %1\n").arg(fname);
       
   579             m_errorDevice->write(msg.toUtf8());
       
   580         }
       
   581 
       
   582         if (!interpretResourceFile(&fileIn, fname, pwd, ignoreErrors))
       
   583             return false;
       
   584     }
       
   585     return true;
       
   586 }
       
   587 
       
   588 QStringList RCCResourceLibrary::dataFiles() const
       
   589 {
       
   590     QStringList ret;
       
   591     QStack<RCCFileInfo*> pending;
       
   592 
       
   593     if (!m_root)
       
   594         return ret;
       
   595     pending.push(m_root);
       
   596     while (!pending.isEmpty()) {
       
   597         RCCFileInfo *file = pending.pop();
       
   598         for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
       
   599             it != file->m_children.end(); ++it) {
       
   600             RCCFileInfo *child = it.value();
       
   601             if (child->m_flags & RCCFileInfo::Directory)
       
   602                 pending.push(child);
       
   603             ret.append(child->m_fileInfo.filePath());
       
   604         }
       
   605     }
       
   606     return ret;
       
   607 }
       
   608 
       
   609 // Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
       
   610 static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
       
   611 {
       
   612     typedef QHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
       
   613     const QChar slash = QLatin1Char('/');
       
   614     const ChildConstIterator cend = m_root->m_children.constEnd();
       
   615     for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) {
       
   616         const RCCFileInfo *child = it.value();
       
   617         QString childName = path;
       
   618         childName += slash;
       
   619         childName += child->m_name;
       
   620         if (child->m_flags & RCCFileInfo::Directory) {
       
   621             resourceDataFileMapRecursion(child, childName, m);
       
   622         } else {
       
   623             m.insert(childName, child->m_fileInfo.filePath());
       
   624         }
       
   625     }
       
   626 }
       
   627 
       
   628 RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap() const
       
   629 {
       
   630     ResourceDataFileMap rc;
       
   631     if (m_root)
       
   632         resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')),  rc);
       
   633     return rc;
       
   634 }
       
   635 
       
   636 bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &errorDevice)
       
   637 {
       
   638     m_errorDevice = &errorDevice;
       
   639     //write out
       
   640     if (m_verbose)
       
   641         m_errorDevice->write("Outputting code\n");
       
   642     if (!writeHeader()) {
       
   643         m_errorDevice->write("Could not write header\n");
       
   644         return false;
       
   645     }
       
   646     if (m_root) {
       
   647         if (!writeDataBlobs()) {
       
   648             m_errorDevice->write("Could not write data blobs.\n");
       
   649             return false;
       
   650         }
       
   651         if (!writeDataNames()) {
       
   652             m_errorDevice->write("Could not write file names\n");
       
   653             return false;
       
   654         }
       
   655         if (!writeDataStructure()) {
       
   656             m_errorDevice->write("Could not write data tree\n");
       
   657             return false;
       
   658         }
       
   659     }
       
   660     if (!writeInitializer()) {
       
   661         m_errorDevice->write("Could not write footer\n");
       
   662         return false;
       
   663     }
       
   664     outDevice.write(m_out, m_out.size());
       
   665     return true;
       
   666 }
       
   667 
       
   668 void RCCResourceLibrary::writeHex(quint8 tmp)
       
   669 {
       
   670     const char * const digits = "0123456789abcdef";
       
   671     writeChar('0');
       
   672     writeChar('x');
       
   673     if (tmp < 16) {
       
   674         writeChar(digits[tmp]);
       
   675     } else {
       
   676         writeChar(digits[tmp >> 4]);
       
   677         writeChar(digits[tmp & 0xf]);
       
   678     }
       
   679     writeChar(',');
       
   680 }
       
   681 
       
   682 void RCCResourceLibrary::writeNumber2(quint16 number)
       
   683 {
       
   684     if (m_format == RCCResourceLibrary::Binary) {
       
   685         writeChar(number >> 8);
       
   686         writeChar(number);
       
   687     } else {
       
   688         writeHex(number >> 8);
       
   689         writeHex(number);
       
   690     }
       
   691 }
       
   692 
       
   693 void RCCResourceLibrary::writeNumber4(quint32 number)
       
   694 {
       
   695     if (m_format == RCCResourceLibrary::Binary) {
       
   696         writeChar(number >> 24);
       
   697         writeChar(number >> 16);
       
   698         writeChar(number >> 8);
       
   699         writeChar(number);
       
   700     } else {
       
   701         writeHex(number >> 24);
       
   702         writeHex(number >> 16);
       
   703         writeHex(number >> 8);
       
   704         writeHex(number);
       
   705     }
       
   706 }
       
   707 
       
   708 bool RCCResourceLibrary::writeHeader()
       
   709 {
       
   710     if (m_format == C_Code) {
       
   711         writeString("/****************************************************************************\n");
       
   712         writeString("**\n");
       
   713         writeString("** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).\n");
       
   714         writeString("** All rights reserved.\n");
       
   715         writeString("** Contact: Nokia Corporation (qt-info@nokia.com)\n");
       
   716         writeString("**\n");
       
   717         writeString("** This file is part of the tools applications of the Qt Toolkit.\n");
       
   718         writeString("**\n");
       
   719         writeString("** $QT_BEGIN_LICENSE:LGPL$\n");
       
   720         writeString("** No Commercial Usage\n");
       
   721         writeString("** This file contains pre-release code and may not be distributed.\n");
       
   722         writeString("** You may use this file in accordance with the terms and conditions\n");
       
   723         writeString("** contained in the Technology Preview License Agreement accompanying\n");
       
   724         writeString("** this package.\n");
       
   725         writeString("**\n");
       
   726         writeString("** GNU Lesser General Public License Usage\n");
       
   727         writeString("** Alternatively, this file may be used under the terms of the GNU Lesser\n");
       
   728         writeString("** General Public License version 2.1 as published by the Free Software\n");
       
   729         writeString("** Foundation and appearing in the file LICENSE.LGPL included in the\n");
       
   730         writeString("** packaging of this file.  Please review the following information to\n");
       
   731         writeString("** ensure the GNU Lesser General Public License version 2.1 requirements\n");
       
   732         writeString("** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\n");
       
   733         writeString("**\n");
       
   734         writeString("** In addition, as a special exception, Nokia gives you certain additional\n");
       
   735         writeString("** rights.  These rights are described in the Nokia Qt LGPL Exception\n");
       
   736         writeString("** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\n");
       
   737         writeString("**\n");
       
   738         writeString("** If you have questions regarding the use of this file, please contact\n");
       
   739         writeString("** Nokia at qt-info@nokia.com.\n");
       
   740         writeString("**\n");
       
   741         writeString("**\n");
       
   742         writeString("**\n");
       
   743         writeString("**\n");
       
   744         writeString("**\n");
       
   745         writeString("**\n");
       
   746         writeString("**\n");
       
   747         writeString("**\n");
       
   748         writeString("** $QT_END_LICENSE$\n");
       
   749         writeString("**\n");
       
   750         writeString("****************************************************************************/\n");
       
   751         writeString("/****************************************************************************\n");
       
   752         writeString("** Resource object code\n");
       
   753         writeString("**\n");
       
   754         writeString("** Created: ");
       
   755         writeByteArray(QDateTime::currentDateTime().toString().toLatin1());
       
   756         writeString("\n**      by: The Resource Compiler for Qt version ");
       
   757         writeByteArray(QT_VERSION_STR);
       
   758         writeString("\n**\n");
       
   759         writeString("** WARNING! All changes made in this file will be lost!\n");
       
   760         writeString( "*****************************************************************************/\n\n");
       
   761         writeString("#include <QtCore/qglobal.h>\n\n");
       
   762     } else if (m_format == Binary) {
       
   763         writeString("qres");
       
   764         writeNumber4(0);
       
   765         writeNumber4(0);
       
   766         writeNumber4(0);
       
   767         writeNumber4(0);
       
   768     }
       
   769     return true;
       
   770 }
       
   771 
       
   772 bool RCCResourceLibrary::writeDataBlobs()
       
   773 {
       
   774     Q_ASSERT(m_errorDevice);
       
   775     if (m_format == C_Code)
       
   776         writeString("static const unsigned char qt_resource_data[] = {\n");
       
   777     else if (m_format == Binary)
       
   778         m_dataOffset = m_out.size();
       
   779     QStack<RCCFileInfo*> pending;
       
   780 
       
   781     if (!m_root)
       
   782         return false;
       
   783 
       
   784     pending.push(m_root);
       
   785     qint64 offset = 0;
       
   786     QString errorMessage;
       
   787     while (!pending.isEmpty()) {
       
   788         RCCFileInfo *file = pending.pop();
       
   789         for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
       
   790             it != file->m_children.end(); ++it) {
       
   791             RCCFileInfo *child = it.value();
       
   792             if (child->m_flags & RCCFileInfo::Directory)
       
   793                 pending.push(child);
       
   794             else {
       
   795                 offset = child->writeDataBlob(*this, offset, &errorMessage);
       
   796                 if (offset == 0)
       
   797                     m_errorDevice->write(errorMessage.toUtf8());
       
   798             }
       
   799         }
       
   800     }
       
   801     if (m_format == C_Code)
       
   802         writeString("\n};\n\n");
       
   803     return true;
       
   804 }
       
   805 
       
   806 bool RCCResourceLibrary::writeDataNames()
       
   807 {
       
   808     if (m_format == C_Code)
       
   809         writeString("static const unsigned char qt_resource_name[] = {\n");
       
   810     else if (m_format == Binary)
       
   811         m_namesOffset = m_out.size();
       
   812 
       
   813     QHash<QString, int> names;
       
   814     QStack<RCCFileInfo*> pending;
       
   815 
       
   816     if (!m_root)
       
   817         return false;
       
   818 
       
   819     pending.push(m_root);
       
   820     qint64 offset = 0;
       
   821     while (!pending.isEmpty()) {
       
   822         RCCFileInfo *file = pending.pop();
       
   823         for (QHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
       
   824             it != file->m_children.end(); ++it) {
       
   825             RCCFileInfo *child = it.value();
       
   826             if (child->m_flags & RCCFileInfo::Directory)
       
   827                 pending.push(child);
       
   828             if (names.contains(child->m_name)) {
       
   829                 child->m_nameOffset = names.value(child->m_name);
       
   830             } else {
       
   831                 names.insert(child->m_name, offset);
       
   832                 offset = child->writeDataName(*this, offset);
       
   833             }
       
   834         }
       
   835     }
       
   836     if (m_format == C_Code)
       
   837         writeString("\n};\n\n");
       
   838     return true;
       
   839 }
       
   840 
       
   841 static bool qt_rcc_compare_hash(const RCCFileInfo *left, const RCCFileInfo *right)
       
   842 {
       
   843     return qHash(left->m_name) < qHash(right->m_name);
       
   844 }
       
   845 
       
   846 bool RCCResourceLibrary::writeDataStructure()
       
   847 {
       
   848     if (m_format == C_Code)
       
   849         writeString("static const unsigned char qt_resource_struct[] = {\n");
       
   850     else if (m_format == Binary)
       
   851         m_treeOffset = m_out.size();
       
   852     QStack<RCCFileInfo*> pending;
       
   853 
       
   854     if (!m_root)
       
   855         return false;
       
   856 
       
   857     //calculate the child offsets (flat)
       
   858     pending.push(m_root);
       
   859     int offset = 1;
       
   860     while (!pending.isEmpty()) {
       
   861         RCCFileInfo *file = pending.pop();
       
   862         file->m_childOffset = offset;
       
   863 
       
   864         //sort by hash value for binary lookup
       
   865         QList<RCCFileInfo*> m_children = file->m_children.values();
       
   866         qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
       
   867 
       
   868         //write out the actual data now
       
   869         for (int i = 0; i < m_children.size(); ++i) {
       
   870             RCCFileInfo *child = m_children.at(i);
       
   871             ++offset;
       
   872             if (child->m_flags & RCCFileInfo::Directory)
       
   873                 pending.push(child);
       
   874         }
       
   875     }
       
   876 
       
   877     //write out the structure (ie iterate again!)
       
   878     pending.push(m_root);
       
   879     m_root->writeDataInfo(*this);
       
   880     while (!pending.isEmpty()) {
       
   881         RCCFileInfo *file = pending.pop();
       
   882 
       
   883         //sort by hash value for binary lookup
       
   884         QList<RCCFileInfo*> m_children = file->m_children.values();
       
   885         qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
       
   886 
       
   887         //write out the actual data now
       
   888         for (int i = 0; i < m_children.size(); ++i) {
       
   889             RCCFileInfo *child = m_children.at(i);
       
   890             child->writeDataInfo(*this);
       
   891             if (child->m_flags & RCCFileInfo::Directory)
       
   892                 pending.push(child);
       
   893         }
       
   894     }
       
   895     if (m_format == C_Code)
       
   896         writeString("\n};\n\n");
       
   897 
       
   898     return true;
       
   899 }
       
   900 
       
   901 void RCCResourceLibrary::writeMangleNamespaceFunction(const QByteArray &name)
       
   902 {
       
   903     if (m_useNameSpace) {
       
   904         writeString("QT_MANGLE_NAMESPACE(");
       
   905         writeByteArray(name);
       
   906         writeChar(')');
       
   907     } else {
       
   908         writeByteArray(name);
       
   909     }
       
   910 }
       
   911 
       
   912 void RCCResourceLibrary::writeAddNamespaceFunction(const QByteArray &name)
       
   913 {
       
   914     if (m_useNameSpace) {
       
   915         writeString("QT_PREPEND_NAMESPACE(");
       
   916         writeByteArray(name);
       
   917         writeChar(')');
       
   918     } else {
       
   919         writeByteArray(name);
       
   920     }
       
   921 }
       
   922 
       
   923 bool RCCResourceLibrary::writeInitializer()
       
   924 {
       
   925     if (m_format == C_Code) {
       
   926         //write("\nQT_BEGIN_NAMESPACE\n");
       
   927         QString initName = m_initName;
       
   928         if (!initName.isEmpty()) {
       
   929             initName.prepend(QLatin1Char('_'));
       
   930             initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
       
   931         }
       
   932 
       
   933         //init
       
   934         if (m_useNameSpace)
       
   935             writeString("QT_BEGIN_NAMESPACE\n\n");
       
   936         if (m_root) {
       
   937             writeString("extern Q_CORE_EXPORT bool qRegisterResourceData\n    "
       
   938                 "(int, const unsigned char *, "
       
   939                 "const unsigned char *, const unsigned char *);\n\n");
       
   940             writeString("extern Q_CORE_EXPORT bool qUnregisterResourceData\n    "
       
   941                 "(int, const unsigned char *, "
       
   942                 "const unsigned char *, const unsigned char *);\n\n");
       
   943         }
       
   944         if (m_useNameSpace)
       
   945             writeString("QT_END_NAMESPACE\n\n\n");
       
   946         QString initResources = QLatin1String("qInitResources");
       
   947         initResources += initName;
       
   948         writeString("int ");
       
   949         writeMangleNamespaceFunction(initResources.toLatin1());
       
   950         writeString("()\n{\n");
       
   951 
       
   952         if (m_root) {
       
   953             writeString("    ");
       
   954             writeAddNamespaceFunction("qRegisterResourceData");
       
   955             writeString("\n        (0x01, qt_resource_struct, "
       
   956                        "qt_resource_name, qt_resource_data);\n");
       
   957         }
       
   958         writeString("    return 1;\n");
       
   959         writeString("}\n\n");
       
   960         writeString("Q_CONSTRUCTOR_FUNCTION(");
       
   961         writeMangleNamespaceFunction(initResources.toLatin1());
       
   962         writeString(")\n\n");
       
   963 
       
   964         //cleanup
       
   965         QString cleanResources = QLatin1String("qCleanupResources");
       
   966         cleanResources += initName;
       
   967         writeString("int ");
       
   968         writeMangleNamespaceFunction(cleanResources.toLatin1());
       
   969         writeString("()\n{\n");
       
   970         if (m_root) {
       
   971             writeString("    ");
       
   972             writeAddNamespaceFunction("qUnregisterResourceData");
       
   973             writeString("\n       (0x01, qt_resource_struct, "
       
   974                       "qt_resource_name, qt_resource_data);\n");
       
   975         }
       
   976         writeString("    return 1;\n");
       
   977         writeString("}\n\n");
       
   978         writeString("Q_DESTRUCTOR_FUNCTION(");
       
   979         writeMangleNamespaceFunction(cleanResources.toLatin1());
       
   980         writeString(")\n\n");
       
   981     } else if (m_format == Binary) {
       
   982         int i = 4;
       
   983         char *p = m_out.data();
       
   984         p[i++] = 0; // 0x01
       
   985         p[i++] = 0;
       
   986         p[i++] = 0;
       
   987         p[i++] = 1;
       
   988 
       
   989         p[i++] = (m_treeOffset >> 24) & 0xff;
       
   990         p[i++] = (m_treeOffset >> 16) & 0xff;
       
   991         p[i++] = (m_treeOffset >>  8) & 0xff;
       
   992         p[i++] = (m_treeOffset >>  0) & 0xff;
       
   993 
       
   994         p[i++] = (m_dataOffset >> 24) & 0xff;
       
   995         p[i++] = (m_dataOffset >> 16) & 0xff;
       
   996         p[i++] = (m_dataOffset >>  8) & 0xff;
       
   997         p[i++] = (m_dataOffset >>  0) & 0xff;
       
   998 
       
   999         p[i++] = (m_namesOffset >> 24) & 0xff;
       
  1000         p[i++] = (m_namesOffset >> 16) & 0xff;
       
  1001         p[i++] = (m_namesOffset >>  8) & 0xff;
       
  1002         p[i++] = (m_namesOffset >>  0) & 0xff;
       
  1003     }
       
  1004     return true;
       
  1005 }
       
  1006 
       
  1007 QT_END_NAMESPACE