File indexing completed on 2024-04-28 04:40:03

0001 #include "utils.h"
0002 #include "pole.h"
0003 #include "leinputstream.h"
0004 #include "leoutputstream.h"
0005 #include <QByteArray>
0006 #include <QBuffer>
0007 #include <QFile>
0008 #include <QXmlStreamWriter>
0009 #include <QDebug>
0010 
0011 using namespace std;
0012 
0013 QByteArray
0014 escapeByteArray(const QByteArray& b) {
0015     // we escape all non printable byte values
0016     // printable is 9, 10, 13, 32-126
0017     QByteArray exclude(96, ' ');
0018     exclude[0] = 9; exclude[1] = 10; exclude[2] = 13;
0019     for (int i=3; i<8; ++i) {
0020         exclude[i] = i+29;
0021     }
0022     for (int i=8; i<96; ++i) {
0023         exclude[i] = i+30;
0024     }
0025 
0026     QByteArray result = b.toPercentEncoding(exclude);
0027     if (QByteArray::fromPercentEncoding(result) != b) {
0028         qDebug() << "Escaping of bytearray " << b << " is not reversible.";
0029         exit(1);
0030     }
0031     return result;
0032 }
0033 
0034 QVector<quint16>
0035 toUInt16Vector(const QString& s) {
0036     QVector<quint16> v;
0037     QString z = QString::fromUtf8(QByteArray::fromPercentEncoding(s.toUtf8()));
0038     v.resize(z.size());
0039     for (int i=0; i<z.size(); ++i) {
0040         v[i] = z.utf16()[i];
0041     }
0042     return v;
0043 }
0044 
0045 QString
0046 toString(const QVector<quint16>& v) {
0047     QString s;
0048     foreach(quint16 c, v) {
0049         s.append(c);
0050     }
0051     s = QString::fromUtf8(escapeByteArray(s.toUtf8()));
0052     // TODO: implement and check reversibility
0053 /*
0054     if (toUInt16Vector(s) != v) {
0055         qDebug() << "Escaping of string " << v << " is not reversible.";
0056         qDebug() << toUInt16Vector(s);
0057         exit(1);
0058     }
0059 */
0060     return s;
0061 }
0062 
0063 void
0064 print(QXmlStreamWriter& out, const Introspectable* i) {
0065     const Introspection* is = i->getIntrospection();
0066     for (int j=0; j<is->numberOfMembers; ++j) {
0067         for (int k=0; k<is->numberOfInstances[j](i); ++k) {
0068             out.writeStartElement(is->names[j]);
0069             const Introspectable* ci = is->introspectable[j](i, k);
0070             if (ci) {
0071                 QString type = ci->getIntrospection()->name;
0072                 out.writeAttribute("type", type);
0073                 uint32_t offset = ci->streamOffset;
0074                 out.writeAttribute("offset", QString::number(offset));
0075                 print(out, ci);
0076             } else {
0077                 QVariant v(is->value[j](i, k));
0078                 if (v.canConvert<QVector<quint16> >()) {
0079                     out.writeCharacters(toString(v.value<QVector<quint16> >()));
0080                 } else {
0081                     if (v.type() == QVariant::ByteArray) {
0082                         v = escapeByteArray(v.toByteArray());
0083                     }
0084                     out.writeCharacters(v.toString());
0085                 }
0086             }
0087             out.writeEndElement();
0088         }
0089     }
0090 }
0091 
0092 QMap<QString, QByteArray>
0093 readStreams(const QString& file) {
0094     QMap<QString, QByteArray> streams;
0095     POLE::Storage storage(file.toLocal8Bit());
0096     if (!storage.open()) return streams;
0097 
0098     // TODO add support for directories
0099     string prefix = "/";
0100     list<string> entries = storage.entries(prefix);
0101     for (list<string>::const_iterator i=entries.begin(); i!=entries.end(); ++i){
0102         string path(prefix+*i);
0103         if (!storage.isDirectory(path)) {
0104             POLE::Stream stream(&storage, path);
0105             QString streamname(QString::fromStdString(*i));
0106             QByteArray array;
0107             array.resize(stream.size());
0108             unsigned long read = stream.read((unsigned char*)array.data(),
0109                     stream.size());
0110             if (read != stream.size()) {
0111                 qDebug() << "Error reading stream " << streamname;
0112                 streams.clear();
0113                 return streams;
0114             }
0115             streams[path.c_str()] = array;
0116         }
0117     }
0118 
0119     return streams;
0120 }
0121 
0122 QMap<QString, QSharedPointer<const Introspectable> >
0123 parseStreams(const QMap<QString, QByteArray>& streams) {
0124     QMap<QString, QSharedPointer<const Introspectable> > result;
0125     QMap<QString, QByteArray>::const_iterator i;
0126     for (i = streams.begin(); i!= streams.end(); ++i) {
0127         const QString streampath(i.key());
0128         const QString streamname = streampath.mid(streampath.lastIndexOf('/')+1);
0129         const QByteArray array(i.value());
0130         QBuffer buffer;
0131         buffer.setData(array);
0132         buffer.open(QIODevice::ReadOnly);
0133         LEInputStream listream(&buffer);
0134         qDebug() << "Parsing stream '" << streampath << "'";
0135 
0136         const Introspectable* i;
0137         try {
0138             i = parse(streamname, listream);
0139         } catch (IOException& e) {
0140             qDebug() << "Error: " << e.msg;
0141             continue;
0142         }
0143 
0144         if (listream.getPosition() != (qint64)array.size()) {
0145             qDebug() << array.size() - listream.getPosition()
0146                 << "trailing bytes in stream " << streampath;
0147             result.clear();
0148             return result;
0149         }
0150         buffer.close();
0151 
0152         // test if serialization gives the same bytearray as that which came in
0153         buffer.buffer().clear();
0154         buffer.open(QIODevice::WriteOnly);
0155         LEOutputStream lostream(&buffer);
0156         serialize(i, streamname, lostream);
0157         if (array != buffer.data()) {
0158             qDebug() << "Serialized data different from original in "
0159                 << streampath;
0160         }
0161 
0162         result[streampath] = QSharedPointer<const Introspectable>(i);
0163     }
0164     return result;
0165 }
0166 
0167 QMap<QString, QByteArray>
0168 serialize(const QMap<QString, QSharedPointer<const Introspectable> >& m) {
0169     QMap<QString, QByteArray> streams;
0170     QMap<QString, QSharedPointer<const Introspectable> >::const_iterator i;
0171     for (i = m.begin(); i!= m.end(); ++i) {
0172         const QString streampath(i.key());
0173         const QString streamname
0174             = streampath.mid(streampath.lastIndexOf('/')+1);
0175         QBuffer buffer;
0176         buffer.open(QIODevice::WriteOnly);
0177         LEOutputStream lostream(&buffer);
0178         const Introspectable* is = i.value().data();
0179         serialize(is, streamname, lostream);
0180         streams[streampath] = buffer.data();
0181     }
0182     return streams;
0183 }
0184 
0185 void
0186 write(const QString& name, const QByteArray& data) {
0187     QFile out(name);
0188     out.open(QIODevice::WriteOnly);
0189     out.write(data);
0190     out.close();
0191 }
0192 
0193 void printWithExtendedParser(QXmlStreamWriter& out, const Introspectable* i);
0194 
0195 void
0196 printStyleTextPropAtom(QXmlStreamWriter& out, const Introspectable* i,
0197             int characterCount) {
0198     const Introspection* is = i->getIntrospection();
0199 
0200     // rh
0201     const Introspectable* ci = is->introspectable[0](i, 0);
0202     out.writeStartElement(is->names[0]);
0203     QString type = ci->getIntrospection()->name;
0204     out.writeAttribute("type", type);
0205     printWithExtendedParser(out, is->introspectable[0](i, 0)); // rh
0206     out.writeEndElement();
0207 
0208     // styles
0209     QByteArray blob = is->value[1](i, 0).toByteArray();
0210     QBuffer buffer;
0211     buffer.setData(blob);
0212     buffer.open(QIODevice::ReadOnly);
0213     LEInputStream listream(&buffer);
0214 
0215     try {
0216         int sum = 0;
0217         do {
0218             ci = parse("textPFRun", listream);
0219             const Introspection* cis = ci->getIntrospection();
0220             sum += cis->value[0](ci, 0).toInt();
0221             out.writeStartElement("rgTextPFRun");
0222             out.writeAttribute("type", "TextPFRun");
0223             printWithExtendedParser(out, ci);
0224             //qDebug() << "PF " << characterCount << " " << cis->value[0](ci, 0).toInt() << " " << sum;
0225             delete ci;
0226             out.writeEndElement();
0227         } while (sum <= characterCount);
0228         sum = 0;
0229         do {
0230             ci = parse("textCFRun", listream);
0231             const Introspection* cis = ci->getIntrospection();
0232             sum += cis->value[0](ci, 0).toInt();
0233             out.writeStartElement("rgTextCFRun");
0234             out.writeAttribute("type", "TextCFRun");
0235             printWithExtendedParser(out, ci);
0236             //qDebug() << "CF " << characterCount << " " << cis->value[0](ci, 0).toInt() << " " << sum;
0237             delete ci;
0238             out.writeEndElement();
0239         } while (sum <= characterCount);
0240     } catch (IOException& e) {
0241         qDebug() << "Error: " << e.msg;
0242     }
0243     if (buffer.size() != listream.getPosition()) {
0244         qDebug() << "Error: " << buffer.size() - listream.getPosition()
0245             << " bytes left for StyleTextPropAtom at position "
0246             << i->streamOffset;
0247     }
0248 }
0249 
0250 void
0251 printWithExtendedParser(QXmlStreamWriter& out, const Introspectable* i) {
0252     int lastCharacterCount = 0; // needed for parsing StyleTextPropAtom
0253 
0254     const Introspection* is = i->getIntrospection();
0255     for (int j=0; j<is->numberOfMembers; ++j) {
0256         for (int k=0; k<is->numberOfInstances[j](i); ++k) {
0257             out.writeStartElement(is->names[j]);
0258             const Introspectable* ci = is->introspectable[j](i, k);
0259             if (ci) {
0260                 QString type = ci->getIntrospection()->name;
0261                 out.writeAttribute("type", type);
0262                 uint32_t offset = ci->streamOffset;
0263                 out.writeAttribute("offset", QString::number(offset));
0264                 if (type == "StyleTextPropAtom") {
0265                     // StyleTextPropAtom is currently too hard to parse by the generated code
0266                     printStyleTextPropAtom(out, ci, lastCharacterCount);
0267                 } else if (type == "TextCharsAtom") {
0268                     const Introspection* cis = ci->getIntrospection();
0269                     lastCharacterCount = cis->value[1](ci, 0).value<QVector<quint16> >().count();
0270                     printWithExtendedParser(out, ci);
0271                 } else if (type == "TextBytesAtom") {
0272                     const Introspection* cis = ci->getIntrospection();
0273                     lastCharacterCount = cis->value[1](ci, 0).toByteArray().count();
0274                     printWithExtendedParser(out, ci);
0275                 } else {
0276                     printWithExtendedParser(out, ci);
0277                 }
0278             } else {
0279                 QVariant v(is->value[j](i, k));
0280                 if (v.canConvert<QVector<quint16> >()) {
0281                     out.writeCharacters(toString(v.value<QVector<quint16> >()));
0282                 } else {
0283                     if (v.type() == QVariant::ByteArray) {
0284                         v = escapeByteArray(v.toByteArray());
0285                     }
0286                     out.writeCharacters(v.toString());
0287                 }
0288             }
0289             out.writeEndElement();
0290         }
0291     }
0292 }
0293 
0294 QByteArray
0295 streamsToXml(
0296             const QMap<QString, QSharedPointer<const Introspectable> >& streams,
0297             void (*printFunction)(QXmlStreamWriter&,const Introspectable*)) {
0298     QBuffer xml;
0299     xml.open(QBuffer::WriteOnly);
0300     QXmlStreamWriter xmlout(&xml);
0301     xmlout.setAutoFormatting(true);
0302     xmlout.writeStartDocument();
0303     xmlout.writeStartElement("ppt");
0304     QMap<QString, QSharedPointer<const Introspectable> >::const_iterator i;
0305     for (i = streams.begin(); i!= streams.end(); ++i) {
0306         const Introspection* in = i.value()->getIntrospection();
0307         xmlout.writeStartElement(in->name);
0308         xmlout.writeAttribute("stream-path", i.key());
0309         if (in->name != "TODOS") {
0310             printFunction(xmlout, i.value().data());
0311         }
0312         xmlout.writeEndElement();
0313     }
0314     xmlout.writeEndElement();
0315     xmlout.writeEndDocument();
0316     return xml.data();
0317 }
0318 
0319 QByteArray
0320 streamsToXml(const QMap<QString,
0321             QSharedPointer<const Introspectable> >& streams) {
0322     return streamsToXml(streams, print);
0323 }
0324 
0325 QByteArray
0326 streamsToExtendedXml(const QMap<QString,
0327             QSharedPointer<const Introspectable> >& streams) {
0328     return streamsToXml(streams, printWithExtendedParser);
0329 }