diff options
Diffstat (limited to 'paperreplay.cc')
-rw-r--r-- | paperreplay.cc | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/paperreplay.cc b/paperreplay.cc new file mode 100644 index 0000000..8bfb20e --- /dev/null +++ b/paperreplay.cc @@ -0,0 +1,212 @@ +#include <QtCore/QDebug> +#include <QtCore/QDataStream> +#include <QtCore/QDirIterator> +#include "smartpen.h" +#include "paperreplay.h" + +namespace +{ +bool readUtfString(QDataStream &stream, QString &s) +{ + quint16 len; + stream >> len; + QByteArray buffer(len, Qt::Uninitialized); + if (stream.readRawData(buffer.data(), len) != len) { + return false; + } + s = QString::fromUtf8(buffer); + return true; +} +} + +PaperReplay::PaperReplay(QObject *parent) : + QObject(parent) +{ +} + +bool PaperReplay::open(const QString &path, const QString &penSerial, quint64 notebookGuid) +{ + _dir.setPath(path + QString("/userdata/%1/Paper Replay/99/%2/sessions") + .arg(penSerial) + .arg(qulonglong(notebookGuid), notebookGuid == 0 ? 1 : 16, 16, QLatin1Char('0'))); + if (!_dir.exists()) { + qDebug() << "Cannot open paper replay:" << _dir.absolutePath() << "does not exist"; + return false; + } + + QDirIterator iter(_dir.path(), QStringList("PRS-*"), QDir::Dirs | QDir::NoDotAndDotDot); + while (iter.hasNext()) { + QDir sessionDir(iter.next()); + bool ok; + quint64 sessionId = sessionDir.dirName().mid(4).toULongLong(&ok, 16); + if (!ok) { + qWarning() << "Invalid session identifier:" << sessionDir.dirName(); + } + Session &session = _sessions[sessionId]; + if (!parseSessionInfo(session, sessionDir.filePath("session.info"))) { + qWarning() << "Could not parse:" << sessionDir.absoluteFilePath("session.info"); + } + if (!parseSessionPages(session, sessionDir.filePath("session.pages"))) { + qWarning() << "Could not parse:" << sessionDir.absoluteFilePath("session.pages"); + } + } + + return true; +} + +void PaperReplay::close() +{ + _sessions.clear(); + _byPage.clear(); + _dir.setPath(QString()); +} + +QList<PaperReplay::Session> PaperReplay::sessions() +{ + return _sessions.values(); +} + +QList<PaperReplay::Session> PaperReplay::sessions(quint64 pageAddress) +{ + QList<Session> sessions; + QMultiMap<quint64, quint64>::iterator it = _byPage.find(pageAddress); + while (it != _byPage.end() && it.key() == pageAddress) { + sessions.append(_sessions[it.value()]); + } + return sessions; +} + +bool PaperReplay::parseSessionInfo(Session &session, const QString &path) +{ + QFile f(path); + if (f.open(QIODevice::ReadOnly)) { + return parseSessionInfo(session, &f); + } else { + return false; + } +} + +bool PaperReplay::parseSessionInfo(Session &session, QIODevice *dev) +{ + unsigned char magic[2]; + if (dev->read(reinterpret_cast<char*>(magic), 2) != 2 || + magic[0] != 0xFA || magic[1] != 0xCE) { + qWarning() << "Invalid magic"; + return false; + } + + char version = 0; + if (!dev->getChar(&version)) { + qWarning() << "Short read while getting version number"; + return false; + } + + switch (version) { + case 3: + return parseSessionInfoV3(session, dev); + default: + qWarning() << "Unknown version:" << version; + return false; + } +} + +bool PaperReplay::parseSessionInfoV3(Session &session, QIODevice *dev) +{ + QDataStream s(dev); + if (s.skipRawData(5) != 5) return false; + + qint64 startTime, endTime, creationTime; + QString name; + + s >> startTime >> endTime >> creationTime; + if (!readUtfString(s, name)) { + return false; + } + + qDebug() << "Name:" << name << Smartpen::fromPenTime(startTime) << Smartpen::fromPenTime(endTime) << creationTime; + + quint16 num_clips; + s >> num_clips; + + for (uint i = 0; i < num_clips; ++i) { + QString file; + if (!readUtfString(s, file)) { + return false; + } + s >> startTime >> endTime; + qDebug() << " Clip:" << file << startTime << endTime; + qDebug() << " " << QDateTime::fromTime_t(startTime) << QDateTime::fromTime_t(endTime); + } + + quint16 num_strokes; + s >> num_strokes; + + for (uint i = 0; i < num_strokes; ++i) { + quint64 a, b, c; + quint32 d; + QString nbGuid; + quint8 f, g; + s >> a >> b >> c >> d; + if (!readUtfString(s, nbGuid)) { + return false; + } + s >> f >> g; + qDebug() << " Stroke:" << a << b << c << d << nbGuid << f << g; + } + + return true; +} + +bool PaperReplay::parseSessionPages(Session &session, const QString &path) +{ + QFile f(path); + if (f.open(QIODevice::ReadOnly)) { + return parseSessionPages(session, &f); + } else { + return false; + } +} + +bool PaperReplay::parseSessionPages(Session &session, QIODevice *dev) +{ + unsigned char magic[2]; + if (dev->read(reinterpret_cast<char*>(magic), 2) != 2 || + magic[0] != 0xCA || magic[1] != 0xBE) { + qWarning() << "Invalid magic"; + return false; + } + + char version = 0; + if (!dev->getChar(&version)) { + qWarning() << "Short read while getting version number"; + return false; + } + + switch (version) { + case 1: + return parseSessionPagesV1(session, dev); + default: + qWarning() << "Unknown version:" << version; + return false; + } +} + +bool PaperReplay::parseSessionPagesV1(Session &session, QIODevice *dev) +{ + QDataStream s(dev); + if (s.skipRawData(1) != 1) return false; + + quint16 num_pages = 0; + s >> num_pages; + session.pages.reserve(session.pages.size() + num_pages); + + for (uint i = 0; i < num_pages; ++i) { + quint64 address, unk; + s >> address >> unk; + + session.pages.append(address); + qDebug() << " Page:" << address << unk; + } + + return true; +} |