From c98104f07855c5a04ee8852500ed07449869f7fb Mon Sep 17 00:00:00 2001 From: Srivats P Date: Mon, 28 Mar 2016 21:23:10 +0530 Subject: [PATCH] Refactored native streams file format to share code with native session file format. Added skeletal code for session/ossn file format --- common/fileformat.cpp | 405 +----------------------------- common/fileformat.h | 23 +- common/nativefileformat.cpp | 470 +++++++++++++++++++++++++++++++++++ common/nativefileformat.h | 75 ++++++ common/ossnfileformat.cpp | 46 ++++ common/ossnfileformat.h | 40 +++ common/ostprotogui.pro | 6 + common/sessionfileformat.cpp | 111 +++++++++ common/sessionfileformat.h | 90 +++++++ 9 files changed, 848 insertions(+), 418 deletions(-) create mode 100644 common/nativefileformat.cpp create mode 100644 common/nativefileformat.h create mode 100644 common/ossnfileformat.cpp create mode 100644 common/ossnfileformat.h create mode 100644 common/sessionfileformat.cpp create mode 100644 common/sessionfileformat.h diff --git a/common/fileformat.cpp b/common/fileformat.cpp index 4edd980..2ccf270 100644 --- a/common/fileformat.cpp +++ b/common/fileformat.cpp @@ -19,160 +19,23 @@ along with this program. If not, see #include "fileformat.h" -#include "crc32c.h" - -#include -#include -#include - -#include - -const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; - FileFormat fileFormat; -const int kBaseHex = 16; - FileFormat::FileFormat() + : AbstractFileFormat(), NativeFileFormat() { - /* - * We don't have any "real" work to do here in the constructor. - * What we do is run some "assert" tests so that these get caught - * at init itself instead of while saving/restoring when a user - * might lose some data! - */ - OstProto::FileMagic magic; - OstProto::FileChecksum cksum; - - magic.set_value(kFileMagicValue); - cksum.set_value(quint32(0)); - - // TODO: convert Q_ASSERT to something that will run in RELEASE mode also - Q_ASSERT(magic.IsInitialized()); - Q_ASSERT(cksum.IsInitialized()); - Q_ASSERT(magic.ByteSize() == kFileMagicSize); - Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); -} - -FileFormat::~FileFormat() -{ + // Do Nothing! } bool FileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { - QFile file(fileName); - QByteArray buf; - int size, contentOffset, contentSize; - quint32 calcCksum; - OstProto::FileMagic magic; OstProto::FileMeta meta; OstProto::FileContent content; - OstProto::FileChecksum cksum, zeroCksum; - - if (!file.open(QIODevice::ReadOnly)) - goto _open_fail; - - if (file.size() < kFileMagicSize) - goto _magic_missing; - - if (file.size() < kFileMinSize) - goto _checksum_missing; - - buf.resize(file.size()); - size = file.read(buf.data(), buf.size()); - if (size < 0) - goto _read_fail; - - Q_ASSERT(file.atEnd()); - file.close(); - - qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); - qDebug("%s: size = %d", __FUNCTION__, size); - - //qDebug("Read %d bytes", buf.size()); - //qDebug("%s", QString(buf.toHex()).toAscii().constData()); - - // Parse and verify magic - if (!magic.ParseFromArray( - (void*)(buf.constData() + kFileMagicOffset), - kFileMagicSize)) - { - goto _magic_parse_fail; - } - if (magic.value() != kFileMagicValue) - goto _magic_match_fail; - - // Parse and verify checksum - if (!cksum.ParseFromArray( - (void*)(buf.constData() + size - kFileChecksumSize), - kFileChecksumSize)) - { - goto _cksum_parse_fail; - } - - zeroCksum.set_value(0); - if (!zeroCksum.SerializeToArray( - (void*) (buf.data() + size - kFileChecksumSize), - kFileChecksumSize)) - { - goto _zero_cksum_serialize_fail; - } + bool ret = NativeFileFormat::open(fileName, meta, content, error); - calcCksum = checksumCrc32C((quint8*) buf.constData(), size); - - qDebug("checksum \nExpected:%x Actual:%x", - calcCksum, cksum.value()); - - if (cksum.value() != calcCksum) - goto _cksum_verify_fail; - - // Parse the metadata first before we parse the full contents - if (!meta.ParseFromArray( - (void*)(buf.constData() + kFileMetaDataOffset), - size - kFileMetaDataOffset)) - { - goto _metadata_parse_fail; - } - - qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, - QString().fromStdString(meta.DebugString()).toAscii().constData()); - - // MetaData Validation(s) - if (meta.data().file_type() != OstProto::kStreamsFileType) - goto _unexpected_file_type; - - if (meta.data().format_version_major() != kFileFormatVersionMajor) - goto _incompatible_file_version; - - if (meta.data().format_version_minor() > kFileFormatVersionMinor) - goto _incompatible_file_version; - - if (meta.data().format_version_minor() < kFileFormatVersionMinor) - { - // TODO: need to modify 'buf' such that we can parse successfully - // assuming the native minor version - } - - if (meta.data().format_version_revision() > kFileFormatVersionRevision) - { - error = QString(tr("%1 was created using a newer version of Ostinato." - " New features/protocols will not be available.")).arg(fileName); - } - - Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); - - // ByteSize() does not include the Tag/Key, so we add 2 for that - contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; - contentSize = size - contentOffset - kFileChecksumSize; - - // Parse full contents - if (!content.ParseFromArray( - (void*)(buf.constData() + contentOffset), - contentSize)) - { - goto _content_parse_fail; - } + if (!ret) + goto _fail; if (!content.matter().has_streams()) goto _missing_streams; @@ -186,75 +49,6 @@ bool FileFormat::openStreams(const QString fileName, _missing_streams: error = QString(tr("%1 does not contain any streams")).arg(fileName); goto _fail; -_content_parse_fail: - error = QString(tr("Failed parsing %1 contents")).arg(fileName); - qDebug("Error: %s", QString().fromStdString( - content.matter().InitializationErrorString()) - .toAscii().constData()); - qDebug("Debug: %s", QString().fromStdString( - content.matter().DebugString()).toAscii().constData()); - goto _fail; -_incompatible_file_version: - error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" - " (Native version is %5.%6.%7)")) - .arg(fileName) - .arg(meta.data().format_version_major()) - .arg(meta.data().format_version_minor()) - .arg(meta.data().format_version_revision()) - .arg(kFileFormatVersionMajor) - .arg(kFileFormatVersionMinor) - .arg(kFileFormatVersionRevision); - goto _fail; -_unexpected_file_type: - error = QString(tr("%1 is not a streams file")).arg(fileName); - goto _fail; -_metadata_parse_fail: - error = QString(tr("Failed parsing %1 meta data")).arg(fileName); - qDebug("Error: %s", QString().fromStdString( - meta.data().InitializationErrorString()) - .toAscii().constData()); - goto _fail; -_cksum_verify_fail: - error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) - .arg(fileName) - .arg(calcCksum, 0, kBaseHex) - .arg(cksum.value(), 0, kBaseHex); - goto _fail; -_zero_cksum_serialize_fail: - error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" - "Error: %1\nDebug: %2")) - .arg(QString().fromStdString( - cksum.InitializationErrorString())) - .arg(QString().fromStdString(cksum.DebugString())); - goto _fail; -_cksum_parse_fail: - error = QString(tr("Failed parsing %1 checksum")).arg(fileName); - qDebug("Error: %s", QString().fromStdString( - cksum.InitializationErrorString()) - .toAscii().constData()); - goto _fail; -_magic_match_fail: - error = QString(tr("%1 is not an Ostinato file")).arg(fileName); - goto _fail; -_magic_parse_fail: - error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); - qDebug("Error: %s", QString().fromStdString( - magic.InitializationErrorString()) - .toAscii().constData()); - goto _fail; -_read_fail: - error = QString(tr("Error reading from %1")).arg(fileName); - goto _fail; -_checksum_missing: - error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); - goto _fail; -_magic_missing: - error = QString(tr("%1 is too small (missing magic value)")) - .arg(fileName); - goto _fail; -_open_fail: - error = QString(tr("Error opening %1")).arg(fileName); - goto _fail; _fail: qDebug("%s", error.toAscii().constData()); return false; @@ -263,25 +57,7 @@ _fail: bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { - OstProto::FileMagic magic; - OstProto::FileMeta meta; OstProto::FileContent content; - OstProto::FileChecksum cksum; - QFile file(fileName); - int metaSize, contentSize; - int contentOffset, cksumOffset; - QByteArray buf; - quint32 calcCksum; - - magic.set_value(kFileMagicValue); - Q_ASSERT(magic.IsInitialized()); - - cksum.set_value(0); - Q_ASSERT(cksum.IsInitialized()); - - initFileMetaData(*(meta.mutable_data())); - meta.mutable_data()->set_file_type(OstProto::kStreamsFileType); - Q_ASSERT(meta.IsInitialized()); if (!streams.IsInitialized()) goto _stream_not_init; @@ -289,104 +65,9 @@ bool FileFormat::saveStreams(const OstProto::StreamConfigList streams, content.mutable_matter()->mutable_streams()->CopyFrom(streams); Q_ASSERT(content.IsInitialized()); - metaSize = meta.ByteSize(); - contentSize = content.ByteSize(); - contentOffset = kFileMetaDataOffset + metaSize; - cksumOffset = contentOffset + contentSize; + return NativeFileFormat::save(OstProto::kStreamsFileType, content, + fileName, error); - Q_ASSERT(magic.ByteSize() == kFileMagicSize); - Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); - buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); - - // Serialize everything - if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), - kFileMagicSize)) - { - goto _magic_serialize_fail; - } - - if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), - metaSize)) - { - goto _meta_serialize_fail; - } - - if (!content.SerializeToArray((void*) (buf.data() + contentOffset), - contentSize)) - { - goto _content_serialize_fail; - } - - if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), - kFileChecksumSize)) - { - goto _zero_cksum_serialize_fail; - } - - emit status("Calculating checksum..."); - - // Calculate and write checksum - calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); - cksum.set_value(calcCksum); - if (!cksum.SerializeToArray( - (void*) (buf.data() + cksumOffset), - kFileChecksumSize)) - { - goto _cksum_serialize_fail; - } - - qDebug("Writing %d bytes", buf.size()); - //qDebug("%s", QString(buf.toHex()).toAscii().constData()); - - emit status("Writing to disk..."); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) - goto _open_fail; - - if (file.write(buf) < 0) - goto _write_fail; - - file.close(); - - return true; - -_write_fail: - error = QString(tr("Error writing to %1")).arg(fileName); - goto _fail; -_open_fail: - error = QString(tr("Error opening %1 (Error Code = %2)")) - .arg(fileName) - .arg(file.error()); - goto _fail; -_cksum_serialize_fail: - error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) - .arg(QString().fromStdString( - cksum.InitializationErrorString())) - .arg(QString().fromStdString(cksum.DebugString())); - goto _fail; -_zero_cksum_serialize_fail: - error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) - .arg(QString().fromStdString( - cksum.InitializationErrorString())) - .arg(QString().fromStdString(cksum.DebugString())); - goto _fail; -_content_serialize_fail: - error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) - .arg(QString().fromStdString( - content.InitializationErrorString())) - .arg(QString().fromStdString(content.DebugString())); - goto _fail; -_meta_serialize_fail: - error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) - .arg(QString().fromStdString( - meta.InitializationErrorString())) - .arg(QString().fromStdString(meta.DebugString())); - goto _fail; -_magic_serialize_fail: - error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) - .arg(QString().fromStdString( - magic.InitializationErrorString())) - .arg(QString().fromStdString(magic.DebugString())); - goto _fail; _stream_not_init: error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) .arg(QString().fromStdString( @@ -400,26 +81,7 @@ _fail: bool FileFormat::isMyFileFormat(const QString fileName) { - bool ret = false; - QFile file(fileName); - QByteArray buf; - OstProto::FileMagic magic; - - if (!file.open(QIODevice::ReadOnly)) - goto _exit; - - buf = file.peek(kFileMagicOffset + kFileMagicSize); - if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), - kFileMagicSize)) - goto _close_exit; - - if (magic.value() == kFileMagicValue) - ret = true; - -_close_exit: - file.close(); -_exit: - return ret; + return isNativeFileFormat(fileName, OstProto::kStreamsFileType); } bool FileFormat::isMyFileType(const QString fileType) @@ -430,54 +92,3 @@ bool FileFormat::isMyFileType(const QString fileType) return false; } -void FileFormat::initFileMetaData(OstProto::FileMetaData &metaData) -{ - // Fill in the "native" file format version - metaData.set_format_version_major(kFileFormatVersionMajor); - metaData.set_format_version_minor(kFileFormatVersionMinor); - metaData.set_format_version_revision(kFileFormatVersionRevision); - - metaData.set_generator_name( - qApp->applicationName().toUtf8().constData()); - metaData.set_generator_version( - qApp->property("version").toString().toUtf8().constData()); - metaData.set_generator_revision( - qApp->property("revision").toString().toUtf8().constData()); -} - -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -/*! Fixup content to what is expected in the native version */ -void FileFormat::postParseFixup(OstProto::FileMetaData metaData, - OstProto::FileContent &content) -{ - Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); - - // Do fixups from oldest to newest versions - switch (metaData.format_version_minor()) - { - case 1: - { - int n = content.matter().streams().stream_size(); - for (int i = 0; i < n; i++) - { - OstProto::StreamControl *sctl = - content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); - sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); - sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); - } - - // fall-through to next higher version until native version - } - case kFileFormatVersionMinor: // native version - break; - - case 0: - default: - qWarning("%s: minor version %u unhandled", __FUNCTION__, - metaData.format_version_minor()); - Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); - } - -} -#pragma GCC diagnostic warning "-Wdeprecated-declarations" - diff --git a/common/fileformat.h b/common/fileformat.h index f2c5b32..95eb310 100644 --- a/common/fileformat.h +++ b/common/fileformat.h @@ -20,14 +20,14 @@ along with this program. If not, see #define _FILE_FORMAT_H #include "abstractfileformat.h" +#include "nativefileformat.h" #include "fileformat.pb.h" -class FileFormat : public AbstractFileFormat +class FileFormat : public AbstractFileFormat, public NativeFileFormat { public: FileFormat(); - ~FileFormat(); virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); @@ -36,25 +36,6 @@ public: bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); - -private: - void initFileMetaData(OstProto::FileMetaData &metaData); - void postParseFixup(OstProto::FileMetaData metaData, - OstProto::FileContent &content); - - static const int kFileMagicSize = 12; - static const int kFileChecksumSize = 5; - static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; - - static const int kFileMagicOffset = 0; - static const int kFileMetaDataOffset = kFileMagicSize; - - static const std::string kFileMagicValue; - - // Native file format version - static const uint kFileFormatVersionMajor = 0; - static const uint kFileFormatVersionMinor = 2; - static const uint kFileFormatVersionRevision = 4; }; extern FileFormat fileFormat; diff --git a/common/nativefileformat.cpp b/common/nativefileformat.cpp new file mode 100644 index 0000000..83e13ba --- /dev/null +++ b/common/nativefileformat.cpp @@ -0,0 +1,470 @@ +/* +Copyright (C) 2010, 2016 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#include "nativefileformat.h" + +#include "crc32c.h" + +#include +#include +#include + +#define tr(str) QObject::tr(str) + +const std::string NativeFileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; + +static const int kBaseHex = 16; + +NativeFileFormat::NativeFileFormat() +{ + /* + * We don't have any "real" work to do here in the constructor. + * What we do is run some "assert" tests so that these get caught + * at init itself instead of while saving/restoring when a user + * might lose some data! + */ + OstProto::FileMagic magic; + OstProto::FileChecksum cksum; + + magic.set_value(kFileMagicValue); + cksum.set_value(quint32(0)); + + // TODO: convert Q_ASSERT to something that will run in RELEASE mode also + Q_ASSERT(magic.IsInitialized()); + Q_ASSERT(cksum.IsInitialized()); + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); +} + +bool NativeFileFormat::open( + const QString fileName, + OstProto::FileMeta &meta, + OstProto::FileContent &content, + QString &error) +{ + QFile file(fileName); + QByteArray buf; + int size, contentOffset, contentSize; + quint32 calcCksum; + OstProto::FileMagic magic; + OstProto::FileChecksum cksum, zeroCksum; + + if (!file.open(QIODevice::ReadOnly)) + goto _open_fail; + + if (file.size() < kFileMagicSize) + goto _magic_missing; + + if (file.size() < kFileMinSize) + goto _checksum_missing; + + buf.resize(file.size()); + size = file.read(buf.data(), buf.size()); + if (size < 0) + goto _read_fail; + + Q_ASSERT(file.atEnd()); + file.close(); + + qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); + qDebug("%s: size = %d", __FUNCTION__, size); + + //qDebug("Read %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // Parse and verify magic + if (!magic.ParseFromArray( + (void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_parse_fail; + } + if (magic.value() != kFileMagicValue) + goto _magic_match_fail; + + // Parse and verify checksum + if (!cksum.ParseFromArray( + (void*)(buf.constData() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _cksum_parse_fail; + } + + zeroCksum.set_value(0); + if (!zeroCksum.SerializeToArray( + (void*) (buf.data() + size - kFileChecksumSize), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + calcCksum = checksumCrc32C((quint8*) buf.constData(), size); + + qDebug("checksum \nExpected:%x Actual:%x", + calcCksum, cksum.value()); + + if (cksum.value() != calcCksum) + goto _cksum_verify_fail; + + // Parse the metadata first before we parse the full contents + // FIXME: metadata size is not known beforehand, so we end up + // parsing till EOF + if (!meta.ParseFromArray( + (void*)(buf.constData() + kFileMetaDataOffset), + size - kFileMetaDataOffset)) + { + goto _metadata_parse_fail; + } + + qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, + QString().fromStdString(meta.DebugString()).toAscii().constData()); + qDebug("%s: END MetaData", __FUNCTION__); + + // MetaData Validation(s) + if (meta.data().file_type() != OstProto::kStreamsFileType) + goto _unexpected_file_type; + + if (meta.data().format_version_major() != kFileFormatVersionMajor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() > kFileFormatVersionMinor) + goto _incompatible_file_version; + + if (meta.data().format_version_minor() < kFileFormatVersionMinor) + { + // TODO: need to modify 'buf' such that we can parse successfully + // assuming the native minor version + } + + if (meta.data().format_version_revision() > kFileFormatVersionRevision) + { + error = QString(tr("%1 was created using a newer version of Ostinato." + " New features/protocols will not be available.")).arg(fileName); + } + + Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); + + // ByteSize() does not include the Tag/Key, so we add 2 for that + contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; + contentSize = size - contentOffset - kFileChecksumSize; + qDebug("%s: content offset/size = %d/%d", __FUNCTION__, + contentOffset, contentSize); + + // Parse full contents + if (!content.ParseFromArray( + (void*)(buf.constData() + contentOffset), + contentSize)) + { + goto _content_parse_fail; + } + + return true; + +_content_parse_fail: + error = QString(tr("Failed parsing %1 contents")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + content.InitializationErrorString()) + .toAscii().constData()); + qDebug("Debug: %s", QString().fromStdString( + content.DebugString()).toAscii().constData()); + goto _fail; +_incompatible_file_version: + error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" + " (Native version is %5.%6.%7)")) + .arg(fileName) + .arg(meta.data().format_version_major()) + .arg(meta.data().format_version_minor()) + .arg(meta.data().format_version_revision()) + .arg(kFileFormatVersionMajor) + .arg(kFileFormatVersionMinor) + .arg(kFileFormatVersionRevision); + goto _fail; +_unexpected_file_type: + error = QString(tr("%1 is not a streams file")).arg(fileName); + goto _fail; +_metadata_parse_fail: + error = QString(tr("Failed parsing %1 meta data")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + meta.data().InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_cksum_verify_fail: + error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) + .arg(fileName) + .arg(calcCksum, 0, kBaseHex) + .arg(cksum.value(), 0, kBaseHex); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" + "Error: %1\nDebug: %2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_cksum_parse_fail: + error = QString(tr("Failed parsing %1 checksum")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + cksum.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_magic_match_fail: + error = QString(tr("%1 is not an Ostinato file")).arg(fileName); + goto _fail; +_magic_parse_fail: + error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); + qDebug("Error: %s", QString().fromStdString( + magic.InitializationErrorString()) + .toAscii().constData()); + goto _fail; +_read_fail: + error = QString(tr("Error reading from %1")).arg(fileName); + goto _fail; +_checksum_missing: + error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); + goto _fail; +_magic_missing: + error = QString(tr("%1 is too small (missing magic value)")) + .arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1")).arg(fileName); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool NativeFileFormat::save( + OstProto::FileType fileType, + const OstProto::FileContent &content, + const QString fileName, + QString &error) +{ + OstProto::FileMagic magic; + OstProto::FileMeta meta; + OstProto::FileChecksum cksum; + QFile file(fileName); + int metaSize, contentSize; + int contentOffset, cksumOffset; + QByteArray buf; + quint32 calcCksum; + + magic.set_value(kFileMagicValue); + Q_ASSERT(magic.IsInitialized()); + + cksum.set_value(0); + Q_ASSERT(cksum.IsInitialized()); + + initFileMetaData(*(meta.mutable_data())); + meta.mutable_data()->set_file_type(fileType); + Q_ASSERT(meta.IsInitialized()); + + if (!content.IsInitialized()) + goto _content_not_init; + + Q_ASSERT(content.IsInitialized()); + + metaSize = meta.ByteSize(); + contentSize = content.ByteSize(); + contentOffset = kFileMetaDataOffset + metaSize; + cksumOffset = contentOffset + contentSize; + + Q_ASSERT(magic.ByteSize() == kFileMagicSize); + Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); + buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); + + // Serialize everything + if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), + kFileMagicSize)) + { + goto _magic_serialize_fail; + } + + if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), + metaSize)) + { + goto _meta_serialize_fail; + } + + if (!content.SerializeToArray((void*) (buf.data() + contentOffset), + contentSize)) + { + goto _content_serialize_fail; + } + + if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _zero_cksum_serialize_fail; + } + + // TODO: emit status("Calculating checksum..."); + + // Calculate and write checksum + calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); + cksum.set_value(calcCksum); + if (!cksum.SerializeToArray( + (void*) (buf.data() + cksumOffset), + kFileChecksumSize)) + { + goto _cksum_serialize_fail; + } + + qDebug("Writing %d bytes", buf.size()); + //qDebug("%s", QString(buf.toHex()).toAscii().constData()); + + // TODO: emit status("Writing to disk..."); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + goto _open_fail; + + if (file.write(buf) < 0) + goto _write_fail; + + file.close(); + + return true; + +_write_fail: + error = QString(tr("Error writing to %1")).arg(fileName); + goto _fail; +_open_fail: + error = QString(tr("Error opening %1 (Error Code = %2)")) + .arg(fileName) + .arg(file.error()); + goto _fail; +_cksum_serialize_fail: + error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_zero_cksum_serialize_fail: + error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + cksum.InitializationErrorString())) + .arg(QString().fromStdString(cksum.DebugString())); + goto _fail; +_content_serialize_fail: + error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_meta_serialize_fail: + error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + meta.InitializationErrorString())) + .arg(QString().fromStdString(meta.DebugString())); + goto _fail; +_magic_serialize_fail: + error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) + .arg(QString().fromStdString( + magic.InitializationErrorString())) + .arg(QString().fromStdString(magic.DebugString())); + goto _fail; +_content_not_init: + error = QString(tr("Internal Error: Content not initialized\n%1\n%2")) + .arg(QString().fromStdString( + content.InitializationErrorString())) + .arg(QString().fromStdString(content.DebugString())); + goto _fail; +_fail: + qDebug("%s", error.toAscii().constData()); + return false; +} + +bool NativeFileFormat::isNativeFileFormat( + const QString fileName, + OstProto::FileType fileType) +{ + bool ret = false; + QFile file(fileName); + QByteArray buf; + OstProto::FileMagic magic; + + if (!file.open(QIODevice::ReadOnly)) + goto _exit; + + buf = file.peek(kFileMagicOffset + kFileMagicSize); + if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), + kFileMagicSize)) + goto _close_exit; + + if (magic.value() == kFileMagicValue) + ret = true; + + // TODO: check fileType + +_close_exit: + file.close(); +_exit: + return ret; +} + +void NativeFileFormat::initFileMetaData(OstProto::FileMetaData &metaData) +{ + // Fill in the "native" file format version + metaData.set_format_version_major(kFileFormatVersionMajor); + metaData.set_format_version_minor(kFileFormatVersionMinor); + metaData.set_format_version_revision(kFileFormatVersionRevision); + + metaData.set_generator_name( + qApp->applicationName().toUtf8().constData()); + metaData.set_generator_version( + qApp->property("version").toString().toUtf8().constData()); + metaData.set_generator_revision( + qApp->property("revision").toString().toUtf8().constData()); +} + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +/*! Fixup content to what is expected in the native version */ +void NativeFileFormat::postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content) +{ + Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); + + // Do fixups from oldest to newest versions + switch (metaData.format_version_minor()) + { + case 1: + { + int n = content.matter().streams().stream_size(); + for (int i = 0; i < n; i++) + { + OstProto::StreamControl *sctl = + content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); + sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); + sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); + } + + // fall-through to next higher version until native version + } + case kFileFormatVersionMinor: // native version + break; + + case 0: + default: + qWarning("%s: minor version %u unhandled", __FUNCTION__, + metaData.format_version_minor()); + Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); + } + +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + diff --git a/common/nativefileformat.h b/common/nativefileformat.h new file mode 100644 index 0000000..fb49fc8 --- /dev/null +++ b/common/nativefileformat.h @@ -0,0 +1,75 @@ +/* +Copyright (C) 2010, 2016 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +#ifndef _NATIVE_FILE_FORMAT_H +#define _NATIVE_FILE_FORMAT_H + +/* + * This file contains helper functions for the native file format + * defined in fileformat.proto + * + * The actual file format classes - (Ostm)FileFormat and OssnFileFormat + * use multiple inheritance from the abstract interface class and this + * helper class + * + * The primary reason for the existence of this class is to have a common + * code for dealing with native file formats + */ + +#include "fileformat.pb.h" + +#include + +class NativeFileFormat +{ +public: + NativeFileFormat(); + + bool open(const QString fileName, + OstProto::FileMeta &meta, + OstProto::FileContent &content, + QString &error); + bool save(OstProto::FileType fileType, + const OstProto::FileContent &content, + const QString fileName, + QString &error); + + bool isNativeFileFormat(const QString fileName, + OstProto::FileType fileType); + void postParseFixup(OstProto::FileMetaData metaData, + OstProto::FileContent &content); + +private: + void initFileMetaData(OstProto::FileMetaData &metaData); + + static const int kFileMagicSize = 12; + static const int kFileChecksumSize = 5; + static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; + + static const int kFileMagicOffset = 0; + static const int kFileMetaDataOffset = kFileMagicSize; + + static const std::string kFileMagicValue; + + // Native file format version + static const uint kFileFormatVersionMajor = 0; + static const uint kFileFormatVersionMinor = 2; + static const uint kFileFormatVersionRevision = 4; +}; + +#endif diff --git a/common/ossnfileformat.cpp b/common/ossnfileformat.cpp new file mode 100644 index 0000000..ed6ebc2 --- /dev/null +++ b/common/ossnfileformat.cpp @@ -0,0 +1,46 @@ +/* +Copyright (C) 2016 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#include "ossnfileformat.h" + +bool OssnFileFormat::open(const QString fileName, + OstProto::SessionContent &session, QString &error) +{ + // TODO + return false; +} + +bool OssnFileFormat::save(const OstProto::SessionContent &session, + const QString fileName, QString &error) +{ + // TODO + return false; +} + +bool OssnFileFormat::isMyFileFormat(const QString fileName) +{ + // TODO + return true; +} + +bool OssnFileFormat::isMyFileType(const QString fileType) +{ + // TODO + return true; +} diff --git a/common/ossnfileformat.h b/common/ossnfileformat.h new file mode 100644 index 0000000..73eb5a8 --- /dev/null +++ b/common/ossnfileformat.h @@ -0,0 +1,40 @@ +/* +Copyright (C) 2016 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#ifndef _OSSN_FILE_FORMAT_H +#define _OSSN_FILE_FORMAT_H + +#include "sessionfileformat.h" + +class OssnFileFormat : public SessionFileFormat +{ +public: + virtual bool open(const QString fileName, + OstProto::SessionContent &session, QString &error); + virtual bool save(const OstProto::SessionContent &session, + const QString fileName, QString &error); + + virtual bool isMyFileFormat(const QString fileName); + virtual bool isMyFileType(const QString fileType); +}; + +extern OssnFileFormat ossnFileFormat; + +#endif + diff --git a/common/ostprotogui.pro b/common/ostprotogui.pro index b39526e..44f1061 100644 --- a/common/ostprotogui.pro +++ b/common/ostprotogui.pro @@ -39,12 +39,15 @@ HEADERS = \ fileformat.h \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ + nativefileformat.h \ + ossnfileformat.h \ pcapfileformat.h \ pdmlfileformat.h \ pythonfileformat.h \ pdmlprotocol.h \ pdmlprotocols.h \ pdmlreader.h \ + sessionfileformat.h \ spinboxdelegate.h HEADERS += \ @@ -82,12 +85,15 @@ SOURCES += \ ostprotolib.cpp \ abstractfileformat.cpp \ fileformat.cpp \ + nativefileformat.cpp \ + ossnfileformat.cpp \ pcapfileformat.cpp \ pdmlfileformat.cpp \ pythonfileformat.cpp \ pdmlprotocol.cpp \ pdmlprotocols.cpp \ pdmlreader.cpp \ + sessionfileformat.cpp \ spinboxdelegate.cpp SOURCES += \ diff --git a/common/sessionfileformat.cpp b/common/sessionfileformat.cpp new file mode 100644 index 0000000..644b956 --- /dev/null +++ b/common/sessionfileformat.cpp @@ -0,0 +1,111 @@ +/* +Copyright (C) 2016 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#include "sessionfileformat.h" + +#include "ossnfileformat.h" + +#include + +SessionFileFormat::SessionFileFormat() +{ + stop_ = false; +} + +SessionFileFormat::~SessionFileFormat() +{ +} + +QDialog* SessionFileFormat::openOptionsDialog() +{ + return NULL; +} + +QDialog* SessionFileFormat::saveOptionsDialog() +{ + return NULL; +} + +QStringList SessionFileFormat::supportedFileTypes() +{ + return QStringList() + << "Ostinato Session (*.ossn)"; +} + +void SessionFileFormat::openOffline(const QString fileName, + OstProto::SessionContent &session, QString &error) +{ + fileName_ = fileName; + openSession_ = &session; + error_ = &error; + op_ = kOpen; + stop_ = false; + + start(); +} + +void SessionFileFormat::saveOffline( + const OstProto::SessionContent &session, + const QString fileName, QString &error) +{ + saveSession_ = &session; + fileName_ = fileName; + error_ = &error; + op_ = kSave; + stop_ = false; + + start(); +} + +bool SessionFileFormat::result() +{ + return result_; +} + +SessionFileFormat* SessionFileFormat::fileFormatFromFile( + const QString fileName) +{ + if (ossnFileFormat.isMyFileFormat(fileName)) + return &ossnFileFormat; + + return NULL; +} + +SessionFileFormat* SessionFileFormat::fileFormatFromType( + const QString fileType) +{ + + if (ossnFileFormat.isMyFileType(fileType)) + return &ossnFileFormat; + + return NULL; +} + +void SessionFileFormat::cancel() +{ + stop_ = true; +} + +void SessionFileFormat::run() +{ + if (op_ == kOpen) + result_ = open(fileName_, *openSession_, *error_); + else if (op_ == kSave) + result_ = save(*saveSession_, fileName_, *error_); +} diff --git a/common/sessionfileformat.h b/common/sessionfileformat.h new file mode 100644 index 0000000..0146324 --- /dev/null +++ b/common/sessionfileformat.h @@ -0,0 +1,90 @@ +/* +Copyright (C) 2016 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#ifndef _SESSION_FILE_FORMAT_H +#define _SESSION_FILE_FORMAT_H + +#include "fileformat.pb.h" +#include "protocol.pb.h" + +#include +#include + +class QDialog; + +class SessionFileFormat : public QThread +{ + Q_OBJECT +public: + SessionFileFormat(); + virtual ~SessionFileFormat(); + + virtual bool open(const QString fileName, + OstProto::SessionContent &session, QString &error) = 0; + virtual bool save(const OstProto::SessionContent &session, + const QString fileName, QString &error) = 0; + + virtual QDialog* openOptionsDialog(); + virtual QDialog* saveOptionsDialog(); + + void openOffline(const QString fileName, + OstProto::SessionContent &session, QString &error); + void saveOffline(const OstProto::SessionContent &session, + const QString fileName, QString &error); + + bool result(); + + static QStringList supportedFileTypes(); + + static SessionFileFormat* fileFormatFromFile(const QString fileName); + static SessionFileFormat* fileFormatFromType(const QString fileType); + + virtual bool isMyFileFormat(const QString fileName) = 0; + virtual bool isMyFileType(const QString fileType) = 0; + +signals: + void status(QString text); + void target(int value); + void progress(int value); + +public slots: + void cancel(); + +protected: + void run(); + + bool stop_; + +private: + enum kOp { + kOpen, + kSave + }; + + QString fileName_; + OstProto::SessionContent *openSession_; + const OstProto::SessionContent *saveSession_; + QString *error_; + kOp op_; + bool result_; + +}; + +#endif +