25cd4bb by Jos van den Oever at 2009-11-01 1
#include "utils.h"
2
#include "pole.h"
3
#include "leinputstream.h"
4
#include "leoutputstream.h"
5
#include <QByteArray>
6
#include <QBuffer>
7
#include <QFile>
8
#include <QXmlStreamWriter>
9
#include <QDebug>
10
11
using namespace std;
12
13
QByteArray
14
escapeByteArray(const QByteArray& b) {
15
    // we escape all non printable byte values
16
    // printable is 9, 10, 13, 32-126
17
    QByteArray exclude(96, ' ');
18
    exclude[0] = 9; exclude[1] = 10; exclude[2] = 13;
19
    for (int i=3; i<8; ++i) {
20
        exclude[i] = i+29;
21
    }
22
    for (int i=8; i<96; ++i) {
23
        exclude[i] = i+30;
24
    }
25
26
    QByteArray result = b.toPercentEncoding(exclude);
27
    if (QByteArray::fromPercentEncoding(result) != b) {
28
        qDebug() << "Escaping of bytearray " << b << " is not reversible.";
29
        exit(1);
30
    }
31
    return result;
32
}
33
34
QVector<quint16>
35
toUInt16Vector(const QString& s) {
36
    QVector<quint16> v;
37
    QString z = QString::fromUtf8(QByteArray::fromPercentEncoding(s.toUtf8()));
38
    v.resize(z.size());
39
    for (int i=0; i<z.size(); ++i) {
40
        v[i] = z.utf16()[i];
41
    }
42
    return v;
43
}
44
45
QString
46
toString(const QVector<quint16>& v) {
47
    QString s;
48
    foreach(quint16 c, v) {
49
        s.append(c);
50
    }
51
    s = QString::fromUtf8(escapeByteArray(s.toUtf8()));
52
    // TODO: implement and check reversibility
53
/*
54
    if (toUInt16Vector(s) != v) {
55
        qDebug() << "Escaping of string " << v << " is not reversible.";
56
        qDebug() << toUInt16Vector(s);
57
        exit(1);
58
    }
59
*/
60
    return s;
61
}
62
63
void
64
print(QXmlStreamWriter& out, const Introspectable* i) {
65
    const Introspection* is = i->getIntrospection();
66
    for (int j=0; j<is->numberOfMembers; ++j) {
67
        for (int k=0; k<is->numberOfInstances[j](i); ++k) {
68
            out.writeStartElement(is->names[j]);
69
            const Introspectable* ci = is->introspectable[j](i, k);
70
            if (ci) {
71
                QString type = ci->getIntrospection()->name;
72
                out.writeAttribute("type", type);
124a9d4 by Jos van den Oever at 2010-01-12 73
                uint32_t offset = ci->streamOffset;
74
                out.writeAttribute("offset", QString::number(offset));
25cd4bb by Jos van den Oever at 2009-11-01 75
                print(out, ci);
76
            } else {
77
                QVariant v(is->value[j](i, k));
78
                if (v.canConvert<QVector<quint16> >()) {
79
                    out.writeCharacters(toString(v.value<QVector<quint16> >()));
80
                } else {
81
                    if (v.type() == QVariant::ByteArray) {
82
                        v = escapeByteArray(v.toByteArray());
83
                    }
84
                    out.writeCharacters(v.toString());
85
                }
86
            }
87
            out.writeEndElement();
88
        }
89
    }
90
}
91
92
QMap<QString, QByteArray>
93
readStreams(const QString& file) {
94
    QMap<QString, QByteArray> streams;
95
    POLE::Storage storage(file.toLocal8Bit());
96
    if (!storage.open()) return streams;
97
98
    // TODO add support for directories
99
    string prefix = "/";
100
    list<string> entries = storage.entries(prefix);
56bd8e4 by Jos van den Oever at 2009-11-04 101
    for (list<string>::const_iterator i=entries.begin(); i!=entries.end(); ++i){
25cd4bb by Jos van den Oever at 2009-11-01 102
        string path(prefix+*i);
103
        if (!storage.isDirectory(path)) {
104
            POLE::Stream stream(&storage, path);
105
            QString streamname(QString::fromStdString(*i));
106
            QByteArray array;
107
            array.resize(stream.size());
108
            unsigned long read = stream.read((unsigned char*)array.data(),
109
                    stream.size());
110
            if (read != stream.size()) {
111
                qDebug() << "Error reading stream " << streamname;
112
                streams.clear();
113
                return streams;
114
            }
115
            streams[path.c_str()] = array;
116
        }
117
    }
118
119
    return streams;
120
}
121
122
QMap<QString, QSharedPointer<const Introspectable> >
123
parseStreams(const QMap<QString, QByteArray>& streams) {
124
    QMap<QString, QSharedPointer<const Introspectable> > result;
125
    QMap<QString, QByteArray>::const_iterator i;
126
    for (i = streams.begin(); i!= streams.end(); ++i) {
127
        const QString streampath(i.key());
128
        const QString streamname = streampath.mid(streampath.lastIndexOf('/')+1);
129
        const QByteArray array(i.value());
130
        QBuffer buffer;
131
        buffer.setData(array);
132
        buffer.open(QIODevice::ReadOnly);
133
        LEInputStream listream(&buffer);
134
        qDebug() << "Parsing stream '" << streampath << "'";
135
136
        const Introspectable* i;
137
        try {
138
            i = parse(streamname, listream);
139
        } catch (IOException& e) {
140
            qDebug() << "Error: " << e.msg;
141
            continue;
142
        }
143
144
        if (listream.getPosition() != (qint64)array.size()) {
145
            qDebug() << array.size() - listream.getPosition()
146
                << "trailing bytes in stream " << streampath;
147
            result.clear();
148
            return result;
149
        }
150
        buffer.close();
151
152
        // test if serialization gives the same bytearray as that which came in
153
        buffer.buffer().clear();
154
        buffer.open(QIODevice::WriteOnly);
155
        LEOutputStream lostream(&buffer);
156
        serialize(i, streamname, lostream);
157
        if (array != buffer.data()) {
158
            qDebug() << "Serialized data different from original in "
159
                << streampath;
160
        }
161
162
        result[streampath] = QSharedPointer<const Introspectable>(i);
163
    }
164
    return result;
165
}
166
167
QMap<QString, QByteArray>
168
serialize(const QMap<QString, QSharedPointer<const Introspectable> >& m) {
169
    QMap<QString, QByteArray> streams;
170
    QMap<QString, QSharedPointer<const Introspectable> >::const_iterator i;
171
    for (i = m.begin(); i!= m.end(); ++i) {
172
        const QString streampath(i.key());
56bd8e4 by Jos van den Oever at 2009-11-04 173
        const QString streamname
174
            = streampath.mid(streampath.lastIndexOf('/')+1);
25cd4bb by Jos van den Oever at 2009-11-01 175
        QBuffer buffer;
176
        buffer.open(QIODevice::WriteOnly);
177
        LEOutputStream lostream(&buffer);
178
        const Introspectable* is = i.value().data();
179
        serialize(is, streamname, lostream);
180
        streams[streampath] = buffer.data();
181
    }
182
    return streams;
183
}
184
56bd8e4 by Jos van den Oever at 2009-11-04 185
void
186
write(const QString& name, const QByteArray& data) {
187
    QFile out(name);
188
    out.open(QIODevice::WriteOnly);
189
    out.write(data);
190
    out.close();
191
}
192
193
void printWithExtendedParser(QXmlStreamWriter& out, const Introspectable* i);
194
195
void
196
printStyleTextPropAtom(QXmlStreamWriter& out, const Introspectable* i,
197
            int characterCount) {
198
    const Introspection* is = i->getIntrospection();
199
200
    // rh
201
    const Introspectable* ci = is->introspectable[0](i, 0);
202
    out.writeStartElement(is->names[0]);
203
    QString type = ci->getIntrospection()->name;
204
    out.writeAttribute("type", type);
205
    printWithExtendedParser(out, is->introspectable[0](i, 0)); // rh
206
    out.writeEndElement();
207
208
    // styles
209
    QByteArray blob = is->value[1](i, 0).toByteArray();
210
    QBuffer buffer;
211
    buffer.setData(blob);
212
    buffer.open(QIODevice::ReadOnly);
213
    LEInputStream listream(&buffer);
214
215
    try {
216
        int sum = 0;
217
        do {
218
            ci = parse("textPFRun", listream);
219
            const Introspection* cis = ci->getIntrospection();
220
            sum += cis->value[0](ci, 0).toInt();
221
            out.writeStartElement("rgTextPFRun");
222
            out.writeAttribute("type", "TextPFRun");
223
            printWithExtendedParser(out, ci);
224
            //qDebug() << "PF " << characterCount << " " << cis->value[0](ci, 0).toInt() << " " << sum;
225
            delete ci;
226
            out.writeEndElement();
227
        } while (sum <= characterCount);
228
        sum = 0;
229
        do {
230
            ci = parse("textCFRun", listream);
231
            const Introspection* cis = ci->getIntrospection();
232
            sum += cis->value[0](ci, 0).toInt();
233
            out.writeStartElement("rgTextCFRun");
234
            out.writeAttribute("type", "TextCFRun");
235
            printWithExtendedParser(out, ci);
236
            //qDebug() << "CF " << characterCount << " " << cis->value[0](ci, 0).toInt() << " " << sum;
237
            delete ci;
238
            out.writeEndElement();
7dd12a4 by Jos van den Oever at 2010-01-22 239
        } while (sum <= characterCount);
56bd8e4 by Jos van den Oever at 2009-11-04 240
    } catch (IOException& e) {
241
        qDebug() << "Error: " << e.msg;
242
    }
7dd12a4 by Jos van den Oever at 2010-01-22 243
    if (buffer.size() != listream.getPosition()) {
244
        qDebug() << "Error: " << buffer.size() - listream.getPosition()
245
            << " bytes left for StyleTextPropAtom at position "
246
            << i->streamOffset;
247
    }
56bd8e4 by Jos van den Oever at 2009-11-04 248
}
249
250
void
251
printWithExtendedParser(QXmlStreamWriter& out, const Introspectable* i) {
252
    int lastCharacterCount = 0; // needed for parsing StyleTextPropAtom
253
254
    const Introspection* is = i->getIntrospection();
255
    for (int j=0; j<is->numberOfMembers; ++j) {
256
        for (int k=0; k<is->numberOfInstances[j](i); ++k) {
257
            out.writeStartElement(is->names[j]);
258
            const Introspectable* ci = is->introspectable[j](i, k);
259
            if (ci) {
260
                QString type = ci->getIntrospection()->name;
261
                out.writeAttribute("type", type);
124a9d4 by Jos van den Oever at 2010-01-12 262
                uint32_t offset = ci->streamOffset;
263
                out.writeAttribute("offset", QString::number(offset));
56bd8e4 by Jos van den Oever at 2009-11-04 264
                if (type == "StyleTextPropAtom") {
265
                    // StyleTextPropAtom is currently too hard to parse by the generated code
266
                    printStyleTextPropAtom(out, ci, lastCharacterCount);
267
                } else if (type == "TextCharsAtom") {
268
                    const Introspection* cis = ci->getIntrospection();
269
                    lastCharacterCount = cis->value[1](ci, 0).value<QVector<quint16> >().count();
270
                    printWithExtendedParser(out, ci);
271
                } else if (type == "TextBytesAtom") {
272
                    const Introspection* cis = ci->getIntrospection();
273
                    lastCharacterCount = cis->value[1](ci, 0).toByteArray().count();
274
                    printWithExtendedParser(out, ci);
275
                } else {
276
                    printWithExtendedParser(out, ci);
277
                }
278
            } else {
279
                QVariant v(is->value[j](i, k));
280
                if (v.canConvert<QVector<quint16> >()) {
281
                    out.writeCharacters(toString(v.value<QVector<quint16> >()));
282
                } else {
283
                    if (v.type() == QVariant::ByteArray) {
284
                        v = escapeByteArray(v.toByteArray());
285
                    }
286
                    out.writeCharacters(v.toString());
287
                }
288
            }
289
            out.writeEndElement();
290
        }
291
    }
292
}
293
25cd4bb by Jos van den Oever at 2009-11-01 294
QByteArray
56bd8e4 by Jos van den Oever at 2009-11-04 295
streamsToXml(
296
            const QMap<QString, QSharedPointer<const Introspectable> >& streams,
297
            void (*printFunction)(QXmlStreamWriter&,const Introspectable*)) {
25cd4bb by Jos van den Oever at 2009-11-01 298
    QBuffer xml;
299
    xml.open(QBuffer::WriteOnly);
300
    QXmlStreamWriter xmlout(&xml);
301
    xmlout.setAutoFormatting(true);
302
    xmlout.writeStartDocument();
303
    xmlout.writeStartElement("ppt");
304
    QMap<QString, QSharedPointer<const Introspectable> >::const_iterator i;
305
    for (i = streams.begin(); i!= streams.end(); ++i) {
306
        const Introspection* in = i.value()->getIntrospection();
307
        xmlout.writeStartElement(in->name);
308
        xmlout.writeAttribute("stream-path", i.key());
309
        if (in->name != "TODOS") {
56bd8e4 by Jos van den Oever at 2009-11-04 310
            printFunction(xmlout, i.value().data());
25cd4bb by Jos van den Oever at 2009-11-01 311
        }
312
        xmlout.writeEndElement();
313
    }
314
    xmlout.writeEndElement();
315
    xmlout.writeEndDocument();
316
    return xml.data();
317
}
318
56bd8e4 by Jos van den Oever at 2009-11-04 319
QByteArray
320
streamsToXml(const QMap<QString,
321
            QSharedPointer<const Introspectable> >& streams) {
322
    return streamsToXml(streams, print);
323
}
324
325
QByteArray
326
streamsToExtendedXml(const QMap<QString,
327
            QSharedPointer<const Introspectable> >& streams) {
328
    return streamsToXml(streams, printWithExtendedParser);
25cd4bb by Jos van den Oever at 2009-11-01 329
}