aboutsummaryrefslogtreecommitdiff
path: root/paperreplay.cc
diff options
context:
space:
mode:
Diffstat (limited to 'paperreplay.cc')
-rw-r--r--paperreplay.cc212
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;
+}