From 109034fe479424bda9bef02ba48649a119ebebc2 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 30 Oct 2021 10:57:34 +0300 Subject: [PATCH 01/39] Moved the list of the pbom source files to the child CMake files --- pbom/CMakeLists.txt | 114 ++------------------------------------ pbom/io/CMakeLists.txt | 44 +++++++++++++++ pbom/model/CMakeLists.txt | 35 ++++++++++++ pbom/ui/CMakeLists.txt | 42 ++++++++++++++ pbom/util/CMakeLists.txt | 12 ++++ 5 files changed, 137 insertions(+), 110 deletions(-) create mode 100644 pbom/io/CMakeLists.txt create mode 100644 pbom/model/CMakeLists.txt create mode 100644 pbom/ui/CMakeLists.txt create mode 100644 pbom/util/CMakeLists.txt diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index 1b9ceeb..cd98327 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -14,85 +14,10 @@ set(CMAKE_FIND_DEBUG_MODE FALSE) find_package(QT NAMES Qt6 COMPONENTS Widgets Test REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Test REQUIRED) -set(PROJECT_SOURCES - io/bb/binarybackend.cpp - io/bb/execbackend.cpp - io/bb/nodefilesystem.cpp - io/bb/sanitizedstring.cpp - io/bb/tempbackend.cpp - io/bb/unpackbackend.cpp - io/bb/unpacktaskbackend.cpp - io/bs/binarysource.cpp - io/bs/fslzhbinarysource.cpp - io/bs/fsrawbinarysource.cpp - io/bs/pbobinarysource.cpp - io/lzh/compressionbuffer.cpp - io/lzh/compressionchunk.cpp - io/lzh/decompressioncontext.cpp - io/lzh/lzh.cpp - io/lzh/lzhdecompressionexception.cpp - io/pbodatastream.cpp - io/pbofile.cpp - io/pboheaderio.cpp - io/pboheaderreader.cpp - io/pboioexception.cpp - io/pbowriter.cpp - model/task/packtask.cpp - model/task/packwindowmodel.cpp - model/task/task.h - model/task/taskwindowmodel.cpp - model/task/unpacktask.cpp - model/task/unpackwindowmodel.cpp - model/conflictsparcel.cpp - model/diskaccessexception.cpp - model/headersmodel.cpp - model/interactionparcel.cpp - model/pboentry.cpp - model/pbofileformatexception.cpp - model/pboheader.cpp - model/pbomodel.cpp - model/pbonode.cpp - model/pbonodeevents.cpp - model/pbopath.cpp - model/rootreader.cpp - model/signaturemodel.cpp - ui/progresswidget/progresswidget.cpp - ui/treewidget/deleteop.cpp - ui/treewidget/treewidget.cpp - ui/treewidget/treewidgetbase.cpp - ui/treewidget/treewidgetitem.cpp - ui/win32/win32iconmgr.cpp - ui/win32/win32fileviewer.cpp - ui/aboutdialog.cpp - ui/aboutdialog.ui - ui/closedialog.cpp - ui/closedialog.ui - ui/compresslist.cpp - ui/conflictslist.cpp - ui/errordialog.ui - ui/errordialog.cpp - ui/fscollector.cpp - ui/headersdialog.cpp - ui/headersdialog.ui - ui/insertdialog.cpp - ui/insertdialog.ui - ui/insertdialogbuttons.cpp - ui/renamedialog.cpp - ui/renamedialog.ui - ui/mainwindow.cpp - ui/mainwindow.ui - ui/packwindow.cpp - ui/signaturedialog.cpp - ui/signaturedialog.ui - ui/statusbar.cpp - ui/statusbar.cpp - ui/taskwindow.cpp - ui/taskwindow.ui - ui/unpackwindow.cpp - util/exception.cpp - util/log.cpp - util/util.cpp -) +add_subdirectory(io) +add_subdirectory(model) +add_subdirectory(ui) +add_subdirectory(util) qt_add_resources(PROJECT_SOURCES res/res.qrc) qt_add_executable(pbom ${PROJECT_SOURCES} main.cpp res/winapp.rc) @@ -126,37 +51,6 @@ install(FILES ${QT_BINARIES_DIR}/plugins/styles/qwindowsvistastyle${QT_BINARIES_SUFFIX}.dll DESTINATION ${CMAKE_INSTALL_BINDIR}/styles) -set(TEST_SOURCES - "io/bb/__test__/execbackend_test.cpp" - "io/bb/__test__/nodefilesystem_test.cpp" - "io/bb/__test__/sanitizedstring_test.cpp" - "io/bb/__test__/tempbackend_test.cpp" - "io/bb/__test__/unpackbackend_test.cpp" - "io/bs/__test__/fslzhbinarysource_test.cpp" - "io/bs/__test__/fsrawbinarysource_test.cpp" - "io/bs/__test__/pbobinarysource_test.cpp" - "io/lzh/__test__/compressionbuffer_test.cpp" - "io/lzh/__test__/compressionchunk_test.cpp" - "io/lzh/__test__/lzh_test.cpp" - "io/__test__/pbofile_test.cpp" - "io/__test__/pboheaderio_test.cpp" - "io/__test__/pboheaderreader_test.cpp" - "io/__test__/pbowriter_test.cpp" - "model/__test__/conflictsparcel_test.cpp" - "model/__test__/headersmodel_test.cpp" - "model/__test__/interactionparcel_test.cpp" - "model/__test__/pboentry_test.cpp" - "model/__test__/pboheader_test.cpp" - "model/__test__/pbonode_test.cpp" - "model/__test__/pbopath_test.cpp" - "model/__test__/rootreader_test.cpp" - "model/__test__/signaturemodel_test.cpp" - "ui/__test__/fscollector__test.cpp" - "ui/treewidget/__test__/treewidgetbase__test.cpp" - "util/__test__/qpointerlistiterator_test.cpp" - "util/__test__/util_test.cpp" - ) - add_executable(pbom_test ${PROJECT_SOURCES} ${TEST_SOURCES} testmain.cpp) add_test(NAME pbom_test COMMAND pbom_test) target_link_libraries(pbom_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets gtest gmock) diff --git a/pbom/io/CMakeLists.txt b/pbom/io/CMakeLists.txt new file mode 100644 index 0000000..716e497 --- /dev/null +++ b/pbom/io/CMakeLists.txt @@ -0,0 +1,44 @@ +list(APPEND PROJECT_SOURCES + "io/bb/binarybackend.cpp" + "io/bb/execbackend.cpp" + "io/bb/nodefilesystem.cpp" + "io/bb/sanitizedstring.cpp" + "io/bb/tempbackend.cpp" + "io/bb/unpackbackend.cpp" + "io/bb/unpacktaskbackend.cpp" + "io/bs/binarysource.cpp" + "io/bs/fslzhbinarysource.cpp" + "io/bs/fsrawbinarysource.cpp" + "io/bs/pbobinarysource.cpp" + "io/lzh/compressionbuffer.cpp" + "io/lzh/compressionchunk.cpp" + "io/lzh/decompressioncontext.cpp" + "io/lzh/lzh.cpp" + "io/lzh/lzhdecompressionexception.cpp" + "io/pbodatastream.cpp" + "io/pbofile.cpp" + "io/pboheaderio.cpp" + "io/pboheaderreader.cpp" + "io/pboioexception.cpp" + "io/pbowriter.cpp") + +set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) + +list(APPEND TEST_SOURCES + "io/bb/__test__/execbackend_test.cpp" + "io/bb/__test__/nodefilesystem_test.cpp" + "io/bb/__test__/sanitizedstring_test.cpp" + "io/bb/__test__/tempbackend_test.cpp" + "io/bb/__test__/unpackbackend_test.cpp" + "io/bs/__test__/fslzhbinarysource_test.cpp" + "io/bs/__test__/fsrawbinarysource_test.cpp" + "io/bs/__test__/pbobinarysource_test.cpp" + "io/lzh/__test__/compressionbuffer_test.cpp" + "io/lzh/__test__/compressionchunk_test.cpp" + "io/lzh/__test__/lzh_test.cpp" + "io/__test__/pbofile_test.cpp" + "io/__test__/pboheaderio_test.cpp" + "io/__test__/pboheaderreader_test.cpp" + "io/__test__/pbowriter_test.cpp") + +set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/model/CMakeLists.txt b/pbom/model/CMakeLists.txt new file mode 100644 index 0000000..31aab80 --- /dev/null +++ b/pbom/model/CMakeLists.txt @@ -0,0 +1,35 @@ +list(APPEND PROJECT_SOURCES + "model/task/packtask.cpp" + "model/task/packwindowmodel.cpp" + "model/task/task.h" + "model/task/taskwindowmodel.cpp" + "model/task/unpacktask.cpp" + "model/task/unpackwindowmodel.cpp" + "model/conflictsparcel.cpp" + "model/diskaccessexception.cpp" + "model/headersmodel.cpp" + "model/interactionparcel.cpp" + "model/pboentry.cpp" + "model/pbofileformatexception.cpp" + "model/pboheader.cpp" + "model/pbomodel.cpp" + "model/pbonode.cpp" + "model/pbonodeevents.cpp" + "model/pbopath.cpp" + "model/rootreader.cpp" + "model/signaturemodel.cpp") + +set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) + +list(APPEND TEST_SOURCES + "model/__test__/conflictsparcel_test.cpp" + "model/__test__/headersmodel_test.cpp" + "model/__test__/interactionparcel_test.cpp" + "model/__test__/pboentry_test.cpp" + "model/__test__/pboheader_test.cpp" + "model/__test__/pbonode_test.cpp" + "model/__test__/pbopath_test.cpp" + "model/__test__/rootreader_test.cpp" + "model/__test__/signaturemodel_test.cpp") + +set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/ui/CMakeLists.txt b/pbom/ui/CMakeLists.txt new file mode 100644 index 0000000..e0ea291 --- /dev/null +++ b/pbom/ui/CMakeLists.txt @@ -0,0 +1,42 @@ +list(APPEND PROJECT_SOURCES + "ui/progresswidget/progresswidget.cpp" + "ui/treewidget/deleteop.cpp" + "ui/treewidget/treewidget.cpp" + "ui/treewidget/treewidgetbase.cpp" + "ui/treewidget/treewidgetitem.cpp" + "ui/win32/win32iconmgr.cpp" + "ui/win32/win32fileviewer.cpp" + "ui/aboutdialog.cpp" + "ui/aboutdialog.ui" + "ui/closedialog.cpp" + "ui/closedialog.ui" + "ui/compresslist.cpp" + "ui/conflictslist.cpp" + "ui/errordialog.ui" + "ui/errordialog.cpp" + "ui/fscollector.cpp" + "ui/headersdialog.cpp" + "ui/headersdialog.ui" + "ui/insertdialog.cpp" + "ui/insertdialog.ui" + "ui/insertdialogbuttons.cpp" + "ui/renamedialog.cpp" + "ui/renamedialog.ui" + "ui/mainwindow.cpp" + "ui/mainwindow.ui" + "ui/packwindow.cpp" + "ui/signaturedialog.cpp" + "ui/signaturedialog.ui" + "ui/statusbar.cpp" + "ui/statusbar.cpp" + "ui/taskwindow.cpp" + "ui/taskwindow.ui" + "ui/unpackwindow.cpp") + +set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) + +list(APPEND TEST_SOURCES + "ui/__test__/fscollector__test.cpp" + "ui/treewidget/__test__/treewidgetbase__test.cpp") + +set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/util/CMakeLists.txt b/pbom/util/CMakeLists.txt new file mode 100644 index 0000000..1bba3de --- /dev/null +++ b/pbom/util/CMakeLists.txt @@ -0,0 +1,12 @@ +list(APPEND PROJECT_SOURCES + "util/exception.cpp" + "util/log.cpp" + "util/util.cpp") + +set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) + +list(APPEND TEST_SOURCES + "util/__test__/qpointerlistiterator_test.cpp" + "util/__test__/util_test.cpp") + +set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) From dd440e099f09916f87a79fe92c17a9aec94a2859 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 30 Oct 2021 18:06:36 +0300 Subject: [PATCH 02/39] Working on the Check For Updates feature --- CMakeLists.txt | 3 +- pbom/CMakeLists.txt | 9 ++- pbom/ui/CMakeLists.txt | 4 +- pbom/ui/mainwindow.cpp | 3 +- pbom/ui/mainwindow.ui | 6 ++ pbom/ui/updatesdialog.cpp | 155 ++++++++++++++++++++++++++++++++++++++ pbom/ui/updatesdialog.h | 74 ++++++++++++++++++ pbom/ui/updatesdialog.ui | 120 +++++++++++++++++++++++++++++ 8 files changed, 367 insertions(+), 7 deletions(-) create mode 100644 pbom/ui/updatesdialog.cpp create mode 100644 pbom/ui/updatesdialog.h create mode 100644 pbom/ui/updatesdialog.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 984dc9a..28dbdfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,13 @@ cmake_minimum_required(VERSION 3.5) project(pboman3 LANGUAGES CXX) set(PBOM_BUILD_NUMBER "build \(ver\)" CACHE STRING "App build number") -set(PBOM_VERSION "version" CACHE STRING "App release version") +set(PBOM_VERSION "0.0.1" CACHE STRING "App release version") add_definitions(-DPBOM_PROJECT_NAME="PBO Manager") add_definitions(-DPBOM_BUILD_NUMBER="${PBOM_BUILD_NUMBER}") add_definitions(-DPBOM_VERSION="${PBOM_VERSION}") add_definitions(-DPBOM_PROJECT_SITE="https://github.com/winseros/pboman3") +add_definitions(-DPBOM_API_SITE="https://api.github.com/repos/winseros/pboman3") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index cd98327..4251b20 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -11,8 +11,8 @@ endif() set(WIN32_VER 0x0A00) #https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-160 set(CMAKE_FIND_DEBUG_MODE FALSE) -find_package(QT NAMES Qt6 COMPONENTS Widgets Test REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Test REQUIRED) +find_package(QT NAMES Qt6 COMPONENTS Widgets Network Test REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Test REQUIRED) add_subdirectory(io) add_subdirectory(model) @@ -22,7 +22,7 @@ add_subdirectory(util) qt_add_resources(PROJECT_SOURCES res/res.qrc) qt_add_executable(pbom ${PROJECT_SOURCES} main.cpp res/winapp.rc) -target_link_libraries(pbom PRIVATE Qt${QT_VERSION_MAJOR}::Widgets CLI11) +target_link_libraries(pbom PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network CLI11) target_include_directories(pbom PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(pbom PRIVATE WINVER=${WIN32_VER} NOMINMAX) @@ -40,6 +40,7 @@ install(FILES ${QT_BINARIES_DIR}/bin/Qt6Core${QT_BINARIES_SUFFIX}.dll ${QT_BINARIES_DIR}/bin/Qt6Gui${QT_BINARIES_SUFFIX}.dll ${QT_BINARIES_DIR}/bin/Qt6Widgets${QT_BINARIES_SUFFIX}.dll + ${QT_BINARIES_DIR}/bin/Qt6Network${QT_BINARIES_SUFFIX}.dll DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${QT_BINARIES_DIR}/plugins/imageformats/qico${QT_BINARIES_SUFFIX}.dll @@ -53,7 +54,7 @@ install(FILES add_executable(pbom_test ${PROJECT_SOURCES} ${TEST_SOURCES} testmain.cpp) add_test(NAME pbom_test COMMAND pbom_test) -target_link_libraries(pbom_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets gtest gmock) +target_link_libraries(pbom_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network gtest gmock) target_include_directories(pbom_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(pbom_test PRIVATE diff --git a/pbom/ui/CMakeLists.txt b/pbom/ui/CMakeLists.txt index e0ea291..ed424ce 100644 --- a/pbom/ui/CMakeLists.txt +++ b/pbom/ui/CMakeLists.txt @@ -31,7 +31,9 @@ list(APPEND PROJECT_SOURCES "ui/statusbar.cpp" "ui/taskwindow.cpp" "ui/taskwindow.ui" - "ui/unpackwindow.cpp") + "ui/unpackwindow.cpp" + "ui/updatesdialog.ui" + "ui/updatesdialog.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index e181ba9..c1dbd9b 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -5,12 +5,12 @@ #include #include #include - #include "aboutdialog.h" #include "closedialog.h" #include "errordialog.h" #include "headersdialog.h" #include "signaturedialog.h" +#include "updatesdialog.h" #include "ui_mainwindow.h" #include "model/diskaccessexception.h" #include "model/pbofileformatexception.h" @@ -108,6 +108,7 @@ namespace pboman3 { connect(ui_->actionSelectionDelete, &QAction::triggered, ui_->treeWidget, &TreeWidget::selectionRemove); connect(ui_->actionHelpAbout, &QAction::triggered, [this]() { AboutDialog(this).exec(); }); + connect(ui_->actionCheckUpdates, &QAction::triggered, [this]() { UpdatesDialog(this).exec(); }); connect(model_, &PboModel::modelChanged, this, [this]() { setHasChanges(true); }); connect(model_, &PboModel::loadedPathChanged, this, &MainWindow::updateWindowTitle); diff --git a/pbom/ui/mainwindow.ui b/pbom/ui/mainwindow.ui index d4ca6b7..104ebc7 100644 --- a/pbom/ui/mainwindow.ui +++ b/pbom/ui/mainwindow.ui @@ -121,6 +121,7 @@ Help + @@ -316,6 +317,11 @@ false + + + Check for updates + + diff --git a/pbom/ui/updatesdialog.cpp b/pbom/ui/updatesdialog.cpp new file mode 100644 index 0000000..626eaee --- /dev/null +++ b/pbom/ui/updatesdialog.cpp @@ -0,0 +1,155 @@ +#include "updatesdialog.h" +#include "ui_updatesdialog.h" +#include +#include +#include "util/log.h" + +#define LOG(...) LOGGER("ui/UpdatesDialog", __VA_ARGS__) + +namespace pboman3 { + + SemanticVersion::SemanticVersion(QString rawVersion) + : raw_(std::move(rawVersion)) { + const QRegularExpression reg("v?(\\d+)\\.(\\d+)\\.(\\d+)"); + const QRegularExpressionMatch match = reg.match(raw_); + if (match.hasMatch()) { + major_ = match.captured(1).toShort(); + minor_ = match.captured(2).toShort(); + patch_ = match.captured(3).toShort(); + } else { + major_ = -1; + minor_ = -1; + patch_ = -1; + } + } + + SemanticVersion::SemanticVersion() + : major_(-1), + minor_(-1), + patch_(-1) { + } + + bool SemanticVersion::isValid() const { + return major_ > 0 || minor_ > 0 || patch_ > 0; + } + + bool operator>(const SemanticVersion& v1, const SemanticVersion& v2) { + return v2 < v1; + } + + bool operator<(const SemanticVersion& v1, const SemanticVersion& v2) { + if (v1.major_ == v2.major_) { + if (v1.minor_ == v2.minor_) { + if (v1.patch_ == v2.patch_) { + return false; + } + return v1.patch_ < v2.patch_; + } + return v1.minor_ < v2.minor_; + } + return v1.major_ < v2.major_; + } + + const QString& SemanticVersion::raw() const { + return raw_; + } + + + void GithubLatestVersion::check() { + QNetworkRequest request(QUrl(PBOM_API_SITE"/releases?per-page=1")); + request.setRawHeader("Accept", "application/vnd.github.v3+json"); + reply_.reset(network_.get(request)); + connect(reply_.get(), &QNetworkReply::finished, this, &GithubLatestVersion::replyReceived); + } + + void GithubLatestVersion::abort() const { + reply_->abort(); + } + +#define MSG_UNEXPECTED_RESPONSE "The server returned unexpected response." +#define F_TAG_NAME "tag_name" + + void GithubLatestVersion::replyReceived() { + if (reply_->error() == QNetworkReply::NoError) { + const QByteArray response = reply_->readAll(); + QJsonParseError jsonParseErr; + const QJsonDocument json = QJsonDocument::fromJson(response, &jsonParseErr); + if (json.isNull()) { + LOG(warning, "Not a JSON response. rror:", jsonParseErr.error, ". Offset:", jsonParseErr.offset, + ". Message:", jsonParseErr.errorString()) + emit error(MSG_UNEXPECTED_RESPONSE); + } else { + if (json.isArray() && json[0].isObject() && json[0][F_TAG_NAME].isString()) { + const QString rawVersion = json[0][F_TAG_NAME].toString(); + const SemanticVersion version(rawVersion); + if (version.isValid()) { + emit success(version); + } else { + LOG(warning, "The JSON version information malformed:", rawVersion) + emit error(MSG_UNEXPECTED_RESPONSE); + } + } else { + LOG(warning, "Could not get the version from the JSON:", json) + emit error(MSG_UNEXPECTED_RESPONSE); + } + } + } else { + LOG(warning, "Network failure. Code:", reply_->error(), ". Message:", reply_->errorString()) + emit error(reply_->errorString()); + } + } + + + UpdatesDialog::UpdatesDialog(QWidget* parent) + : QDialog(parent), + ui_(new Ui::UpdatesDialog) { + ui_->setupUi(this); + + uiSetLoading(); + + connect(&github_, &GithubLatestVersion::success, this, &UpdatesDialog::versionReceiveSuccess); + connect(&github_, &GithubLatestVersion::error, this, &UpdatesDialog::versionReceiveError); + github_.check(); + } + + UpdatesDialog::~UpdatesDialog() { + delete ui_; + } + + void UpdatesDialog::closeEvent(QCloseEvent* evt) { + QDialog::closeEvent(evt); + github_.abort(); + } + + void UpdatesDialog::versionReceiveSuccess(const SemanticVersion& version) const { + const SemanticVersion currentVersion(PBOM_VERSION); + assert(currentVersion.isValid()); + + if (version > currentVersion) { + ui_->label1->setTextFormat(Qt::MarkdownText); + ui_->label1->setText("New version available: **" + version.raw() + "**"); + ui_->label2->setTextFormat(Qt::MarkdownText); + ui_->label2->setText("[Download](" PBOM_PROJECT_SITE"/releases) from GitHub"); + ui_->label2->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui_->label2->setOpenExternalLinks(true); + ui_->label2->show(); + } else { + ui_->label1->setText("No updates available."); + } + + ui_->progressBar->hide(); + } + + void UpdatesDialog::versionReceiveError(const QString& error) const { + ui_->progressBar->hide(); + + ui_->label1->setText("Could not check for updates:"); + + ui_->label2->show(); + ui_->label2->setText(error); + } + + void UpdatesDialog::uiSetLoading() const { + ui_->label2->hide(); + } +} diff --git a/pbom/ui/updatesdialog.h b/pbom/ui/updatesdialog.h new file mode 100644 index 0000000..ef89643 --- /dev/null +++ b/pbom/ui/updatesdialog.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +namespace Ui { + class UpdatesDialog; +} + +namespace pboman3 { + class SemanticVersion { + public: + explicit SemanticVersion(QString rawVersion); + + SemanticVersion(); + + bool isValid() const; + + friend bool operator >(const SemanticVersion& v1, const SemanticVersion& v2); + + friend bool operator <(const SemanticVersion& v1, const SemanticVersion& v2); + + const QString& raw() const; + + private: + qint16 major_; + qint16 minor_; + qint16 patch_; + + QString raw_; + }; + + class GithubLatestVersion: public QObject { + Q_OBJECT + + public: + GithubLatestVersion() = default; + + void check(); + + void abort() const; + + signals: + void success(const SemanticVersion& version); + + void error(const QString& message); + + private: + QNetworkAccessManager network_; + QScopedPointer reply_; + + void replyReceived(); + }; + + class UpdatesDialog: public QDialog { + public: + UpdatesDialog(QWidget* parent); + + ~UpdatesDialog(); + + protected: + void closeEvent(QCloseEvent*) override; + + private: + Ui::UpdatesDialog* ui_; + GithubLatestVersion github_; + + void versionReceiveSuccess(const SemanticVersion& version) const; + + void versionReceiveError(const QString& error) const; + + void uiSetLoading() const; + }; +} diff --git a/pbom/ui/updatesdialog.ui b/pbom/ui/updatesdialog.ui new file mode 100644 index 0000000..7d75e39 --- /dev/null +++ b/pbom/ui/updatesdialog.ui @@ -0,0 +1,120 @@ + + + UpdatesDialog + + + + 0 + 0 + 400 + 120 + + + + + 400 + 120 + + + + Check for updates + + + + :app.ico:app.ico + + + true + + + true + + + + + + Checking for updates.. + + + + + + + TextLabel + + + + + + + 0 + + + -1 + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + UpdatesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UpdatesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From 057b3c5d8722ce8987994515e98fb2f01adab385 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Thu, 4 Nov 2021 20:04:42 +0300 Subject: [PATCH 03/39] Added OpenSSL binaries to the install targets --- pbom/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index 4251b20..2e953aa 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -51,6 +51,10 @@ install(FILES install(FILES ${QT_BINARIES_DIR}/plugins/styles/qwindowsvistastyle${QT_BINARIES_SUFFIX}.dll DESTINATION ${CMAKE_INSTALL_BINDIR}/styles) +install(FILES + ${QT_BINARIES_DIR}/../../Tools/OpenSSL/Win_x64/bin/libssl-1_1-x64.dll + ${QT_BINARIES_DIR}/../../Tools/OpenSSL/Win_x64/bin/libcrypto-1_1-x64.dll + DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(pbom_test ${PROJECT_SOURCES} ${TEST_SOURCES} testmain.cpp) add_test(NAME pbom_test COMMAND pbom_test) From ea53e610bd1891253c5b6c4f3c2ec478ff0ccedb Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Thu, 4 Nov 2021 20:05:51 +0300 Subject: [PATCH 04/39] Described the ENV variables needed to build and/or develop --- README.md | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 23ca48d..49b1116 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,32 @@ A tool to open, pack and unpack ArmA PBO files. ## Building from source -``` -# powershell -git clone --recurse-submodules git@github.com:winseros/pboman3.git -cmake -S -B -cmake --build -``` +1. Set the env variables: + + | Variable | Description | Example | + |----------|-------------------------------------------------------------------|---------------------------------| + | QT_DIR | Where QT is located. Needed for CMAKE to build. | G:\Qt\6.1.1\msvc2019_64 | + + +2. Run the script: + + ``` + # powershell + git clone --recurse-submodules git@github.com:winseros/pboman3.git + cmake -S -B + cmake --build + ``` Also, see [how CI builds](.github/workflows/artifcats.yaml). + +## Open in IDE + +1. Set the env variabls: + + | Variable | Description | Example | + |----------|-------------------------------------------------------------------|---------------------------------| + | QT_DIR | Where QT is located. Needed for CMAKE to build. | G:\Qt\6.1.1\msvc2019_64 | + | PATH | Where QT binaries are located. Needed for the IDE to run/debug. | G:\Qt\6.1.1\msvc2019_64\bin | + | PATH | Where OpenSSL binaries are located. Needed for IDE to run/debug. | G:\Qt\Tools\OpenSSL\Win_x64\bin | + +2. Open the the root folder in IDE From ffa84df19c695de3929256298cf5dbda36279ac6 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Thu, 4 Nov 2021 20:20:03 +0300 Subject: [PATCH 05/39] Added the network-related binaries into the installer --- installer/PBOManager.wxs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/installer/PBOManager.wxs b/installer/PBOManager.wxs index 9f64e8f..df0ffb0 100644 --- a/installer/PBOManager.wxs +++ b/installer/PBOManager.wxs @@ -69,6 +69,9 @@ + + + @@ -152,6 +155,21 @@ + + + + + + + + + + + + From 79910cd718c842a413d214004a3ed284c17afe5d Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Fri, 5 Nov 2021 11:13:58 +0300 Subject: [PATCH 06/39] Added tests for the Check For Updates feature --- pbom/ui/CMakeLists.txt | 1 + pbom/ui/__test__/updatesdialog__test.cpp | 96 ++++++++++++++++++++++++ pbom/ui/updatesdialog.h | 3 +- 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 pbom/ui/__test__/updatesdialog__test.cpp diff --git a/pbom/ui/CMakeLists.txt b/pbom/ui/CMakeLists.txt index ed424ce..21ba616 100644 --- a/pbom/ui/CMakeLists.txt +++ b/pbom/ui/CMakeLists.txt @@ -39,6 +39,7 @@ set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) list(APPEND TEST_SOURCES "ui/__test__/fscollector__test.cpp" + "ui/__test__/updatesdialog__test.cpp" "ui/treewidget/__test__/treewidgetbase__test.cpp") set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/ui/__test__/updatesdialog__test.cpp b/pbom/ui/__test__/updatesdialog__test.cpp new file mode 100644 index 0000000..29ca935 --- /dev/null +++ b/pbom/ui/__test__/updatesdialog__test.cpp @@ -0,0 +1,96 @@ +#include +#include "ui/updatesdialog.h" +#include + +namespace pboman3::test { + TEST(SemanticVersionTest, Ctor_Initializes_Without_Params) { + const SemanticVersion ver; + ASSERT_FALSE(ver.isValid()); + ASSERT_EQ("", ver.raw()); + } + + class CtorWithParamTest1 : public testing::TestWithParam { + }; + + TEST_P(CtorWithParamTest1, Ctor_Initializes_Valid_Version) { + const SemanticVersion ver(GetParam()); + ASSERT_TRUE(ver.isValid()); + ASSERT_EQ(GetParam(), ver.raw()); + } + + INSTANTIATE_TEST_SUITE_P(SemanticVersionTest, CtorWithParamTest1, testing::Values( + QString("1.0.0"), + QString("1.1.0"), + QString("1.1.1"), + QString("1.1.1-alpha"), + QString("0.0.1-alpha") + )); + + class CtorWithParamTest2 : public testing::TestWithParam { + }; + + TEST_P(CtorWithParamTest2, Ctor_Initializes_Valid_Version) { + const SemanticVersion ver(GetParam()); + ASSERT_FALSE(ver.isValid()); + ASSERT_EQ(GetParam(), ver.raw()); + } + + INSTANTIATE_TEST_SUITE_P(SemanticVersionTest, CtorWithParamTest2, testing::Values( + QString("0.0.0"), + QString("1.1.a"), + QString("some text") + )); + + struct OperatorTestParam { + QString ver1; + QString ver2; + bool expectedResult; + }; + + class OperatorLessTest : public testing::TestWithParam { + }; + + TEST_P(OperatorLessTest, Operator_Less_Functional) { + SemanticVersion ver1(GetParam().ver1); + SemanticVersion ver2(GetParam().ver2); + ASSERT_EQ(GetParam().expectedResult, ver1 < ver2); + } + + INSTANTIATE_TEST_SUITE_P(SemanticVersionTest, OperatorLessTest, testing::Values( + OperatorTestParam{ "0.0.1", "0.0.2", true }, + OperatorTestParam{ "0.1.0", "0.2.0", true }, + OperatorTestParam{ "1.0.0", "2.0.0", true }, + OperatorTestParam{ "1.0.0", "1.1.0", true }, + OperatorTestParam{ "1.0.0", "1.0.1", true }, + OperatorTestParam{ "0.1.0", "0.1.1", true }, + + OperatorTestParam{ "0.0.2", "0.0.1", false }, + OperatorTestParam{ "0.2.0", "0.1.0", false }, + OperatorTestParam{ "2.0.0", "1.0.0", false }, + OperatorTestParam{ "1.1.0", "1.0.0", false }, + OperatorTestParam{ "1.0.1", "1.0.0", false }, + OperatorTestParam{ "0.1.1", "0.1.0", false } + )); + + TEST(GithubLatestVersionTest, Returns_Valid_Version) { + GithubLatestVersion githubVersion; + + int argc = 0; + char argv[1]; + QApplication app(argc, reinterpret_cast(argv)); + QEventLoop loop; + + bool successCalled = false; + QObject::connect(&githubVersion, &GithubLatestVersion::success, [&successCalled](const SemanticVersion& ver) { + successCalled = true; + ASSERT_TRUE(ver.isValid()); + }); + + QObject::connect(&githubVersion, &GithubLatestVersion::success, &loop, &QEventLoop::quit); + QObject::connect(&githubVersion, &GithubLatestVersion::error, &loop, &QEventLoop::quit); + githubVersion.check(); + loop.exec(); + + ASSERT_TRUE(successCalled); + } +} diff --git a/pbom/ui/updatesdialog.h b/pbom/ui/updatesdialog.h index ef89643..2f364a3 100644 --- a/pbom/ui/updatesdialog.h +++ b/pbom/ui/updatesdialog.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace Ui { @@ -56,7 +57,7 @@ namespace pboman3 { public: UpdatesDialog(QWidget* parent); - ~UpdatesDialog(); + ~UpdatesDialog() override; protected: void closeEvent(QCloseEvent*) override; From 208a02d2f8461606cf84e4e3d17a3a446e55499d Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Fri, 5 Nov 2021 11:34:18 +0300 Subject: [PATCH 07/39] Fix CI can't prepare artifacts --- .github/workflows/artifcats.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/artifcats.yaml b/.github/workflows/artifcats.yaml index 24fbb7b..7bf7f9f 100644 --- a/.github/workflows/artifcats.yaml +++ b/.github/workflows/artifcats.yaml @@ -46,6 +46,7 @@ jobs: target: desktop arch: win64_msvc2019_64 dir: ${{github.workspace}} + tools: tools_openssl_x64,1.1.1-10,qt.tools.openssl.win_x64 - name: Checkout uses: actions/checkout@v2 From 1f79f81b396852c5007f9871523eaeb2e36f8da4 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 6 Nov 2021 13:33:19 +0300 Subject: [PATCH 08/39] Moving the domain logic into a dedicated space --- pbom/CMakeLists.txt | 1 + pbom/domain/CMakeLists.txt | 14 ++++++++++++++ pbom/{model => domain}/__test__/pboheader_test.cpp | 2 +- pbom/{model => domain}/__test__/pbonode_test.cpp | 2 +- pbom/{model => domain}/__test__/pbopath_test.cpp | 2 +- pbom/{io/bs => domain}/binarysource.cpp | 0 pbom/{io/bs => domain}/binarysource.h | 0 pbom/{model => domain}/conflictresolution.h | 0 pbom/{model => domain}/pbonode.cpp | 0 pbom/{model => domain}/pbonode.h | 2 +- pbom/{model => domain}/pbonodeevents.cpp | 0 pbom/{model => domain}/pbonodeevents.h | 0 pbom/{model => domain}/pbonodetype.h | 0 pbom/{model => domain}/pbopath.cpp | 0 pbom/{model => domain}/pbopath.h | 0 pbom/io/CMakeLists.txt | 4 +++- pbom/{model => io}/__test__/pboentry_test.cpp | 2 +- pbom/io/bb/binarybackend.h | 2 +- pbom/io/bb/execbackend.h | 2 +- pbom/io/bb/nodefilesystem.h | 2 +- pbom/io/bb/tempbackend.h | 2 +- pbom/io/bb/unpackbackend.h | 2 +- pbom/io/bs/fsrawbinarysource.h | 2 +- pbom/io/bs/pbobinarysource.h | 2 +- pbom/io/execstore.h | 2 +- pbom/{model => io}/pboentry.cpp | 1 + pbom/{model => io}/pboentry.h | 3 +-- pbom/{model => io}/pboheader.cpp | 0 pbom/{model => io}/pboheader.h | 0 pbom/io/pboheaderio.h | 5 +++-- pbom/io/pboheaderreader.h | 5 +++-- pbom/io/pbowriter.h | 4 ++-- pbom/model/CMakeLists.txt | 9 --------- pbom/model/__test__/interactionparcel_test.cpp | 4 ++-- pbom/model/conflictsparcel.h | 3 ++- pbom/model/headersmodel.h | 2 +- pbom/model/interactionparcel.h | 6 +++--- pbom/model/pbomodel.h | 2 +- pbom/model/rootreader.h | 2 +- pbom/model/task/unpacktask.cpp | 3 ++- pbom/ui/renamedialog.h | 2 +- pbom/ui/treewidget/treewidgetbase.h | 2 +- pbom/ui/treewidget/treewidgetitem.h | 2 +- pbom/util/__test__/qpointerlistiterator_test.cpp | 2 +- 44 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 pbom/domain/CMakeLists.txt rename pbom/{model => domain}/__test__/pboheader_test.cpp (90%) rename pbom/{model => domain}/__test__/pbonode_test.cpp (97%) rename pbom/{model => domain}/__test__/pbopath_test.cpp (94%) rename pbom/{io/bs => domain}/binarysource.cpp (100%) rename pbom/{io/bs => domain}/binarysource.h (100%) rename pbom/{model => domain}/conflictresolution.h (100%) rename pbom/{model => domain}/pbonode.cpp (100%) rename pbom/{model => domain}/pbonode.h (95%) rename pbom/{model => domain}/pbonodeevents.cpp (100%) rename pbom/{model => domain}/pbonodeevents.h (100%) rename pbom/{model => domain}/pbonodetype.h (100%) rename pbom/{model => domain}/pbopath.cpp (100%) rename pbom/{model => domain}/pbopath.h (100%) rename pbom/{model => io}/__test__/pboentry_test.cpp (96%) rename pbom/{model => io}/pboentry.cpp (95%) rename pbom/{model => io}/pboentry.h (91%) rename pbom/{model => io}/pboheader.cpp (100%) rename pbom/{model => io}/pboheader.h (100%) diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index 2e953aa..5facc98 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -14,6 +14,7 @@ set(CMAKE_FIND_DEBUG_MODE FALSE) find_package(QT NAMES Qt6 COMPONENTS Widgets Network Test REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Test REQUIRED) +add_subdirectory(domain) add_subdirectory(io) add_subdirectory(model) add_subdirectory(ui) diff --git a/pbom/domain/CMakeLists.txt b/pbom/domain/CMakeLists.txt new file mode 100644 index 0000000..bf3cbd1 --- /dev/null +++ b/pbom/domain/CMakeLists.txt @@ -0,0 +1,14 @@ +list(APPEND PROJECT_SOURCES + "domain/binarysource.cpp" + "domain/pbonode.cpp" + "domain/pbonodeevents.cpp" + "domain/pbopath.cpp") + +set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) + +list(APPEND TEST_SOURCES + "domain/__test__/pboheader_test.cpp" + "domain/__test__/pbonode_test.cpp" + "domain/__test__/pbopath_test.cpp") + +set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/model/__test__/pboheader_test.cpp b/pbom/domain/__test__/pboheader_test.cpp similarity index 90% rename from pbom/model/__test__/pboheader_test.cpp rename to pbom/domain/__test__/pboheader_test.cpp index 738ac11..4469420 100644 --- a/pbom/model/__test__/pboheader_test.cpp +++ b/pbom/domain/__test__/pboheader_test.cpp @@ -1,5 +1,5 @@ #include -#include "model/pboheader.h" +#include "io/pboheader.h" namespace pboman3::test { TEST(PboHeaderTest, Ctor_Functional) { diff --git a/pbom/model/__test__/pbonode_test.cpp b/pbom/domain/__test__/pbonode_test.cpp similarity index 97% rename from pbom/model/__test__/pbonode_test.cpp rename to pbom/domain/__test__/pbonode_test.cpp index 624a08e..8b0e6dc 100644 --- a/pbom/model/__test__/pbonode_test.cpp +++ b/pbom/domain/__test__/pbonode_test.cpp @@ -1,4 +1,4 @@ -#include "model/pbonode.h" +#include "domain/pbonode.h" #include #include #include diff --git a/pbom/model/__test__/pbopath_test.cpp b/pbom/domain/__test__/pbopath_test.cpp similarity index 94% rename from pbom/model/__test__/pbopath_test.cpp rename to pbom/domain/__test__/pbopath_test.cpp index 43de867..f541a31 100644 --- a/pbom/model/__test__/pbopath_test.cpp +++ b/pbom/domain/__test__/pbopath_test.cpp @@ -1,4 +1,4 @@ -#include "model/pbopath.h" +#include "domain/pbopath.h" #include namespace pboman3::test { diff --git a/pbom/io/bs/binarysource.cpp b/pbom/domain/binarysource.cpp similarity index 100% rename from pbom/io/bs/binarysource.cpp rename to pbom/domain/binarysource.cpp diff --git a/pbom/io/bs/binarysource.h b/pbom/domain/binarysource.h similarity index 100% rename from pbom/io/bs/binarysource.h rename to pbom/domain/binarysource.h diff --git a/pbom/model/conflictresolution.h b/pbom/domain/conflictresolution.h similarity index 100% rename from pbom/model/conflictresolution.h rename to pbom/domain/conflictresolution.h diff --git a/pbom/model/pbonode.cpp b/pbom/domain/pbonode.cpp similarity index 100% rename from pbom/model/pbonode.cpp rename to pbom/domain/pbonode.cpp diff --git a/pbom/model/pbonode.h b/pbom/domain/pbonode.h similarity index 95% rename from pbom/model/pbonode.h rename to pbom/domain/pbonode.h index 30d44db..6baaada 100644 --- a/pbom/model/pbonode.h +++ b/pbom/domain/pbonode.h @@ -4,7 +4,7 @@ #include "pbonodetype.h" #include "pbopath.h" #include "conflictresolution.h" -#include "io/bs/binarysource.h" +#include "binarysource.h" #include "util/qpointerlistiterator.h" namespace pboman3 { diff --git a/pbom/model/pbonodeevents.cpp b/pbom/domain/pbonodeevents.cpp similarity index 100% rename from pbom/model/pbonodeevents.cpp rename to pbom/domain/pbonodeevents.cpp diff --git a/pbom/model/pbonodeevents.h b/pbom/domain/pbonodeevents.h similarity index 100% rename from pbom/model/pbonodeevents.h rename to pbom/domain/pbonodeevents.h diff --git a/pbom/model/pbonodetype.h b/pbom/domain/pbonodetype.h similarity index 100% rename from pbom/model/pbonodetype.h rename to pbom/domain/pbonodetype.h diff --git a/pbom/model/pbopath.cpp b/pbom/domain/pbopath.cpp similarity index 100% rename from pbom/model/pbopath.cpp rename to pbom/domain/pbopath.cpp diff --git a/pbom/model/pbopath.h b/pbom/domain/pbopath.h similarity index 100% rename from pbom/model/pbopath.h rename to pbom/domain/pbopath.h diff --git a/pbom/io/CMakeLists.txt b/pbom/io/CMakeLists.txt index 716e497..2472d5b 100644 --- a/pbom/io/CMakeLists.txt +++ b/pbom/io/CMakeLists.txt @@ -6,7 +6,6 @@ list(APPEND PROJECT_SOURCES "io/bb/tempbackend.cpp" "io/bb/unpackbackend.cpp" "io/bb/unpacktaskbackend.cpp" - "io/bs/binarysource.cpp" "io/bs/fslzhbinarysource.cpp" "io/bs/fsrawbinarysource.cpp" "io/bs/pbobinarysource.cpp" @@ -16,7 +15,9 @@ list(APPEND PROJECT_SOURCES "io/lzh/lzh.cpp" "io/lzh/lzhdecompressionexception.cpp" "io/pbodatastream.cpp" + "io/pboentry.cpp" "io/pbofile.cpp" + "io/pboheader.cpp" "io/pboheaderio.cpp" "io/pboheaderreader.cpp" "io/pboioexception.cpp" @@ -36,6 +37,7 @@ list(APPEND TEST_SOURCES "io/lzh/__test__/compressionbuffer_test.cpp" "io/lzh/__test__/compressionchunk_test.cpp" "io/lzh/__test__/lzh_test.cpp" + "io/__test__/pboentry_test.cpp" "io/__test__/pbofile_test.cpp" "io/__test__/pboheaderio_test.cpp" "io/__test__/pboheaderreader_test.cpp" diff --git a/pbom/model/__test__/pboentry_test.cpp b/pbom/io/__test__/pboentry_test.cpp similarity index 96% rename from pbom/model/__test__/pboentry_test.cpp rename to pbom/io/__test__/pboentry_test.cpp index a8c841a..42a4fb4 100644 --- a/pbom/model/__test__/pboentry_test.cpp +++ b/pbom/io/__test__/pboentry_test.cpp @@ -1,5 +1,5 @@ #include -#include "model/pboentry.h" +#include "io/pboentry.h" namespace pboman3::test { TEST(PboEntryTest, Ctor_Functional) { diff --git a/pbom/io/bb/binarybackend.h b/pbom/io/bb/binarybackend.h index 15e50ce..41dc5e5 100644 --- a/pbom/io/bb/binarybackend.h +++ b/pbom/io/bb/binarybackend.h @@ -2,7 +2,7 @@ #include "execbackend.h" #include "tempbackend.h" -#include "model/pbonode.h" +#include "domain/pbonode.h" namespace pboman3 { class BinaryBackend { diff --git a/pbom/io/bb/execbackend.h b/pbom/io/bb/execbackend.h index e341555..1d194a1 100644 --- a/pbom/io/bb/execbackend.h +++ b/pbom/io/bb/execbackend.h @@ -1,7 +1,7 @@ #pragma once #include "nodefilesystem.h" -#include "model/pbonode.h" +#include "domain/pbonode.h" namespace pboman3 { class ExecBackend { diff --git a/pbom/io/bb/nodefilesystem.h b/pbom/io/bb/nodefilesystem.h index 0417b19..4b7d90f 100644 --- a/pbom/io/bb/nodefilesystem.h +++ b/pbom/io/bb/nodefilesystem.h @@ -1,7 +1,7 @@ #pragma once #include -#include "model/pbonode.h" +#include "domain/pbonode.h" namespace pboman3 { class NodeFileSystem : public QObject { diff --git a/pbom/io/bb/tempbackend.h b/pbom/io/bb/tempbackend.h index 00d140f..c9fcb6a 100644 --- a/pbom/io/bb/tempbackend.h +++ b/pbom/io/bb/tempbackend.h @@ -1,7 +1,7 @@ #pragma once #include "nodefilesystem.h" -#include "model/pbonode.h" +#include "domain/pbonode.h" #include "util/util.h" namespace pboman3 { diff --git a/pbom/io/bb/unpackbackend.h b/pbom/io/bb/unpackbackend.h index c91b89e..13b0569 100644 --- a/pbom/io/bb/unpackbackend.h +++ b/pbom/io/bb/unpackbackend.h @@ -1,7 +1,7 @@ #pragma once #include "nodefilesystem.h" -#include "model/pbonode.h" +#include "domain/pbonode.h" namespace pboman3 { class UnpackBackend { diff --git a/pbom/io/bs/fsrawbinarysource.h b/pbom/io/bs/fsrawbinarysource.h index 4841381..4dde92b 100644 --- a/pbom/io/bs/fsrawbinarysource.h +++ b/pbom/io/bs/fsrawbinarysource.h @@ -1,6 +1,6 @@ #pragma once -#include "binarysource.h" +#include "domain/binarysource.h" namespace pboman3 { class FsRawBinarySource : public BinarySource { diff --git a/pbom/io/bs/pbobinarysource.h b/pbom/io/bs/pbobinarysource.h index 4629edc..be0c200 100644 --- a/pbom/io/bs/pbobinarysource.h +++ b/pbom/io/bs/pbobinarysource.h @@ -1,6 +1,6 @@ #pragma once -#include "binarysource.h" +#include "domain/binarysource.h" namespace pboman3 { struct PboDataInfo { diff --git a/pbom/io/execstore.h b/pbom/io/execstore.h index 6f0fd99..1d9734b 100644 --- a/pbom/io/execstore.h +++ b/pbom/io/execstore.h @@ -1,7 +1,7 @@ #pragma once #include -#include "model/pbonode.h" +#include "domain/pbonode.h" #include "util/util.h" namespace pboman3 { diff --git a/pbom/model/pboentry.cpp b/pbom/io/pboentry.cpp similarity index 95% rename from pbom/model/pboentry.cpp rename to pbom/io/pboentry.cpp index cdbe16a..ad9cbf4 100644 --- a/pbom/model/pboentry.cpp +++ b/pbom/io/pboentry.cpp @@ -1,4 +1,5 @@ #include "pboentry.h" +#include namespace pboman3 { PboEntry PboEntry::makeSignature() { diff --git a/pbom/model/pboentry.h b/pbom/io/pboentry.h similarity index 91% rename from pbom/model/pboentry.h rename to pbom/io/pboentry.h index 2c9e1c3..cf1560c 100644 --- a/pbom/model/pboentry.h +++ b/pbom/io/pboentry.h @@ -1,7 +1,6 @@ #pragma once -#include "pbopath.h" -#include "io/bs/binarysource.h" +#include "domain/pbopath.h" namespace pboman3 { using namespace std; diff --git a/pbom/model/pboheader.cpp b/pbom/io/pboheader.cpp similarity index 100% rename from pbom/model/pboheader.cpp rename to pbom/io/pboheader.cpp diff --git a/pbom/model/pboheader.h b/pbom/io/pboheader.h similarity index 100% rename from pbom/model/pboheader.h rename to pbom/io/pboheader.h diff --git a/pbom/io/pboheaderio.h b/pbom/io/pboheaderio.h index 5dc25eb..0f48290 100644 --- a/pbom/io/pboheaderio.h +++ b/pbom/io/pboheaderio.h @@ -1,8 +1,9 @@ #pragma once #include "pbofile.h" -#include "model/pboentry.h" -#include "model/pboheader.h" +#include "io/pboentry.h" +#include "io/pboheader.h" +#include namespace pboman3 { using namespace std; diff --git a/pbom/io/pboheaderreader.h b/pbom/io/pboheaderreader.h index 136dddb..3c6bb16 100644 --- a/pbom/io/pboheaderreader.h +++ b/pbom/io/pboheaderreader.h @@ -1,8 +1,9 @@ #pragma once #include "pbofile.h" -#include "model/pboentry.h" -#include "model/pboheader.h" +#include "io/pboentry.h" +#include "io/pboheader.h" +#include namespace pboman3 { struct PboFileHeader { diff --git a/pbom/io/pbowriter.h b/pbom/io/pbowriter.h index 2d427e5..1cc68fb 100644 --- a/pbom/io/pbowriter.h +++ b/pbom/io/pbowriter.h @@ -4,8 +4,8 @@ #include "pbofile.h" #include "bs/pbobinarysource.h" #include "model/headersmodel.h" -#include "model/pboentry.h" -#include "model/pbonode.h" +#include "io/pboentry.h" +#include "domain/pbonode.h" namespace pboman3 { class PboWriter : public QObject { diff --git a/pbom/model/CMakeLists.txt b/pbom/model/CMakeLists.txt index 31aab80..5dbafc8 100644 --- a/pbom/model/CMakeLists.txt +++ b/pbom/model/CMakeLists.txt @@ -9,13 +9,8 @@ list(APPEND PROJECT_SOURCES "model/diskaccessexception.cpp" "model/headersmodel.cpp" "model/interactionparcel.cpp" - "model/pboentry.cpp" "model/pbofileformatexception.cpp" - "model/pboheader.cpp" "model/pbomodel.cpp" - "model/pbonode.cpp" - "model/pbonodeevents.cpp" - "model/pbopath.cpp" "model/rootreader.cpp" "model/signaturemodel.cpp") @@ -25,10 +20,6 @@ list(APPEND TEST_SOURCES "model/__test__/conflictsparcel_test.cpp" "model/__test__/headersmodel_test.cpp" "model/__test__/interactionparcel_test.cpp" - "model/__test__/pboentry_test.cpp" - "model/__test__/pboheader_test.cpp" - "model/__test__/pbonode_test.cpp" - "model/__test__/pbopath_test.cpp" "model/__test__/rootreader_test.cpp" "model/__test__/signaturemodel_test.cpp") diff --git a/pbom/model/__test__/interactionparcel_test.cpp b/pbom/model/__test__/interactionparcel_test.cpp index 3bff468..a97688a 100644 --- a/pbom/model/__test__/interactionparcel_test.cpp +++ b/pbom/model/__test__/interactionparcel_test.cpp @@ -3,8 +3,8 @@ #include #include "io/bs/fsrawbinarysource.h" #include "io/bs/pbobinarysource.h" -#include "model/pbonode.h" -#include "model/pbopath.h" +#include "domain/pbonode.h" +#include "domain/pbopath.h" namespace pboman3::test { TEST(NodeDescriptorTest, Ctor_Initializes_Fields) { diff --git a/pbom/model/conflictsparcel.h b/pbom/model/conflictsparcel.h index b16dfd0..cf7a8b3 100644 --- a/pbom/model/conflictsparcel.h +++ b/pbom/model/conflictsparcel.h @@ -1,6 +1,7 @@ #pragma once -#include "conflictresolution.h" +#include "domain/conflictresolution.h" +#include "domain/pbopath.h" #include "interactionparcel.h" #include diff --git a/pbom/model/headersmodel.h b/pbom/model/headersmodel.h index 5fe3122..4e76c28 100644 --- a/pbom/model/headersmodel.h +++ b/pbom/model/headersmodel.h @@ -1,7 +1,7 @@ #pragma once #include -#include "pboheader.h" +#include "io/pboheader.h" #include "util/qpointerlistiterator.h" namespace pboman3 { diff --git a/pbom/model/interactionparcel.h b/pbom/model/interactionparcel.h index 643343e..c5214df 100644 --- a/pbom/model/interactionparcel.h +++ b/pbom/model/interactionparcel.h @@ -1,8 +1,8 @@ #pragma once -#include "pbonode.h" -#include "pbopath.h" -#include "io/bs/binarysource.h" +#include "domain/pbonode.h" +#include "domain/pbopath.h" +#include "domain/binarysource.h" #include "io/bs/fslzhbinarysource.h" #include "io/bs/fsrawbinarysource.h" #include "io/bs/pbobinarysource.h" diff --git a/pbom/model/pbomodel.h b/pbom/model/pbomodel.h index e0d5fd3..3dc1fb5 100644 --- a/pbom/model/pbomodel.h +++ b/pbom/model/pbomodel.h @@ -5,7 +5,7 @@ #include "conflictsparcel.h" #include "headersmodel.h" #include "interactionparcel.h" -#include "pbonode.h" +#include "domain/pbonode.h" #include "signaturemodel.h" #include "io/bb/binarybackend.h" diff --git a/pbom/model/rootreader.h b/pbom/model/rootreader.h index dd3ecff..85375c3 100644 --- a/pbom/model/rootreader.h +++ b/pbom/model/rootreader.h @@ -1,6 +1,6 @@ #pragma once -#include "pbonode.h" +#include "domain/pbonode.h" #include "io/pboheaderreader.h" namespace pboman3 { diff --git a/pbom/model/task/unpacktask.cpp b/pbom/model/task/unpacktask.cpp index d042fd7..2c0cbf7 100644 --- a/pbom/model/task/unpacktask.cpp +++ b/pbom/model/task/unpacktask.cpp @@ -5,7 +5,8 @@ #include "io/pboioexception.h" #include "io/bb/unpacktaskbackend.h" #include "io/bs/pbobinarysource.h" -#include "model/pboentry.h" +#include "io/pboentry.h" +#include "domain/pbonode.h" #include "model/rootreader.h" #include "util/log.h" diff --git a/pbom/ui/renamedialog.h b/pbom/ui/renamedialog.h index aff0e4f..88aa1d8 100644 --- a/pbom/ui/renamedialog.h +++ b/pbom/ui/renamedialog.h @@ -1,7 +1,7 @@ #pragma once #include -#include "model/pbonode.h" +#include "domain/pbonode.h" #include "ui_renamedialog.h" namespace pboman3 { diff --git a/pbom/ui/treewidget/treewidgetbase.h b/pbom/ui/treewidget/treewidgetbase.h index 460c4f8..9cc8794 100644 --- a/pbom/ui/treewidget/treewidgetbase.h +++ b/pbom/ui/treewidget/treewidgetbase.h @@ -2,7 +2,7 @@ #include #include "treewidgetitem.h" -#include "model/pbonode.h" +#include "domain/pbonode.h" namespace pboman3 { class TreeWidgetBase : public QTreeWidget { diff --git a/pbom/ui/treewidget/treewidgetitem.h b/pbom/ui/treewidget/treewidgetitem.h index 46388d8..beb89d7 100644 --- a/pbom/ui/treewidget/treewidgetitem.h +++ b/pbom/ui/treewidget/treewidgetitem.h @@ -1,7 +1,7 @@ #pragma once #include -#include "model/pbonode.h" +#include "domain/pbonode.h" #include "ui/iconmgr.h" namespace pboman3 { diff --git a/pbom/util/__test__/qpointerlistiterator_test.cpp b/pbom/util/__test__/qpointerlistiterator_test.cpp index 006a2b9..05054a8 100644 --- a/pbom/util/__test__/qpointerlistiterator_test.cpp +++ b/pbom/util/__test__/qpointerlistiterator_test.cpp @@ -1,6 +1,6 @@ #include #include "util/qpointerlistiterator.h" -#include "model/pboheader.h" +#include "io/pboheader.h" namespace pboman3::test { TEST(QPointerListIteratorTest, Ietrator_Works) { From f83caa80d5027ffdbb38436167678318c4197298 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 6 Nov 2021 14:06:38 +0300 Subject: [PATCH 09/39] Cleaned up the PboNode --- pbom/domain/abstractnode.h | 57 ++++++++++++++++++++++++++++++++++++++ pbom/domain/pbonode.cpp | 43 ++-------------------------- pbom/domain/pbonode.h | 22 ++------------- 3 files changed, 61 insertions(+), 61 deletions(-) create mode 100644 pbom/domain/abstractnode.h diff --git a/pbom/domain/abstractnode.h b/pbom/domain/abstractnode.h new file mode 100644 index 0000000..60c9bd9 --- /dev/null +++ b/pbom/domain/abstractnode.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include "util/qpointerlistiterator.h" + +namespace pboman3 { + template + class AbstractNode : public QObject { + + public: + AbstractNode(T* parentNode) + : parentNode_(parentNode) { + } + + T* parentNode() const { + return parentNode_; + } + + T* at(qsizetype index) const { + return children_.at(index).get(); + } + + int depth() const { + int result = 0; + T* parentNode = parentNode_; + while (parentNode) { + result++; + parentNode = parentNode->parentNode_; + } + return result; + } + + int count() const { + return static_cast(children_.count()); + } + + QPointerListIterator begin() { + return QPointerListIterator(children_.data()); + } + + QPointerListIterator end() { + return QPointerListIterator(children_.data() + children_.count()); + } + + QPointerListConstIterator begin() const { + return QPointerListConstIterator(children_.data()); + } + + QPointerListConstIterator end() const { + return QPointerListConstIterator(children_.data() + count()); + } + + protected: + T* parentNode_; + QList> children_; + }; +} diff --git a/pbom/domain/pbonode.cpp b/pbom/domain/pbonode.cpp index 2bf7032..8411b0e 100644 --- a/pbom/domain/pbonode.cpp +++ b/pbom/domain/pbonode.cpp @@ -5,10 +5,9 @@ namespace pboman3 { PboNode::PboNode(QString title, PboNodeType nodeType, PboNode* parentNode) - : QObject(), + : AbstractNode(parentNode), nodeType_(nodeType), - title_(std::move(title)), - parentNode_(parentNode) { + title_(std::move(title)) { } PboNode* PboNode::createHierarchy(const PboPath& entryPath) { @@ -95,10 +94,6 @@ namespace pboman3 { return nodeType_; } - PboNode* PboNode::parentNode() const { - return parentNode_; - } - PboNode* PboNode::get(const PboPath& path) { PboNode* result = this; auto it = path.begin(); @@ -111,24 +106,6 @@ namespace pboman3 { return result; } - PboNode* PboNode::at(qsizetype index) const { - return children_.at(index).get(); - } - - int PboNode::depth() const { - int result = 0; - PboNode* parentNode = parentNode_; - while (parentNode) { - result++; - parentNode = parentNode->parentNode_; - } - return result; - } - - int PboNode::count() const { - return static_cast(children_.count()); - } - PboPath PboNode::makePath() const { PboPath path; path.reserve(depth()); @@ -141,22 +118,6 @@ namespace pboman3 { return path; } - QPointerListIterator PboNode::begin() { - return QPointerListIterator(children_.data()); - } - - QPointerListIterator PboNode::end() { - return QPointerListIterator(children_.data() + children_.count()); - } - - QPointerListConstIterator PboNode::begin() const { - return QPointerListConstIterator(children_.data()); - } - - QPointerListConstIterator PboNode::end() const { - return QPointerListConstIterator(children_.data() + count()); - } - bool PboNode::operator<(const PboNode& node) const { if (nodeType_ == node.nodeType_) { if (nodeType_ == PboNodeType::Folder) { diff --git a/pbom/domain/pbonode.h b/pbom/domain/pbonode.h index 6baaada..56cf779 100644 --- a/pbom/domain/pbonode.h +++ b/pbom/domain/pbonode.h @@ -5,12 +5,12 @@ #include "pbopath.h" #include "conflictresolution.h" #include "binarysource.h" -#include "util/qpointerlistiterator.h" +#include "abstractnode.h" namespace pboman3 { typedef QString TitleError; - class PboNode final : public QObject { + class PboNode final : public AbstractNode { Q_OBJECT public: @@ -34,26 +34,10 @@ namespace pboman3 { PboNodeType nodeType() const; - PboNode* parentNode() const; - PboNode* get(const PboPath& path); - PboNode* at(qsizetype index) const; - - int depth() const; - - int count() const; - PboPath makePath() const; - QPointerListIterator begin(); - - QPointerListIterator end(); - - QPointerListConstIterator begin() const; - - QPointerListConstIterator end() const; - bool operator <(const PboNode& node) const; friend QDebug operator <<(QDebug debug, const PboNode& node); @@ -72,8 +56,6 @@ namespace pboman3 { private: PboNodeType nodeType_; QString title_; - PboNode* parentNode_; - QList> children_; PboNode* createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, bool emitEvents); From 9f84f5c7e240c5f7a0acdfb419086eb41eb01b33 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 6 Nov 2021 14:13:22 +0300 Subject: [PATCH 10/39] Remove unused code --- pbom/domain/CMakeLists.txt | 1 - pbom/domain/pbonode.cpp | 1 - pbom/domain/pbonodeevents.cpp | 20 -------------------- pbom/domain/pbonodeevents.h | 29 ----------------------------- 4 files changed, 51 deletions(-) delete mode 100644 pbom/domain/pbonodeevents.cpp delete mode 100644 pbom/domain/pbonodeevents.h diff --git a/pbom/domain/CMakeLists.txt b/pbom/domain/CMakeLists.txt index bf3cbd1..3bad205 100644 --- a/pbom/domain/CMakeLists.txt +++ b/pbom/domain/CMakeLists.txt @@ -1,7 +1,6 @@ list(APPEND PROJECT_SOURCES "domain/binarysource.cpp" "domain/pbonode.cpp" - "domain/pbonodeevents.cpp" "domain/pbopath.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) diff --git a/pbom/domain/pbonode.cpp b/pbom/domain/pbonode.cpp index 8411b0e..10b0029 100644 --- a/pbom/domain/pbonode.cpp +++ b/pbom/domain/pbonode.cpp @@ -1,6 +1,5 @@ #include "pbonode.h" #include -#include "pbonodeevents.h" #include "util/exception.h" namespace pboman3 { diff --git a/pbom/domain/pbonodeevents.cpp b/pbom/domain/pbonodeevents.cpp deleted file mode 100644 index 868fe11..0000000 --- a/pbom/domain/pbonodeevents.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "pbonodeevents.h" - -namespace pboman3 { - PboNodeCreatedEvent::PboNodeCreatedEvent(const PboPath* pNodePath, PboNodeType pNodeType) - : PboNodeEvent(), - nodePath(pNodePath), - nodeType(pNodeType) { - } - - PboNodeRenamedEvent::PboNodeRenamedEvent(const PboPath* pNodePath, QString pNewNodeTitle) - : PboNodeEvent(), - nodePath(pNodePath), - newNodeTitle(std::move(pNewNodeTitle)) { - } - - PboNodeRemovedEvent::PboNodeRemovedEvent(const PboPath* pNodePath) - : PboNodeEvent(), - nodePath(pNodePath) { - } -} diff --git a/pbom/domain/pbonodeevents.h b/pbom/domain/pbonodeevents.h deleted file mode 100644 index d3a37f6..0000000 --- a/pbom/domain/pbonodeevents.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "pbonodetype.h" -#include "pbopath.h" - -namespace pboman3 { - class PboNodeEvent { - }; - - class PboNodeCreatedEvent final : public PboNodeEvent { - public: - PboNodeCreatedEvent(const PboPath* pNodePath, PboNodeType pNodeType); - const PboPath* nodePath; - const PboNodeType nodeType; - }; - - class PboNodeRenamedEvent final : public PboNodeEvent { - public: - PboNodeRenamedEvent(const PboPath* pNodePath, QString pNewNodeTitle); - const PboPath* nodePath; - const QString newNodeTitle; - }; - - class PboNodeRemovedEvent final : public PboNodeEvent { - public: - PboNodeRemovedEvent(const PboPath* pNodePath); - const PboPath* nodePath; - }; -} From 5a6f73a2c8c8cedda735b1c48c7d0de411ee99e8 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 6 Nov 2021 17:47:28 +0300 Subject: [PATCH 11/39] Moving the domain logic into a dedicated space --- pbom/domain/CMakeLists.txt | 1 - pbom/io/CMakeLists.txt | 1 + pbom/{domain => io}/__test__/pboheader_test.cpp | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename pbom/{domain => io}/__test__/pboheader_test.cpp (100%) diff --git a/pbom/domain/CMakeLists.txt b/pbom/domain/CMakeLists.txt index 3bad205..1be1028 100644 --- a/pbom/domain/CMakeLists.txt +++ b/pbom/domain/CMakeLists.txt @@ -6,7 +6,6 @@ list(APPEND PROJECT_SOURCES set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) list(APPEND TEST_SOURCES - "domain/__test__/pboheader_test.cpp" "domain/__test__/pbonode_test.cpp" "domain/__test__/pbopath_test.cpp") diff --git a/pbom/io/CMakeLists.txt b/pbom/io/CMakeLists.txt index 2472d5b..81beb2d 100644 --- a/pbom/io/CMakeLists.txt +++ b/pbom/io/CMakeLists.txt @@ -39,6 +39,7 @@ list(APPEND TEST_SOURCES "io/lzh/__test__/lzh_test.cpp" "io/__test__/pboentry_test.cpp" "io/__test__/pbofile_test.cpp" + "io/__test__/pboheader_test.cpp" "io/__test__/pboheaderio_test.cpp" "io/__test__/pboheaderreader_test.cpp" "io/__test__/pbowriter_test.cpp") diff --git a/pbom/domain/__test__/pboheader_test.cpp b/pbom/io/__test__/pboheader_test.cpp similarity index 100% rename from pbom/domain/__test__/pboheader_test.cpp rename to pbom/io/__test__/pboheader_test.cpp From 40b36b4fb0e10d8fd1f7ad454e979c188a0e4f1d Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Wed, 10 Nov 2021 20:36:11 +0300 Subject: [PATCH 12/39] Working on the PBO domain model --- pbom/domain/CMakeLists.txt | 7 +- pbom/domain/documentheader.cpp | 29 +++ pbom/domain/documentheader.h | 24 ++ pbom/domain/documentheaders.cpp | 68 ++++++ pbom/domain/documentheaders.h | 43 ++++ pbom/domain/documentnode.cpp | 272 +++++++++++++++++++++++ pbom/domain/documentnode.h | 74 ++++++ pbom/domain/pbodocument.cpp | 27 +++ pbom/domain/pbodocument.h | 26 +++ pbom/domain/validationexception.cpp | 19 ++ pbom/domain/validationexception.h | 17 ++ pbom/io/CMakeLists.txt | 4 + pbom/io/__test__/documentreader_test.cpp | 82 +++++++ pbom/io/__test__/documentwriter_test.cpp | 238 ++++++++++++++++++++ pbom/io/documentreader.cpp | 43 ++++ pbom/io/documentreader.h | 17 ++ pbom/io/documentwriter.cpp | 260 ++++++++++++++++++++++ pbom/io/documentwriter.h | 79 +++++++ 18 files changed, 1328 insertions(+), 1 deletion(-) create mode 100644 pbom/domain/documentheader.cpp create mode 100644 pbom/domain/documentheader.h create mode 100644 pbom/domain/documentheaders.cpp create mode 100644 pbom/domain/documentheaders.h create mode 100644 pbom/domain/documentnode.cpp create mode 100644 pbom/domain/documentnode.h create mode 100644 pbom/domain/pbodocument.cpp create mode 100644 pbom/domain/pbodocument.h create mode 100644 pbom/domain/validationexception.cpp create mode 100644 pbom/domain/validationexception.h create mode 100644 pbom/io/__test__/documentreader_test.cpp create mode 100644 pbom/io/__test__/documentwriter_test.cpp create mode 100644 pbom/io/documentreader.cpp create mode 100644 pbom/io/documentreader.h create mode 100644 pbom/io/documentwriter.cpp create mode 100644 pbom/io/documentwriter.h diff --git a/pbom/domain/CMakeLists.txt b/pbom/domain/CMakeLists.txt index 1be1028..961755b 100644 --- a/pbom/domain/CMakeLists.txt +++ b/pbom/domain/CMakeLists.txt @@ -1,7 +1,12 @@ list(APPEND PROJECT_SOURCES "domain/binarysource.cpp" + "domain/documentheader.cpp" + "domain/documentheaders.cpp" + "domain/documentnode.cpp" + "domain/pbodocument.cpp" "domain/pbonode.cpp" - "domain/pbopath.cpp") + "domain/pbopath.cpp" + "domain/validationexception.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) diff --git a/pbom/domain/documentheader.cpp b/pbom/domain/documentheader.cpp new file mode 100644 index 0000000..8a33652 --- /dev/null +++ b/pbom/domain/documentheader.cpp @@ -0,0 +1,29 @@ +#include "documentheader.h" + +namespace pboman3::domain { + DocumentHeader::DocumentHeader(QString name, QString value) { + validateName(name); + name_ = std::move(name); + value_ = std::move(value); + } + + const QString& DocumentHeader::name() const { + return name_; + } + + void DocumentHeader::setName(const QString& name) { + validateName(name); + name_ = name; + } + + const QString& DocumentHeader::value() const { + return value_; + } + + void DocumentHeader::setValue(const QString& value) { + value_ = value; + } + + void DocumentHeader::validateName(const QString& name) { + } +} diff --git a/pbom/domain/documentheader.h b/pbom/domain/documentheader.h new file mode 100644 index 0000000..f821707 --- /dev/null +++ b/pbom/domain/documentheader.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace pboman3::domain { + class DocumentHeader { + public: + DocumentHeader(QString name, QString value); + + const QString& name() const; + + void setName(const QString& name); + + const QString& value() const; + + void setValue(const QString& value); + + private: + QString name_; + QString value_; + + void validateName(const QString& name); + }; +} diff --git a/pbom/domain/documentheaders.cpp b/pbom/domain/documentheaders.cpp new file mode 100644 index 0000000..b1cb929 --- /dev/null +++ b/pbom/domain/documentheaders.cpp @@ -0,0 +1,68 @@ +#include "documentheaders.h" +#include "util/exception.h" + +namespace pboman3::domain { + qsizetype DocumentHeaders::count() const { + return headers_.count(); + } + + const DocumentHeader* DocumentHeaders::at(qsizetype index) const { + return headers_.at(index).get(); + } + + void DocumentHeaders::add(const QString& name, const QString& value) { + const QSharedPointer header(new DocumentHeader(name, value)); + headers_.append(header); + emit headerAdded(header.get(), headers_.count() - 1); + } + + void DocumentHeaders::remove(const DocumentHeader* header) { + const qsizetype index = getIndex(header); + headers_.removeAt(index); + emit headerRemoved(header, index); + } + + void DocumentHeaders::moveUp(const DocumentHeader* header) { + const qsizetype currentIndex = getIndex(header); + if (currentIndex == 0) + throw InvalidOperationException("The header is already at the top of the list"); + + const qsizetype newIndex = currentIndex - 1; + headers_.move(currentIndex, newIndex); + } + + void DocumentHeaders::moveDown(const DocumentHeader* header) { + const qsizetype currentIndex = getIndex(header); + const qsizetype newIndex = currentIndex + 1; + if (newIndex >= count()) + throw InvalidOperationException("The header is already at the bottom of the list"); + + headers_.move(currentIndex, newIndex); + } + + QPointerListIterator DocumentHeaders::begin() { + return QPointerListIterator(headers_.begin()); + } + + QPointerListIterator DocumentHeaders::end() { + return QPointerListIterator(headers_.end()); + } + + QPointerListConstIterator DocumentHeaders::begin() const { + return QPointerListConstIterator(headers_.constBegin()); + } + + QPointerListConstIterator DocumentHeaders::end() const { + return QPointerListConstIterator(headers_.constEnd()); + } + + qsizetype DocumentHeaders::getIndex(const DocumentHeader* header) const { + qsizetype index = 0; + for (const QSharedPointer& h : headers_) { + if (h.get() == header) + return index; + index++; + } + throw InvalidOperationException("The header does not belong to this instance"); + } +} diff --git a/pbom/domain/documentheaders.h b/pbom/domain/documentheaders.h new file mode 100644 index 0000000..094b07c --- /dev/null +++ b/pbom/domain/documentheaders.h @@ -0,0 +1,43 @@ +#pragma once + +#include "documentheader.h" +#include "util/qpointerlistiterator.h" + +namespace pboman3::domain { + class DocumentHeaders : public QObject { + Q_OBJECT + + public: + qsizetype count() const; + + const DocumentHeader* at(qsizetype index) const; + + void add(const QString& name, const QString& value); + + void remove(const DocumentHeader* header); + + void moveUp(const DocumentHeader* header); + + void moveDown(const DocumentHeader* header); + + QPointerListIterator begin(); + + QPointerListIterator end(); + + QPointerListConstIterator begin() const; + + QPointerListConstIterator end() const; + + signals: + void headerAdded(const DocumentHeader* header, qsizetype index); + + void headerRemoved(const DocumentHeader* header, qsizetype index); + + void headerMoved(const DocumentHeader* header, qsizetype prevIndex, qsizetype newIndex); + + private: + QList> headers_; + + qsizetype getIndex(const DocumentHeader* header) const; + }; +} diff --git a/pbom/domain/documentnode.cpp b/pbom/domain/documentnode.cpp new file mode 100644 index 0000000..a77ac65 --- /dev/null +++ b/pbom/domain/documentnode.cpp @@ -0,0 +1,272 @@ +#include "documentnode.h" +#include +#include "util/exception.h" +#include "validationexception.h" + +namespace pboman3::domain { + DocumentNode::DocumentNode(QString title, PboNodeType nodeType, DocumentNode* parentNode) + : AbstractNode(parentNode), + nodeType_(nodeType), + title_(std::move(title)) { + if (title_.isEmpty()) + throw ValidationException("Title must not be empty"); + } + + DocumentNode* DocumentNode::createHierarchy(const PboPath& entryPath) { + return createHierarchy(entryPath, ConflictResolution::Copy, false); + } + + DocumentNode* DocumentNode::createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict) { + return createHierarchy(entryPath, onConflict, true); + } + + void DocumentNode::removeFromHierarchy() { + if (!parentNode_) + throw InvalidOperationException("Must not be called on the root node"); + + DocumentNode* node = this; + while (node->parentNode_ + && node->parentNode_->nodeType_ != PboNodeType::Container + && node->parentNode_->count() == 1) { + node = node->parentNode_; + } + + DocumentNode* p = node->parentNode_; + assert(p && "Must not be null"); + + const qsizetype index = p->children_.indexOf(node); + node->parentNode_->children_.remove(index, 1); + + emit p->childRemoved(index); + p->emitHierarchyChanged(); + } + + bool DocumentNode::isPathConflict(const PboPath& path) const { + const DocumentNode* node = this; + for (qsizetype i = 0; i < path.length() - 1; i++) { + const DocumentNode* folder = node->findChild(path.at(i)); + if (folder) { + if (folder->nodeType_ == PboNodeType::File) + return true; + node = folder; + } else { + return false; + } + } + + const DocumentNode* file = node->findChild(path.last()); + return file; + } + + const QString& DocumentNode::title() const { + return title_; + } + + void DocumentNode::setTitle(QString title) { + if (title != title_) { + // if (const TitleError err = verifyTitle(title); err != nullptr) + // throw InvalidOperationException(err); + + title_ = std::move(title); + emit titleChanged(title_); + + if (parentNode_) { + const qsizetype prevIndex = parentNode_->children_.indexOf(this); + const qsizetype newIndex = parentNode_->getChildListIndex(this); + if (prevIndex != newIndex) { + parentNode_->children_.move(prevIndex, newIndex); + emit parentNode_->childMoved(prevIndex, newIndex); + } + + } + emitHierarchyChanged(); + } + } + + PboNodeType DocumentNode::nodeType() const { + return nodeType_; + } + + DocumentNode* DocumentNode::get(const PboPath& path) { + DocumentNode* result = this; + auto it = path.begin(); + while (it != path.end()) { + result = result->findChild(*it); + if (!result) + return nullptr; + ++it; + } + return result; + } + + PboPath DocumentNode::makePath() const { + PboPath path; + path.reserve(depth()); + + const DocumentNode* p = this; + while (p->parentNode_) { + path.prepend(p->title_); + p = p->parentNode_; + } + return path; + } + + bool DocumentNode::operator<(const DocumentNode& node) const { + if (nodeType_ == node.nodeType_) { + if (nodeType_ == PboNodeType::Folder) { + return title_ < node.title_; //folders alphabetically + } + QString name1, ext1; + SplitByNameAndExtension(title_, name1, ext1); + QString name2, ext2; + SplitByNameAndExtension(node.title_, name2, ext2); + if (name1 == name2) { + return ext1 < ext2; //files alphabetically by extension + } + return name1 < name2; //files alphabetically by name + } + + return nodeType_ > node.nodeType_; //folders first + } + + DocumentNode* DocumentNode::createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, + bool emitEvents) { + DocumentNode* node = this; + for (qsizetype i = 0; i < entryPath.length() - 1; i++) { + DocumentNode* folder = node->findChild(entryPath.at(i)); + if (!folder) { + folder = node->createChild(entryPath.at(i), PboNodeType::Folder); + } else if (folder->nodeType_ == PboNodeType::File) { + switch (onConflict) { + case ConflictResolution::Replace: { + DocumentNode* replaceFolder = folder->parentNode_->createChild( + entryPath.last(), PboNodeType::File); + folder->removeFromHierarchy(); + folder = replaceFolder; + break; + } + case ConflictResolution::Copy: { + const QString folderTitle = pickFolderTitle(node, entryPath.at(i)); + folder = node->createChild(folderTitle, PboNodeType::Folder); + break; + } + default: + throw InvalidOperationException("Unsupported conflict resolution strategy"); + } + } + node = folder; + } + + DocumentNode* file = node->findChild(entryPath.last()); + if (!file) { + file = node->createChild(entryPath.last(), PboNodeType::File); + if (emitEvents) + emitHierarchyChanged(); + } else { + switch (onConflict) { + case ConflictResolution::Replace: { + DocumentNode* replaceFile = file->parentNode_->createChild(entryPath.last(), PboNodeType::File); + //this emits "hierarchyChanged"; this branch never called with emitEvents==false + file->removeFromHierarchy(); + file = replaceFile; + break; + } + case ConflictResolution::Copy: { + const QString fileTitle = pickFileTitle(node, entryPath.last()); + file = node->createChild(fileTitle, PboNodeType::File); + if (emitEvents) + emitHierarchyChanged(); + break; + } + default: + throw InvalidOperationException("Unsupported conflict resolution strategy"); + } + } + + return file; + } + + DocumentNode* DocumentNode::findChild(const QString& title) const { + for (const QSharedPointer& child : children_) { + if (child->title_ == title) + return child.get(); + } + return nullptr; + } + + QString DocumentNode::pickFolderTitle(const DocumentNode* parent, const QString& expectedTitle) const { + int index = 1; + QString attemptTitle = formatFolderTitleCopy(expectedTitle, index); + auto it = parent->children_.constBegin(); + while (it != parent->children_.constEnd()) { + if ((*it)->title() == attemptTitle && (*it)->nodeType_ == PboNodeType::File) { + index++; + attemptTitle = formatFolderTitleCopy(expectedTitle, index); + it = parent->children_.constBegin(); + } else { + ++it; + } + } + return attemptTitle; + } + + QString DocumentNode::pickFileTitle(const DocumentNode* parent, const QString& expectedTitle) const { + int index = 1; + QString attemptTitle = formatFileTitleCopy(expectedTitle, index); + auto it = parent->children_.constBegin(); + while (it < parent->children_.constEnd()) { + if ((*it)->title() == attemptTitle) { + index++; + attemptTitle = formatFileTitleCopy(expectedTitle, index); + it = parent->children_.constBegin(); + } else { + ++it; + } + } + return attemptTitle; + } + + QString DocumentNode::formatFolderTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const { + return expectedTitle + "(" + QString::number(copyIndex) + ")"; + } + + QString DocumentNode::formatFileTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const { + QString expectedName, expectedExt; + SplitByNameAndExtension(expectedTitle, expectedName, expectedExt); + return expectedName + "(" + QString::number(copyIndex) + ")." + expectedExt; + } + + DocumentNode* DocumentNode::createChild(const QString& title, PboNodeType nodeType) { + const auto child = QSharedPointer(new DocumentNode(title, nodeType, this)); + const qsizetype index = getChildListIndex(child.get()); + children_.insert(index, child); + emit childCreated(child.get(), index); + return child.get(); + } + + qsizetype DocumentNode::getChildListIndex(const DocumentNode* node) const { + qsizetype index = 0; + for (const QSharedPointer& sibling : children_) { + if (sibling != node) { + if (*sibling < *node) { + index++; + } else { + break; + } + } + } + return index; + } + + void DocumentNode::emitHierarchyChanged() { + DocumentNode* parent = this; + while (parent->parentNode_) { + parent = parent->parentNode_; + } + emit parent->hierarchyChanged(); + } + + QDebug operator<<(QDebug debug, const DocumentNode& node) { + return debug << "DocumentNode(" << node.makePath() << ")"; + } +} diff --git a/pbom/domain/documentnode.h b/pbom/domain/documentnode.h new file mode 100644 index 0000000..69d16b5 --- /dev/null +++ b/pbom/domain/documentnode.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include "pbonodetype.h" +#include "pbopath.h" +#include "conflictresolution.h" +#include "binarysource.h" +#include "abstractnode.h" + +namespace pboman3::domain { + class DocumentNode final : public AbstractNode { + Q_OBJECT + + public: + QSharedPointer binarySource; + + DocumentNode(QString title, PboNodeType nodeType, DocumentNode* parentNode); + + DocumentNode* createHierarchy(const PboPath& entryPath); + + DocumentNode* createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict); + + void removeFromHierarchy(); + + bool isPathConflict(const PboPath& path) const; + + const QString& title() const; + + void setTitle(QString title); + + PboNodeType nodeType() const; + + DocumentNode* get(const PboPath& path); + + PboPath makePath() const; + + bool operator <(const DocumentNode& node) const; + + friend QDebug operator <<(QDebug debug, const DocumentNode& node); + + signals: + void titleChanged(const QString& title); + + void childCreated(DocumentNode* child, qsizetype index); + + void childMoved(qsizetype prevIndex, qsizetype newIndex); + + void childRemoved(qsizetype index); + + void hierarchyChanged(); + + private: + PboNodeType nodeType_; + QString title_; + + DocumentNode* createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, bool emitEvents); + + DocumentNode* findChild(const QString& title) const; + + QString pickFolderTitle(const DocumentNode* parent, const QString& expectedTitle) const; + + QString pickFileTitle(const DocumentNode* parent, const QString& expectedTitle) const; + + QString formatFolderTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const; + + QString formatFileTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const; + + DocumentNode* createChild(const QString& title, PboNodeType nodeType); + + qsizetype getChildListIndex(const DocumentNode* node) const; + + void emitHierarchyChanged(); + }; +} diff --git a/pbom/domain/pbodocument.cpp b/pbom/domain/pbodocument.cpp new file mode 100644 index 0000000..ad208c2 --- /dev/null +++ b/pbom/domain/pbodocument.cpp @@ -0,0 +1,27 @@ +#include "pbodocument.h" +#include "validationexception.h" + +namespace pboman3::domain { + PboDocument::PboDocument(QString pboName) { + headers_ = QSharedPointer(new DocumentHeaders); + root_ = QSharedPointer(new DocumentNode(std::move(pboName), PboNodeType::Container, nullptr)); + } + + DocumentHeaders* PboDocument::headers() const { + return headers_.get(); + } + + DocumentNode* PboDocument::root() const { + return root_.get(); + } + + const QByteArray& PboDocument::signature() const { + return signature_; + } + + void PboDocument::setSignature(QByteArray signature) { + if (signature.length() != 20) + throw ValidationException("The signature must be 20 bytes long"); + signature_ = std::move(signature); + } +} diff --git a/pbom/domain/pbodocument.h b/pbom/domain/pbodocument.h new file mode 100644 index 0000000..8e578e1 --- /dev/null +++ b/pbom/domain/pbodocument.h @@ -0,0 +1,26 @@ +#pragma once + +#include "documentheaders.h" +#include "documentnode.h" + +namespace pboman3::domain { + class PboDocument : public QObject { + Q_OBJECT + + public: + PboDocument(QString pboName); + + DocumentHeaders* headers() const; + + DocumentNode* root() const; + + const QByteArray& signature() const; + + void setSignature(QByteArray signature); + + private: + QSharedPointer headers_; + QSharedPointer root_; + QByteArray signature_; + }; +} diff --git a/pbom/domain/validationexception.cpp b/pbom/domain/validationexception.cpp new file mode 100644 index 0000000..b6e1521 --- /dev/null +++ b/pbom/domain/validationexception.cpp @@ -0,0 +1,19 @@ +#include "validationexception.h" + +namespace pboman3::domain { + ValidationException::ValidationException(QString message) + : AppException(std::move(message)) { + } + + QDebug operator<<(QDebug debug, const ValidationException& ex) { + return debug << "ValidationException(" << ex.message_ << ")"; + } + + void ValidationException::raise() const { + throw* this; + } + + QException* ValidationException::clone() const { + return new ValidationException(*this); + } +} diff --git a/pbom/domain/validationexception.h b/pbom/domain/validationexception.h new file mode 100644 index 0000000..7a18159 --- /dev/null +++ b/pbom/domain/validationexception.h @@ -0,0 +1,17 @@ +#pragma once + +#include "util/exception.h" +#include + +namespace pboman3::domain { + class ValidationException : public AppException { + public: + ValidationException(QString message); + + friend QDebug operator<<(QDebug debug, const ValidationException& ex); + + void raise() const override; + + QException* clone() const override; + }; +} diff --git a/pbom/io/CMakeLists.txt b/pbom/io/CMakeLists.txt index 81beb2d..526d3e0 100644 --- a/pbom/io/CMakeLists.txt +++ b/pbom/io/CMakeLists.txt @@ -14,6 +14,8 @@ list(APPEND PROJECT_SOURCES "io/lzh/decompressioncontext.cpp" "io/lzh/lzh.cpp" "io/lzh/lzhdecompressionexception.cpp" + "io/documentreader.cpp" + "io/documentwriter.cpp" "io/pbodatastream.cpp" "io/pboentry.cpp" "io/pbofile.cpp" @@ -37,6 +39,8 @@ list(APPEND TEST_SOURCES "io/lzh/__test__/compressionbuffer_test.cpp" "io/lzh/__test__/compressionchunk_test.cpp" "io/lzh/__test__/lzh_test.cpp" + "io/__test__/documentreader_test.cpp" + "io/__test__/documentwriter_test.cpp" "io/__test__/pboentry_test.cpp" "io/__test__/pbofile_test.cpp" "io/__test__/pboheader_test.cpp" diff --git a/pbom/io/__test__/documentreader_test.cpp b/pbom/io/__test__/documentreader_test.cpp new file mode 100644 index 0000000..17fc70a --- /dev/null +++ b/pbom/io/__test__/documentreader_test.cpp @@ -0,0 +1,82 @@ +#include +#include "io/documentreader.h" +#include +#include "io/pbofile.h" +#include "io/pboheaderio.h" +#include +#include "io/bs/pbobinarysource.h" + +namespace pboman3::io::test { + TEST(PboReaderTest, Read_Reads_File) { + //build a mock pbo file + QTemporaryFile t; + t.open(); + + PboFile p(t.fileName()); + p.open(QIODeviceBase::WriteOnly); + const PboHeaderIO io(&p); + + const PboEntry e0 = PboEntry::makeSignature(); + const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + 0x03030303, 5); + const PboEntry e2("f2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, + 0x07070707, 10); + const PboEntry e3 = PboEntry::makeBoundary(); + + const PboHeader h1("p1", "v1"); + const PboHeader h2("p2", "v2"); + const PboHeader h3 = PboHeader::makeBoundary(); + + const QByteArray signature(20, 5); + + io.writeEntry(e0); + io.writeHeader(h1); + io.writeHeader(h2); + io.writeHeader(h3); + io.writeEntry(e1); + io.writeEntry(e2); + io.writeEntry(e3); + p.write(QByteArray(e1.dataSize(), 1)); + p.write(QByteArray(e2.dataSize(), 2)); + p.write(QByteArray(1, 0)); //zero byte between data and signature + p.write(signature); + + p.close(); + t.close(); + + //call the method + const DocumentReader reader(t.fileName()); + const QSharedPointer document = reader.read(); + + //verify the results + ASSERT_TRUE(document); + + ASSERT_EQ(document->headers()->count(), 2); + ASSERT_EQ(document->headers()->at(0)->name(), "p1"); + ASSERT_EQ(document->headers()->at(0)->value(), "v1"); + ASSERT_EQ(document->headers()->at(1)->name(), "p2"); + ASSERT_EQ(document->headers()->at(1)->value(), "v2"); + + QFileInfo fi(t.fileName()); + ASSERT_TRUE(document->root()); + ASSERT_EQ(document->root()->count(), 2); + ASSERT_EQ(document->root()->title(), fi.fileName()); + ASSERT_EQ(document->root()->nodeType(), PboNodeType::Container); + ASSERT_FALSE(document->root()->parentNode()); + + ASSERT_EQ(document->root()->at(0)->title(), "f1"); + ASSERT_EQ(document->root()->at(0)->nodeType(), PboNodeType::File); + const auto bs1 = dynamic_cast(document->root()->at(0)->binarySource.get()); + ASSERT_EQ(bs1->getInfo().compressed, true); + ASSERT_EQ(bs1->getInfo().originalSize, 0x01010101); + ASSERT_EQ(bs1->getInfo().timestamp, 0x03030303); + ASSERT_EQ(bs1->getInfo().dataSize, 5); + ASSERT_EQ(bs1->getInfo().dataOffset, 101);//where the actual data begins + const auto bs2 = dynamic_cast(document->root()->at(1)->binarySource.get()); + ASSERT_EQ(bs2->getInfo().compressed, false); + ASSERT_EQ(bs2->getInfo().originalSize, 0x05050505); + ASSERT_EQ(bs2->getInfo().timestamp, 0x07070707); + ASSERT_EQ(bs2->getInfo().dataSize, 10); + ASSERT_EQ(bs2->getInfo().dataOffset, 106);//bs1.dataOffset + bs1.dataSize + } +} diff --git a/pbom/io/__test__/documentwriter_test.cpp b/pbom/io/__test__/documentwriter_test.cpp new file mode 100644 index 0000000..76fd65b --- /dev/null +++ b/pbom/io/__test__/documentwriter_test.cpp @@ -0,0 +1,238 @@ +#include "io/documentwriter.h" +#include +#include +#include +#include "domain/pbodocument.h" +#include "io/pboheaderreader.h" +#include "io/bs/fsrawbinarysource.h" + +namespace pboman3::io::test { + using namespace domain; + TEST(DocumentWriterTest, Write_Writes_File_WithHeaders) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + const QByteArray mockContent2(10, 2); + QTemporaryFile e2; + e2.open(); + e2.write(mockContent2); + e2.close(); + + //pbo document + PboDocument document("file.pbo"); + + //pbo headers + document.headers()->add("h1", "v1"); + document.headers()->add("h2", "v2"); + + //pbo entries with content + DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + DocumentNode* n2 = document.root()->createHierarchy(PboPath("f2/e2.txt")); + n2->binarySource = QSharedPointer(new FsRawBinarySource(e2.fileName())); + n2->binarySource->open(); + + //write the file + const QTemporaryDir temp; + const QString filePath = temp.filePath("file.pbo"); + + DocumentWriter writer(filePath); + writer.write(&document, []() { return false; }); + + //assert the result + PboFile pbo(filePath); + pbo.open(QIODeviceBase::ReadOnly); + const PboFileHeader header = PboHeaderReader::readFileHeader(&pbo); + + //pbo header + ASSERT_EQ(header.headers.count(), 2); + ASSERT_EQ(header.headers.at(0)->name, "h1"); + ASSERT_EQ(header.headers.at(0)->value, "v1"); + ASSERT_EQ(header.headers.at(1)->name, "h2"); + ASSERT_EQ(header.headers.at(1)->value, "v2"); + + ASSERT_EQ(header.entries.count(), 2); + ASSERT_EQ(header.entries.at(0)->fileName(), "f2/e2.txt"); + ASSERT_EQ(header.entries.at(1)->fileName(), "e1.txt"); + + //pbo contents + pbo.seek(header.dataBlockStart); + + QByteArray contents; + contents.resize(mockContent2.size()); + pbo.read(contents.data(), contents.size()); + ASSERT_EQ(contents, mockContent2); + + contents.resize(mockContent1.size()); + pbo.read(contents.data(), contents.size()); + ASSERT_EQ(contents, mockContent1); + + //zero byte + QByteArray zero; + zero.resize(1); + pbo.read(zero.data(), zero.size()); + ASSERT_EQ(zero.at(0), 0); + + //signature + ASSERT_EQ(document.signature().size(), 20);//sha1 is 20 bytes + contents.resize(document.signature().size()); + pbo.read(contents.data(), contents.size()); + ASSERT_EQ(contents.size(), document.signature().size()); + + //file has ended + ASSERT_TRUE(pbo.atEnd()); + } + + class DocumentWriterTest : public testing::TestWithParam {}; + + TEST_P(DocumentWriterTest, Write_Cleans_Files_On_Cancel) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + //pbo document with content + PboDocument document("file.pbo"); + DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + + //pbo file + const QTemporaryDir temp; + const QString filePath = temp.filePath("file.pbo"); + + //call the method + int count = 0; + int expectedHitCount = GetParam();//experimental way + DocumentWriter writer(filePath); + writer.write(&document, [&count, expectedHitCount]() { count++; return count > expectedHitCount - 1; }); + + ASSERT_FALSE(QFileInfo(filePath).exists()); + ASSERT_FALSE(QFileInfo(filePath + ".b").exists()); + } + + INSTANTIATE_TEST_SUITE_P(Write_Cleans_On_Cancel, DocumentWriterTest, testing::Range(1, 13)); + + TEST(DocumentWriterTest, Write_Cleans_Temporary_Files_On_Write) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + //pbo file + const QTemporaryDir temp; + const QString filePath = temp.filePath("file.pbo"); + + //pbo content structure + PboDocument document("file.pbo"); + DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + + //call the method + DocumentWriter writer(filePath); + writer.write(&document, []() { return false; }); + + //ensure no junk left + ASSERT_TRUE(QFileInfo(filePath).exists()); + ASSERT_FALSE(QFileInfo(filePath + ".b").exists()); + } + + TEST_P(DocumentWriterTest, Write_Leaves_Binary_Sources_Open_If_Cancelled) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + //pbo file + const QTemporaryDir temp; + const QString filePath = temp.filePath("file.pbo"); + + //pbo content structure + PboDocument document("file.pbo"); + DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + + //call the method + int count = 0; + int expectedHitCount = GetParam();//experimental way + DocumentWriter writer(filePath); + writer.write(&document, [&count, expectedHitCount]() { count++; return count > expectedHitCount - 1; }); + + //ensure bs left open + ASSERT_TRUE(n1->binarySource->isOpen()); + } + + INSTANTIATE_TEST_SUITE_P(Write_Leaves_Binary_Sources_Open_If_Cancelled, DocumentWriterTest, testing::Range(1, 13)); + + TEST(DocumentWriterTest, Write_Opens_Binary_Sources_After_Write) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + //pbo file + const QTemporaryDir temp; + const QString filePath = temp.filePath("file.pbo"); + + //pbo content structure + PboDocument document("file.pbo"); + DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + + //call the method + DocumentWriter writer(filePath); + writer.write(&document, []() { return false; }); + + //ensure no junk left + ASSERT_TRUE(n1->binarySource->isOpen()); + } + + TEST(DocumentWriterTest, Write_Replaces_Existing_File) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + //pbo file + const QTemporaryDir temp; + QFile existingFile(temp.filePath("file.pbo")); + existingFile.open(QIODeviceBase::WriteOnly); + existingFile.write(QByteArray("some content")); + existingFile.close(); + + //pbo content structure + PboDocument document("file.pbo"); + DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + + //call the method + DocumentWriter writer(existingFile.fileName()); + writer.write(&document, []() { return false; }); + + //ensure files in place + ASSERT_TRUE(QFileInfo(existingFile.fileName()).exists()); + ASSERT_EQ(QFileInfo(existingFile.fileName()).size(), 106);//pbo written + ASSERT_TRUE(QFileInfo(existingFile.fileName() + ".bak").exists()); + ASSERT_EQ(QFileInfo(existingFile.fileName() + ".bak").size(), 12);//the original file + } + +} diff --git a/pbom/io/documentreader.cpp b/pbom/io/documentreader.cpp new file mode 100644 index 0000000..50cf4ec --- /dev/null +++ b/pbom/io/documentreader.cpp @@ -0,0 +1,43 @@ +#include "documentreader.h" +#include +#include "pboheaderreader.h" +#include "bs/pbobinarysource.h" + +namespace pboman3::io { + DocumentReader::DocumentReader(QString path) + : path_(std::move(path)) { + } + + QSharedPointer DocumentReader::read() const { + PboFile pbo(path_); + pbo.open(QIODeviceBase::ReadOnly); + PboFileHeader header = PboHeaderReader::readFileHeader(&pbo); + + const QFileInfo fi(path_); + + QSharedPointer document(new PboDocument(fi.fileName())); + + for (const QSharedPointer& h : header.headers) { + document->headers()->add(h->name, h->value); + } + + qsizetype entryDataOffset = header.dataBlockStart; + for (const QSharedPointer& e : header.entries) { + DocumentNode* node = document->root()->createHierarchy(e->makePath()); + PboDataInfo dataInfo{0, 0, 0, 0, 0}; + dataInfo.originalSize = e->originalSize(); + dataInfo.dataSize = e->dataSize(); + dataInfo.dataOffset = entryDataOffset; + dataInfo.timestamp = e->timestamp(); + dataInfo.compressed = e->packingMethod() == PboPackingMethod::Packed; + entryDataOffset += dataInfo.dataSize; + node->binarySource = QSharedPointer( + new PboBinarySource(path_, dataInfo)); + node->binarySource->open(); + } + + document->setSignature(std::move(header.signature)); + + return document; + } +} diff --git a/pbom/io/documentreader.h b/pbom/io/documentreader.h new file mode 100644 index 0000000..8ebc709 --- /dev/null +++ b/pbom/io/documentreader.h @@ -0,0 +1,17 @@ +#pragma once + +#include "domain/pbodocument.h" + +namespace pboman3::io { + using namespace domain; + + class DocumentReader { + public: + DocumentReader(QString path); + + QSharedPointer read() const; + + private: + QString path_; + }; +} diff --git a/pbom/io/documentwriter.cpp b/pbom/io/documentwriter.cpp new file mode 100644 index 0000000..df2d202 --- /dev/null +++ b/pbom/io/documentwriter.cpp @@ -0,0 +1,260 @@ +#include "documentwriter.h" +#include +#include +#include "pboheader.h" +#include "pboheaderio.h" +#include "pboioexception.h" + +namespace pboman3::io { + DocumentWriter::DocumentWriter(QString path) + : path_(std::move(path)) { + assert(!path_.isEmpty() && "Path must not be empty"); + } + + void DocumentWriter::write(PboDocument* document, const Cancel& cancel) { + assert(document && "Document must not be null"); + + const QString filePath = QFile::exists(path_) ? path_ + ".t" : path_; + writeInternal(document, filePath, cancel); + + suspendBinarySources(document->root()); + + if (filePath != path_ && !cancel()) { + const QString backupPath = path_ + ".bak"; + //LOG(info, "Cleaning up the temporary files") + if (QFile::exists(backupPath) && !QFile::remove(backupPath)) { + //LOG(info, "Could not remove the prev backup file - throwing;", backupPath) + resumeBinarySources(document->root()); + throw PboIoException( + "Could not remove the file. Check you have enough permissions and the file is not locked by another process.", + backupPath); + } + if (!QFile::rename(path_, backupPath)) { + //LOG(info, "Could not replace the prev PBO file with a write copy - throwing;", loadedPath_) + resumeBinarySources(document->root()); + throw PboIoException( + "Could not write to the file. Check you have enough permissions and the file is not locked by another process.", + path_); + } + const bool renamed = QFile::rename(filePath, path_); + assert(renamed); + } + + if (cancel()) { + resumeBinarySources(document->root()); + } else { + assignBinarySources(document->root()); + } + } + + void DocumentWriter::writeInternal(PboDocument* document, const QString& path, const Cancel& cancel) { + QTemporaryFile body; + body.setFileName(path + ".b"); + if (!body.open()) + throw PboIoException("Could not create the file.", body.fileName()); + + QList> entries; + writeNode(&body, document->root(), entries, cancel); + + if (cancel()) + return; + + PboFile pbo(path); + if (!pbo.open(QIODeviceBase::ReadWrite)) + throw PboIoException("Could not create the file.", path); + + writeHeader(&pbo, document->headers(), entries, cancel); + + if (cancel()) { + pbo.close(); + pbo.remove(); + return; + } + + const bool seek = body.seek(0); + assert(seek); + + copyBody(&pbo, &body, cancel); + + writeSignature(&pbo, document, cancel); + + if (cancel()) { + pbo.close(); + pbo.remove(); + } + } + + void DocumentWriter::writeNode(QFileDevice* file, DocumentNode* node, QList>& entries, + const Cancel& cancel) { + for (DocumentNode* child : *node) { + if (cancel()) + return; + + if (child->nodeType() == PboNodeType::File) { + const qint64 before = file->pos(); + child->binarySource->writeToPbo(file, cancel); + const qint64 after = file->pos(); + + const qint32 originalSize = child->binarySource->readOriginalSize(); + const auto dataSize = static_cast(after - before); + + QSharedPointer entry(new PboEntry( + child->makePath().toString(), + child->binarySource->isCompressed() ? PboPackingMethod::Packed : PboPackingMethod::Uncompressed, + child->binarySource->readOriginalSize(), + 0, + child->binarySource->readTimestamp(), + dataSize)); + entries.append(entry); + + PboDataInfo data{0, 0, 0, 0, 0}; + data.originalSize = originalSize; + data.dataSize = dataSize; + data.dataOffset = before; + data.timestamp = child->binarySource->readTimestamp(); + data.compressed = child->binarySource->isCompressed(); + + binarySources_.insert(child, data); + + emitWriteEntry(); + } else { + writeNode(file, child, entries, cancel); + } + } + } + + void DocumentWriter::writeHeader(PboFile* file, const DocumentHeaders* headers, + const QList>& entries, const Cancel& cancel) { + const PboHeaderIO io(file); + io.writeEntry(PboEntry::makeSignature()); + + for (const DocumentHeader* header : *headers) { + if (cancel()) { + break; + } + io.writeHeader(PboHeader(header->name(), header->value())); + } + + if (cancel()) { + return; + } + + io.writeHeader(PboHeader::makeBoundary()); + + for (const QSharedPointer& entry : entries) { + if (cancel()) { + break; + } + io.writeEntry(*entry); + } + + if (cancel()) { + return; + } + + io.writeEntry(PboEntry::makeBoundary()); + + for (DocumentNode* key : binarySources_.keys()) { + PboDataInfo& existing = binarySources_[key]; + existing.dataOffset += file->pos(); + } + } + + void DocumentWriter::copyBody(QFileDevice* pbo, QFileDevice* body, const Cancel& cancel) { + QByteArray data; + data.resize(1024 * 1024); + + qsizetype copiedBytes = 0; + const qsizetype totalBytes = body->size(); + + qint64 read = body->read(data.data(), data.size()); + while (read > 0) { + pbo->write(data.data(), read); + + copiedBytes += read; + emitCopyBytes(copiedBytes, totalBytes); + + if (cancel()) + return; + read = body->read(data.data(), data.size()); + } + } + + void DocumentWriter::writeSignature(QFileDevice* pbo, PboDocument* document, const Cancel& cancel) { + const bool seek = pbo->seek(0); + assert(seek); + + QCryptographicHash sha1(QCryptographicHash::Sha1); + + qsizetype processed = 0; + const qsizetype total = pbo->size(); + + constexpr qint64 bufferSize = 1024; + char buffer[bufferSize]; + qint64 read; + + while (!cancel() && (read = pbo->read(buffer, bufferSize)) > 0) { + sha1.addData(buffer, read); + processed += read; + emitCalcHash(processed, total); + } + + if (cancel()) + return; + + document->setSignature(sha1.result()); + + pbo->write(QByteArray(1, 0)); + pbo->write(document->signature(), document->signature().count()); + } + + void DocumentWriter::suspendBinarySources(DocumentNode* node) const { + for (DocumentNode* child : *node) { + if (child->nodeType() == PboNodeType::File) { + child->binarySource->close(); + } + else { + suspendBinarySources(child); + } + } + } + + void DocumentWriter::resumeBinarySources(DocumentNode* node) const { + for (DocumentNode* child : *node) { + if (child->nodeType() == PboNodeType::File) { + child->binarySource->open(); + } + else { + resumeBinarySources(child); + } + } + } + + void DocumentWriter::assignBinarySources(DocumentNode* node) { + for (DocumentNode* child : *node) { + if (child->nodeType() == PboNodeType::File) { + const PboDataInfo& existing = binarySources_.take(child); + child->binarySource = QSharedPointer(new PboBinarySource(path_, existing)); + child->binarySource->open(); + } + else { + assignBinarySources(child); + } + } + } + + void DocumentWriter::emitWriteEntry() { + const WriteEntryEvent evt; + emit progress(&evt); + } + + void DocumentWriter::emitCopyBytes(qsizetype copied, qsizetype total) { + const CopyBytesEvent evt(copied, total); + emit progress(&evt); + } + + void DocumentWriter::emitCalcHash(qsizetype processed, qsizetype total) { + const CalcHashEvent evt(processed, total); + emit progress(&evt); + } +} diff --git a/pbom/io/documentwriter.h b/pbom/io/documentwriter.h new file mode 100644 index 0000000..1b1b9eb --- /dev/null +++ b/pbom/io/documentwriter.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include "pboentry.h" +#include "pbofile.h" +#include "bs/pbobinarysource.h" +#include "domain/pbodocument.h" +#include "util/util.h" + +namespace pboman3::io { + using namespace domain; + + class DocumentWriter: public QObject { + Q_OBJECT + + public: + DocumentWriter(QString path); + + void write(PboDocument* document, const Cancel& cancel); + + struct ProgressEvent; + + signals: + void progress(const ProgressEvent* evt); + + private: + QString path_; + QHash binarySources_; + + void writeInternal(PboDocument* document, const QString& path, const Cancel& cancel); + + void writeNode(QFileDevice* file, DocumentNode* node, QList>& entries, const Cancel& cancel); + + void writeHeader(PboFile* file, const DocumentHeaders* headers, const QList>& entries, const Cancel& cancel); + + void copyBody(QFileDevice* pbo, QFileDevice* body, const Cancel& cancel); + + void writeSignature(QFileDevice* pbo, PboDocument* document, const Cancel& cancel); + + void suspendBinarySources(DocumentNode* node) const; + + void resumeBinarySources(DocumentNode* node) const; + + void assignBinarySources(DocumentNode* node); + + void emitWriteEntry(); + + void emitCopyBytes(qsizetype copied, qsizetype total); + + void emitCalcHash(qsizetype processed, qsizetype total); + + public: + struct ProgressEvent { + virtual ~ProgressEvent() = default; + }; + + struct WriteEntryEvent : ProgressEvent { + }; + + struct CopyBytesEvent : ProgressEvent { + CopyBytesEvent(qsizetype c, qsizetype t) : + copied(c), total(t) { + } + + const qsizetype copied; + const qsizetype total; + }; + + struct CalcHashEvent : ProgressEvent { + CalcHashEvent(qsizetype p, qsizetype t) : + processed(p), total(t) { + } + + const qsizetype processed; + const qsizetype total; + }; + }; +} From 0cbf74501e5ca97ae1c230428cd0700f4d6072ae Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sun, 14 Nov 2021 11:45:15 +0300 Subject: [PATCH 13/39] Making the DocumentNode a ddd aggregate --- pbom/domain/CMakeLists.txt | 8 +- pbom/domain/__test__/documentheader_test.cpp | 48 ++++ pbom/domain/__test__/documentheaders_test.cpp | 96 +++++++ .../documentheaderstransaction_test.cpp | 126 ++++++++ pbom/domain/__test__/pbodocument_test.cpp | 185 ++++++++++++ pbom/domain/__test__/pbonode_test.cpp | 147 +++++----- .../__test__/pbonodetransaction_test.cpp | 61 ++++ pbom/domain/binarysource.cpp | 30 -- pbom/domain/binarysource.h | 22 +- pbom/domain/documentheader.cpp | 26 +- pbom/domain/documentheader.h | 11 + pbom/domain/documentheaders.cpp | 72 +++-- pbom/domain/documentheaders.h | 26 +- pbom/domain/documentheaderstransaction.cpp | 52 ++++ pbom/domain/documentheaderstransaction.h | 38 +++ pbom/domain/documentnode.cpp | 272 ------------------ pbom/domain/documentnode.h | 74 ----- pbom/domain/pbodocument.cpp | 30 +- pbom/domain/pbodocument.h | 15 +- pbom/domain/pbonode.cpp | 61 ++-- pbom/domain/pbonode.h | 14 +- pbom/domain/pbonodetransaction.cpp | 45 +++ pbom/domain/pbonodetransaction.h | 24 ++ pbom/domain/pbonodetype.h | 2 +- pbom/io/CMakeLists.txt | 10 +- pbom/io/__test__/documentwriter_test.cpp | 22 +- pbom/io/__test__/pboheaderio_test.cpp | 2 +- pbom/io/__test__/pboheaderreader_test.cpp | 12 +- pbom/io/__test__/pbowriter_test.cpp | 242 ---------------- pbom/io/bb/__test__/execbackend_test.cpp | 2 +- pbom/io/bb/__test__/nodefilesystem_test.cpp | 4 +- pbom/io/bb/__test__/tempbackend_test.cpp | 2 +- pbom/io/bb/__test__/unpackbackend_test.cpp | 2 +- pbom/io/bb/binarybackend.cpp | 9 +- pbom/io/bb/binarybackend.h | 4 +- pbom/io/bb/execbackend.cpp | 8 +- pbom/io/bb/execbackend.h | 2 +- pbom/io/bb/nodefilesystem.cpp | 8 +- pbom/io/bb/nodefilesystem.h | 4 +- pbom/io/bb/tempbackend.cpp | 10 +- pbom/io/bb/tempbackend.h | 4 +- pbom/io/bb/unpackbackend.cpp | 8 +- pbom/io/bb/unpackbackend.h | 2 +- pbom/io/bb/unpacktaskbackend.cpp | 6 +- pbom/io/bb/unpacktaskbackend.h | 2 +- .../io/bs/__test__/fslzhbinarysource_test.cpp | 2 +- .../io/bs/__test__/fsrawbinarysource_test.cpp | 2 +- pbom/io/bs/__test__/pbobinarysource_test.cpp | 2 +- pbom/io/bs/binarysourcebase.cpp | 31 ++ pbom/io/bs/binarysourcebase.h | 40 +++ pbom/io/bs/fslzhbinarysource.cpp | 2 +- pbom/io/bs/fslzhbinarysource.h | 2 +- pbom/io/bs/fsrawbinarysource.cpp | 4 +- pbom/io/bs/fsrawbinarysource.h | 6 +- pbom/io/bs/pbobinarysource.cpp | 8 +- pbom/io/bs/pbobinarysource.h | 6 +- pbom/{model => io}/diskaccessexception.cpp | 7 +- pbom/{model => io}/diskaccessexception.h | 5 +- pbom/io/documentreader.cpp | 21 +- pbom/io/documentwriter.cpp | 28 +- pbom/io/documentwriter.h | 10 +- pbom/io/execstore.cpp | 7 +- pbom/io/execstore.h | 2 +- pbom/{model => io}/pbofileformatexception.cpp | 2 +- pbom/{model => io}/pbofileformatexception.h | 2 +- pbom/io/pboheaderio.cpp | 2 +- pbom/io/pboheaderio.h | 2 +- pbom/io/pboheaderreader.cpp | 13 +- pbom/io/pboheaderreader.h | 2 +- pbom/io/pboioexception.cpp | 24 -- pbom/io/pboioexception.h | 22 -- pbom/io/pbowriter.cpp | 262 ----------------- pbom/io/pbowriter.h | 91 ------ pbom/model/CMakeLists.txt | 12 +- pbom/model/__test__/conflictsparcel_test.cpp | 2 +- pbom/model/__test__/headersmodel_test.cpp | 73 ----- .../model/__test__/interactionparcel_test.cpp | 2 +- pbom/model/__test__/rootreader_test.cpp | 46 --- pbom/model/__test__/signaturemodel_test.cpp | 19 -- pbom/model/conflictsparcel.cpp | 2 +- pbom/model/conflictsparcel.h | 2 +- pbom/model/headersmodel.cpp | 41 --- pbom/model/headersmodel.h | 27 -- pbom/model/interactionparcel.cpp | 4 +- pbom/model/interactionparcel.h | 9 +- pbom/model/pbomodel.cpp | 170 +++-------- pbom/model/pbomodel.h | 22 +- pbom/model/rootreader.cpp | 32 --- pbom/model/rootreader.h | 17 -- pbom/model/signaturemodel.cpp | 14 - pbom/model/signaturemodel.h | 15 - pbom/model/task/packtask.cpp | 29 +- pbom/model/task/packtask.h | 2 + pbom/model/task/unpacktask.cpp | 55 ++-- pbom/model/task/unpacktask.h | 10 +- pbom/ui/compresslist.h | 10 +- pbom/ui/conflictslist.h | 8 +- pbom/ui/errordialog.cpp | 7 + pbom/ui/errordialog.h | 7 +- pbom/ui/fscollector.h | 2 + pbom/ui/headersdialog.cpp | 34 ++- pbom/ui/headersdialog.h | 10 +- pbom/ui/mainwindow.cpp | 23 +- pbom/ui/mainwindow.h | 4 +- pbom/ui/renamedialog.cpp | 57 ++-- pbom/ui/renamedialog.h | 9 +- pbom/ui/signaturedialog.cpp | 6 +- pbom/ui/signaturedialog.h | 3 +- pbom/ui/treewidget/deleteop.h | 4 +- pbom/ui/treewidget/treewidget.cpp | 4 +- pbom/ui/treewidget/treewidget.h | 2 + pbom/ui/treewidget/treewidgetbase.h | 2 + pbom/ui/treewidget/treewidgetitem.h | 2 + 113 files changed, 1353 insertions(+), 1903 deletions(-) create mode 100644 pbom/domain/__test__/documentheader_test.cpp create mode 100644 pbom/domain/__test__/documentheaders_test.cpp create mode 100644 pbom/domain/__test__/documentheaderstransaction_test.cpp create mode 100644 pbom/domain/__test__/pbodocument_test.cpp create mode 100644 pbom/domain/__test__/pbonodetransaction_test.cpp create mode 100644 pbom/domain/documentheaderstransaction.cpp create mode 100644 pbom/domain/documentheaderstransaction.h delete mode 100644 pbom/domain/documentnode.cpp delete mode 100644 pbom/domain/documentnode.h create mode 100644 pbom/domain/pbonodetransaction.cpp create mode 100644 pbom/domain/pbonodetransaction.h delete mode 100644 pbom/io/__test__/pbowriter_test.cpp create mode 100644 pbom/io/bs/binarysourcebase.cpp create mode 100644 pbom/io/bs/binarysourcebase.h rename pbom/{model => io}/diskaccessexception.cpp (71%) rename pbom/{model => io}/diskaccessexception.h (70%) rename pbom/{model => io}/pbofileformatexception.cpp (91%) rename pbom/{model => io}/pbofileformatexception.h (88%) delete mode 100644 pbom/io/pboioexception.cpp delete mode 100644 pbom/io/pboioexception.h delete mode 100644 pbom/io/pbowriter.cpp delete mode 100644 pbom/io/pbowriter.h delete mode 100644 pbom/model/__test__/headersmodel_test.cpp delete mode 100644 pbom/model/__test__/rootreader_test.cpp delete mode 100644 pbom/model/__test__/signaturemodel_test.cpp delete mode 100644 pbom/model/headersmodel.cpp delete mode 100644 pbom/model/headersmodel.h delete mode 100644 pbom/model/rootreader.cpp delete mode 100644 pbom/model/rootreader.h delete mode 100644 pbom/model/signaturemodel.cpp delete mode 100644 pbom/model/signaturemodel.h diff --git a/pbom/domain/CMakeLists.txt b/pbom/domain/CMakeLists.txt index 961755b..5f9b5dc 100644 --- a/pbom/domain/CMakeLists.txt +++ b/pbom/domain/CMakeLists.txt @@ -2,16 +2,22 @@ list(APPEND PROJECT_SOURCES "domain/binarysource.cpp" "domain/documentheader.cpp" "domain/documentheaders.cpp" - "domain/documentnode.cpp" + "domain/documentheaderstransaction.cpp" "domain/pbodocument.cpp" "domain/pbonode.cpp" + "domain/pbonodetransaction.cpp" "domain/pbopath.cpp" "domain/validationexception.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) list(APPEND TEST_SOURCES + "domain/__test__/documentheader_test.cpp" + "domain/__test__/documentheaders_test.cpp" + "domain/__test__/documentheaderstransaction_test.cpp" + "domain/__test__/pbodocument_test.cpp" "domain/__test__/pbonode_test.cpp" + "domain/__test__/pbonodetransaction_test.cpp" "domain/__test__/pbopath_test.cpp") set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/domain/__test__/documentheader_test.cpp b/pbom/domain/__test__/documentheader_test.cpp new file mode 100644 index 0000000..de8f84c --- /dev/null +++ b/pbom/domain/__test__/documentheader_test.cpp @@ -0,0 +1,48 @@ +#include +#include "domain/documentheader.h" +#include "domain/validationexception.h" + +namespace pboman3::domain::test { + TEST(DocumentHeaderTest, Ctor_Initializes_Object) { + const DocumentHeader header1("h1", "v1"); + ASSERT_EQ(header1.name(), "h1"); + ASSERT_EQ(header1.value(), "v1"); + + const DocumentHeader header2("h2", ""); + ASSERT_EQ(header2.name(), "h2"); + ASSERT_EQ(header2.value(), ""); + } + + TEST(DocumentHeaderTest, Ctor_Throws_If_Name_Empty) { + ASSERT_THROW(DocumentHeader header("", "v1"), ValidationException); + } + + TEST(DocumentHeaderTest, Repository_Does_Not_Throw) { + const DocumentHeader header1(DocumentHeader::InternalData{ "h1", "v1" }); + ASSERT_EQ(header1.name(), "h1"); + ASSERT_EQ(header1.value(), "v1"); + + const DocumentHeader header2(DocumentHeader::InternalData{ "", "" }); + ASSERT_EQ(header2.name(), ""); + ASSERT_EQ(header2.value(), ""); + } + + TEST(DocumentHeaderTest, Equality_Operator_Returns_True) { + const DocumentHeader header1(DocumentHeader::InternalData{ "h1", "v1" }); + const DocumentHeader header2(DocumentHeader::InternalData{ "h1", "v1" }); + + ASSERT_EQ(header1, header2); + } + + TEST(DocumentHeaderTest, Equality_Operator_Returns_False) { + const DocumentHeader header1(DocumentHeader::InternalData{ "h1", "v2" }); + const DocumentHeader header2(DocumentHeader::InternalData{ "h1", "v1" }); + + ASSERT_NE(header1, header2); + + const DocumentHeader header3(DocumentHeader::InternalData{ "h2", "v1" }); + const DocumentHeader header4(DocumentHeader::InternalData{ "h1", "v1" }); + + ASSERT_NE(header3, header4); + } +} diff --git a/pbom/domain/__test__/documentheaders_test.cpp b/pbom/domain/__test__/documentheaders_test.cpp new file mode 100644 index 0000000..3af81d7 --- /dev/null +++ b/pbom/domain/__test__/documentheaders_test.cpp @@ -0,0 +1,96 @@ +#include +#include "domain/documentheaders.h" +#include "domain/documentheaderstransaction.h" + +namespace pboman3::domain::test { + TEST(DocumentHeadersTest, Count_Returns_Headers_Count) { + const DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + ASSERT_EQ(headers.count(), 2); + } + + TEST(DocumentHeadersTest, At_Returns_Header) { + const DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + + ASSERT_EQ(headers.at(0)->name(), "h1"); + ASSERT_EQ(headers.at(0)->value(), "v1"); + + ASSERT_EQ(headers.at(1)->name(), "h2"); + ASSERT_EQ(headers.at(1)->value(), "v2"); + } + + TEST(DocumentHeadersTest, Begin_End_Work) { + DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + + int count = 0; + for (const DocumentHeader* header : headers) { + ASSERT_FALSE(header->name().isEmpty()); + count++; + } + + ASSERT_EQ(count, 2); + } + + TEST(DocumentHeadersTest, HeadersChanged_Fires_If_Count_Of_Headers_Changed) { + DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + + int count = 0; + QObject::connect(&headers, &DocumentHeaders::headersChanged, [&count]() { count++; }); + + QSharedPointer tran = headers.beginTransaction(); + tran->add("h3", "v3"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + } + + TEST(DocumentHeadersTest, HeadersChanged_Fires_If_Header_Changed) { + DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + + int count = 0; + QObject::connect(&headers, &DocumentHeaders::headersChanged, [&count]() { count++; }); + + QSharedPointer tran = headers.beginTransaction(); + tran->clear(); + tran->add("h2", "v2"); + tran->add("h1", "v1"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + } + + TEST(DocumentHeadersTest, HeadersChanged_Not_Fires_If_Header_Replaced) { + DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + + int count = 0; + QObject::connect(&headers, &DocumentHeaders::headersChanged, [&count]() { count++; }); + + QSharedPointer tran = headers.beginTransaction(); + tran->clear(); + tran->add("h1", "v1"); + tran->add("h2", "v2"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 0); + } +} diff --git a/pbom/domain/__test__/documentheaderstransaction_test.cpp b/pbom/domain/__test__/documentheaderstransaction_test.cpp new file mode 100644 index 0000000..c00add6 --- /dev/null +++ b/pbom/domain/__test__/documentheaderstransaction_test.cpp @@ -0,0 +1,126 @@ +#include +#include "domain/documentheaders.h" +#include "domain/documentheaderstransaction.h" +#include "util/exception.h" + +namespace pboman3::domain::test { + TEST(DocumentHeadersTransactionTest, Count_Returns_Headers_Count) { + DocumentHeaders headers; + + const QSharedPointer tran = headers.beginTransaction(); + ASSERT_EQ(tran->count(), 0); + + tran->add("h1", "v1"); + ASSERT_EQ(tran->count(), 1); + + tran->add("h2", "v2"); + ASSERT_EQ(tran->count(), 2); + + tran->clear(); + ASSERT_EQ(tran->count(), 0); + } + + TEST(DocumentHeadersTransactionTest, At_Returns_Header) { + DocumentHeaders headers; + + const QSharedPointer tran = headers.beginTransaction(); + tran->add("h1", "v1"); + tran->add("h2", "v2"); + + ASSERT_EQ(tran->at(0)->name(), "h1"); + ASSERT_EQ(tran->at(0)->value(), "v1"); + + ASSERT_EQ(tran->at(1)->name(), "h2"); + ASSERT_EQ(tran->at(1)->value(), "v2"); + } + + TEST(DocumentHeadersTransactionTest, Add_Throws_If_Committed) { + DocumentHeaders headers; + + const QSharedPointer tran = headers.beginTransaction(); + tran->commit(); + + try { + tran->add("h1", "v1"); + FAIL() << "Should have not reached this line"; + } catch (InvalidOperationException& ex) { + ASSERT_EQ(ex.message(), "You must not modify a committed transaction."); + } + } + + + TEST(DocumentHeadersTransactionTest, Clear_Throws_If_Committed) { + DocumentHeaders headers; + + const QSharedPointer tran = headers.beginTransaction(); + tran->commit(); + + try { + tran->clear(); + FAIL() << "Should have not reached this line"; + } catch (InvalidOperationException& ex) { + ASSERT_EQ(ex.message(), "You must not modify a committed transaction."); + } + } + + TEST(DocumentHeadersTransactionTest, Begin_End_Work) { + DocumentHeaders headers; + + const QSharedPointer tran = headers.beginTransaction(); + tran->add("h1", "v1"); + tran->add("h2", "v2"); + + int count = 0; + for (const DocumentHeader* header : *tran) { + ASSERT_FALSE(header->name().isEmpty()); + count++; + } + + ASSERT_EQ(count, 2); + } + + TEST(DocumentHeadersTransactionTest, Transaction_Commits) { + DocumentHeaders headers; + + QSharedPointer tran = headers.beginTransaction(); + tran->add("h1", "v1"); + tran->add("h2", "v2"); + + tran->commit(); + tran.clear();//explicitly clear + + ASSERT_EQ(headers.count(), 2); + ASSERT_EQ(headers.at(0)->name(), "h1"); + ASSERT_EQ(headers.at(1)->name(), "h2"); + } + + TEST(DocumentHeadersTransactionTest, Transaction_RollsBack) { + DocumentHeaders headers; + + QSharedPointer tran = headers.beginTransaction(); + tran->add("h1", "v1"); + tran->add("h2", "v2"); + + //tran->commit();//no commit call! + //tran.clear();//explicitly clear + + ASSERT_EQ(headers.count(), 0); + } + + TEST(DocumentHeadersTransactionTest, Transaction_Does_Not_Change_Source_Data_On_Rollback) { + DocumentHeaders headers(QList{ + QSharedPointer(new DocumentHeader("h1", "v1")), + QSharedPointer(new DocumentHeader("h2", "v2")), + }); + + QSharedPointer tran = headers.beginTransaction(); + tran->add("h3", "v3"); + + //tran->commit();//no commit call! + tran.clear();//explicitly clear + + ASSERT_EQ(headers.count(), 2); + ASSERT_EQ(headers.at(0)->name(), "h1"); + ASSERT_EQ(headers.at(1)->name(), "h2"); + } +} diff --git a/pbom/domain/__test__/pbodocument_test.cpp b/pbom/domain/__test__/pbodocument_test.cpp new file mode 100644 index 0000000..e9cc383 --- /dev/null +++ b/pbom/domain/__test__/pbodocument_test.cpp @@ -0,0 +1,185 @@ +#include "domain/pbodocument.h" +#include +#include "domain/documentheaderstransaction.h" +#include "domain/pbonodetransaction.h" + +namespace pboman3::domain::test { + TEST(PboDocumentTest, Public_Ctor_Changed_Fires_When_Hierarchy_Changes) { + const PboDocument document("file.pbo"); + + int count = 0; + QObject::connect(&document, &PboDocument::changed, [&count]() { + count++; + }); + + document.root()->createHierarchy(PboPath("f1.txt"), ConflictResolution::Unset); + + ASSERT_EQ(count, 1); + } + + TEST(PboDocumentTest, Repository_Ctor_Changed_Fires_When_Hierarchy_Changes) { + const PboDocument document("file.pbo", QList>{}, QByteArray{}); + + int count = 0; + QObject::connect(&document, &PboDocument::changed, [&count]() { + count++; + }); + + document.root()->createHierarchy(PboPath("f1.txt"), ConflictResolution::Unset); + + ASSERT_EQ(count, 1); + } + + TEST(PboDocumentTest, Public_Ctor_Changed_Clears_Signature_When_Hierarchy_Changes) { + QByteArray signature; + signature.fill('p', 20); + + PboDocument document("file.pbo"); + document.setSignature(std::move(signature)); + + document.root()->createHierarchy(PboPath("f1.txt"), ConflictResolution::Unset); + + ASSERT_EQ(document.signature(), QByteArray()); + } + + TEST(PboDocumentTest, Repository_Ctor_Changed_Clears_Signature_When_Hierarchy_Changes) { + QByteArray signature; + signature.fill('p', 10); + + const PboDocument document("file.pbo", QList>{}, signature); + + document.root()->createHierarchy(PboPath("f1.txt"), ConflictResolution::Unset); + + ASSERT_EQ(document.signature(), QByteArray()); + } + + + TEST(PboDocumentTest, Public_Ctor_Changed_Fires_When_Headers_Change) { + const PboDocument document("file.pbo"); + + int count = 0; + QObject::connect(&document, &PboDocument::changed, [&count]() { + count++; + }); + + QSharedPointer tran = document.headers()->beginTransaction(); + tran->add("h1", "v1"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + } + + TEST(PboDocumentTest, Repository_Ctor_Changed_Fires_When_Header_Change) { + const PboDocument document("file.pbo", QList>{}, QByteArray{}); + + int count = 0; + QObject::connect(&document, &PboDocument::changed, [&count]() { + count++; + }); + + QSharedPointer tran = document.headers()->beginTransaction(); + tran->add("h1", "v1"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + } + + TEST(PboDocumentTest, Public_Ctor_Changed_Clears_Signature_When_Header_Change) { + QByteArray signature; + signature.fill('p', 20); + + PboDocument document("file.pbo"); + document.setSignature(std::move(signature)); + + QSharedPointer tran = document.headers()->beginTransaction(); + tran->add("h1", "v1"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(document.signature(), QByteArray()); + } + + TEST(PboDocumentTest, Repository_Ctor_Changed_Clears_Signature_When_Header_Change) { + QByteArray signature; + signature.fill('p', 10); + + const PboDocument document("file.pbo", QList>{}, signature); + + QSharedPointer tran = document.headers()->beginTransaction(); + tran->add("h1", "v1"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(document.signature(), QByteArray()); + } + + + TEST(PboDocumentTest, Public_Ctor_TitleChanged_Fires) { + const PboDocument document("file.pbo"); + + int count = 0; + QString title; + QObject::connect(&document, &PboDocument::titleChanged, [&count, &title](const QString& t) { + count++; + title = t; + }); + + QSharedPointer tran = document.root()->beginTransaction(); + tran->setTitle("new.pbo"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + ASSERT_EQ(title, "new.pbo"); + } + + TEST(PboDocumentTest, Repository_Ctor_TitleChanged_Fires) { + const PboDocument document("file.pbo", QList>{}, QByteArray{}); + + int count = 0; + QString title; + QObject::connect(&document, &PboDocument::titleChanged, [&count, &title](const QString& t) { + count++; + title = t; + }); + + QSharedPointer tran = document.root()->beginTransaction(); + tran->setTitle("new.pbo"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + ASSERT_EQ(title, "new.pbo"); + } + + TEST(PboDocumentTest, Public_Ctor_TitleChanged_Clears_Signature) { + QByteArray signature; + signature.fill('p', 20); + + PboDocument document("file.pbo"); + document.setSignature(std::move(signature)); + + QSharedPointer tran = document.root()->beginTransaction(); + tran->setTitle("new.pbo"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(document.signature(), QByteArray()); + } + + TEST(PboDocumentTest, Repository_Ctor_TitleChanged_Clears_Signature) { + QByteArray signature; + signature.fill('p', 10); + + const PboDocument document("file.pbo", QList>{}, signature); + + QSharedPointer tran = document.root()->beginTransaction(); + tran->setTitle("new.pbo"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(document.signature(), QByteArray()); + } +} diff --git a/pbom/domain/__test__/pbonode_test.cpp b/pbom/domain/__test__/pbonode_test.cpp index 8b0e6dc..fe144f9 100644 --- a/pbom/domain/__test__/pbonode_test.cpp +++ b/pbom/domain/__test__/pbonode_test.cpp @@ -2,10 +2,12 @@ #include #include #include + +#include "domain/pbonodetransaction.h" #include "gmock/gmock.h" #include "util/exception.h" -namespace pboman3::test { +namespace pboman3::domain::test { TEST(PboNodeTest, Ctor_Initializes_Node) { PboNode nodeA("a-node", PboNodeType::Folder, nullptr); ASSERT_EQ(nodeA.nodeType(), PboNodeType::Folder); @@ -202,67 +204,6 @@ namespace pboman3::test { ASSERT_EQ(node, &root); } - TEST(PboNodeTest, SetTitle_Throws_If_Can_Not_Set_Title) { - PboNode root("file-name", PboNodeType::Container, nullptr); - PboNode* e1 = root.createHierarchy(PboPath("e1")); - root.createHierarchy(PboPath("e2")); - - ASSERT_THROW(e1->setTitle("e2"), InvalidOperationException); - } - - TEST(PboNodeTest, SetTitle_Emits_If_Set_Title) { - PboNode root("file-name", PboNodeType::Container, nullptr); - PboNode* e1 = root.createHierarchy(PboPath("e1")); - - //callback variables - int count = 0; - - //connect and wait for the callback - QObject::connect(e1, &PboNode::titleChanged, [&count](const QString& title) { - ASSERT_EQ(title, "e2"); - count++; - }); - - e1->setTitle("e2"); - ASSERT_EQ(count, 1); - } - - TEST(PboNodeTest, SetTitle_Does_Not_Emit_If_Did_Not_Set_Title) { - PboNode root("file-name", PboNodeType::Container, nullptr); - PboNode* e1 = root.createHierarchy(PboPath("e1")); - - //callback variables - int callbackCount = 0; - - //connect and wait for the callback - QObject::connect(e1, &PboNode::titleChanged, [&callbackCount]() { - callbackCount++; - }); - - e1->setTitle("e1"); //the same name - not changing - - ASSERT_EQ(callbackCount, 0); - } - - TEST(PboNodeTest, SetTitle_Emits_HierarchyChanged_On_Root) { - PboNode root("file-name", PboNodeType::Container, nullptr); - PboNode* e2 = root.createHierarchy(PboPath("f1/e2")); - - int count = 0; - auto hierarchyChanged1 = []() { FAIL() << "Should not have been called"; }; - auto hierarchyChanged2 = [&count]() {count++; }; - - QObject::connect(root.at(0), &PboNode::hierarchyChanged, hierarchyChanged1); - QObject::connect(&root, &PboNode::hierarchyChanged, hierarchyChanged2); - - //only the root fires the callback - e2->setTitle("e1"); - ASSERT_EQ(count, 1); - - e2->setTitle("e1");//does not emit if no actual change - ASSERT_EQ(count, 1); - } - struct VerifyTitleTestParam { QString input; QString expectedOutput; @@ -271,20 +212,6 @@ namespace pboman3::test { class VerifyTitleTest : public testing::TestWithParam { }; - TEST_P(VerifyTitleTest, VerifyTitle_Is_Functional) { - PboNode root("file-name", PboNodeType::Container, nullptr); - root.createHierarchy(PboPath("e1")); - const PboNode* e2 = root.createHierarchy(PboPath("e2")); - - ASSERT_EQ(e2->verifyTitle(GetParam().input), GetParam().expectedOutput); - } - - INSTANTIATE_TEST_SUITE_P(PboNodeTest, VerifyTitleTest, testing::Values( - VerifyTitleTestParam {nullptr, "The value can not be empty"}, - VerifyTitleTestParam {"", "The value can not be empty"}, - VerifyTitleTestParam {"e1", "The item with this name already exists"}, - VerifyTitleTestParam {"e2", ""})); - TEST(PboNodeTest, RemoveFromHierarchy_Removes) { PboNode root("file-name", PboNodeType::Container, nullptr); PboNode* e1 = root.createHierarchy(PboPath("e1")); @@ -389,4 +316,72 @@ namespace pboman3::test { ASSERT_FALSE(root.isPathConflict(PboPath("f2/e3.txt"))); ASSERT_FALSE(root.isPathConflict(PboPath("f3/e4.txt"))); } + + TEST(PboNodeTest, SetTitle_Wont_Emit_If_Title_Not_Changed) { + PboNode root("file-name", PboNodeType::Container, nullptr); + + int count = 0; + auto changed = [&count]() {count++; }; + QObject::connect(&root, &PboNode::titleChanged, changed); + QObject::connect(&root, &PboNode::hierarchyChanged, changed); + + QSharedPointer tran = root.beginTransaction(); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 0); + } + + TEST(PboNodeTest, SetTitle_Emits_If_Title_Changed) { + PboNode root("file-name", PboNodeType::Container, nullptr); + + int count = 0; + QObject::connect(&root, &PboNode::titleChanged, [&count](const QString& title) { + count++; + ASSERT_EQ(title, "new-title"); + }); + QObject::connect(&root, &PboNode::hierarchyChanged, [&count]() {count++; }); + + QSharedPointer tran = root.beginTransaction(); + tran->setTitle("new-title"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 2); + } + + TEST(PboNodeTest, SetTitle_Emits_ChildMoved) { + PboNode root("node.pbo", PboNodeType::Container, nullptr); + root.createHierarchy(PboPath("f1.txt")); + PboNode* f2 = root.createHierarchy(PboPath("f2.txt")); + + int count = 0; + QObject::connect(&root, &PboNode::childMoved, [&count](qsizetype prevIndex, qsizetype newIndex) { + count++; + ASSERT_EQ(prevIndex, 1); + ASSERT_EQ(newIndex, 0); + }); + + QSharedPointer tran = f2->beginTransaction(); + tran->setTitle("f0.txt"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + } + + TEST(PboNodeTest, SetTitle_Emits_Changed_On_Root) { + PboNode root("node.pbo", PboNodeType::Container, nullptr); + PboNode* f1 = root.createHierarchy(PboPath("f1.txt")); + + int count = 0; + QObject::connect(&root, &PboNode::hierarchyChanged, [&count]() {count++; }); + + QSharedPointer tran = f1->beginTransaction(); + tran->setTitle("f0.txt"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(count, 1); + } } diff --git a/pbom/domain/__test__/pbonodetransaction_test.cpp b/pbom/domain/__test__/pbonodetransaction_test.cpp new file mode 100644 index 0000000..1d3355b --- /dev/null +++ b/pbom/domain/__test__/pbonodetransaction_test.cpp @@ -0,0 +1,61 @@ +#include +#include "domain/pbonodetransaction.h" +#include "domain/validationexception.h" +#include "util/exception.h" + +namespace pboman3::domain::test { + TEST(PboNodeTransactionTest, Title_Returns_Title) { + PboNode node("node.pbo", PboNodeType::Container, nullptr); + + const QSharedPointer tran = node.beginTransaction(); + ASSERT_EQ(tran->title(), "node.pbo"); + } + + TEST(PboNodeTransactionTest, SetTitle_Throws_If_Committed) { + PboNode node("node.pbo", PboNodeType::Container, nullptr); + + const QSharedPointer tran = node.beginTransaction(); + tran->commit(); + ASSERT_THROW(tran->setTitle("new title"), InvalidOperationException); + } + + TEST(PboNodeTransactionTest, SetTitle_Throws_If_Title_Emmpty) { + PboNode node("node.pbo", PboNodeType::Container, nullptr); + + const QSharedPointer tran = node.beginTransaction(); + try { + tran->setTitle(""); + FAIL() << "Should have not reached this line"; + } catch (ValidationException& ex) { + ASSERT_EQ(ex.message(), "The value can not be empty"); + } + } + + TEST(PboNodeTransactionTest, SetTitle_Throws_If_Node_With_This_Name_Exists) { + PboNode node("node.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("f1.txt")); + PboNode* f2 = node.createHierarchy(PboPath("f2.txt")); + + const QSharedPointer tran = f2->beginTransaction(); + try { + tran->setTitle("f1.txt"); + FAIL() << "Should have not reached this line"; + } + catch (ValidationException& ex) { + ASSERT_EQ(ex.message(), "The item with this name already exists"); + } + } + + TEST(PboNodeTransactionTest, SetTitle_Renames_Node) { + PboNode node("node.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("f1.txt")); + PboNode* f2 = node.createHierarchy(PboPath("f2.txt")); + + QSharedPointer tran = f2->beginTransaction(); + tran->setTitle("f3.txt"); + tran->commit(); + tran.clear(); + + ASSERT_EQ(f2->title(), "f3.txt"); + } +} diff --git a/pbom/domain/binarysource.cpp b/pbom/domain/binarysource.cpp index 947fad6..2aefc11 100644 --- a/pbom/domain/binarysource.cpp +++ b/pbom/domain/binarysource.cpp @@ -1,31 +1 @@ #include "binarysource.h" -#include -#include "io/pboioexception.h" - -namespace pboman3 { - BinarySource::BinarySource(QString path) - : path_(std::move(path)) { - file_ = new QFile(path_); - } - - BinarySource::~BinarySource() { - delete file_; - } - - void BinarySource::open() const { - if (!file_->open(QIODeviceBase::ReadOnly)) - throw PboIoException("Can not open the file. Check you have enough permissions and the file is not locked by another process.", path_); - } - - void BinarySource::close() const { - file_->close(); - } - - bool BinarySource::isOpen() const { - return file_->isOpen(); - } - - const QString& BinarySource::path() const { - return path_; - } -} diff --git a/pbom/domain/binarysource.h b/pbom/domain/binarysource.h index 356f69b..7204fb5 100644 --- a/pbom/domain/binarysource.h +++ b/pbom/domain/binarysource.h @@ -6,22 +6,22 @@ namespace pboman3 { class BinarySource { - public: - BinarySource(QString path); - virtual ~BinarySource(); + protected: + ~BinarySource() = default; + public: virtual void writeToPbo(QFileDevice* targetFile, const Cancel& cancel) = 0; virtual void writeToFs(QFileDevice* targetFile, const Cancel& cancel) = 0; - void open() const; + virtual void open() const = 0; - void close() const; + virtual void close() const = 0; - bool isOpen() const; + virtual bool isOpen() const = 0; - const QString& path() const; + virtual const QString& path() const = 0; virtual qint32 readOriginalSize() const = 0; @@ -30,13 +30,7 @@ namespace pboman3 { virtual bool isCompressed() const = 0; friend QDebug operator <<(QDebug debug, const BinarySource& bs) { - return debug << "BinarySource(Compressed=" << bs.isCompressed() << ", Path=" << bs.path_ << ")"; + return debug << "BinarySource()"; } - - protected: - QFileDevice* file_; - - private: - QString path_; }; } diff --git a/pbom/domain/documentheader.cpp b/pbom/domain/documentheader.cpp index 8a33652..f223a96 100644 --- a/pbom/domain/documentheader.cpp +++ b/pbom/domain/documentheader.cpp @@ -1,10 +1,18 @@ #include "documentheader.h" +#include "validationexception.h" namespace pboman3::domain { - DocumentHeader::DocumentHeader(QString name, QString value) { - validateName(name); - name_ = std::move(name); - value_ = std::move(value); + DocumentHeader::DocumentHeader(QString name, QString value) + : name_(std::move(name)), + value_(std::move(value)) { + validateName(name_); + } + + DocumentHeader::DocumentHeader(const InternalData& data) + : name_(data.name), + value_(data.value) { + //this is a repository constructor + //it does no validation but must be used only from the persistence layer } const QString& DocumentHeader::name() const { @@ -25,5 +33,15 @@ namespace pboman3::domain { } void DocumentHeader::validateName(const QString& name) { + if (name.isEmpty()) + throw ValidationException("The name can not be empty"); + } + + bool operator==(const DocumentHeader& h1, const DocumentHeader& h2) { + return h1.name_ == h2.name_ && h1.value_ == h2.value_; + } + + bool operator!=(const DocumentHeader& h1, const DocumentHeader& h2) { + return !(h1 == h2); } } diff --git a/pbom/domain/documentheader.h b/pbom/domain/documentheader.h index f821707..513c981 100644 --- a/pbom/domain/documentheader.h +++ b/pbom/domain/documentheader.h @@ -7,6 +7,13 @@ namespace pboman3::domain { public: DocumentHeader(QString name, QString value); + struct InternalData { + QString name; + QString value; + }; + + explicit DocumentHeader(const InternalData& data);//Repository Ctor + const QString& name() const; void setName(const QString& name); @@ -15,6 +22,10 @@ namespace pboman3::domain { void setValue(const QString& value); + friend bool operator==(const DocumentHeader& h1, const DocumentHeader& h2); + + friend bool operator!=(const DocumentHeader& h1, const DocumentHeader& h2); + private: QString name_; QString value_; diff --git a/pbom/domain/documentheaders.cpp b/pbom/domain/documentheaders.cpp index b1cb929..d280bd5 100644 --- a/pbom/domain/documentheaders.cpp +++ b/pbom/domain/documentheaders.cpp @@ -1,7 +1,16 @@ #include "documentheaders.h" +#include "documentheaderstransaction.h" #include "util/exception.h" namespace pboman3::domain { + DocumentHeaders::DocumentHeaders() = default; + + DocumentHeaders::DocumentHeaders(QList> headers) + : headers_(std::move(headers)) { + //this is a repository constructor + //it does no validation but must be used only from the persistence layer + } + qsizetype DocumentHeaders::count() const { return headers_.count(); } @@ -10,36 +19,6 @@ namespace pboman3::domain { return headers_.at(index).get(); } - void DocumentHeaders::add(const QString& name, const QString& value) { - const QSharedPointer header(new DocumentHeader(name, value)); - headers_.append(header); - emit headerAdded(header.get(), headers_.count() - 1); - } - - void DocumentHeaders::remove(const DocumentHeader* header) { - const qsizetype index = getIndex(header); - headers_.removeAt(index); - emit headerRemoved(header, index); - } - - void DocumentHeaders::moveUp(const DocumentHeader* header) { - const qsizetype currentIndex = getIndex(header); - if (currentIndex == 0) - throw InvalidOperationException("The header is already at the top of the list"); - - const qsizetype newIndex = currentIndex - 1; - headers_.move(currentIndex, newIndex); - } - - void DocumentHeaders::moveDown(const DocumentHeader* header) { - const qsizetype currentIndex = getIndex(header); - const qsizetype newIndex = currentIndex + 1; - if (newIndex >= count()) - throw InvalidOperationException("The header is already at the bottom of the list"); - - headers_.move(currentIndex, newIndex); - } - QPointerListIterator DocumentHeaders::begin() { return QPointerListIterator(headers_.begin()); } @@ -56,13 +35,32 @@ namespace pboman3::domain { return QPointerListConstIterator(headers_.constEnd()); } - qsizetype DocumentHeaders::getIndex(const DocumentHeader* header) const { - qsizetype index = 0; - for (const QSharedPointer& h : headers_) { - if (h.get() == header) - return index; - index++; + QSharedPointer DocumentHeaders::beginTransaction() { + return QSharedPointer(new DocumentHeadersTransaction(headers_, this)); + } + + void DocumentHeaders::setHeaders(QList> headers) { + if (areDifferent(headers_, headers)) { + headers_ = std::move(headers); + emit headersChanged(); } - throw InvalidOperationException("The header does not belong to this instance"); + } + + bool DocumentHeaders::areDifferent(const QList>& list1, const QList>& list2) { + if (list1.count() != list2.count()) + return true; + + auto it1 = list1.begin(); + auto it2 = list2.begin(); + + while (it1 != list1.end()) { + if (**it1 != **it2) + return true; + + ++it1; + ++it2; + } + + return false; } } diff --git a/pbom/domain/documentheaders.h b/pbom/domain/documentheaders.h index 094b07c..e31bfbc 100644 --- a/pbom/domain/documentheaders.h +++ b/pbom/domain/documentheaders.h @@ -4,21 +4,19 @@ #include "util/qpointerlistiterator.h" namespace pboman3::domain { + class DocumentHeadersTransaction; + class DocumentHeaders : public QObject { Q_OBJECT public: - qsizetype count() const; - - const DocumentHeader* at(qsizetype index) const; - - void add(const QString& name, const QString& value); + DocumentHeaders(); - void remove(const DocumentHeader* header); + explicit DocumentHeaders(QList> headers);//Repository Ctor - void moveUp(const DocumentHeader* header); + qsizetype count() const; - void moveDown(const DocumentHeader* header); + const DocumentHeader* at(qsizetype index) const; QPointerListIterator begin(); @@ -28,16 +26,18 @@ namespace pboman3::domain { QPointerListConstIterator end() const; - signals: - void headerAdded(const DocumentHeader* header, qsizetype index); + QSharedPointer beginTransaction(); - void headerRemoved(const DocumentHeader* header, qsizetype index); + friend DocumentHeadersTransaction; - void headerMoved(const DocumentHeader* header, qsizetype prevIndex, qsizetype newIndex); + signals: + void headersChanged(); private: QList> headers_; - qsizetype getIndex(const DocumentHeader* header) const; + void setHeaders(QList> headers); + + static bool areDifferent(const QList>& list1, const QList>& list2); }; } diff --git a/pbom/domain/documentheaderstransaction.cpp b/pbom/domain/documentheaderstransaction.cpp new file mode 100644 index 0000000..3a4864b --- /dev/null +++ b/pbom/domain/documentheaderstransaction.cpp @@ -0,0 +1,52 @@ +#include "documentheaders.h" +#include "documentheaderstransaction.h" +#include "util/exception.h" + +namespace pboman3::domain { + DocumentHeadersTransaction::DocumentHeadersTransaction(QList> headers, DocumentHeaders* parent) + : committed_(false), + headers_(std::move(headers)), + parent_(parent) { + } + + DocumentHeadersTransaction::~DocumentHeadersTransaction() { + if (committed_) { + parent_->setHeaders(std::move(headers_)); + } + } + + void DocumentHeadersTransaction::commit() { + committed_ = true; + } + + qsizetype DocumentHeadersTransaction::count() const { + return headers_.count(); + } + + const DocumentHeader* DocumentHeadersTransaction::at(qsizetype index) const { + return headers_.at(index).data(); + } + + void DocumentHeadersTransaction::add(const QString& name, const QString& value) { + throwIfCommitted(); + const QSharedPointer header(new DocumentHeader(name, value)); + headers_.append(header); + } + + void DocumentHeadersTransaction::clear() { + throwIfCommitted(); + headers_.clear(); + } + + QPointerListIterator DocumentHeadersTransaction::begin() { + return QPointerListIterator(headers_.begin()); + } + + QPointerListIterator DocumentHeadersTransaction::end() { + return QPointerListIterator(headers_.end()); + } + + void DocumentHeadersTransaction::throwIfCommitted() const { + if (committed_) throw InvalidOperationException("You must not modify a committed transaction."); + } +} diff --git a/pbom/domain/documentheaderstransaction.h b/pbom/domain/documentheaderstransaction.h new file mode 100644 index 0000000..a56a524 --- /dev/null +++ b/pbom/domain/documentheaderstransaction.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include "domain/documentheader.h" +#include "domain/documentheaders.h" + +namespace pboman3::domain { + class DocumentHeadersTransaction : public QObject { + Q_OBJECT + + public: + explicit DocumentHeadersTransaction(QList> headers, DocumentHeaders* parent); + + ~DocumentHeadersTransaction() override; + + qsizetype count() const; + + const DocumentHeader* at(qsizetype index) const; + + void add(const QString& name, const QString& value); + + void clear(); + + QPointerListIterator begin(); + + QPointerListIterator end(); + + void commit(); + + private: + bool committed_; + QList> headers_; + DocumentHeaders* parent_; + + void throwIfCommitted() const; + }; +} diff --git a/pbom/domain/documentnode.cpp b/pbom/domain/documentnode.cpp deleted file mode 100644 index a77ac65..0000000 --- a/pbom/domain/documentnode.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include "documentnode.h" -#include -#include "util/exception.h" -#include "validationexception.h" - -namespace pboman3::domain { - DocumentNode::DocumentNode(QString title, PboNodeType nodeType, DocumentNode* parentNode) - : AbstractNode(parentNode), - nodeType_(nodeType), - title_(std::move(title)) { - if (title_.isEmpty()) - throw ValidationException("Title must not be empty"); - } - - DocumentNode* DocumentNode::createHierarchy(const PboPath& entryPath) { - return createHierarchy(entryPath, ConflictResolution::Copy, false); - } - - DocumentNode* DocumentNode::createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict) { - return createHierarchy(entryPath, onConflict, true); - } - - void DocumentNode::removeFromHierarchy() { - if (!parentNode_) - throw InvalidOperationException("Must not be called on the root node"); - - DocumentNode* node = this; - while (node->parentNode_ - && node->parentNode_->nodeType_ != PboNodeType::Container - && node->parentNode_->count() == 1) { - node = node->parentNode_; - } - - DocumentNode* p = node->parentNode_; - assert(p && "Must not be null"); - - const qsizetype index = p->children_.indexOf(node); - node->parentNode_->children_.remove(index, 1); - - emit p->childRemoved(index); - p->emitHierarchyChanged(); - } - - bool DocumentNode::isPathConflict(const PboPath& path) const { - const DocumentNode* node = this; - for (qsizetype i = 0; i < path.length() - 1; i++) { - const DocumentNode* folder = node->findChild(path.at(i)); - if (folder) { - if (folder->nodeType_ == PboNodeType::File) - return true; - node = folder; - } else { - return false; - } - } - - const DocumentNode* file = node->findChild(path.last()); - return file; - } - - const QString& DocumentNode::title() const { - return title_; - } - - void DocumentNode::setTitle(QString title) { - if (title != title_) { - // if (const TitleError err = verifyTitle(title); err != nullptr) - // throw InvalidOperationException(err); - - title_ = std::move(title); - emit titleChanged(title_); - - if (parentNode_) { - const qsizetype prevIndex = parentNode_->children_.indexOf(this); - const qsizetype newIndex = parentNode_->getChildListIndex(this); - if (prevIndex != newIndex) { - parentNode_->children_.move(prevIndex, newIndex); - emit parentNode_->childMoved(prevIndex, newIndex); - } - - } - emitHierarchyChanged(); - } - } - - PboNodeType DocumentNode::nodeType() const { - return nodeType_; - } - - DocumentNode* DocumentNode::get(const PboPath& path) { - DocumentNode* result = this; - auto it = path.begin(); - while (it != path.end()) { - result = result->findChild(*it); - if (!result) - return nullptr; - ++it; - } - return result; - } - - PboPath DocumentNode::makePath() const { - PboPath path; - path.reserve(depth()); - - const DocumentNode* p = this; - while (p->parentNode_) { - path.prepend(p->title_); - p = p->parentNode_; - } - return path; - } - - bool DocumentNode::operator<(const DocumentNode& node) const { - if (nodeType_ == node.nodeType_) { - if (nodeType_ == PboNodeType::Folder) { - return title_ < node.title_; //folders alphabetically - } - QString name1, ext1; - SplitByNameAndExtension(title_, name1, ext1); - QString name2, ext2; - SplitByNameAndExtension(node.title_, name2, ext2); - if (name1 == name2) { - return ext1 < ext2; //files alphabetically by extension - } - return name1 < name2; //files alphabetically by name - } - - return nodeType_ > node.nodeType_; //folders first - } - - DocumentNode* DocumentNode::createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, - bool emitEvents) { - DocumentNode* node = this; - for (qsizetype i = 0; i < entryPath.length() - 1; i++) { - DocumentNode* folder = node->findChild(entryPath.at(i)); - if (!folder) { - folder = node->createChild(entryPath.at(i), PboNodeType::Folder); - } else if (folder->nodeType_ == PboNodeType::File) { - switch (onConflict) { - case ConflictResolution::Replace: { - DocumentNode* replaceFolder = folder->parentNode_->createChild( - entryPath.last(), PboNodeType::File); - folder->removeFromHierarchy(); - folder = replaceFolder; - break; - } - case ConflictResolution::Copy: { - const QString folderTitle = pickFolderTitle(node, entryPath.at(i)); - folder = node->createChild(folderTitle, PboNodeType::Folder); - break; - } - default: - throw InvalidOperationException("Unsupported conflict resolution strategy"); - } - } - node = folder; - } - - DocumentNode* file = node->findChild(entryPath.last()); - if (!file) { - file = node->createChild(entryPath.last(), PboNodeType::File); - if (emitEvents) - emitHierarchyChanged(); - } else { - switch (onConflict) { - case ConflictResolution::Replace: { - DocumentNode* replaceFile = file->parentNode_->createChild(entryPath.last(), PboNodeType::File); - //this emits "hierarchyChanged"; this branch never called with emitEvents==false - file->removeFromHierarchy(); - file = replaceFile; - break; - } - case ConflictResolution::Copy: { - const QString fileTitle = pickFileTitle(node, entryPath.last()); - file = node->createChild(fileTitle, PboNodeType::File); - if (emitEvents) - emitHierarchyChanged(); - break; - } - default: - throw InvalidOperationException("Unsupported conflict resolution strategy"); - } - } - - return file; - } - - DocumentNode* DocumentNode::findChild(const QString& title) const { - for (const QSharedPointer& child : children_) { - if (child->title_ == title) - return child.get(); - } - return nullptr; - } - - QString DocumentNode::pickFolderTitle(const DocumentNode* parent, const QString& expectedTitle) const { - int index = 1; - QString attemptTitle = formatFolderTitleCopy(expectedTitle, index); - auto it = parent->children_.constBegin(); - while (it != parent->children_.constEnd()) { - if ((*it)->title() == attemptTitle && (*it)->nodeType_ == PboNodeType::File) { - index++; - attemptTitle = formatFolderTitleCopy(expectedTitle, index); - it = parent->children_.constBegin(); - } else { - ++it; - } - } - return attemptTitle; - } - - QString DocumentNode::pickFileTitle(const DocumentNode* parent, const QString& expectedTitle) const { - int index = 1; - QString attemptTitle = formatFileTitleCopy(expectedTitle, index); - auto it = parent->children_.constBegin(); - while (it < parent->children_.constEnd()) { - if ((*it)->title() == attemptTitle) { - index++; - attemptTitle = formatFileTitleCopy(expectedTitle, index); - it = parent->children_.constBegin(); - } else { - ++it; - } - } - return attemptTitle; - } - - QString DocumentNode::formatFolderTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const { - return expectedTitle + "(" + QString::number(copyIndex) + ")"; - } - - QString DocumentNode::formatFileTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const { - QString expectedName, expectedExt; - SplitByNameAndExtension(expectedTitle, expectedName, expectedExt); - return expectedName + "(" + QString::number(copyIndex) + ")." + expectedExt; - } - - DocumentNode* DocumentNode::createChild(const QString& title, PboNodeType nodeType) { - const auto child = QSharedPointer(new DocumentNode(title, nodeType, this)); - const qsizetype index = getChildListIndex(child.get()); - children_.insert(index, child); - emit childCreated(child.get(), index); - return child.get(); - } - - qsizetype DocumentNode::getChildListIndex(const DocumentNode* node) const { - qsizetype index = 0; - for (const QSharedPointer& sibling : children_) { - if (sibling != node) { - if (*sibling < *node) { - index++; - } else { - break; - } - } - } - return index; - } - - void DocumentNode::emitHierarchyChanged() { - DocumentNode* parent = this; - while (parent->parentNode_) { - parent = parent->parentNode_; - } - emit parent->hierarchyChanged(); - } - - QDebug operator<<(QDebug debug, const DocumentNode& node) { - return debug << "DocumentNode(" << node.makePath() << ")"; - } -} diff --git a/pbom/domain/documentnode.h b/pbom/domain/documentnode.h deleted file mode 100644 index 69d16b5..0000000 --- a/pbom/domain/documentnode.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include -#include "pbonodetype.h" -#include "pbopath.h" -#include "conflictresolution.h" -#include "binarysource.h" -#include "abstractnode.h" - -namespace pboman3::domain { - class DocumentNode final : public AbstractNode { - Q_OBJECT - - public: - QSharedPointer binarySource; - - DocumentNode(QString title, PboNodeType nodeType, DocumentNode* parentNode); - - DocumentNode* createHierarchy(const PboPath& entryPath); - - DocumentNode* createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict); - - void removeFromHierarchy(); - - bool isPathConflict(const PboPath& path) const; - - const QString& title() const; - - void setTitle(QString title); - - PboNodeType nodeType() const; - - DocumentNode* get(const PboPath& path); - - PboPath makePath() const; - - bool operator <(const DocumentNode& node) const; - - friend QDebug operator <<(QDebug debug, const DocumentNode& node); - - signals: - void titleChanged(const QString& title); - - void childCreated(DocumentNode* child, qsizetype index); - - void childMoved(qsizetype prevIndex, qsizetype newIndex); - - void childRemoved(qsizetype index); - - void hierarchyChanged(); - - private: - PboNodeType nodeType_; - QString title_; - - DocumentNode* createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, bool emitEvents); - - DocumentNode* findChild(const QString& title) const; - - QString pickFolderTitle(const DocumentNode* parent, const QString& expectedTitle) const; - - QString pickFileTitle(const DocumentNode* parent, const QString& expectedTitle) const; - - QString formatFolderTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const; - - QString formatFileTitleCopy(const QString& expectedTitle, qsizetype copyIndex) const; - - DocumentNode* createChild(const QString& title, PboNodeType nodeType); - - qsizetype getChildListIndex(const DocumentNode* node) const; - - void emitHierarchyChanged(); - }; -} diff --git a/pbom/domain/pbodocument.cpp b/pbom/domain/pbodocument.cpp index ad208c2..30ae843 100644 --- a/pbom/domain/pbodocument.cpp +++ b/pbom/domain/pbodocument.cpp @@ -4,14 +4,24 @@ namespace pboman3::domain { PboDocument::PboDocument(QString pboName) { headers_ = QSharedPointer(new DocumentHeaders); - root_ = QSharedPointer(new DocumentNode(std::move(pboName), PboNodeType::Container, nullptr)); + root_ = QSharedPointer(new PboNode(std::move(pboName), PboNodeType::Container, nullptr)); + setupConnections(); + } + + PboDocument::PboDocument(QString pboName, QList> headers, QByteArray signature) + : signature_(std::move(signature)) { + root_ = QSharedPointer(new PboNode(std::move(pboName), PboNodeType::Container, nullptr)); + headers_ = QSharedPointer(new DocumentHeaders(std::move(headers))); + setupConnections(); + //this is a repository constructor + //it does no validation but must be used only from the persistence layer } DocumentHeaders* PboDocument::headers() const { return headers_.get(); } - DocumentNode* PboDocument::root() const { + PboNode* PboDocument::root() const { return root_.get(); } @@ -24,4 +34,20 @@ namespace pboman3::domain { throw ValidationException("The signature must be 20 bytes long"); signature_ = std::move(signature); } + + void PboDocument::setupConnections() { + connect(root_.get(), &PboNode::hierarchyChanged, [this] { + signature_.clear(); + emit changed(); + }); + + connect(root_.get(), &PboNode::titleChanged, [this](const QString& title) { + emit titleChanged(title); + }); + + connect(headers_.get(), &DocumentHeaders::headersChanged, [this]() { + signature_.clear(); + emit changed(); + }); + } } diff --git a/pbom/domain/pbodocument.h b/pbom/domain/pbodocument.h index 8e578e1..dc63e5b 100644 --- a/pbom/domain/pbodocument.h +++ b/pbom/domain/pbodocument.h @@ -1,7 +1,7 @@ #pragma once #include "documentheaders.h" -#include "documentnode.h" +#include "pbonode.h" namespace pboman3::domain { class PboDocument : public QObject { @@ -10,17 +10,26 @@ namespace pboman3::domain { public: PboDocument(QString pboName); + PboDocument(QString pboName, QList> headers, QByteArray signature);//Repository Ctor + DocumentHeaders* headers() const; - DocumentNode* root() const; + PboNode* root() const; const QByteArray& signature() const; void setSignature(QByteArray signature); + signals: + void changed(); + + void titleChanged(const QString& title); + private: QSharedPointer headers_; - QSharedPointer root_; + QSharedPointer root_; QByteArray signature_; + + void setupConnections(); }; } diff --git a/pbom/domain/pbonode.cpp b/pbom/domain/pbonode.cpp index 10b0029..634fe4a 100644 --- a/pbom/domain/pbonode.cpp +++ b/pbom/domain/pbonode.cpp @@ -1,12 +1,16 @@ #include "pbonode.h" #include #include "util/exception.h" +#include "validationexception.h" +#include "pbonodetransaction.h" -namespace pboman3 { +namespace pboman3::domain { PboNode::PboNode(QString title, PboNodeType nodeType, PboNode* parentNode) : AbstractNode(parentNode), nodeType_(nodeType), title_(std::move(title)) { + if (title_.isEmpty()) + throw ValidationException("Title must not be empty"); } PboNode* PboNode::createHierarchy(const PboPath& entryPath) { @@ -59,36 +63,6 @@ namespace pboman3 { return title_; } - void PboNode::setTitle(QString title) { - if (title != title_) { - if (const TitleError err = verifyTitle(title); err != nullptr) - throw InvalidOperationException(err); - - title_ = std::move(title); - emit titleChanged(title_); - - if (parentNode_) { - const qsizetype prevIndex = parentNode_->children_.indexOf(this); - const qsizetype newIndex = parentNode_->getChildListIndex(this); - if (prevIndex != newIndex) { - parentNode_->children_.move(prevIndex, newIndex); - emit parentNode_->childMoved(prevIndex, newIndex); - } - - } - emitHierarchyChanged(); - } - } - - TitleError PboNode::verifyTitle(const QString& title) const { - if (title == nullptr || title.isEmpty()) - return "The value can not be empty"; - if (!parentNode_) - return ""; - PboNode* existing = parentNode_->findChild(title); - return existing && existing != this ? "The item with this name already exists" : ""; - } - PboNodeType PboNode::nodeType() const { return nodeType_; } @@ -117,6 +91,10 @@ namespace pboman3 { return path; } + QSharedPointer PboNode::beginTransaction() { + return QSharedPointer(new PboNodeTransaction(this)); + } + bool PboNode::operator<(const PboNode& node) const { if (nodeType_ == node.nodeType_) { if (nodeType_ == PboNodeType::Folder) { @@ -136,7 +114,7 @@ namespace pboman3 { } PboNode* PboNode::createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, - bool emitEvents) { + bool emitEvents) { PboNode* node = this; for (qsizetype i = 0; i < entryPath.length() - 1; i++) { PboNode* folder = node->findChild(entryPath.at(i)); @@ -272,6 +250,25 @@ namespace pboman3 { emit parent->hierarchyChanged(); } + void PboNode::setTitle(QString title) { + if (title != title_) { + + title_ = std::move(title); + emit titleChanged(title_); + + if (parentNode_) { + const qsizetype prevIndex = parentNode_->children_.indexOf(this); + const qsizetype newIndex = parentNode_->getChildListIndex(this); + if (prevIndex != newIndex) { + parentNode_->children_.move(prevIndex, newIndex); + emit parentNode_->childMoved(prevIndex, newIndex); + } + + } + emitHierarchyChanged(); + } + } + QDebug operator<<(QDebug debug, const PboNode& node) { return debug << "PboNode(" << node.makePath() << ")"; } diff --git a/pbom/domain/pbonode.h b/pbom/domain/pbonode.h index 56cf779..83007c1 100644 --- a/pbom/domain/pbonode.h +++ b/pbom/domain/pbonode.h @@ -7,8 +7,8 @@ #include "binarysource.h" #include "abstractnode.h" -namespace pboman3 { - typedef QString TitleError; +namespace pboman3::domain { + class PboNodeTransaction; class PboNode final : public AbstractNode { Q_OBJECT @@ -28,16 +28,14 @@ namespace pboman3 { const QString& title() const; - void setTitle(QString title); - - TitleError verifyTitle(const QString& title) const; - PboNodeType nodeType() const; PboNode* get(const PboPath& path); PboPath makePath() const; + QSharedPointer beginTransaction(); + bool operator <(const PboNode& node) const; friend QDebug operator <<(QDebug debug, const PboNode& node); @@ -74,5 +72,9 @@ namespace pboman3 { qsizetype getChildListIndex(const PboNode* node) const; void emitHierarchyChanged(); + + void setTitle(QString title); + + friend PboNodeTransaction; }; } diff --git a/pbom/domain/pbonodetransaction.cpp b/pbom/domain/pbonodetransaction.cpp new file mode 100644 index 0000000..bafde22 --- /dev/null +++ b/pbom/domain/pbonodetransaction.cpp @@ -0,0 +1,45 @@ +#include "pbonodetransaction.h" +#include "validationexception.h" +#include "util/exception.h" + +namespace pboman3::domain { + PboNodeTransaction::PboNodeTransaction(PboNode* node) + : committed_(false), + node_(node), + title_(node->title()) { + } + + PboNodeTransaction::~PboNodeTransaction() { + if (committed_) + node_->setTitle(std::move(title_)); + } + + const QString& PboNodeTransaction::title() const { + return title_; + } + + void PboNodeTransaction::setTitle(QString title) { + if (committed_) + throw InvalidOperationException("The transaction has already committed"); + + if (title != title_) { + if (const QString err = verifyTitle(title); !err.isEmpty()) + throw ValidationException(err); + } + + title_ = std::move(title); + } + + void PboNodeTransaction::commit() { + committed_ = true; + } + + QString PboNodeTransaction::verifyTitle(const QString& title) const { + if (title.isEmpty()) + return "The value can not be empty"; + if (!node_->parentNode_) + return ""; + PboNode* existing = node_->parentNode_->findChild(title); + return existing && existing != node_ ? "The item with this name already exists" : ""; + } +} diff --git a/pbom/domain/pbonodetransaction.h b/pbom/domain/pbonodetransaction.h new file mode 100644 index 0000000..9aee712 --- /dev/null +++ b/pbom/domain/pbonodetransaction.h @@ -0,0 +1,24 @@ +#pragma once + +#include "pbonode.h" + +namespace pboman3::domain { + class PboNodeTransaction { + public: + PboNodeTransaction(PboNode* node); + + ~PboNodeTransaction(); + + const QString& title() const; + + void setTitle(QString title); + + void commit(); + private: + bool committed_; + PboNode* node_; + QString title_; + + QString verifyTitle(const QString& title) const; + }; +} diff --git a/pbom/domain/pbonodetype.h b/pbom/domain/pbonodetype.h index 4fd15e5..c16b1a5 100644 --- a/pbom/domain/pbonodetype.h +++ b/pbom/domain/pbonodetype.h @@ -1,6 +1,6 @@ #pragma once -namespace pboman3 { +namespace pboman3::domain { enum class PboNodeType { File, Folder, diff --git a/pbom/io/CMakeLists.txt b/pbom/io/CMakeLists.txt index 526d3e0..409f19a 100644 --- a/pbom/io/CMakeLists.txt +++ b/pbom/io/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND PROJECT_SOURCES "io/bb/tempbackend.cpp" "io/bb/unpackbackend.cpp" "io/bb/unpacktaskbackend.cpp" + "io/bs/binarysourcebase.cpp" "io/bs/fslzhbinarysource.cpp" "io/bs/fsrawbinarysource.cpp" "io/bs/pbobinarysource.cpp" @@ -14,16 +15,16 @@ list(APPEND PROJECT_SOURCES "io/lzh/decompressioncontext.cpp" "io/lzh/lzh.cpp" "io/lzh/lzhdecompressionexception.cpp" + "io/diskaccessexception.cpp" "io/documentreader.cpp" "io/documentwriter.cpp" "io/pbodatastream.cpp" "io/pboentry.cpp" "io/pbofile.cpp" + "io/pbofileformatexception.cpp" "io/pboheader.cpp" "io/pboheaderio.cpp" - "io/pboheaderreader.cpp" - "io/pboioexception.cpp" - "io/pbowriter.cpp") + "io/pboheaderreader.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) @@ -45,7 +46,6 @@ list(APPEND TEST_SOURCES "io/__test__/pbofile_test.cpp" "io/__test__/pboheader_test.cpp" "io/__test__/pboheaderio_test.cpp" - "io/__test__/pboheaderreader_test.cpp" - "io/__test__/pbowriter_test.cpp") + "io/__test__/pboheaderreader_test.cpp") set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/io/__test__/documentwriter_test.cpp b/pbom/io/__test__/documentwriter_test.cpp index 76fd65b..d175cf9 100644 --- a/pbom/io/__test__/documentwriter_test.cpp +++ b/pbom/io/__test__/documentwriter_test.cpp @@ -3,6 +3,7 @@ #include #include #include "domain/pbodocument.h" +#include "domain/documentheaderstransaction.h" #include "io/pboheaderreader.h" #include "io/bs/fsrawbinarysource.h" @@ -26,14 +27,17 @@ namespace pboman3::io::test { PboDocument document("file.pbo"); //pbo headers - document.headers()->add("h1", "v1"); - document.headers()->add("h2", "v2"); + QSharedPointer tran = document.headers()->beginTransaction(); + tran->add("h1", "v1"); + tran->add("h2", "v2"); + tran->commit(); + tran.clear(); //pbo entries with content - DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); n1->binarySource->open(); - DocumentNode* n2 = document.root()->createHierarchy(PboPath("f2/e2.txt")); + PboNode* n2 = document.root()->createHierarchy(PboPath("f2/e2.txt")); n2->binarySource = QSharedPointer(new FsRawBinarySource(e2.fileName())); n2->binarySource->open(); @@ -100,7 +104,7 @@ namespace pboman3::io::test { //pbo document with content PboDocument document("file.pbo"); - DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); n1->binarySource->open(); @@ -134,7 +138,7 @@ namespace pboman3::io::test { //pbo content structure PboDocument document("file.pbo"); - DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); n1->binarySource->open(); @@ -161,7 +165,7 @@ namespace pboman3::io::test { //pbo content structure PboDocument document("file.pbo"); - DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); n1->binarySource->open(); @@ -191,7 +195,7 @@ namespace pboman3::io::test { //pbo content structure PboDocument document("file.pbo"); - DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); n1->binarySource->open(); @@ -220,7 +224,7 @@ namespace pboman3::io::test { //pbo content structure PboDocument document("file.pbo"); - DocumentNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); n1->binarySource->open(); diff --git a/pbom/io/__test__/pboheaderio_test.cpp b/pbom/io/__test__/pboheaderio_test.cpp index 5a3cfab..88e6938 100644 --- a/pbom/io/__test__/pboheaderio_test.cpp +++ b/pbom/io/__test__/pboheaderio_test.cpp @@ -4,7 +4,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { // ReSharper disable once CppInconsistentNaming class PboHeaderIOTest_ReadNextEntry : public testing::TestWithParam { }; diff --git a/pbom/io/__test__/pboheaderreader_test.cpp b/pbom/io/__test__/pboheaderreader_test.cpp index fee0d4f..e2f852d 100644 --- a/pbom/io/__test__/pboheaderreader_test.cpp +++ b/pbom/io/__test__/pboheaderreader_test.cpp @@ -3,9 +3,9 @@ #include #include "io/pbofile.h" #include "io/pboheaderio.h" -#include "io/pboioexception.h" +#include "io/pbofileformatexception.h" -namespace pboman3::test { +namespace pboman3::io::test { TEST(PboHeaderReaderTest, ReadFileHeader_Reads_File_Without_Headers_Without_Signature) { //build a mock pbo file QTemporaryFile t; @@ -201,7 +201,7 @@ namespace pboman3::test { //call the method PboFile p(t.fileName()); p.open(QIODeviceBase::ReadOnly); - ASSERT_THROW(PboHeaderReader::readFileHeader(&p), PboIoException); + ASSERT_THROW(PboHeaderReader::readFileHeader(&p), PboFileFormatException); p.close(); } @@ -215,7 +215,7 @@ namespace pboman3::test { //call the method PboFile p(t.fileName()); p.open(QIODeviceBase::ReadOnly); - ASSERT_THROW(PboHeaderReader::readFileHeader(&p), PboIoException); + ASSERT_THROW(PboHeaderReader::readFileHeader(&p), PboFileFormatException); p.close(); } @@ -235,7 +235,7 @@ namespace pboman3::test { //call the method PboFile r(t.fileName()); r.open(QIODeviceBase::ReadOnly); - ASSERT_THROW(PboHeaderReader::readFileHeader(&r), PboIoException); + ASSERT_THROW(PboHeaderReader::readFileHeader(&r), PboFileFormatException); r.close(); } @@ -266,7 +266,7 @@ namespace pboman3::test { //call the method PboFile r(t.fileName()); r.open(QIODeviceBase::ReadOnly); - ASSERT_THROW(PboHeaderReader::readFileHeader(&r), PboIoException); + ASSERT_THROW(PboHeaderReader::readFileHeader(&r), PboFileFormatException); r.close(); } } diff --git a/pbom/io/__test__/pbowriter_test.cpp b/pbom/io/__test__/pbowriter_test.cpp deleted file mode 100644 index c828788..0000000 --- a/pbom/io/__test__/pbowriter_test.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include "io/pbowriter.h" -#include -#include -#include "io/bs/fsrawbinarysource.h" -#include -#include -#include "io/pboheaderreader.h" - -namespace pboman3::test { - TEST(PboWriterTest, Write_Writes_File_With_Headers) { - //mock files contents - const QByteArray mockContent1(15, 1); - QTemporaryFile e1; - e1.open(); - e1.write(mockContent1); - e1.close(); - - const QByteArray mockContent2(10, 2); - QTemporaryFile e2; - e2.open(); - e2.write(mockContent2); - e2.close(); - - //pbo file - const QTemporaryDir temp; - const QString filePath = temp.filePath("file.pbo"); - - //pbo content structure - PboNode root("file.pbo", PboNodeType::Container, nullptr); - PboNode* n1 = root.createHierarchy(PboPath("e1.txt")); - n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); - n1->binarySource->open(); - PboNode* n2 = root.createHierarchy(PboPath("f2/e2.txt")); - n2->binarySource = QSharedPointer(new FsRawBinarySource(e2.fileName())); - n2->binarySource->open(); - - //pbo headers - HeadersModel headers; - headers.setData(QList({ - QSharedPointer(new PboHeader("h1", "v1")), - QSharedPointer(new PboHeader("h2", "v2")) - })); - - QByteArray signature; - - //write the file - PboWriter writer; - writer.usePath(filePath) - .useRoot(&root) - .useHeaders(&headers) - .copySignatureTo(&signature); - - writer.write([]() { return false; }); - - //assert the result - PboFile pbo(filePath); - pbo.open(QIODeviceBase::ReadOnly); - const PboFileHeader header = PboHeaderReader::readFileHeader(&pbo); - - //pbo header - ASSERT_EQ(header.headers.count(), 2); - ASSERT_EQ(header.headers.at(0)->name, "h1"); - ASSERT_EQ(header.headers.at(0)->value, "v1"); - ASSERT_EQ(header.headers.at(1)->name, "h2"); - ASSERT_EQ(header.headers.at(1)->value, "v2"); - - ASSERT_EQ(header.entries.count(), 2); - ASSERT_EQ(header.entries.at(0)->fileName(), "f2/e2.txt"); - ASSERT_EQ(header.entries.at(1)->fileName(), "e1.txt"); - - //pbo contents - pbo.seek(header.dataBlockStart); - - QByteArray contents; - contents.resize(mockContent2.size()); - pbo.read(contents.data(), contents.size()); - ASSERT_EQ(contents, mockContent2); - - contents.resize(mockContent1.size()); - pbo.read(contents.data(), contents.size()); - ASSERT_EQ(contents, mockContent1); - - //zero byte - QByteArray zero; - zero.resize(1); - pbo.read(zero.data(), zero.size()); - ASSERT_EQ(zero.at(0), 0); - - //signature - ASSERT_EQ(signature.size(), 20);//sha1 is 20 bytes - contents.resize(signature.size()); - pbo.read(contents.data(), contents.size()); - ASSERT_EQ(contents.size(), signature.size()); - - //file has ended - ASSERT_TRUE(pbo.atEnd()); - } - - class PboWriterTest: public testing::TestWithParam{}; - - TEST_P(PboWriterTest, Cleans_Files_On_Cancel) { - //mock files contents - const QByteArray mockContent1(15, 1); - QTemporaryFile e1; - e1.open(); - e1.write(mockContent1); - e1.close(); - - //pbo file - const QTemporaryDir temp; - const QString filePath = temp.filePath("file.pbo"); - - //pbo content structure - PboNode root("file.pbo", PboNodeType::Container, nullptr); - PboNode* n1 = root.createHierarchy(PboPath("e1.txt")); - n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); - n1->binarySource->open(); - - //pbo headers - HeadersModel headers; - - //write the file - PboWriter writer; - writer.usePath(filePath) - .useRoot(&root) - .useHeaders(&headers); - - int count = 0; - int expectedHitCount = GetParam();//experimental way - writer.write([&count, expectedHitCount]() { count++; return count > expectedHitCount - 1; }); - - ASSERT_FALSE(QFileInfo(filePath).exists()); - ASSERT_FALSE(QFileInfo(filePath + ".b").exists()); - } - - INSTANTIATE_TEST_SUITE_P(Write_Cleans_On_Cancel, PboWriterTest, testing::Range(1, 13)); - - TEST(PboWriterTest, Cleans_Temporary_Files_On_Write) { - //mock files contents - const QByteArray mockContent1(15, 1); - QTemporaryFile e1; - e1.open(); - e1.write(mockContent1); - e1.close(); - - //pbo file - const QTemporaryDir temp; - const QString filePath = temp.filePath("file.pbo"); - - //pbo content structure - PboNode root("file.pbo", PboNodeType::Container, nullptr); - PboNode* n1 = root.createHierarchy(PboPath("e1.txt")); - n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); - n1->binarySource->open(); - - //pbo headers - HeadersModel headers; - - //write the file - PboWriter writer; - writer.usePath(filePath) - .useRoot(&root) - .useHeaders(&headers); - - writer.write([]() { return false; }); - - ASSERT_TRUE(QFileInfo(filePath).exists()); - ASSERT_FALSE(QFileInfo(filePath + ".b").exists()); - } - - TEST(PboWriterTest, Write_Does_Not_Throw_If_No_Signature_Copy_Target_Set) { - //mock files contents - const QByteArray mockContent1(15, 1); - QTemporaryFile e1; - e1.open(); - e1.write(mockContent1); - e1.close(); - - //pbo file - const QTemporaryDir temp; - const QString filePath = temp.filePath("file.pbo"); - - //pbo content structure - PboNode root("file.pbo", PboNodeType::Container, nullptr); - PboNode* n1 = root.createHierarchy(PboPath("e1.txt")); - n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); - n1->binarySource->open(); - - //pbo headers - HeadersModel headers; - - //write the file - PboWriter writer; - writer.usePath(filePath) - .useRoot(&root) - .useHeaders(&headers);//don't specify signature target - - ASSERT_NO_THROW(writer.write([]() { return false; })); - } - - TEST(PboWriterTest, SuspendBinarySources_Closes_Binary_Sources_And_ResumeBinarySources_Opens_Them) { - // mock files contents - const QByteArray mockContent1(15, 1); - QTemporaryFile e1; - e1.open(); - e1.write(mockContent1); - e1.close(); - - const QByteArray mockContent2(10, 1); - QTemporaryFile e2; - e2.open(); - e2.write(mockContent2); - e2.close(); - - //pbo content structure - PboNode root("file.pbo", PboNodeType::Container, nullptr); - PboNode* node1 = root.createHierarchy(PboPath("f1/e1.txt")); - node1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); - node1->binarySource->open(); - PboNode* node2 = root.createHierarchy(PboPath("f1/e2.txt")); - node2->binarySource = QSharedPointer(new FsRawBinarySource(e2.fileName())); - node2->binarySource->open(); - - PboWriter writer; - - //Suspend the sources - writer.useRoot(&root) - .suspendBinarySources(); - - //Ensure sources closed - ASSERT_FALSE(node1->binarySource->isOpen()); - ASSERT_FALSE(node2->binarySource->isOpen()); - - //Resume the sources - writer.useRoot(&root) - .resumeBinarySources(); - - //Ensure sources opened - ASSERT_TRUE(node1->binarySource->isOpen()); - ASSERT_TRUE(node2->binarySource->isOpen()); - } -} diff --git a/pbom/io/bb/__test__/execbackend_test.cpp b/pbom/io/bb/__test__/execbackend_test.cpp index 208fdf6..9dda9bc 100644 --- a/pbom/io/bb/__test__/execbackend_test.cpp +++ b/pbom/io/bb/__test__/execbackend_test.cpp @@ -9,7 +9,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(ExecBackendTest, ExecSync_Extracts_New_File) { //dummy files QTemporaryFile f1; diff --git a/pbom/io/bb/__test__/nodefilesystem_test.cpp b/pbom/io/bb/__test__/nodefilesystem_test.cpp index c0eef50..b4f7a55 100644 --- a/pbom/io/bb/__test__/nodefilesystem_test.cpp +++ b/pbom/io/bb/__test__/nodefilesystem_test.cpp @@ -1,9 +1,9 @@ #include "io/bb/nodefilesystem.h" -#include "io/pboioexception.h" +#include "util/exception.h" #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(NodeFileSystemTest, AllocatePath_Creates_And_Sanitizes_Path_For_Node) { const QTemporaryDir dir; const NodeFileSystem fs(QDir(dir.path())); diff --git a/pbom/io/bb/__test__/tempbackend_test.cpp b/pbom/io/bb/__test__/tempbackend_test.cpp index 1874f92..ef03a4a 100644 --- a/pbom/io/bb/__test__/tempbackend_test.cpp +++ b/pbom/io/bb/__test__/tempbackend_test.cpp @@ -7,7 +7,7 @@ #include #include "io/bs/fsrawbinarysource.h" -namespace pboman3::test { +namespace pboman3::io::test { TEST(TempBackendTest, HddSync_Creates_Files_On_Disk) { //dummy files QTemporaryFile f1; diff --git a/pbom/io/bb/__test__/unpackbackend_test.cpp b/pbom/io/bb/__test__/unpackbackend_test.cpp index 0fc476e..dcef5f8 100644 --- a/pbom/io/bb/__test__/unpackbackend_test.cpp +++ b/pbom/io/bb/__test__/unpackbackend_test.cpp @@ -7,7 +7,7 @@ #include "io/bs/pbobinarysource.h" #include "util/exception.h" -namespace pboman3::test { +namespace pboman3::io::test { TEST(UnpackBackendTest, UnpackSync_Extracts_Nodes_To_File_System) { const QTemporaryDir dir; diff --git a/pbom/io/bb/binarybackend.cpp b/pbom/io/bb/binarybackend.cpp index 1f24ab1..bec0e97 100644 --- a/pbom/io/bb/binarybackend.cpp +++ b/pbom/io/bb/binarybackend.cpp @@ -1,11 +1,12 @@ #include "binarybackend.h" #include "unpackbackend.h" -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" #include "util/log.h" #define LOG(...) LOGGER("io/bb/BinaryBackend", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::io { + using namespace domain; #define TEMP_PBOMAN "pboman3" BinaryBackend::BinaryBackend(const QString& name) { @@ -20,9 +21,9 @@ namespace pboman3 { LOG(info, "Binary backend exec:", exec) if (!QDir::temp().mkpath(treePath)) - throw PboIoException("Could not create the folder.", tree.path()); + throw DiskAccessException("Could not create the folder.", tree.path()); if (!QDir::temp().mkpath(execPath)) - throw PboIoException("Could not create the folder.", exec.path()); + throw DiskAccessException("Could not create the folder.", exec.path()); tempBackend_ = QSharedPointer(new TempBackend(tree)); execBackend_ = QSharedPointer(new ExecBackend(exec)); diff --git a/pbom/io/bb/binarybackend.h b/pbom/io/bb/binarybackend.h index 41dc5e5..bf3f144 100644 --- a/pbom/io/bb/binarybackend.h +++ b/pbom/io/bb/binarybackend.h @@ -4,7 +4,9 @@ #include "tempbackend.h" #include "domain/pbonode.h" -namespace pboman3 { +namespace pboman3::io { + using namespace domain; + class BinaryBackend { public: BinaryBackend(const QString& name); diff --git a/pbom/io/bb/execbackend.cpp b/pbom/io/bb/execbackend.cpp index 0cff71e..46bdb8b 100644 --- a/pbom/io/bb/execbackend.cpp +++ b/pbom/io/bb/execbackend.cpp @@ -2,9 +2,9 @@ #include #include #include -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" -namespace pboman3 { +namespace pboman3::io { ExecBackend::ExecBackend(const QDir& folder) : folder_(folder) { if (!folder.exists()) @@ -85,11 +85,11 @@ namespace pboman3 { const QFileInfo fi(execPath); if (!folder_.mkpath(folder_.relativeFilePath(fi.dir().path()))) - throw PboIoException("Could not create the folder.", fi.dir().path()); + throw DiskAccessException("Could not create the folder.", fi.dir().path()); QFile file(execPath); if (!file.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly)) - throw PboIoException( + throw DiskAccessException( "Could not open file. Check you have enough permissions and the file is not locked by another process.", execPath); diff --git a/pbom/io/bb/execbackend.h b/pbom/io/bb/execbackend.h index 1d194a1..8c57301 100644 --- a/pbom/io/bb/execbackend.h +++ b/pbom/io/bb/execbackend.h @@ -3,7 +3,7 @@ #include "nodefilesystem.h" #include "domain/pbonode.h" -namespace pboman3 { +namespace pboman3::io { class ExecBackend { public: explicit ExecBackend(const QDir& folder); diff --git a/pbom/io/bb/nodefilesystem.cpp b/pbom/io/bb/nodefilesystem.cpp index 3dec634..a278a19 100644 --- a/pbom/io/bb/nodefilesystem.cpp +++ b/pbom/io/bb/nodefilesystem.cpp @@ -1,11 +1,13 @@ #include "nodefilesystem.h" #include "sanitizedstring.h" -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" #include "util/log.h" #define LOG(...) LOGGER("io/bb/NodeFileSystem", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::io { + using namespace domain; + NodeFileSystem::NodeFileSystem(const QDir& folder) : QObject(), folder_(folder) { @@ -69,7 +71,7 @@ namespace pboman3 { for (const PboNode* par : parents) { SanitizedString title(par->title()); if (!QDir(local.filePath(title)).exists() && !local.mkdir(title)) - throw PboIoException("Could not create the folder.", local.filePath(title)); + throw DiskAccessException("Could not create the folder.", local.filePath(title)); local.cd(title); } diff --git a/pbom/io/bb/nodefilesystem.h b/pbom/io/bb/nodefilesystem.h index 4b7d90f..4e1b6a0 100644 --- a/pbom/io/bb/nodefilesystem.h +++ b/pbom/io/bb/nodefilesystem.h @@ -3,7 +3,9 @@ #include #include "domain/pbonode.h" -namespace pboman3 { +namespace pboman3::io { + using namespace domain; + class NodeFileSystem : public QObject { Q_OBJECT public: diff --git a/pbom/io/bb/tempbackend.cpp b/pbom/io/bb/tempbackend.cpp index bc19176..5d8dcaf 100644 --- a/pbom/io/bb/tempbackend.cpp +++ b/pbom/io/bb/tempbackend.cpp @@ -1,8 +1,10 @@ #include "tempbackend.h" #include -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" + +namespace pboman3::io { + using namespace domain; -namespace pboman3 { TempBackend::TempBackend(const QDir& folder) : folder_(folder) { if (!folder.exists()) @@ -32,7 +34,7 @@ namespace pboman3 { const QString path = nodeFileSystem_->composeAbsolutePath(node); if (QFileInfo(path).exists()) { if (!QFile::remove(path)) { - throw PboIoException( + throw DiskAccessException( "Could not remove the file. Check you have enough permissions and the file is not locked by another process.", path); } @@ -45,7 +47,7 @@ namespace pboman3 { if (const QFileInfo fi(fsPath); !fi.exists()) { QFile file(fsPath); if (!file.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly)) - throw PboIoException( + throw DiskAccessException( "Could not open the file. Check you have enough permissions and the file is not locked by another process.", fsPath); diff --git a/pbom/io/bb/tempbackend.h b/pbom/io/bb/tempbackend.h index c9fcb6a..b341192 100644 --- a/pbom/io/bb/tempbackend.h +++ b/pbom/io/bb/tempbackend.h @@ -4,7 +4,9 @@ #include "domain/pbonode.h" #include "util/util.h" -namespace pboman3 { +namespace pboman3::io { + using namespace domain; + class TempBackend { public: TempBackend(const QDir& folder); diff --git a/pbom/io/bb/unpackbackend.cpp b/pbom/io/bb/unpackbackend.cpp index 9833eca..8a7537b 100644 --- a/pbom/io/bb/unpackbackend.cpp +++ b/pbom/io/bb/unpackbackend.cpp @@ -1,13 +1,13 @@ #include "unpackbackend.h" #include -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" #include "util/exception.h" #include "util/log.h" #define LOG(...) LOGGER("io/bb/UnpackBackend", __VA_ARGS__) -namespace pboman3 { - UnpackBackend::UnpackBackend(const QDir& folder) { +namespace pboman3::io { + io::UnpackBackend::UnpackBackend(const QDir& folder) { if (!folder.exists()) throw InvalidOperationException("The folder provided must exist"); nodeFileSystem_ = QSharedPointer(new NodeFileSystem(folder)); @@ -59,7 +59,7 @@ namespace pboman3 { QFile file(filePath); //WriteOnly won't work for LZH unpacking if (!file.open(QIODeviceBase::ReadWrite)) { LOG(critical, "Can not access the file:", file.fileName()) - throw PboIoException( + throw DiskAccessException( "Can not open the file. Check you have enough permissions and the file is not locked by another process.", file.fileName()); } diff --git a/pbom/io/bb/unpackbackend.h b/pbom/io/bb/unpackbackend.h index 13b0569..5051630 100644 --- a/pbom/io/bb/unpackbackend.h +++ b/pbom/io/bb/unpackbackend.h @@ -3,7 +3,7 @@ #include "nodefilesystem.h" #include "domain/pbonode.h" -namespace pboman3 { +namespace pboman3::io { class UnpackBackend { public: explicit UnpackBackend(const QDir& folder); diff --git a/pbom/io/bb/unpacktaskbackend.cpp b/pbom/io/bb/unpacktaskbackend.cpp index 961efbd..966021d 100644 --- a/pbom/io/bb/unpacktaskbackend.cpp +++ b/pbom/io/bb/unpacktaskbackend.cpp @@ -1,10 +1,10 @@ #include "unpacktaskbackend.h" -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" #include "util/log.h" #define LOG(...) LOGGER("io/bb/UnpackTaskBackend", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::io { UnpackTaskBackend::UnpackTaskBackend(const QDir& folder) : UnpackBackend(folder), onError_(nullptr), @@ -26,7 +26,7 @@ namespace pboman3 { QString filePath; try { filePath = nodeFileSystem_->allocatePath(rootNode, childNode); - } catch (const PboIoException& ex) { + } catch (const DiskAccessException& ex) { LOG(warning, ex) //remove the "." symbol from the end error(ex.message().left(ex.message().length() - 1) + " | " + ex.file()); diff --git a/pbom/io/bb/unpacktaskbackend.h b/pbom/io/bb/unpacktaskbackend.h index 0749cfc..9d8bef5 100644 --- a/pbom/io/bb/unpacktaskbackend.h +++ b/pbom/io/bb/unpacktaskbackend.h @@ -2,7 +2,7 @@ #include "unpackbackend.h" -namespace pboman3 { +namespace pboman3::io { class UnpackTaskBackend : public UnpackBackend { public: diff --git a/pbom/io/bs/__test__/fslzhbinarysource_test.cpp b/pbom/io/bs/__test__/fslzhbinarysource_test.cpp index 2994884..3168da4 100644 --- a/pbom/io/bs/__test__/fslzhbinarysource_test.cpp +++ b/pbom/io/bs/__test__/fslzhbinarysource_test.cpp @@ -3,7 +3,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(FsLzhBinarySourceTest, WriteToPbo_Compresses_Data) { //create a binary source QTemporaryFile sourceFile; diff --git a/pbom/io/bs/__test__/fsrawbinarysource_test.cpp b/pbom/io/bs/__test__/fsrawbinarysource_test.cpp index 3029d42..f74c694 100644 --- a/pbom/io/bs/__test__/fsrawbinarysource_test.cpp +++ b/pbom/io/bs/__test__/fsrawbinarysource_test.cpp @@ -4,7 +4,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(FsRawBinarySource, WriteToPbo_Writes_When_Buffer_Size_Less_Than_Data_Size) { //create a binary source QTemporaryFile sourceFile; diff --git a/pbom/io/bs/__test__/pbobinarysource_test.cpp b/pbom/io/bs/__test__/pbobinarysource_test.cpp index 1a0f676..9b523c5 100644 --- a/pbom/io/bs/__test__/pbobinarysource_test.cpp +++ b/pbom/io/bs/__test__/pbobinarysource_test.cpp @@ -3,7 +3,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(PboBinarySource, WriteToPbo_Writes_When_Buffer_Size_Less_Than_Data_Size) { //create a binary source QTemporaryFile sourceFile; diff --git a/pbom/io/bs/binarysourcebase.cpp b/pbom/io/bs/binarysourcebase.cpp new file mode 100644 index 0000000..867dc06 --- /dev/null +++ b/pbom/io/bs/binarysourcebase.cpp @@ -0,0 +1,31 @@ +#include "binarysourcebase.h" +#include +#include "io/diskaccessexception.h" + +namespace pboman3::io { + BinarySourceBase::BinarySourceBase(QString path) + : path_(std::move(path)) { + file_ = new QFile(path_); + } + + BinarySourceBase::~BinarySourceBase() { + delete file_; + } + + void BinarySourceBase::open() const { + if (!file_->open(QIODeviceBase::ReadOnly)) + throw DiskAccessException("Can not open the file. Check you have enough permissions and the file is not locked by another process.", path_); + } + + void BinarySourceBase::close() const { + file_->close(); + } + + bool BinarySourceBase::isOpen() const { + return file_->isOpen(); + } + + const QString& BinarySourceBase::path() const { + return path_; + } +} diff --git a/pbom/io/bs/binarysourcebase.h b/pbom/io/bs/binarysourcebase.h new file mode 100644 index 0000000..c7865bf --- /dev/null +++ b/pbom/io/bs/binarysourcebase.h @@ -0,0 +1,40 @@ +#pragma once + +#include "domain/binarysource.h" + +namespace pboman3::io { + class BinarySourceBase: public BinarySource { + public: + BinarySourceBase(QString path); + + virtual ~BinarySourceBase(); + + virtual void writeToPbo(QFileDevice* targetFile, const Cancel& cancel) = 0; + + virtual void writeToFs(QFileDevice* targetFile, const Cancel& cancel) = 0; + + void open() const override; + + void close() const override; + + bool isOpen() const override; + + const QString& path() const override; + + qint32 readOriginalSize() const override = 0; + + qint32 readTimestamp() const override = 0; + + bool isCompressed() const override = 0; + + friend QDebug operator <<(QDebug debug, const BinarySourceBase& bs) { + return debug << "BinarySourceBase(Compressed=" << bs.isCompressed() << ", Path=" << bs.path_ << ")"; + } + + protected: + QFileDevice* file_; + + private: + QString path_; + }; +} diff --git a/pbom/io/bs/fslzhbinarysource.cpp b/pbom/io/bs/fslzhbinarysource.cpp index 3d77ac5..ddf73f6 100644 --- a/pbom/io/bs/fslzhbinarysource.cpp +++ b/pbom/io/bs/fslzhbinarysource.cpp @@ -1,7 +1,7 @@ #include "fslzhbinarysource.h" #include "io/lzh/lzh.h" -namespace pboman3 { +namespace pboman3::io { FsLzhBinarySource::FsLzhBinarySource(QString path, qsizetype bufferSize) : FsRawBinarySource(std::move(path), bufferSize){ } diff --git a/pbom/io/bs/fslzhbinarysource.h b/pbom/io/bs/fslzhbinarysource.h index 96c2e56..669a91c 100644 --- a/pbom/io/bs/fslzhbinarysource.h +++ b/pbom/io/bs/fslzhbinarysource.h @@ -2,7 +2,7 @@ #include "fsrawbinarysource.h" -namespace pboman3 { +namespace pboman3::io { class FsLzhBinarySource: public FsRawBinarySource { public: FsLzhBinarySource(QString path, qsizetype bufferSize = 1024 * 1024); diff --git a/pbom/io/bs/fsrawbinarysource.cpp b/pbom/io/bs/fsrawbinarysource.cpp index b01749d..2107668 100644 --- a/pbom/io/bs/fsrawbinarysource.cpp +++ b/pbom/io/bs/fsrawbinarysource.cpp @@ -1,9 +1,9 @@ #include "fsrawbinarysource.h" #include -namespace pboman3 { +namespace pboman3::io { FsRawBinarySource::FsRawBinarySource(QString path, qsizetype bufferSize) - : BinarySource(std::move(path)), + : BinarySourceBase(std::move(path)), bufferSize_(bufferSize) { } diff --git a/pbom/io/bs/fsrawbinarysource.h b/pbom/io/bs/fsrawbinarysource.h index 4dde92b..bcf24f7 100644 --- a/pbom/io/bs/fsrawbinarysource.h +++ b/pbom/io/bs/fsrawbinarysource.h @@ -1,9 +1,9 @@ #pragma once -#include "domain/binarysource.h" +#include "binarysourcebase.h" -namespace pboman3 { - class FsRawBinarySource : public BinarySource { +namespace pboman3::io { + class FsRawBinarySource : public BinarySourceBase { public: FsRawBinarySource(QString path, qsizetype bufferSize = 1024 * 1024); diff --git a/pbom/io/bs/pbobinarysource.cpp b/pbom/io/bs/pbobinarysource.cpp index 92568c7..42688bd 100644 --- a/pbom/io/bs/pbobinarysource.cpp +++ b/pbom/io/bs/pbobinarysource.cpp @@ -1,11 +1,11 @@ #include "pbobinarysource.h" -#include "io/pboioexception.h" +#include "io/diskaccessexception.h" #include "io/lzh/lzh.h" #include "io/lzh/lzhdecompressionexception.h" -namespace pboman3 { +namespace pboman3::io { PboBinarySource::PboBinarySource(const QString& path, const PboDataInfo& dataInfo, qsizetype bufferSize) - : BinarySource(path), + : BinarySourceBase(path), dataInfo_(dataInfo), bufferSize_(bufferSize) { } @@ -36,7 +36,7 @@ namespace pboman3 { const qsizetype willRead = remaining > buf.size() ? buf.size() : remaining; const qint64 hasRead = file_->read(buf.data(), willRead); if (hasRead <= 0) - throw PboIoException("For some reason could not read from the file.", file_->fileName()); + throw DiskAccessException("For some reason could not read from the file.", file_->fileName()); targetFile->write(buf.data(), hasRead); remaining -= hasRead; } diff --git a/pbom/io/bs/pbobinarysource.h b/pbom/io/bs/pbobinarysource.h index be0c200..727ef72 100644 --- a/pbom/io/bs/pbobinarysource.h +++ b/pbom/io/bs/pbobinarysource.h @@ -1,8 +1,8 @@ #pragma once -#include "domain/binarysource.h" +#include "binarysourcebase.h" -namespace pboman3 { +namespace pboman3::io { struct PboDataInfo { qint32 originalSize; qint32 dataSize; @@ -11,7 +11,7 @@ namespace pboman3 { qint32 compressed; }; - class PboBinarySource : public BinarySource { + class PboBinarySource : public BinarySourceBase { public: PboBinarySource(const QString& path, const PboDataInfo& dataInfo, qsizetype bufferSize = 1024 * 1024); diff --git a/pbom/model/diskaccessexception.cpp b/pbom/io/diskaccessexception.cpp similarity index 71% rename from pbom/model/diskaccessexception.cpp rename to pbom/io/diskaccessexception.cpp index d16f9e3..a55cb41 100644 --- a/pbom/model/diskaccessexception.cpp +++ b/pbom/io/diskaccessexception.cpp @@ -1,17 +1,12 @@ #include "diskaccessexception.h" #include -namespace pboman3 { +namespace pboman3::io { DiskAccessException::DiskAccessException(QString message, QString file) : AppException(std::move(message)), file_(std::move(file)) { } - DiskAccessException::DiskAccessException(const PboIoException& ex): - AppException(ex.message()), - file_(ex.file()) { - } - void DiskAccessException::raise() const { throw *this; } diff --git a/pbom/model/diskaccessexception.h b/pbom/io/diskaccessexception.h similarity index 70% rename from pbom/model/diskaccessexception.h rename to pbom/io/diskaccessexception.h index 55190a0..251d3c3 100644 --- a/pbom/model/diskaccessexception.h +++ b/pbom/io/diskaccessexception.h @@ -1,15 +1,12 @@ #pragma once -#include "io/pboioexception.h" #include "util/exception.h" -namespace pboman3 { +namespace pboman3::io { class DiskAccessException : public AppException { public: DiskAccessException(QString message, QString file); - explicit DiskAccessException(const PboIoException& ex); - void raise() const override; QException* clone() const override; diff --git a/pbom/io/documentreader.cpp b/pbom/io/documentreader.cpp index 50cf4ec..f77748c 100644 --- a/pbom/io/documentreader.cpp +++ b/pbom/io/documentreader.cpp @@ -1,5 +1,6 @@ #include "documentreader.h" #include +#include "diskaccessexception.h" #include "pboheaderreader.h" #include "bs/pbobinarysource.h" @@ -10,20 +11,22 @@ namespace pboman3::io { QSharedPointer DocumentReader::read() const { PboFile pbo(path_); - pbo.open(QIODeviceBase::ReadOnly); + if (!pbo.open(QIODeviceBase::ReadOnly)) { + throw DiskAccessException("Can not access the file. Check if it is used by other processes.", path_); + } PboFileHeader header = PboHeaderReader::readFileHeader(&pbo); - const QFileInfo fi(path_); - - QSharedPointer document(new PboDocument(fi.fileName())); + QList> headers; + headers.reserve(header.headers.count()); + for (const QSharedPointer& h : header.headers) + headers.append(QSharedPointer(new DocumentHeader(DocumentHeader::InternalData{ h->name, h->value }))); - for (const QSharedPointer& h : header.headers) { - document->headers()->add(h->name, h->value); - } + const QFileInfo fi(path_); + QSharedPointer document(new PboDocument(fi.fileName(), std::move(headers), std::move(header.signature))); qsizetype entryDataOffset = header.dataBlockStart; for (const QSharedPointer& e : header.entries) { - DocumentNode* node = document->root()->createHierarchy(e->makePath()); + PboNode* node = document->root()->createHierarchy(e->makePath()); PboDataInfo dataInfo{0, 0, 0, 0, 0}; dataInfo.originalSize = e->originalSize(); dataInfo.dataSize = e->dataSize(); @@ -36,8 +39,6 @@ namespace pboman3::io { node->binarySource->open(); } - document->setSignature(std::move(header.signature)); - return document; } } diff --git a/pbom/io/documentwriter.cpp b/pbom/io/documentwriter.cpp index df2d202..328b546 100644 --- a/pbom/io/documentwriter.cpp +++ b/pbom/io/documentwriter.cpp @@ -1,9 +1,9 @@ #include "documentwriter.h" #include #include +#include "diskaccessexception.h" #include "pboheader.h" #include "pboheaderio.h" -#include "pboioexception.h" namespace pboman3::io { DocumentWriter::DocumentWriter(QString path) @@ -25,14 +25,14 @@ namespace pboman3::io { if (QFile::exists(backupPath) && !QFile::remove(backupPath)) { //LOG(info, "Could not remove the prev backup file - throwing;", backupPath) resumeBinarySources(document->root()); - throw PboIoException( + throw DiskAccessException( "Could not remove the file. Check you have enough permissions and the file is not locked by another process.", backupPath); } if (!QFile::rename(path_, backupPath)) { //LOG(info, "Could not replace the prev PBO file with a write copy - throwing;", loadedPath_) resumeBinarySources(document->root()); - throw PboIoException( + throw DiskAccessException( "Could not write to the file. Check you have enough permissions and the file is not locked by another process.", path_); } @@ -51,7 +51,7 @@ namespace pboman3::io { QTemporaryFile body; body.setFileName(path + ".b"); if (!body.open()) - throw PboIoException("Could not create the file.", body.fileName()); + throw DiskAccessException("Could not create the file.", body.fileName()); QList> entries; writeNode(&body, document->root(), entries, cancel); @@ -61,7 +61,7 @@ namespace pboman3::io { PboFile pbo(path); if (!pbo.open(QIODeviceBase::ReadWrite)) - throw PboIoException("Could not create the file.", path); + throw DiskAccessException("Could not create the file.", path); writeHeader(&pbo, document->headers(), entries, cancel); @@ -84,9 +84,9 @@ namespace pboman3::io { } } - void DocumentWriter::writeNode(QFileDevice* file, DocumentNode* node, QList>& entries, + void DocumentWriter::writeNode(QFileDevice* file, PboNode* node, QList>& entries, const Cancel& cancel) { - for (DocumentNode* child : *node) { + for (PboNode* child : *node) { if (cancel()) return; @@ -154,7 +154,7 @@ namespace pboman3::io { io.writeEntry(PboEntry::makeBoundary()); - for (DocumentNode* key : binarySources_.keys()) { + for (PboNode* key : binarySources_.keys()) { PboDataInfo& existing = binarySources_[key]; existing.dataOffset += file->pos(); } @@ -208,8 +208,8 @@ namespace pboman3::io { pbo->write(document->signature(), document->signature().count()); } - void DocumentWriter::suspendBinarySources(DocumentNode* node) const { - for (DocumentNode* child : *node) { + void DocumentWriter::suspendBinarySources(PboNode* node) const { + for (PboNode* child : *node) { if (child->nodeType() == PboNodeType::File) { child->binarySource->close(); } @@ -219,8 +219,8 @@ namespace pboman3::io { } } - void DocumentWriter::resumeBinarySources(DocumentNode* node) const { - for (DocumentNode* child : *node) { + void DocumentWriter::resumeBinarySources(PboNode* node) const { + for (PboNode* child : *node) { if (child->nodeType() == PboNodeType::File) { child->binarySource->open(); } @@ -230,8 +230,8 @@ namespace pboman3::io { } } - void DocumentWriter::assignBinarySources(DocumentNode* node) { - for (DocumentNode* child : *node) { + void DocumentWriter::assignBinarySources(PboNode* node) { + for (PboNode* child : *node) { if (child->nodeType() == PboNodeType::File) { const PboDataInfo& existing = binarySources_.take(child); child->binarySource = QSharedPointer(new PboBinarySource(path_, existing)); diff --git a/pbom/io/documentwriter.h b/pbom/io/documentwriter.h index 1b1b9eb..e196c4d 100644 --- a/pbom/io/documentwriter.h +++ b/pbom/io/documentwriter.h @@ -26,11 +26,11 @@ namespace pboman3::io { private: QString path_; - QHash binarySources_; + QHash binarySources_; void writeInternal(PboDocument* document, const QString& path, const Cancel& cancel); - void writeNode(QFileDevice* file, DocumentNode* node, QList>& entries, const Cancel& cancel); + void writeNode(QFileDevice* file, PboNode* node, QList>& entries, const Cancel& cancel); void writeHeader(PboFile* file, const DocumentHeaders* headers, const QList>& entries, const Cancel& cancel); @@ -38,11 +38,11 @@ namespace pboman3::io { void writeSignature(QFileDevice* pbo, PboDocument* document, const Cancel& cancel); - void suspendBinarySources(DocumentNode* node) const; + void suspendBinarySources(PboNode* node) const; - void resumeBinarySources(DocumentNode* node) const; + void resumeBinarySources(PboNode* node) const; - void assignBinarySources(DocumentNode* node); + void assignBinarySources(PboNode* node); void emitWriteEntry(); diff --git a/pbom/io/execstore.cpp b/pbom/io/execstore.cpp index 893cc41..13e8c83 100644 --- a/pbom/io/execstore.cpp +++ b/pbom/io/execstore.cpp @@ -2,8 +2,9 @@ #include #include #include "pboioexception.h" +#include "io/diskaccessexception.h" -namespace pboman3 { +namespace pboman3::io { ExecStore::ExecStore(QString fileSystemPath) : fileSystemPath_(std::move(fileSystemPath)) { } @@ -93,11 +94,11 @@ namespace pboman3 { QString tempDirPath = fi.dir().absolutePath(); if (!QDir::temp().mkpath(QDir::temp().relativeFilePath(tempDirPath))) - throw PboIoException("Could not create the folder.", std::move(tempDirPath)); + throw DiskAccessException("Could not create the folder.", std::move(tempDirPath)); QFile file(execPath); if (!file.open(QIODeviceBase::ReadWrite | QIODeviceBase::NewOnly)) - throw PboIoException("Could not open file. Check you have enough permissions and the file is not locked by another process.", execPath); + throw DiskAccessException("Could not open file. Check you have enough permissions and the file is not locked by another process.", execPath); node->binarySource->writeToFs(&file, cancel); diff --git a/pbom/io/execstore.h b/pbom/io/execstore.h index 1d9734b..e995217 100644 --- a/pbom/io/execstore.h +++ b/pbom/io/execstore.h @@ -4,7 +4,7 @@ #include "domain/pbonode.h" #include "util/util.h" -namespace pboman3 { +namespace pboman3::io { class ExecStore { public: explicit ExecStore(QString fileSystemPath); diff --git a/pbom/model/pbofileformatexception.cpp b/pbom/io/pbofileformatexception.cpp similarity index 91% rename from pbom/model/pbofileformatexception.cpp rename to pbom/io/pbofileformatexception.cpp index f768d36..128e8b3 100644 --- a/pbom/model/pbofileformatexception.cpp +++ b/pbom/io/pbofileformatexception.cpp @@ -1,7 +1,7 @@ #include "pbofileformatexception.h" #include -namespace pboman3 { +namespace pboman3::io { PboFileFormatException::PboFileFormatException(QString message) : AppException(std::move(message)) { } diff --git a/pbom/model/pbofileformatexception.h b/pbom/io/pbofileformatexception.h similarity index 88% rename from pbom/model/pbofileformatexception.h rename to pbom/io/pbofileformatexception.h index 2c8ab06..2b3f53f 100644 --- a/pbom/model/pbofileformatexception.h +++ b/pbom/io/pbofileformatexception.h @@ -2,7 +2,7 @@ #include "util/exception.h" -namespace pboman3 { +namespace pboman3::io { class PboFileFormatException : public AppException { public: explicit PboFileFormatException(QString message); diff --git a/pbom/io/pboheaderio.cpp b/pbom/io/pboheaderio.cpp index 62208e7..ee0befb 100644 --- a/pbom/io/pboheaderio.cpp +++ b/pbom/io/pboheaderio.cpp @@ -1,7 +1,7 @@ #include "pboheaderio.h" #include "pbodatastream.h" -namespace pboman3 { +namespace pboman3::io { using namespace std; PboHeaderIO::PboHeaderIO(PboFile* file) diff --git a/pbom/io/pboheaderio.h b/pbom/io/pboheaderio.h index 0f48290..9a01033 100644 --- a/pbom/io/pboheaderio.h +++ b/pbom/io/pboheaderio.h @@ -5,7 +5,7 @@ #include "io/pboheader.h" #include -namespace pboman3 { +namespace pboman3::io { using namespace std; class PboHeaderIO { diff --git a/pbom/io/pboheaderreader.cpp b/pbom/io/pboheaderreader.cpp index 3d8c1b0..99e97a8 100644 --- a/pbom/io/pboheaderreader.cpp +++ b/pbom/io/pboheaderreader.cpp @@ -1,9 +1,9 @@ #include "pboheaderreader.h" #include +#include "pbofileformatexception.h" #include "pboheaderio.h" -#include "pboioexception.h" -namespace pboman3 { +namespace pboman3::io { PboFileHeader PboHeaderReader::readFileHeader(PboFile* file) { QList> headers; QList> entries; @@ -12,7 +12,7 @@ namespace pboman3 { QSharedPointer entry = reader.readNextEntry(); if (!entry) { - throw PboIoException("The file is not a valid PBO.", file->fileName()); + throw PboFileFormatException("The file is not a valid PBO."); } qsizetype dataBlockEnd = 0; @@ -23,13 +23,13 @@ namespace pboman3 { header = reader.readNextHeader(); } if (!header) { - throw PboIoException("The file headers are corrupted.", file->fileName()); + throw PboFileFormatException("The file headers are corrupted."); } } else if (entry->isContent()) { entries.append(entry); dataBlockEnd += entry->dataSize(); } else { - throw PboIoException("The file first entry is corrupted.", file->fileName()); + throw PboFileFormatException("The file first entry is corrupted."); } entry = reader.readNextEntry(); @@ -39,10 +39,9 @@ namespace pboman3 { entry = reader.readNextEntry(); } if (!entry || !entry->isBoundary()) { - throw PboIoException("The file entries list is corrupted.", file->fileName()); + throw PboFileFormatException("The file entries list is corrupted."); } - const qsizetype dataBlockStart = file->pos(); dataBlockEnd += dataBlockStart; diff --git a/pbom/io/pboheaderreader.h b/pbom/io/pboheaderreader.h index 3c6bb16..bdc1522 100644 --- a/pbom/io/pboheaderreader.h +++ b/pbom/io/pboheaderreader.h @@ -5,7 +5,7 @@ #include "io/pboheader.h" #include -namespace pboman3 { +namespace pboman3::io { struct PboFileHeader { QList> headers; QList> entries; diff --git a/pbom/io/pboioexception.cpp b/pbom/io/pboioexception.cpp deleted file mode 100644 index ce28325..0000000 --- a/pbom/io/pboioexception.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "pboioexception.h" - -namespace pboman3 { - PboIoException::PboIoException(QString message, QString file) - : AppException(std::move(message)), - file_(std::move(file)){ - } - - QDebug operator<<(QDebug debug, const PboIoException& ex) { - return debug << "PboIoException(Message=" << ex.message_ << "; File=" << ex.file_ << ")"; - } - - void PboIoException::raise() const { - throw* this; - } - - QException* PboIoException::clone() const { - return new PboIoException(*this); - } - - QString PboIoException::file() const { - return file_; - } -} diff --git a/pbom/io/pboioexception.h b/pbom/io/pboioexception.h deleted file mode 100644 index fcc7d79..0000000 --- a/pbom/io/pboioexception.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include "util/exception.h" - -namespace pboman3 { - class PboIoException : public AppException { - public: - PboIoException(QString message, QString file); - - friend QDebug operator<<(QDebug debug, const PboIoException& ex); - - void raise() const override; - - QException* clone() const override; - - QString file() const; - - private: - QString file_; - }; -} diff --git a/pbom/io/pbowriter.cpp b/pbom/io/pbowriter.cpp deleted file mode 100644 index d1f05fa..0000000 --- a/pbom/io/pbowriter.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "pbowriter.h" -#include -#include -#include "pbofile.h" -#include "pboheaderio.h" -#include "pboioexception.h" -#include "bs/pbobinarysource.h" - -namespace pboman3 { - PboWriter::PboWriter() - : root_(nullptr), - headers_(nullptr), - signature_(nullptr) { - } - - PboWriter& PboWriter::usePath(QString path) { - path_ = std::move(path); - return *this; - } - - PboWriter& PboWriter::useHeaders(HeadersModel* headers) { - headers_ = headers; - return *this; - } - - PboWriter& PboWriter::useRoot(PboNode* root) { - root_ = root; - return *this; - } - - PboWriter& PboWriter::copySignatureTo(QByteArray* signature) { - signature_ = signature; - return *this; - } - - void PboWriter::write(const Cancel& cancel) { - assert(!path_.isEmpty() && "Path must not be empty"); - assert(root_ && "Root must not be null"); - - QTemporaryFile body; - body.setFileName(path_ + ".b"); - if (!body.open()) - throw PboIoException("Could not create the file.", body.fileName()); - - QList entries; - writeNode(&body, root_, entries, cancel); - - if (cancel()) - return; - - PboFile pbo(path_); - if (!pbo.open(QIODeviceBase::ReadWrite)) - throw PboIoException("Could not create the file.", path_); - - writeHeader(&pbo, entries, cancel); - - if (cancel()) { - pbo.close(); - pbo.remove(); - return; - } - - const bool seek = body.seek(0); - assert(seek); - - copyBody(&pbo, &body, cancel); - - writeSignature(&pbo, cancel); - - body.close(); - pbo.close(); - - if (cancel()) { - pbo.remove(); - } - } - - void PboWriter::suspendBinarySources() const { - suspendBinarySources(root_); - } - - void PboWriter::resumeBinarySources() const { - resumeBinarySources(root_); - } - - void PboWriter::assignBinarySources(const QString& path) { - assignBinarySources(root_, path); - } - - void PboWriter::writeNode(QFileDevice* file, PboNode* node, QList& entries, const Cancel& cancel) { - for (PboNode* child : *node) { - if (cancel()) - return; - - if (child->nodeType() == PboNodeType::File) { - const qint64 before = file->pos(); - child->binarySource->writeToPbo(file, cancel); - const qint64 after = file->pos(); - - const qint32 originalSize = child->binarySource->readOriginalSize(); - const auto dataSize = static_cast(after - before); - - PboEntry entry( - child->makePath().toString(), - child->binarySource->isCompressed() ? PboPackingMethod::Packed : PboPackingMethod::Uncompressed, - child->binarySource->readOriginalSize(), - 0, - child->binarySource->readTimestamp(), - dataSize); - entries.append(entry); - - PboDataInfo data{0, 0, 0, 0, 0}; - data.originalSize = originalSize; - data.dataSize = dataSize; - data.dataOffset = before; - data.timestamp = child->binarySource->readTimestamp(); - data.compressed = child->binarySource->isCompressed(); - - binarySources_.insert(child, data); - - emitWriteEntry(); - } else { - writeNode(file, child, entries, cancel); - } - } - } - - void PboWriter::writeHeader(PboFile* file, const QList& entries, const Cancel& cancel) { - const PboHeaderIO io(file); - io.writeEntry(PboEntry::makeSignature()); - - for (const QSharedPointer& header : *headers_) { - if (cancel()) { - break; - } - io.writeHeader(*header); - } - - if (cancel()) { - return; - } - - io.writeHeader(PboHeader::makeBoundary()); - - for (const PboEntry& entry : entries) { - if (cancel()) { - break; - } - io.writeEntry(entry); - } - - if (cancel()) { - return; - } - - io.writeEntry(PboEntry::makeBoundary()); - - for (PboNode* key : binarySources_.keys()) { - PboDataInfo& existing = binarySources_[key]; - existing.dataOffset += file->pos(); - } - } - - void PboWriter::copyBody(QFileDevice* pbo, QFileDevice* body, const Cancel& cancel) { - QByteArray data; - data.resize(1024 * 1024); - - qsizetype copiedBytes = 0; - const qsizetype totalBytes = body->size(); - - qint64 read = body->read(data.data(), data.size()); - while (read > 0) { - pbo->write(data.data(), read); - - copiedBytes += read; - emitCopyBytes(copiedBytes, totalBytes); - - if (cancel()) - return; - read = body->read(data.data(), data.size()); - } - } - - void PboWriter::writeSignature(QFileDevice* pbo, const Cancel& cancel) { - const bool seek = pbo->seek(0); - assert(seek); - - QCryptographicHash sha1(QCryptographicHash::Sha1); - - qsizetype processed = 0; - const qsizetype total = pbo->size(); - - constexpr qint64 bufferSize = 1024; - char buffer[bufferSize]; - qint64 read; - - while (!cancel() && (read = pbo->read(buffer, bufferSize)) > 0) { - sha1.addData(buffer, read); - processed += read; - emitCalcHash(processed, total); - } - - if (cancel()) - return; - - const QByteArray sha1Bytes = sha1.result(); - - pbo->write(QByteArray(1, 0)); - pbo->write(sha1Bytes, sha1Bytes.count()); - - if (signature_) { - signature_->append(sha1Bytes); - } - } - - void PboWriter::suspendBinarySources(PboNode* node) const { - for (PboNode* child : *node) { - if (child->nodeType() == PboNodeType::File) { - child->binarySource->close(); - } else { - suspendBinarySources(child); - } - } - } - - void PboWriter::resumeBinarySources(PboNode* node) const { - for (PboNode* child : *node) { - if (child->nodeType() == PboNodeType::File) { - child->binarySource->open(); - } else { - resumeBinarySources(child); - } - } - } - - void PboWriter::assignBinarySources(PboNode* node, const QString& path) { - for (PboNode* child : *node) { - if (child->nodeType() == PboNodeType::File) { - const PboDataInfo& existing = binarySources_.take(child); - child->binarySource = QSharedPointer(new PboBinarySource(path, existing)); - child->binarySource->open(); - } else { - assignBinarySources(child, path); - } - } - } - - void PboWriter::emitWriteEntry() { - const WriteEntryEvent evt; - emit progress(&evt); - } - - void PboWriter::emitCopyBytes(qsizetype copied, qsizetype total) { - const CopyBytesEvent evt(copied, total); - emit progress(&evt); - } - - void PboWriter::emitCalcHash(qsizetype processed, qsizetype total) { - const CalcHashEvent evt(processed, total); - emit progress(&evt); - } -} diff --git a/pbom/io/pbowriter.h b/pbom/io/pbowriter.h deleted file mode 100644 index 1cc68fb..0000000 --- a/pbom/io/pbowriter.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include "pbofile.h" -#include "bs/pbobinarysource.h" -#include "model/headersmodel.h" -#include "io/pboentry.h" -#include "domain/pbonode.h" - -namespace pboman3 { - class PboWriter : public QObject { - Q_OBJECT - - public: - struct ProgressEvent; - - PboWriter(); - - PboWriter& usePath(QString path); - - PboWriter& useHeaders(HeadersModel* headers); - - PboWriter& useRoot(PboNode* root); - - PboWriter& copySignatureTo(QByteArray* signature); - - void write(const Cancel& cancel); - - void suspendBinarySources() const; - - void resumeBinarySources() const; - - void assignBinarySources(const QString& path); - - signals: - void progress(const ProgressEvent* evt); - - private: - QString path_; - PboNode* root_; - HeadersModel* headers_; - QByteArray* signature_; - QHash binarySources_; - - void writeNode(QFileDevice* file, PboNode* node, QList& entries, const Cancel& cancel); - - void writeHeader(PboFile* file, const QList& entries, const Cancel& cancel); - - void copyBody(QFileDevice* pbo, QFileDevice* body, const Cancel& cancel); - - void writeSignature(QFileDevice* pbo, const Cancel& cancel); - - void suspendBinarySources(PboNode* node) const; - - void resumeBinarySources(PboNode* node) const; - - void assignBinarySources(PboNode* node, const QString& path); - - void emitWriteEntry(); - - void emitCopyBytes(qsizetype copied, qsizetype total); - - void emitCalcHash(qsizetype processed, qsizetype total); - - public: - struct ProgressEvent { - virtual ~ProgressEvent() = default; - }; - - struct WriteEntryEvent : ProgressEvent { - }; - - struct CopyBytesEvent : ProgressEvent { - CopyBytesEvent(qsizetype c, qsizetype t): - copied(c), total(t) { - } - - const qsizetype copied; - const qsizetype total; - }; - - struct CalcHashEvent : ProgressEvent { - CalcHashEvent(qsizetype p, qsizetype t) : - processed(p), total(t) { - } - - const qsizetype processed; - const qsizetype total; - }; - }; -} diff --git a/pbom/model/CMakeLists.txt b/pbom/model/CMakeLists.txt index 5dbafc8..471dad9 100644 --- a/pbom/model/CMakeLists.txt +++ b/pbom/model/CMakeLists.txt @@ -6,21 +6,13 @@ list(APPEND PROJECT_SOURCES "model/task/unpacktask.cpp" "model/task/unpackwindowmodel.cpp" "model/conflictsparcel.cpp" - "model/diskaccessexception.cpp" - "model/headersmodel.cpp" "model/interactionparcel.cpp" - "model/pbofileformatexception.cpp" - "model/pbomodel.cpp" - "model/rootreader.cpp" - "model/signaturemodel.cpp") + "model/pbomodel.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) list(APPEND TEST_SOURCES "model/__test__/conflictsparcel_test.cpp" - "model/__test__/headersmodel_test.cpp" - "model/__test__/interactionparcel_test.cpp" - "model/__test__/rootreader_test.cpp" - "model/__test__/signaturemodel_test.cpp") + "model/__test__/interactionparcel_test.cpp") set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/model/__test__/conflictsparcel_test.cpp b/pbom/model/__test__/conflictsparcel_test.cpp index 070a51f..f69ae1f 100644 --- a/pbom/model/__test__/conflictsparcel_test.cpp +++ b/pbom/model/__test__/conflictsparcel_test.cpp @@ -1,7 +1,7 @@ #include #include "model/conflictsparcel.h" -namespace pboman3::test { +namespace pboman3::model::test { TEST(ConflictsParcelTest, GetResolution_Returns_Unset_If_Resolution_Was_Not_Set) { const ConflictsParcel conflicts; const ConflictResolution res = conflicts.getResolution(NodeDescriptor(nullptr, PboPath("some-path"))); diff --git a/pbom/model/__test__/headersmodel_test.cpp b/pbom/model/__test__/headersmodel_test.cpp deleted file mode 100644 index 9ada894..0000000 --- a/pbom/model/__test__/headersmodel_test.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "model/headersmodel.h" -#include - -namespace pboman3::test { - TEST(HeadersModelTest, SetData_Sets_Data) { - const QSharedPointer p1(new PboHeader("p1", "v1")); - const QSharedPointer p2(new PboHeader("p2", "v2")); - - HeadersModel model; - model.setData(QList({p1, p2})); - - auto it = model.begin(); - ASSERT_EQ(*it, p1); - ++it; - ASSERT_EQ(*it, p2); - ++it; - ASSERT_EQ(it, model.end()); - } - - TEST(HeadersModelTest, SetData_Replaces_Data) { - const QSharedPointer p1(new PboHeader("p1", "v1")); - const QSharedPointer p2(new PboHeader("p2", "v2")); - const QSharedPointer p3(new PboHeader("p3", "v3")); - const QSharedPointer p4(new PboHeader("p4", "v4")); - - HeadersModel model; - model.setData(QList({p1, p2})); - model.setData(QList({p3, p4})); - - auto it = model.begin(); - ASSERT_EQ(*it, p3); - ++it; - ASSERT_EQ(*it, p4); - ++it; - ASSERT_EQ(it, model.end()); - } - - TEST(HeadersModelTest, SetData_Triggers_If_Data_Different) { - const QSharedPointer p1(new PboHeader("p1", "v1")); - const QSharedPointer p2(new PboHeader("p2", "v2")); - const QSharedPointer p3(new PboHeader("p3", "v3")); - const QSharedPointer p4(new PboHeader("p4", "v4")); - - HeadersModel model; - model.setData(QList({p1, p2})); - - int count = 0; - auto callback = [&count]() { count++; }; - QObject::connect(&model, &HeadersModel::changed, callback); - - model.setData(QList({p3, p4})); - - ASSERT_EQ(count, 1); - } - - TEST(HeadersModelTest, SetData_Triggers_If_Data_Same) { - const QSharedPointer p1(new PboHeader("p1", "v1")); - const QSharedPointer p2(new PboHeader("p2", "v2")); - const QSharedPointer p3(new PboHeader("p1", "v1")); - const QSharedPointer p4(new PboHeader("p2", "v2")); - - HeadersModel model; - model.setData(QList({p1, p2})); - - int count = 0; - auto callback = [&count]() { count++; }; - QObject::connect(&model, &HeadersModel::changed, callback); - - model.setData(QList({p3, p4})); - - ASSERT_EQ(count, 0); - } -} diff --git a/pbom/model/__test__/interactionparcel_test.cpp b/pbom/model/__test__/interactionparcel_test.cpp index a97688a..9c8199b 100644 --- a/pbom/model/__test__/interactionparcel_test.cpp +++ b/pbom/model/__test__/interactionparcel_test.cpp @@ -6,7 +6,7 @@ #include "domain/pbonode.h" #include "domain/pbopath.h" -namespace pboman3::test { +namespace pboman3::model::test { TEST(NodeDescriptorTest, Ctor_Initializes_Fields) { QTemporaryFile t; t.open(); diff --git a/pbom/model/__test__/rootreader_test.cpp b/pbom/model/__test__/rootreader_test.cpp deleted file mode 100644 index f53c70f..0000000 --- a/pbom/model/__test__/rootreader_test.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "model/rootreader.h" -#include -#include -#include "io/pboheaderreader.h" -#include "io/bs/pbobinarysource.h" - -namespace pboman3::test { - TEST(RootReaderTest, InflateRoot_Functional) { - QTemporaryFile file; - file.open(); - file.close(); - - const PboFileHeader header{ - QList>(0), - QList{ - QSharedPointer(new PboEntry("f1", PboPackingMethod::Packed, 1, 2, 3, 4)), - QSharedPointer(new PboEntry("f2", PboPackingMethod::Uncompressed, 5, 6, 7, 8)), - }, - 100, - QByteArray() - }; - - PboNode root("root", PboNodeType::Container, nullptr); - RootReader(&header, file.fileName()).inflateRoot(&root); - - ASSERT_EQ(root.count(), 2); - - ASSERT_EQ(root.at(0)->title(), "f1"); - const auto source1 = dynamic_cast(root.at(0)->binarySource.get()); - ASSERT_TRUE(source1); - ASSERT_EQ(source1->getInfo().originalSize, header.entries.at(0)->originalSize()); - ASSERT_EQ(source1->getInfo().dataSize, header.entries.at(0)->dataSize()); - ASSERT_EQ(source1->getInfo().dataOffset, header.dataBlockStart); - ASSERT_EQ(source1->getInfo().timestamp, header.entries.at(0)->timestamp()); - ASSERT_TRUE(source1->getInfo().compressed); - - ASSERT_EQ(root.at(1)->title(), "f2"); - const auto source2 = dynamic_cast(root.at(1)->binarySource.get()); - ASSERT_TRUE(source2); - ASSERT_EQ(source2->getInfo().originalSize, header.entries.at(1)->originalSize()); - ASSERT_EQ(source2->getInfo().dataSize, header.entries.at(1)->dataSize()); - ASSERT_EQ(source2->getInfo().dataOffset, header.dataBlockStart + source1->getInfo().dataSize); - ASSERT_EQ(source2->getInfo().timestamp, header.entries.at(1)->timestamp()); - ASSERT_FALSE(source2->getInfo().compressed); - } -} diff --git a/pbom/model/__test__/signaturemodel_test.cpp b/pbom/model/__test__/signaturemodel_test.cpp deleted file mode 100644 index 8e50e72..0000000 --- a/pbom/model/__test__/signaturemodel_test.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include "model/signaturemodel.h" - -namespace pboman3::test { - TEST(SignatureModelTest, SignatureBytes_Returns_Empty_By_Default) { - const SignatureModel m; - ASSERT_TRUE(m.signatureString().isNull()); - } - - TEST(SignatureModelTest, SetSignatureBytes_Sets_Bytes) { - SignatureModel m; - - m.setSignatureBytes(QByteArray(10, 5)); - ASSERT_EQ(m.signatureString(), "05 05 05 05 05 05 05 05 05 05"); - - m.setSignatureBytes(QByteArray()); - ASSERT_TRUE(m.signatureString().isNull()); - } -} diff --git a/pbom/model/conflictsparcel.cpp b/pbom/model/conflictsparcel.cpp index 3908b5f..b0f4634 100644 --- a/pbom/model/conflictsparcel.cpp +++ b/pbom/model/conflictsparcel.cpp @@ -1,6 +1,6 @@ #include "conflictsparcel.h" -namespace pboman3 { +namespace pboman3::model { ConflictResolution ConflictsParcel::getResolution(const NodeDescriptor& descriptor) const { return conflicts_.contains(descriptor.path()) ? conflicts_[descriptor.path()] : ConflictResolution::Unset; } diff --git a/pbom/model/conflictsparcel.h b/pbom/model/conflictsparcel.h index cf7a8b3..a0fc3dd 100644 --- a/pbom/model/conflictsparcel.h +++ b/pbom/model/conflictsparcel.h @@ -5,7 +5,7 @@ #include "interactionparcel.h" #include -namespace pboman3 { +namespace pboman3::model { class ConflictsParcel { public: ConflictResolution getResolution(const NodeDescriptor& descriptor) const; diff --git a/pbom/model/headersmodel.cpp b/pbom/model/headersmodel.cpp deleted file mode 100644 index 3edcf6b..0000000 --- a/pbom/model/headersmodel.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "headersmodel.h" - -namespace pboman3 { - QList>::iterator HeadersModel::begin() { - return data_.begin(); - } - - QList>::iterator HeadersModel::end() { - return data_.end(); - } - - void HeadersModel::setData(QList> data) { - if (hasChanges(data)) { - data_ = std::move(data); - emit changed(); - } - } - - bool HeadersModel::hasChanges(const QList>& data) const { - if (data.count() != data_.count()) { - return true; - } - - auto d1 = data.begin(); - auto d2 = data_.begin(); - - while (d1 != data.end()) { - if (areDifferent(**d1, **d2)) { - return true; - } - ++d1; - ++d2; - } - - return false; - } - - bool HeadersModel::areDifferent(const PboHeader& h1, const PboHeader& h2) const { - return h1.name != h2.name || h1.value != h2.value; - } -} diff --git a/pbom/model/headersmodel.h b/pbom/model/headersmodel.h deleted file mode 100644 index 4e76c28..0000000 --- a/pbom/model/headersmodel.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "io/pboheader.h" -#include "util/qpointerlistiterator.h" - -namespace pboman3 { - class HeadersModel : public QObject { - Q_OBJECT - public: - QList>::iterator begin(); - - QList>::iterator end(); - - void setData(QList> data); - - signals: - void changed(); - - private: - QList> data_; - - bool hasChanges(const QList>& data) const; - - bool areDifferent(const PboHeader& h1, const PboHeader& h2) const; - }; -} diff --git a/pbom/model/interactionparcel.cpp b/pbom/model/interactionparcel.cpp index 2b46367..86bdc94 100644 --- a/pbom/model/interactionparcel.cpp +++ b/pbom/model/interactionparcel.cpp @@ -3,7 +3,7 @@ #include #include "util/exception.h" -namespace pboman3 { +namespace pboman3::model { const QSharedPointer& NodeDescriptor::binarySource() const { return binarySource_; } @@ -148,7 +148,7 @@ namespace pboman3 { return QSharedPointer(new FsRawBinarySource(fsPath)); } - NodeDescriptors NodeDescriptors::packNodes(const QList& nodes) { + NodeDescriptors NodeDescriptors::packNodes(const QList& nodes) { NodeDescriptors descriptors; descriptors.reserve(nodes.length() * 2); diff --git a/pbom/model/interactionparcel.h b/pbom/model/interactionparcel.h index c5214df..6975f1c 100644 --- a/pbom/model/interactionparcel.h +++ b/pbom/model/interactionparcel.h @@ -1,14 +1,17 @@ #pragma once -#include "domain/pbonode.h" #include "domain/pbopath.h" #include "domain/binarysource.h" #include "io/bs/fslzhbinarysource.h" #include "io/bs/fsrawbinarysource.h" #include "io/bs/pbobinarysource.h" #include +#include "domain/pbonode.h" + +namespace pboman3::model { + using namespace domain; + using namespace io; -namespace pboman3 { enum class BinarySourceType { Pbo = 0, FsLzh = 1, @@ -46,7 +49,7 @@ namespace pboman3 { static NodeDescriptors deserialize(const QByteArray& data); - static NodeDescriptors packNodes(const QList& nodes); + static NodeDescriptors packNodes(const QList& nodes); private: static void writeNodeInfo(QDataStream& stream, const NodeDescriptor& nodeInfo); diff --git a/pbom/model/pbomodel.cpp b/pbom/model/pbomodel.cpp index 02c2ed7..4794375 100644 --- a/pbom/model/pbomodel.cpp +++ b/pbom/model/pbomodel.cpp @@ -1,18 +1,18 @@ #include "pbomodel.h" +#include "domain/pbonode.h" #include #include #include -#include "diskaccessexception.h" -#include "pbofileformatexception.h" -#include "rootreader.h" -#include "io/pboheaderreader.h" -#include "io/pboioexception.h" -#include "io/pbowriter.h" +#include "io/documentreader.h" +#include "io/documentwriter.h" +#include "util/exception.h" #include "util/log.h" #define LOG(...) LOGGER("model/PboModel", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::model { + using namespace io; + void PboModel::loadFile(const QString& path) { LOG(info, "Loading the file:", path) @@ -23,34 +23,10 @@ namespace pboman3 { setLoadedPath(path); - PboFile file(loadedPath_); - if (!file.open(QIODeviceBase::OpenModeFlag::ReadWrite)) - throw DiskAccessException("Can not access the file. Check if it is used by other processes.", path); - - QString title = QFileInfo(path).fileName(); - LOG(info, "The file title is:", title) - - rootEntry_ = QSharedPointer(new PboNode(std::move(title), PboNodeType::Container, nullptr)); - connect(rootEntry_.get(), &PboNode::hierarchyChanged, this, &PboModel::modelChanged); - connect(rootEntry_.get(), &PboNode::titleChanged, this, &PboModel::rootTitleChanged); - - PboFileHeader header; - try { - header = PboHeaderReader::readFileHeader(&file); - LOG(info, "The file header:", header) - } catch (const PboIoException& ex) { - LOG(warning, "Got error while reading the file header:", ex) - throw PboFileFormatException("Can not open the file. It is not a valid PBO."); - } - - headers_ = QSharedPointer(new HeadersModel); - headers_->setData(std::move(header.headers)); - connect(headers_.get(), &HeadersModel::changed, this, &PboModel::modelChanged); - - signature_ = QSharedPointer(new SignatureModel); - signature_->setSignatureBytes(header.signature); - - RootReader(&header, path).inflateRoot(rootEntry_.get()); + const DocumentReader reader(path); + document_ = reader.read(); + connect(document_.get(), &PboDocument::changed, this, &PboModel::modelChanged); + connect(document_.get(), &PboDocument::titleChanged, this, &PboModel::titleChanged); LOG(info, "Creating the binary backend") binaryBackend_ = QSharedPointer( @@ -61,82 +37,31 @@ namespace pboman3 { LOG(info, "Saving the model to:", filePath) const QString savePath = filePath.isNull() ? loadedPath_ : filePath; - const QString tempPath(savePath + ".t"); - LOG(info, "The savePath was set as:", savePath) - LOG(info, "The tempPath was set as:", tempPath) - - QByteArray signature; - - PboWriter writer; - writer.usePath(savePath == loadedPath_ ? tempPath : savePath) - .useHeaders(headers_.get()) - .useRoot(rootEntry_.get()) - .copySignatureTo(&signature); - - try { - LOG(info, "Writing the file") - writer.write(cancel); - } catch (const PboIoException& ex) { - LOG(warning, "Got error while writing:", ex) - throw DiskAccessException(ex); - } - if (cancel()) { - LOG(info, "The write process was canceled - exit") - return; - } + DocumentWriter writer(savePath); + writer.write(document_.get(), cancel); - LOG(info, "Clean up the previous binary sources") - writer.suspendBinarySources(); - - LOG(info, "Update the model signature") - signature_->setSignatureBytes(signature); - - if (savePath == loadedPath_) { - const QString backupPath = loadedPath_ + ".bak"; - LOG(info, "Cleaning up the temporary files") - if (QFile::exists(backupPath) && !QFile::remove(backupPath)) { - LOG(info, "Could not remove the prev backup file - throwing;", backupPath) - writer.resumeBinarySources(); - throw DiskAccessException( - "Could not remove the file. Check you have enough permissions and the file is not locked by another process.", - backupPath); - } - if (!QFile::rename(loadedPath_, backupPath)) { - LOG(info, "Could not replace the prev PBO file with a write copy - throwing;", loadedPath_) - writer.resumeBinarySources(); - throw DiskAccessException( - "Could not write to the file. Check you have enough permissions and the file is not locked by another process.", - loadedPath_); - } - const bool renamed = QFile::rename(tempPath, loadedPath_); - assert(renamed); + if (!cancel()) { + setLoadedPath(savePath); } - - LOG(info, "Assign binary sources back") - writer.assignBinarySources(savePath); - - setLoadedPath(savePath); } void PboModel::unloadFile() { - if (!rootEntry_) + if (!document_) throw InvalidOperationException("The model is not initialized"); LOG(info, "Unloading the current file") setLoadedPath(nullptr); - signature_.clear(); - headers_.clear(); - rootEntry_.clear(); + document_.clear(); binaryBackend_.clear(); } void PboModel::createNodeSet(PboNode* parent, const QList& descriptors, - const ConflictsParcel& conflicts) const { - if (!rootEntry_) + const ConflictsParcel& conflicts) const { + if (!document_) throw InvalidOperationException("The model is not initialized"); LOG(info, "Creating the set of nodes, parent:", *parent) @@ -158,14 +83,8 @@ namespace pboman3 { InteractionParcel PboModel::interactionPrepare(const QList& nodes, const Cancel& cancel) const { LOG(info, "Preparing the interaction for", nodes.count(), "nodes") - QList files; - try { - files = binaryBackend_->hddSync(nodes, cancel); - LOG(info, "Got files:", files) - } catch (const PboIoException& ex) { - LOG(warning, "Got error while syncing:", ex) - throw DiskAccessException(ex); - } + QList files = binaryBackend_->hddSync(nodes, cancel); + LOG(info, "Got files:", files) NodeDescriptors descriptors = NodeDescriptors::packNodes(nodes); LOG(info, "Got descriptors:", descriptors) @@ -176,20 +95,15 @@ namespace pboman3 { QString PboModel::execPrepare(const PboNode* node, const Cancel& cancel) const { LOG(info, "Preparing the execution of the node", *node) - QString file; - try { - file = binaryBackend_->execSync(node, cancel); - LOG(info, "The node contents was stored to:", file) - } catch (const PboIoException& ex) { - LOG(warning, "Got error while syncing:", ex) - throw DiskAccessException(ex); - } + QString file = binaryBackend_->execSync(node, cancel); + LOG(info, "The node contents was stored to:", file) return file; } - ConflictsParcel PboModel::checkConflicts(const PboNode* parent, const QList& descriptors) const { - if (!rootEntry_) + ConflictsParcel PboModel::checkConflicts(const PboNode* parent, + const QList& descriptors) const { + if (!document_) throw InvalidOperationException("The model is not initialized"); LOG(info, "Check conflicts for the set of descriptors") @@ -205,27 +119,15 @@ namespace pboman3 { return conflicts; } - void PboModel::unpackNodesTo(const QDir& dest, const PboNode* rootNode, const QList& childNodes, - const Cancel& cancel) const { - try { - LOG(info, "Unpack", childNodes.count(), "nodes to", dest) - binaryBackend_->unpackSync(dest, rootNode, childNodes, cancel); - } catch (const PboIoException& ex) { - LOG(warning, "Got error while unpacking:", ex) - throw DiskAccessException(ex); - } - } - - PboNode* PboModel::rootEntry() const { - return rootEntry_.get(); - } - - HeadersModel* PboModel::headers() const { - return headers_.get(); + void PboModel::unpackNodesTo(const QDir& dest, const PboNode* rootNode, + const QList& childNodes, + const Cancel& cancel) const { + LOG(info, "Unpack", childNodes.count(), "nodes to", dest) + binaryBackend_->unpackSync(dest, rootNode, childNodes, cancel); } - SignatureModel* PboModel::signature() const { - return signature_.get(); + PboDocument* PboModel::document() const { + return document_.get(); } const QString& PboModel::loadedPath() const { @@ -240,10 +142,10 @@ namespace pboman3 { } } - void PboModel::rootTitleChanged() { - LOG(info, "The root title was changed") + void PboModel::titleChanged() { + LOG(info, "The document title was changed") QFileInfo fi(loadedPath_); - fi.setFile(fi.dir(), rootEntry_->title()); + fi.setFile(fi.dir(), document_->root()->title()); setLoadedPath(fi.absoluteFilePath()); } } diff --git a/pbom/model/pbomodel.h b/pbom/model/pbomodel.h index 3dc1fb5..ecc7d1f 100644 --- a/pbom/model/pbomodel.h +++ b/pbom/model/pbomodel.h @@ -1,17 +1,17 @@ #pragma once +#include "domain/pbodocument.h" #include #include #include "conflictsparcel.h" -#include "headersmodel.h" #include "interactionparcel.h" -#include "domain/pbonode.h" -#include "signaturemodel.h" #include "io/bb/binarybackend.h" -namespace pboman3 { +namespace pboman3::model { + using namespace domain; + class PboModel : public QObject { - Q_OBJECT + Q_OBJECT public: void loadFile(const QString& path); @@ -29,11 +29,7 @@ namespace pboman3 { void unpackNodesTo(const QDir& dest, const PboNode* rootNode, const QList& childNodes, const Cancel& cancel) const; - PboNode* rootEntry() const; - - HeadersModel* headers() const; - - SignatureModel* signature() const; + PboDocument* document() const; const QString& loadedPath() const; @@ -44,13 +40,11 @@ namespace pboman3 { private: QString loadedPath_; - QSharedPointer rootEntry_; - QSharedPointer headers_; - QSharedPointer signature_; + QSharedPointer document_; QSharedPointer binaryBackend_; void setLoadedPath(const QString& loadedFile); - void rootTitleChanged(); + void titleChanged(); }; } diff --git a/pbom/model/rootreader.cpp b/pbom/model/rootreader.cpp deleted file mode 100644 index 2daedfb..0000000 --- a/pbom/model/rootreader.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "rootreader.h" -#include "io/bs/pbobinarysource.h" -#include "util/log.h" - -#define LOG(...) LOGGER("model/RootReader", __VA_ARGS__) - -namespace pboman3 { - RootReader::RootReader(const PboFileHeader* header, const QString& path) - : header_(header), - path_(path) { - } - - void RootReader::inflateRoot(PboNode* root) const { - LOG(info, "Inflating the nodes hierarchy") - qsizetype entryDataOffset = header_->dataBlockStart; - for (const QSharedPointer& entry : header_->entries) { - LOG(debug, "Processing the entry:", *entry) - PboNode* node = root->createHierarchy(entry->makePath()); - PboDataInfo dataInfo{0, 0, 0, 0, 0}; - dataInfo.originalSize = entry->originalSize(); - dataInfo.dataSize = entry->dataSize(); - dataInfo.dataOffset = entryDataOffset; - dataInfo.timestamp = entry->timestamp(); - dataInfo.compressed = entry->packingMethod() == PboPackingMethod::Packed; - entryDataOffset += dataInfo.dataSize; - node->binarySource = QSharedPointer( - new PboBinarySource(path_, dataInfo)); - node->binarySource->open(); - } - - } -} diff --git a/pbom/model/rootreader.h b/pbom/model/rootreader.h deleted file mode 100644 index 85375c3..0000000 --- a/pbom/model/rootreader.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "domain/pbonode.h" -#include "io/pboheaderreader.h" - -namespace pboman3 { - class RootReader { - public: - RootReader(const PboFileHeader* header, const QString& path); - - void inflateRoot(PboNode* root) const; - - private: - const PboFileHeader* header_; - const QString& path_; - }; -} diff --git a/pbom/model/signaturemodel.cpp b/pbom/model/signaturemodel.cpp deleted file mode 100644 index cdd2a1a..0000000 --- a/pbom/model/signaturemodel.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "signaturemodel.h" - -namespace pboman3 { - void SignatureModel::setSignatureBytes(const QByteArray& bytes) { - if (bytes.count() == 0) - signatureString_.clear(); - else - signatureString_ = bytes.toHex(' '); - } - - const QString& SignatureModel::signatureString() const { - return signatureString_; - } -} diff --git a/pbom/model/signaturemodel.h b/pbom/model/signaturemodel.h deleted file mode 100644 index f12a016..0000000 --- a/pbom/model/signaturemodel.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace pboman3 { - class SignatureModel { - public: - void setSignatureBytes(const QByteArray& bytes); - - const QString& signatureString() const; - - private: - QString signatureString_; - }; -} diff --git a/pbom/model/task/packtask.cpp b/pbom/model/task/packtask.cpp index 89fa4c0..f013c3d 100644 --- a/pbom/model/task/packtask.cpp +++ b/pbom/model/task/packtask.cpp @@ -1,11 +1,14 @@ #include "packtask.h" -#include "io/pboioexception.h" -#include "io/pbowriter.h" + +#include "io/diskaccessexception.h" +#include "io/documentwriter.h" #include "util/log.h" #define LOG(...) LOGGER("model/task/PackTask", __VA_ARGS__) namespace pboman3 { + using namespace io; + PackTask::PackTask(QString folder, QString outputDir) : folder_(std::move(folder)), outputDir_(std::move(outputDir)) { @@ -27,8 +30,8 @@ namespace pboman3 { emit taskThinking(fi.absoluteFilePath()); - PboNode root("root", PboNodeType::Container, nullptr); - const qint32 filesCount = collectDir(fi, fi.dir(), root, cancel); + PboDocument document("root"); + const qint32 filesCount = collectDir(fi, fi.dir(), *document.root(), cancel); if (cancel()) return; @@ -39,11 +42,7 @@ namespace pboman3 { return; } - HeadersModel headers; - PboWriter writer; - writer.usePath(pboFile) - .useRoot(&root) - .useHeaders(&headers); + DocumentWriter writer(pboFile); //it is tricky to display real PBO pack progress as the process consists of four independent steps. //1. Scan the source folder and grab files. It might take time we can't estimate at all. So just show "indeterminate" progress indicator. @@ -63,15 +62,15 @@ namespace pboman3 { emit taskInitialized(fi.absoluteFilePath(), 0, filesCount * (WT_ENTRIES + WT_BODY + WT_SIGNATURE)); qint32 progress = 0; - connect(&writer, &PboWriter::progress, [this, &progress, filesCount](const PboWriter::ProgressEvent* evt) { - if (dynamic_cast(evt)) { + connect(&writer, &DocumentWriter::progress, [this, &progress, filesCount](const DocumentWriter::ProgressEvent* evt) { + if (dynamic_cast(evt)) { //2nd step - just increment progress by 1 for each processed file progress++; - } else if (const auto evt1 = dynamic_cast(evt)) { + } else if (const auto evt1 = dynamic_cast(evt)) { //3rd step - see how many bytes copied and how it corresponds to the overall progress progress = filesCount * WT_ENTRIES + static_cast(1.0 * filesCount * WT_BODY / static_cast(evt1->total) * static_cast(evt1->copied)); - } else if (const auto evt2 = dynamic_cast(evt)) { + } else if (const auto evt2 = dynamic_cast(evt)) { //4th step - see how many bytes were processed for signature and update the overall progress //once all bytes processed - report 100% progress explicitly progress = evt2->processed == evt2->total @@ -84,9 +83,9 @@ namespace pboman3 { }); try { - writer.write(cancel); + writer.write(&document, cancel); LOG(info, "Unpack complete") - } catch (const PboIoException& ex) { + } catch (const DiskAccessException& ex) { LOG(warning, "Task failed with exception:", ex) emit taskMessage("Failure | " + ex.message() + " | " + fi.absolutePath()); } diff --git a/pbom/model/task/packtask.h b/pbom/model/task/packtask.h index 2544a59..815fb94 100644 --- a/pbom/model/task/packtask.h +++ b/pbom/model/task/packtask.h @@ -5,6 +5,8 @@ #include "model/interactionparcel.h" namespace pboman3 { + using namespace domain; + class PackTask : public Task { public: PackTask(QString folder, QString outputDir); diff --git a/pbom/model/task/unpacktask.cpp b/pbom/model/task/unpacktask.cpp index 2c0cbf7..1c00518 100644 --- a/pbom/model/task/unpacktask.cpp +++ b/pbom/model/task/unpacktask.cpp @@ -1,18 +1,20 @@ #include "unpacktask.h" #include #include -#include "io/pboheaderreader.h" -#include "io/pboioexception.h" #include "io/bb/unpacktaskbackend.h" #include "io/bs/pbobinarysource.h" #include "io/pboentry.h" #include "domain/pbonode.h" -#include "model/rootreader.h" +#include "io/diskaccessexception.h" +#include "io/documentreader.h" +#include "io/pbofileformatexception.h" #include "util/log.h" #define LOG(...) LOGGER("model/task/UnpackTask", __VA_ARGS__) namespace pboman3 { + using namespace io; + UnpackTask::UnpackTask(QString pboPath, const QString& outputDir) : pboPath_(std::move(pboPath)), outputDir_(outputDir) { @@ -22,15 +24,17 @@ namespace pboman3 { LOG(info, "PBO file: ", pboPath_) LOG(info, "Output dir: ", outputDir_.absolutePath()) - PboFileHeader header; - if (!tryReadPboHeader(&header)) + QSharedPointer document; + if (!tryReadPboHeader(&document)) return; QDir pboDir; if (!tryCreatePboDir(&pboDir)) return; constexpr qsizetype startProgress = 0; - emit taskInitialized(pboPath_, startProgress, static_cast(header.entries.count())); + qint32 endProgress = 0; + countNodeFiles(document->root(), endProgress); + emit taskInitialized(pboPath_, startProgress, endProgress); std::function onError = [this](const QString& error) { emit taskMessage(error); @@ -42,18 +46,15 @@ namespace pboman3 { emit taskProgress(progress); }; - PboNode root("root", PboNodeType::Container, nullptr); - RootReader(&header, pboPath_).inflateRoot(&root); - UnpackTaskBackend be(pboDir); be.setOnError(&onError); be.setOnProgress(&onProgress); QList childNodes; - childNodes.reserve(root.count()); - for (PboNode* node: root) + childNodes.reserve(document->root()->count()); + for (PboNode* node : *document->root()) childNodes.append(node); - be.unpackSync(&root, childNodes, cancel); + be.unpackSync(document->root(), childNodes, cancel); LOG(info, "Unpack complete") } @@ -62,15 +63,18 @@ namespace pboman3 { return debug << "UnpackTask(PboPath=" << task.pboPath_ << ", OutputDir=" << task.outputDir_ << ")"; } - bool UnpackTask::tryReadPboHeader(PboFileHeader* header) { + bool UnpackTask::tryReadPboHeader(QSharedPointer* document) { try { - PboFile file(pboPath_); - file.open(QIODeviceBase::OpenModeFlag::ReadOnly); - *header = PboHeaderReader::readFileHeader(&file); - LOG(info, "The file header:", *header) + const DocumentReader reader(pboPath_); + *document = reader.read(); + LOG(info, "The document:", *document) return true; - } catch (const PboIoException& ex) { - LOG(warning, "Got error while reading the file header:", ex) + } catch (const DiskAccessException& ex) { + LOG(warning, "Got error while opening the file:", ex) + emit taskMessage("Can not read the file | " + pboPath_); + return false; + } catch (const PboFileFormatException& ex) { + LOG(warning, "Got error while reading the file document:", ex) emit taskMessage("The file is not a PBO | " + pboPath_); return false; } @@ -88,9 +92,9 @@ namespace pboman3 { return true; } - bool UnpackTask::tryCreateEntryDir(const QDir& pboDir, const QSharedPointer& entry) { + bool UnpackTask::tryCreateEntryDir(const QDir& pboDir, const QSharedPointer& entry) { QDir local(pboDir); - PboPath path(entry->fileName()); + PboPath path = entry->makePath(); auto it = path.begin(); const auto last = path.end() - 1; @@ -106,4 +110,13 @@ namespace pboman3 { return true; } + + void UnpackTask::countNodeFiles(const PboNode* node, qint32& count) { + if (node->nodeType() == PboNodeType::File) { + count++; + } else { + for (const PboNode* child : *node) + countNodeFiles(child, count); + } + } } diff --git a/pbom/model/task/unpacktask.h b/pbom/model/task/unpacktask.h index 61031ef..202a5a5 100644 --- a/pbom/model/task/unpacktask.h +++ b/pbom/model/task/unpacktask.h @@ -2,9 +2,11 @@ #include #include "task.h" -#include "io/pboheaderreader.h" +#include "domain/pbodocument.h" namespace pboman3 { + using namespace domain; + class UnpackTask : public Task { public: UnpackTask(QString pboPath, const QString& outputDir); @@ -17,10 +19,12 @@ namespace pboman3 { const QString pboPath_; const QDir outputDir_; - bool tryReadPboHeader(PboFileHeader* header); + bool tryReadPboHeader(QSharedPointer* document); bool tryCreatePboDir(QDir* dir); - bool tryCreateEntryDir(const QDir& pboDir, const QSharedPointer& entry); + bool tryCreateEntryDir(const QDir& pboDir, const QSharedPointer& entry); + + static void countNodeFiles(const PboNode* node, qint32& count); }; } diff --git a/pbom/ui/compresslist.h b/pbom/ui/compresslist.h index 77be89e..61b4d6a 100644 --- a/pbom/ui/compresslist.h +++ b/pbom/ui/compresslist.h @@ -4,10 +4,14 @@ #include "model/interactionparcel.h" namespace pboman3 { + using namespace model; + class CompressListItem : public QTreeWidgetItem { public: - CompressListItem(int id) : QTreeWidgetItem() { - id_ = id; + CompressListItem(int id) + : QTreeWidgetItem(), + id_(id) { + } int id() const { return id_; } @@ -16,7 +20,7 @@ namespace pboman3 { int id_; }; - class CompressList: public QTreeWidget { + class CompressList : public QTreeWidget { public: CompressList(QWidget* parent = nullptr); diff --git a/pbom/ui/conflictslist.h b/pbom/ui/conflictslist.h index 9832d5d..d0a2ed4 100644 --- a/pbom/ui/conflictslist.h +++ b/pbom/ui/conflictslist.h @@ -4,10 +4,14 @@ #include "model/conflictsparcel.h" namespace pboman3 { + using namespace model; + class ConflictsListItem : public QTreeWidgetItem { public: - ConflictsListItem(int id): QTreeWidgetItem() { - id_ = id; + ConflictsListItem(int id): + QTreeWidgetItem(), + id_(id) { + } int id() const { return id_; } diff --git a/pbom/ui/errordialog.cpp b/pbom/ui/errordialog.cpp index 4b90076..f4e9773 100644 --- a/pbom/ui/errordialog.cpp +++ b/pbom/ui/errordialog.cpp @@ -8,6 +8,13 @@ namespace pboman3 { ui_->label->setText(ex.message() + "
" + ex.file() + ""); } + ErrorDialog::ErrorDialog(const PboFileFormatException& ex, QWidget* parent) + : QDialog(parent), + ui_(new Ui::ErrorDialog) { + ui_->setupUi(this); + ui_->label->setText("Can not open the file. It is not a valid PBO."); + } + ErrorDialog::ErrorDialog(const AppException& ex, QWidget* parent) : QDialog(parent), ui_(new Ui::ErrorDialog) { diff --git a/pbom/ui/errordialog.h b/pbom/ui/errordialog.h index 5322114..3ab3cc6 100644 --- a/pbom/ui/errordialog.h +++ b/pbom/ui/errordialog.h @@ -1,7 +1,8 @@ #pragma once #include "ui_errordialog.h" -#include "model/diskaccessexception.h" +#include "io/diskaccessexception.h" +#include "io/pbofileformatexception.h" #include "util/exception.h" namespace Ui { @@ -9,10 +10,14 @@ namespace Ui { } namespace pboman3 { + using namespace io; + class ErrorDialog : public QDialog { public: ErrorDialog(const DiskAccessException& ex, QWidget* parent = nullptr); + ErrorDialog(const PboFileFormatException& ex, QWidget* parent = nullptr); + ErrorDialog(const AppException& ex, QWidget* parent = nullptr); ErrorDialog(const QString& text, QWidget* parent = nullptr); diff --git a/pbom/ui/fscollector.h b/pbom/ui/fscollector.h index 90e622d..0f9cd41 100644 --- a/pbom/ui/fscollector.h +++ b/pbom/ui/fscollector.h @@ -4,6 +4,8 @@ #include "model/interactionparcel.h" namespace pboman3 { + using namespace model; + class FsCollector { public: static NodeDescriptors collectFiles(const QList& urls); diff --git a/pbom/ui/headersdialog.cpp b/pbom/ui/headersdialog.cpp index 9b672a7..9ee49e1 100644 --- a/pbom/ui/headersdialog.cpp +++ b/pbom/ui/headersdialog.cpp @@ -2,6 +2,8 @@ #include #include #include +#include "domain/documentheaderstransaction.h" +#include "domain/validationexception.h" #include "util/log.h" #define LOG(...) LOGGER("ui/HeadersDialog", __VA_ARGS__) @@ -11,12 +13,13 @@ namespace pboman3 { constexpr int colValue = 1; constexpr Qt::ItemFlags itemFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; - HeadersDialog::HeadersDialog(HeadersModel* model, QWidget* parent) + HeadersDialog::HeadersDialog(DocumentHeaders* headers, QWidget* parent) : QDialog(parent), - ui_(new Ui::HeadersDialog), - model_(model) { + ui_(new Ui::HeadersDialog) { ui_->setupUi(this); + tran_ = headers->beginTransaction(); + renderHeaderItems(); setupConnections(); } @@ -28,21 +31,20 @@ namespace pboman3 { void HeadersDialog::accept() { LOG(info, "User clicked the Accept button") - QList> headers; - headers.reserve(ui_->treeWidget->topLevelItemCount()); - + tran_->clear(); for (int i = 0; i < ui_->treeWidget->topLevelItemCount(); i++) { const QTreeWidgetItem* item = ui_->treeWidget->topLevelItem(i); - if (isValidHeader(item->text(colName), item->text(colValue))) { + try { LOG(debug, "Append header, name=", item->text(colName), "value=", item->text(colValue)) - headers.append(QSharedPointer(new PboHeader(item->text(colName), item->text(colValue)))); + tran_->add(item->text(colName), item->text(colValue)); + } catch (const ValidationException& ex) { + LOG(debug, "Header failed validation:", ex.message()) } } - model_->setData(std::move(headers)); - LOG(info, "Accepting the dialog") + tran_->commit(); QDialog::accept(); } @@ -55,12 +57,12 @@ namespace pboman3 { LOG(info, "Rendering headers") ui_->treeWidget->setHeaderLabels(QList({"Name", "Value"})); - for (const QSharedPointer& header : *model_) { - LOG(debug, "Render header, name=", header->name, "value=", header->value) + for (const DocumentHeader* header : *tran_) { + LOG(debug, "Render header, name=", header->name(), "value=", header->value()) const auto item = new QTreeWidgetItem(); - item->setText(colName, header->name); - item->setText(colValue, header->value); + item->setText(colName, header->name()); + item->setText(colValue, header->value()); item->setFlags(itemFlags); ui_->treeWidget->addTopLevelItem(item); } @@ -98,10 +100,6 @@ namespace pboman3 { menu.exec(ui_->treeWidget->mapToGlobal(pos)); } - bool HeadersDialog::isValidHeader(const QString& name, const QString& value) const { - return !name.isEmpty() && !value.isEmpty(); - } - void HeadersDialog::onInsertClick(int index) const { LOG(info, "User clicked the Insert button") diff --git a/pbom/ui/headersdialog.h b/pbom/ui/headersdialog.h index 3c3d9c4..29f40aa 100644 --- a/pbom/ui/headersdialog.h +++ b/pbom/ui/headersdialog.h @@ -1,7 +1,7 @@ #pragma once #include "ui_headersdialog.h" -#include "model/headersmodel.h" +#include "domain/documentheaders.h" #include namespace Ui { @@ -10,9 +10,11 @@ namespace Ui { namespace pboman3 { + using namespace domain; + class HeadersDialog : public QDialog { public: - HeadersDialog(HeadersModel* model, QWidget* parent = nullptr); + HeadersDialog(DocumentHeaders* headers, QWidget* parent = nullptr); ~HeadersDialog() override; @@ -23,7 +25,7 @@ namespace pboman3 { private: Ui::HeadersDialog* ui_; - HeadersModel* model_; + QSharedPointer tran_; void renderHeaderItems() const; @@ -31,8 +33,6 @@ namespace pboman3 { void contextMenuRequested(const QPoint& pos) const; - bool isValidHeader(const QString& name, const QString& value) const; - void onInsertClick(int index) const; void onMoveClick(int index) const; diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index c1dbd9b..65debd0 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -12,15 +12,16 @@ #include "signaturedialog.h" #include "updatesdialog.h" #include "ui_mainwindow.h" -#include "model/diskaccessexception.h" -#include "model/pbofileformatexception.h" +#include "io/diskaccessexception.h" +#include "io/pbofileformatexception.h" +#include "model/pbomodel.h" #include "treewidget/treewidget.h" #include "util/log.h" #define LOG(...) LOGGER("ui/MainWindow", __VA_ARGS__) namespace pboman3 { - MainWindow::MainWindow(QWidget* parent, PboModel* model) + MainWindow::MainWindow(QWidget* parent, model::PboModel* model) : QMainWindow(parent), ui_(new Ui::MainWindow), model_(model), @@ -155,12 +156,12 @@ namespace pboman3 { void MainWindow::onViewHeadersClick() { LOG(info, "User clicked the ViewHeaders button") - HeadersDialog(model_->headers(), this).exec(); + HeadersDialog(model_->document()->headers(), this).exec(); } void MainWindow::onViewSignatureClick() { LOG(info, "User clicked the ViewSignature button") - SignatureDialog(model_->signature(), this).exec(); + SignatureDialog(&model_->document()->signature(), this).exec(); } void MainWindow::selectionExtractToClick() { @@ -215,7 +216,7 @@ namespace pboman3 { void MainWindow::selectionExtractContainerClick() const { LOG(info, "User clicked the ExtractToContainer button") const QDir dir = QFileInfo(model_->loadedPath()).dir(); - const QString folderName = GetFileNameWithoutExtension(model_->rootEntry()->title()); + const QString folderName = GetFileNameWithoutExtension(model_->document()->root()->title()); const QString folderPath = dir.filePath(folderName); if (!QDir(dir.filePath(folderName)).exists() && !dir.mkdir(folderName)) { LOG(critical, "Could not create the dir:", folderPath) @@ -224,7 +225,7 @@ namespace pboman3 { } LOG(info, "Extracting to:", folderPath) - ui_->treeWidget->selectionExtract(folderPath, model_->rootEntry()); + ui_->treeWidget->selectionExtract(folderPath, model_->document()->root()); } bool MainWindow::queryCloseUnsaved() { @@ -363,11 +364,11 @@ namespace pboman3 { LOG(info, "The Loaded status set to:", loaded) if (loaded) { - ui_->treeWidget->setRoot(model_->rootEntry()); + ui_->treeWidget->setRoot(model_->document()->root()); ui_->treeWidget->setDragDropMode(QAbstractItemView::DragDrop); - ui_->actionSelectionExtractContainer->setText(makeExtractToTitle(model_->rootEntry())); - connect(model_->rootEntry(), &PboNode::titleChanged, [this]() { - ui_->actionSelectionExtractContainer->setText(makeExtractToTitle(model_->rootEntry())); + ui_->actionSelectionExtractContainer->setText(makeExtractToTitle(model_->document()->root())); + connect(model_->document()->root(), &PboNode::titleChanged, [this]() { + ui_->actionSelectionExtractContainer->setText(makeExtractToTitle(model_->document()->root())); }); } else { ui_->treeWidget->resetRoot(); diff --git a/pbom/ui/mainwindow.h b/pbom/ui/mainwindow.h index d70b173..30355c9 100644 --- a/pbom/ui/mainwindow.h +++ b/pbom/ui/mainwindow.h @@ -11,11 +11,13 @@ namespace Ui { } namespace pboman3 { + using namespace model; + class MainWindow : public QMainWindow { Q_OBJECT public: - explicit MainWindow(QWidget* parent, PboModel* model); + explicit MainWindow(QWidget* parent, model::PboModel* model); ~MainWindow() override; diff --git a/pbom/ui/renamedialog.cpp b/pbom/ui/renamedialog.cpp index 947c8ef..d3b0dfd 100644 --- a/pbom/ui/renamedialog.cpp +++ b/pbom/ui/renamedialog.cpp @@ -1,21 +1,22 @@ #include "renamedialog.h" #include +#include "domain/pbonodetransaction.h" +#include "domain/validationexception.h" #include "util/log.h" #define LOG(...) LOGGER("ui/RenameDialog", __VA_ARGS__) namespace pboman3 { - RenameDialog::RenameDialog(QWidget* parent, - PboNode* node) + RenameDialog::RenameDialog(QWidget* parent, PboNode* node) : QDialog(parent), ui_(new Ui::RenameDialog), - node_(node), + initialTitle_(node->title()), isDirty_(false) { ui_->setupUi(this); - disableAccept(setErrorState("")); - - setTextAndSelect(); + transaction_ = node->beginTransaction(); + setErrorState(""); + setTextAndSelect(node->title()); } RenameDialog::~RenameDialog() { @@ -23,47 +24,46 @@ namespace pboman3 { } void RenameDialog::onTextEdited(const QString& title) const { - if (title == node_->title()) { + if (title == initialTitle_) { LOG(info, "Title was set to the initial value") - disableAccept(setErrorState("")); + setErrorState(""); } else { LOG(info, "Title was set to the value:", title) - if (isDirty_) { - LOG(info, "The input is dirty - validating") - disableAccept(setErrorState(node_->verifyTitle(title))); + try { + transaction_->setTitle(title); + setErrorState(""); + } catch (const ValidationException& ex) { + if (isDirty_) { + LOG(info, "The input is dirty - validating") + setErrorState(ex.message()); + } } } } void RenameDialog::accept() { const QString title = ui_->input->text(); - if (title == node_->title()) { + if (title == initialTitle_) { LOG(info, "The user clicked Accept having not changed the title") QDialog::reject(); } else { - if (isDirty_) { + try { LOG(info, "Updating the node title to:", title) - node_->setTitle(title); - QDialog::accept(); - } else { - LOG(info, "The user clicked Accept the 1st time - running validations") isDirty_ = true; - if (setErrorState(node_->verifyTitle(title))) { - LOG(info, "The title contained errors:", title) - disableAccept(true); - } else { - LOG(info, "The title contained no errors:", title) - node_->setTitle(title); - QDialog::accept(); - } + transaction_->setTitle(title); + transaction_->commit(); + QDialog::accept(); + } catch (const ValidationException& ex) { + LOG(info, "The title contained errors:", ex.message()) + setErrorState(ex.message()); } } } - bool RenameDialog::setErrorState(const TitleError& err) const { + void RenameDialog::setErrorState(const QString& err) const { LOG(info, "Set validation error to:", err) ui_->error->setText(err); - return !err.isEmpty(); + disableAccept(!err.isEmpty()); } void RenameDialog::disableAccept(bool disable) const { @@ -71,8 +71,7 @@ namespace pboman3 { ui_->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(disable); } - void RenameDialog::setTextAndSelect() const { - const QString title = node_->title(); + void RenameDialog::setTextAndSelect(const QString& title) const { qsizetype index = title.lastIndexOf("."); if (index < 1) index = title.length(); diff --git a/pbom/ui/renamedialog.h b/pbom/ui/renamedialog.h index 88aa1d8..fd100ff 100644 --- a/pbom/ui/renamedialog.h +++ b/pbom/ui/renamedialog.h @@ -5,6 +5,8 @@ #include "ui_renamedialog.h" namespace pboman3 { + using namespace domain; + class RenameDialog : public QDialog { Q_OBJECT @@ -20,13 +22,14 @@ namespace pboman3 { private: Ui::RenameDialog* ui_; - PboNode* node_; + QSharedPointer transaction_; + QString initialTitle_; bool isDirty_; - bool setErrorState(const TitleError& err) const; + void setErrorState(const QString& err) const; void disableAccept(bool disable) const; - void setTextAndSelect() const; + void setTextAndSelect(const QString& title) const; }; } diff --git a/pbom/ui/signaturedialog.cpp b/pbom/ui/signaturedialog.cpp index 5a7f6e5..59f2436 100644 --- a/pbom/ui/signaturedialog.cpp +++ b/pbom/ui/signaturedialog.cpp @@ -4,13 +4,13 @@ #define LOG(...) LOGGER("ui/SignatureDialog", __VA_ARGS__) namespace pboman3 { - SignatureDialog::SignatureDialog(const SignatureModel* model, QWidget* parent) + SignatureDialog::SignatureDialog(const QByteArray* signature, QWidget* parent) : QDialog(parent), ui_(new Ui::SignatureDialog) { ui_->setupUi(this); - QString sig = model->signatureString(); - if (sig.isNull()) { + QString sig = signature->toHex(' '); + if (sig.isEmpty()) { sig = "/*The file has no signature*/"; } LOG(info, "Show signature as:", sig) diff --git a/pbom/ui/signaturedialog.h b/pbom/ui/signaturedialog.h index bff7f60..19d5ab0 100644 --- a/pbom/ui/signaturedialog.h +++ b/pbom/ui/signaturedialog.h @@ -2,7 +2,6 @@ #include #include "ui_signaturedialog.h" -#include "model/signaturemodel.h" namespace Ui { class SignatureDialog; @@ -12,7 +11,7 @@ namespace pboman3 { class SignatureDialog : public QDialog { Q_OBJECT public: - SignatureDialog(const SignatureModel* model, QWidget* parent); + SignatureDialog(const QByteArray* signature, QWidget* parent); ~SignatureDialog() override; diff --git a/pbom/ui/treewidget/deleteop.h b/pbom/ui/treewidget/deleteop.h index 9a06d5b..fddac61 100644 --- a/pbom/ui/treewidget/deleteop.h +++ b/pbom/ui/treewidget/deleteop.h @@ -1,8 +1,10 @@ #pragma once -#include "model/pbomodel.h" +#include "domain/pbonode.h" namespace pboman3 { + using namespace domain; + class DeleteOp { public: void schedule(QList nodes); diff --git a/pbom/ui/treewidget/treewidget.cpp b/pbom/ui/treewidget/treewidget.cpp index 1190876..3ab924d 100644 --- a/pbom/ui/treewidget/treewidget.cpp +++ b/pbom/ui/treewidget/treewidget.cpp @@ -7,7 +7,7 @@ #include "ui/insertdialog.h" #include "util/exception.h" #include -#include "model/diskaccessexception.h" +#include "io/diskaccessexception.h" #include "ui/errordialog.h" #include "ui/win32/win32fileviewer.h" #include "util/log.h" @@ -379,7 +379,7 @@ namespace pboman3 { try { files = FsCollector::collectFiles(urls); LOG(debug, "Collected descriptors:", files) - } catch (const PboIoException& ex) { + } catch (const DiskAccessException& ex) { LOG(info, "Error when collecting - show error modal:", ex) UI_HANDLE_ERROR_RET(ex) } diff --git a/pbom/ui/treewidget/treewidget.h b/pbom/ui/treewidget/treewidget.h index 6129640..8fa8437 100644 --- a/pbom/ui/treewidget/treewidget.h +++ b/pbom/ui/treewidget/treewidget.h @@ -6,6 +6,8 @@ #include namespace pboman3 { + using namespace model; + class TreeWidget : public TreeWidgetBase { Q_OBJECT public: diff --git a/pbom/ui/treewidget/treewidgetbase.h b/pbom/ui/treewidget/treewidgetbase.h index 9cc8794..24723b5 100644 --- a/pbom/ui/treewidget/treewidgetbase.h +++ b/pbom/ui/treewidget/treewidgetbase.h @@ -5,6 +5,8 @@ #include "domain/pbonode.h" namespace pboman3 { + using namespace domain; + class TreeWidgetBase : public QTreeWidget { Q_OBJECT public: diff --git a/pbom/ui/treewidget/treewidgetitem.h b/pbom/ui/treewidget/treewidgetitem.h index beb89d7..b989fdf 100644 --- a/pbom/ui/treewidget/treewidgetitem.h +++ b/pbom/ui/treewidget/treewidgetitem.h @@ -5,6 +5,8 @@ #include "ui/iconmgr.h" namespace pboman3 { + using namespace domain; + class TreeWidgetItem : public QTreeWidgetItem, public QObject { public: TreeWidgetItem(PboNode* node); From bf3b0ac06432bb2547c800df700ec5b443cbecee Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Fri, 19 Nov 2021 19:34:45 +0300 Subject: [PATCH 14/39] The MainWindow now relies on the PboModel loaded state instead of its own --- pbom/model/pbomodel.cpp | 17 ++++++++++++++++- pbom/model/pbomodel.h | 4 ++++ pbom/ui/mainwindow.cpp | 18 ++++++++---------- pbom/ui/mainwindow.h | 2 +- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/pbom/model/pbomodel.cpp b/pbom/model/pbomodel.cpp index 4794375..0745f8d 100644 --- a/pbom/model/pbomodel.cpp +++ b/pbom/model/pbomodel.cpp @@ -24,13 +24,22 @@ namespace pboman3::model { setLoadedPath(path); const DocumentReader reader(path); - document_ = reader.read(); + try { + document_ = reader.read(); + } catch (const AppException& ex) { + LOG(debug, "Could not load the file:", ex) + setLoadedPath(nullptr); + throw; + } + connect(document_.get(), &PboDocument::changed, this, &PboModel::modelChanged); connect(document_.get(), &PboDocument::titleChanged, this, &PboModel::titleChanged); LOG(info, "Creating the binary backend") binaryBackend_ = QSharedPointer( new BinaryBackend(QUuid::createUuid().toString(QUuid::WithoutBraces))); + + emit loadedStatusChanged(true); } void PboModel::saveFile(const Cancel& cancel, const QString& filePath) { @@ -55,6 +64,8 @@ namespace pboman3::model { setLoadedPath(nullptr); + emit loadedStatusChanged(false); + document_.clear(); binaryBackend_.clear(); } @@ -134,6 +145,10 @@ namespace pboman3::model { return loadedPath_; } + bool PboModel::isLoaded() const { + return !loadedPath_.isNull(); + } + void PboModel::setLoadedPath(const QString& loadedFile) { if (loadedPath_ != loadedFile) { LOG(info, "Set the loadedPath to:", loadedFile) diff --git a/pbom/model/pbomodel.h b/pbom/model/pbomodel.h index ecc7d1f..8e90d58 100644 --- a/pbom/model/pbomodel.h +++ b/pbom/model/pbomodel.h @@ -33,11 +33,15 @@ namespace pboman3::model { const QString& loadedPath() const; + bool isLoaded() const; + signals: void modelChanged(); void loadedPathChanged(); + void loadedStatusChanged(bool loaded); + private: QString loadedPath_; QSharedPointer document_; diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index 65debd0..e085939 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -44,21 +44,19 @@ namespace pboman3 { LOG(info, "Loading the file:", fileName) try { model_->loadFile(fileName); - setLoaded(true); } catch (const PboFileFormatException& ex) { LOG(info, "Error when loading file - show error modal:", ex) UI_HANDLE_ERROR(ex) - unloadFile(); + if (model_->isLoaded()) unloadFile(); } catch (const DiskAccessException& ex) { LOG(info, "Error when loading file - show error modal:", ex) UI_HANDLE_ERROR(ex) - unloadFile(); + if (model_->isLoaded()) unloadFile(); } } void MainWindow::unloadFile() { setHasChanges(false); - setLoaded(false); model_->unloadFile(); } @@ -113,6 +111,7 @@ namespace pboman3 { connect(model_, &PboModel::modelChanged, this, [this]() { setHasChanges(true); }); connect(model_, &PboModel::loadedPathChanged, this, &MainWindow::updateWindowTitle); + connect(model_, &PboModel::loadedStatusChanged, this, &MainWindow::updateLoadedStatus); } void MainWindow::onFileOpenClick() { @@ -360,7 +359,7 @@ namespace pboman3 { updateWindowTitle(); } - void MainWindow::setLoaded(bool loaded) const { + void MainWindow::updateLoadedStatus(bool loaded) const { LOG(info, "The Loaded status set to:", loaded) if (loaded) { @@ -390,11 +389,7 @@ namespace pboman3 { } void MainWindow::updateWindowTitle() { - - if (model_->loadedPath().isNull()) { - LOG(info, "There is no loaded file - reset window title to the default") - setWindowTitle(PBOM_PROJECT_NAME); - } else { + if (model_->isLoaded()) { const QFileInfo fi(model_->loadedPath()); const QString title = hasChanges_ ? "*" + fi.fileName() + " - " + PBOM_PROJECT_NAME @@ -402,6 +397,9 @@ namespace pboman3 { LOG(info, "Set window title to:", title) setWindowTitle(title); + } else { + LOG(info, "There is no loaded file - reset window title to the default") + setWindowTitle(PBOM_PROJECT_NAME); } } diff --git a/pbom/ui/mainwindow.h b/pbom/ui/mainwindow.h index 30355c9..236ae12 100644 --- a/pbom/ui/mainwindow.h +++ b/pbom/ui/mainwindow.h @@ -66,7 +66,7 @@ namespace pboman3 { void setHasChanges(bool hasChanges); - void setLoaded(bool loaded) const; + void updateLoadedStatus(bool loaded) const; void updateWindowTitle(); From ea93370d9cffe1f2adc254d5e96c8c81183ee9dd Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Fri, 19 Nov 2021 20:06:24 +0300 Subject: [PATCH 15/39] Added namespaces to the most of the files --- pbom/domain/__test__/pbopath_test.cpp | 2 +- pbom/domain/abstractnode.h | 2 +- pbom/domain/binarysource.h | 2 +- pbom/domain/conflictresolution.h | 2 +- pbom/domain/pbopath.cpp | 2 +- pbom/domain/pbopath.h | 2 +- pbom/io/__test__/pboentry_test.cpp | 2 +- pbom/io/__test__/pbofile_test.cpp | 2 +- pbom/io/__test__/pboheader_test.cpp | 2 +- pbom/io/bb/__test__/sanitizedstring_test.cpp | 2 +- pbom/io/bb/sanitizedstring.cpp | 2 +- pbom/io/bb/sanitizedstring.h | 2 +- pbom/io/bs/binarysourcebase.h | 2 ++ pbom/io/lzh/__test__/compressionbuffer_test.cpp | 2 +- pbom/io/lzh/__test__/compressionchunk_test.cpp | 2 +- pbom/io/lzh/__test__/lzh_test.cpp | 2 +- pbom/io/lzh/compressionbuffer.cpp | 2 +- pbom/io/lzh/compressionbuffer.h | 2 +- pbom/io/lzh/compressionchunk.cpp | 2 +- pbom/io/lzh/compressionchunk.h | 2 +- pbom/io/lzh/decompressioncontext.cpp | 2 +- pbom/io/lzh/decompressioncontext.h | 2 +- pbom/io/lzh/lzh.cpp | 2 +- pbom/io/lzh/lzh.h | 2 +- pbom/io/lzh/lzhdecompressionexception.cpp | 2 +- pbom/io/lzh/lzhdecompressionexception.h | 2 +- pbom/io/pbodatastream.cpp | 2 +- pbom/io/pbodatastream.h | 2 +- pbom/io/pboentry.cpp | 2 +- pbom/io/pboentry.h | 3 ++- pbom/io/pbofile.cpp | 2 +- pbom/io/pbofile.h | 2 +- pbom/io/pboheader.cpp | 2 +- pbom/io/pboheader.h | 2 +- pbom/main.cpp | 8 ++++---- pbom/model/task/packtask.cpp | 2 +- pbom/model/task/packtask.h | 2 +- pbom/model/task/packwindowmodel.cpp | 2 +- pbom/model/task/packwindowmodel.h | 2 +- pbom/model/task/task.h | 2 +- pbom/model/task/taskwindowmodel.cpp | 2 +- pbom/model/task/taskwindowmodel.h | 2 +- pbom/model/task/unpacktask.cpp | 2 +- pbom/model/task/unpacktask.h | 2 +- pbom/model/task/unpackwindowmodel.cpp | 2 +- pbom/model/task/unpackwindowmodel.h | 2 +- pbom/ui/__test__/fscollector__test.cpp | 2 +- pbom/ui/__test__/updatesdialog__test.cpp | 2 +- pbom/ui/aboutdialog.cpp | 2 +- pbom/ui/aboutdialog.h | 2 +- pbom/ui/closedialog.cpp | 3 +-- pbom/ui/closedialog.h | 2 +- pbom/ui/compresslist.cpp | 2 +- pbom/ui/compresslist.h | 2 +- pbom/ui/conflictslist.cpp | 2 +- pbom/ui/conflictslist.h | 2 +- pbom/ui/errordialog.cpp | 2 +- pbom/ui/errordialog.h | 2 +- pbom/ui/fileviewer.h | 2 +- pbom/ui/fscollector.cpp | 2 +- pbom/ui/fscollector.h | 2 +- pbom/ui/headersdialog.cpp | 2 +- pbom/ui/headersdialog.h | 2 +- pbom/ui/iconmgr.h | 2 +- pbom/ui/insertdialog.cpp | 2 +- pbom/ui/insertdialog.h | 2 +- pbom/ui/insertdialog.ui | 12 ++++++------ pbom/ui/insertdialogbuttons.cpp | 2 +- pbom/ui/insertdialogbuttons.h | 2 +- pbom/ui/mainwindow.cpp | 2 +- pbom/ui/mainwindow.h | 2 +- pbom/ui/mainwindow.ui | 8 ++++---- pbom/ui/packwindow.cpp | 4 +++- pbom/ui/packwindow.h | 2 +- pbom/ui/progresswidget/progresswidget.cpp | 2 +- pbom/ui/progresswidget/progresswidget.h | 2 +- pbom/ui/renamedialog.cpp | 2 +- pbom/ui/renamedialog.h | 2 +- pbom/ui/signaturedialog.cpp | 2 +- pbom/ui/signaturedialog.h | 2 +- pbom/ui/statusbar.cpp | 2 +- pbom/ui/statusbar.h | 2 +- pbom/ui/taskwindow.cpp | 2 +- pbom/ui/taskwindow.h | 4 +++- pbom/ui/treewidget/__test__/treewidgetbase__test.cpp | 2 +- pbom/ui/treewidget/__test__/treewidgetbase_test.cpp | 2 +- pbom/ui/treewidget/deleteop.cpp | 2 +- pbom/ui/treewidget/deleteop.h | 2 +- pbom/ui/treewidget/treewidget.cpp | 2 +- pbom/ui/treewidget/treewidget.h | 2 +- pbom/ui/treewidget/treewidgetbase.cpp | 2 +- pbom/ui/treewidget/treewidgetbase.h | 2 +- pbom/ui/treewidget/treewidgetitem.cpp | 2 +- pbom/ui/treewidget/treewidgetitem.h | 2 +- pbom/ui/unpackwindow.cpp | 2 +- pbom/ui/unpackwindow.h | 2 +- pbom/ui/updatesdialog.cpp | 3 +-- pbom/ui/updatesdialog.h | 2 +- pbom/ui/win32/win32fileviewer.cpp | 2 +- pbom/ui/win32/win32fileviewer.h | 2 +- pbom/ui/win32/win32iconmgr.cpp | 2 +- pbom/ui/win32/win32iconmgr.h | 2 +- 102 files changed, 119 insertions(+), 114 deletions(-) diff --git a/pbom/domain/__test__/pbopath_test.cpp b/pbom/domain/__test__/pbopath_test.cpp index f541a31..b8045f9 100644 --- a/pbom/domain/__test__/pbopath_test.cpp +++ b/pbom/domain/__test__/pbopath_test.cpp @@ -1,7 +1,7 @@ #include "domain/pbopath.h" #include -namespace pboman3::test { +namespace pboman3::domain::test { TEST(PboPath, Ctor_Initializes_Empty_Path) { const PboPath p; ASSERT_EQ(p.length(), 0); diff --git a/pbom/domain/abstractnode.h b/pbom/domain/abstractnode.h index 60c9bd9..510eb60 100644 --- a/pbom/domain/abstractnode.h +++ b/pbom/domain/abstractnode.h @@ -3,7 +3,7 @@ #include #include "util/qpointerlistiterator.h" -namespace pboman3 { +namespace pboman3::domain { template class AbstractNode : public QObject { diff --git a/pbom/domain/binarysource.h b/pbom/domain/binarysource.h index 7204fb5..fe7b78c 100644 --- a/pbom/domain/binarysource.h +++ b/pbom/domain/binarysource.h @@ -4,7 +4,7 @@ #include #include "util/util.h" -namespace pboman3 { +namespace pboman3::domain { class BinarySource { protected: diff --git a/pbom/domain/conflictresolution.h b/pbom/domain/conflictresolution.h index a025ec6..f2eeed3 100644 --- a/pbom/domain/conflictresolution.h +++ b/pbom/domain/conflictresolution.h @@ -1,6 +1,6 @@ #pragma once -namespace pboman3 { +namespace pboman3::domain { enum class ConflictResolution { Unset = -1, Skip = 0, diff --git a/pbom/domain/pbopath.cpp b/pbom/domain/pbopath.cpp index 36889cd..bca2d83 100644 --- a/pbom/domain/pbopath.cpp +++ b/pbom/domain/pbopath.cpp @@ -1,7 +1,7 @@ #include "pbopath.h" #include -namespace pboman3 { +namespace pboman3::domain { PboPath::PboPath() : QList() { } diff --git a/pbom/domain/pbopath.h b/pbom/domain/pbopath.h index fbf861b..8da678d 100644 --- a/pbom/domain/pbopath.h +++ b/pbom/domain/pbopath.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::domain { class PboPath : public QList { public: PboPath(); diff --git a/pbom/io/__test__/pboentry_test.cpp b/pbom/io/__test__/pboentry_test.cpp index 42a4fb4..263b8e6 100644 --- a/pbom/io/__test__/pboentry_test.cpp +++ b/pbom/io/__test__/pboentry_test.cpp @@ -1,7 +1,7 @@ #include #include "io/pboentry.h" -namespace pboman3::test { +namespace pboman3::io::test { TEST(PboEntryTest, Ctor_Functional) { const PboEntry entry("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); ASSERT_EQ(entry.fileName(), "some-file"); diff --git a/pbom/io/__test__/pbofile_test.cpp b/pbom/io/__test__/pbofile_test.cpp index 6031721..f7a43de 100644 --- a/pbom/io/__test__/pbofile_test.cpp +++ b/pbom/io/__test__/pbofile_test.cpp @@ -2,7 +2,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(PboFileTest, ReadCString_Reads_Zero_Terminated_String) { static const char* mockString1 = "some string value 11"; static const char* mockString2 = "some string value 22"; diff --git a/pbom/io/__test__/pboheader_test.cpp b/pbom/io/__test__/pboheader_test.cpp index 4469420..4b2262c 100644 --- a/pbom/io/__test__/pboheader_test.cpp +++ b/pbom/io/__test__/pboheader_test.cpp @@ -1,7 +1,7 @@ #include #include "io/pboheader.h" -namespace pboman3::test { +namespace pboman3::io::test { TEST(PboHeaderTest, Ctor_Functional) { const PboHeader header("name1", "value1"); ASSERT_EQ(header.name, "name1"); diff --git a/pbom/io/bb/__test__/sanitizedstring_test.cpp b/pbom/io/bb/__test__/sanitizedstring_test.cpp index 3240021..a8f7786 100644 --- a/pbom/io/bb/__test__/sanitizedstring_test.cpp +++ b/pbom/io/bb/__test__/sanitizedstring_test.cpp @@ -2,7 +2,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { struct CtorParam { const QString sourceText; const QString expectedText; diff --git a/pbom/io/bb/sanitizedstring.cpp b/pbom/io/bb/sanitizedstring.cpp index 2857a6e..422175f 100644 --- a/pbom/io/bb/sanitizedstring.cpp +++ b/pbom/io/bb/sanitizedstring.cpp @@ -1,6 +1,6 @@ #include "sanitizedstring.h" -namespace pboman3 { +namespace pboman3::io { SanitizedString::SanitizedString(const QString& text) : originalText_(nullptr) { diff --git a/pbom/io/bb/sanitizedstring.h b/pbom/io/bb/sanitizedstring.h index 31edb84..7636220 100644 --- a/pbom/io/bb/sanitizedstring.h +++ b/pbom/io/bb/sanitizedstring.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::io { class SanitizedString { public: SanitizedString(const QString& text); diff --git a/pbom/io/bs/binarysourcebase.h b/pbom/io/bs/binarysourcebase.h index c7865bf..4fd702b 100644 --- a/pbom/io/bs/binarysourcebase.h +++ b/pbom/io/bs/binarysourcebase.h @@ -3,6 +3,8 @@ #include "domain/binarysource.h" namespace pboman3::io { + using namespace domain; + class BinarySourceBase: public BinarySource { public: BinarySourceBase(QString path); diff --git a/pbom/io/lzh/__test__/compressionbuffer_test.cpp b/pbom/io/lzh/__test__/compressionbuffer_test.cpp index 5e7e401..dc39e14 100644 --- a/pbom/io/lzh/__test__/compressionbuffer_test.cpp +++ b/pbom/io/lzh/__test__/compressionbuffer_test.cpp @@ -3,7 +3,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(CompressionBufferTest, Add1_Adds_Buffer_Longer_Than_Space_Remaining) { QTemporaryFile t1; t1.open(); diff --git a/pbom/io/lzh/__test__/compressionchunk_test.cpp b/pbom/io/lzh/__test__/compressionchunk_test.cpp index c696acc..a11a160 100644 --- a/pbom/io/lzh/__test__/compressionchunk_test.cpp +++ b/pbom/io/lzh/__test__/compressionchunk_test.cpp @@ -2,7 +2,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { TEST(CompressionChunkTest, Compose_Fulfills_Packet) { QByteArray dummy; dummy.resize(100); diff --git a/pbom/io/lzh/__test__/lzh_test.cpp b/pbom/io/lzh/__test__/lzh_test.cpp index 428c034..fe3364e 100644 --- a/pbom/io/lzh/__test__/lzh_test.cpp +++ b/pbom/io/lzh/__test__/lzh_test.cpp @@ -2,7 +2,7 @@ #include #include -namespace pboman3::test { +namespace pboman3::io::test { struct LzhTestParam { QString original; QString source; diff --git a/pbom/io/lzh/compressionbuffer.cpp b/pbom/io/lzh/compressionbuffer.cpp index 70196c0..a7e49dc 100644 --- a/pbom/io/lzh/compressionbuffer.cpp +++ b/pbom/io/lzh/compressionbuffer.cpp @@ -1,7 +1,7 @@ #include "compressionbuffer.h" #include -namespace pboman3 { +namespace pboman3::io { CompressionBuffer::CompressionBuffer(qint64 size) : size_(size), fullfillment_(0) { diff --git a/pbom/io/lzh/compressionbuffer.h b/pbom/io/lzh/compressionbuffer.h index 7647cdf..40876fc 100644 --- a/pbom/io/lzh/compressionbuffer.h +++ b/pbom/io/lzh/compressionbuffer.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::io { struct BufferIntersection { inline static qint64 posNo = -1; qint64 position; diff --git a/pbom/io/lzh/compressionchunk.cpp b/pbom/io/lzh/compressionchunk.cpp index c7ed913..4851f5a 100644 --- a/pbom/io/lzh/compressionchunk.cpp +++ b/pbom/io/lzh/compressionchunk.cpp @@ -1,6 +1,6 @@ #include "compressionchunk.h" -namespace pboman3 { +namespace pboman3::io { CompressionChunk::CompressionChunk() : format_(0b00000000), length_(0) { diff --git a/pbom/io/lzh/compressionchunk.h b/pbom/io/lzh/compressionchunk.h index 573fe83..100d07f 100644 --- a/pbom/io/lzh/compressionchunk.h +++ b/pbom/io/lzh/compressionchunk.h @@ -2,7 +2,7 @@ #include "compressionbuffer.h" -namespace pboman3 { +namespace pboman3::io { class CompressionChunk { public: CompressionChunk(); diff --git a/pbom/io/lzh/decompressioncontext.cpp b/pbom/io/lzh/decompressioncontext.cpp index 43d2e9e..0b0adde 100644 --- a/pbom/io/lzh/decompressioncontext.cpp +++ b/pbom/io/lzh/decompressioncontext.cpp @@ -1,6 +1,6 @@ #include "decompressioncontext.h" -namespace pboman3 { +namespace pboman3::io { DecompressionContext::DecompressionContext(QFileDevice* pSource, QFileDevice* pTarget) : format(0), crc(0), diff --git a/pbom/io/lzh/decompressioncontext.h b/pbom/io/lzh/decompressioncontext.h index b3103ed..48b8e19 100644 --- a/pbom/io/lzh/decompressioncontext.h +++ b/pbom/io/lzh/decompressioncontext.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::io { class DecompressionContext { public: int format; diff --git a/pbom/io/lzh/lzh.cpp b/pbom/io/lzh/lzh.cpp index d996723..86d2e4a 100644 --- a/pbom/io/lzh/lzh.cpp +++ b/pbom/io/lzh/lzh.cpp @@ -5,7 +5,7 @@ #define LOG(...) LOGGER("io/lzh/Lzh", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::io { void Lzh::decompress(QFileDevice* source, QFileDevice* target, int outputLength, const Cancel& cancel) { DecompressionContext ctx(source, target); const qint64 maxTargetOffset = target->pos() + outputLength; diff --git a/pbom/io/lzh/lzh.h b/pbom/io/lzh/lzh.h index de534f2..3f06841 100644 --- a/pbom/io/lzh/lzh.h +++ b/pbom/io/lzh/lzh.h @@ -3,7 +3,7 @@ #include "decompressioncontext.h" #include "util/util.h" -namespace pboman3 { +namespace pboman3::io { class Lzh { public: static void decompress(QFileDevice* source, QFileDevice* target, int outputLength, const Cancel& cancel); diff --git a/pbom/io/lzh/lzhdecompressionexception.cpp b/pbom/io/lzh/lzhdecompressionexception.cpp index 9a6381d..f078abc 100644 --- a/pbom/io/lzh/lzhdecompressionexception.cpp +++ b/pbom/io/lzh/lzhdecompressionexception.cpp @@ -1,7 +1,7 @@ #include "lzhdecompressionexception.h" #include -namespace pboman3 { +namespace pboman3::io { LzhDecompressionException::LzhDecompressionException(QString message) : AppException(std::move(message)) { } diff --git a/pbom/io/lzh/lzhdecompressionexception.h b/pbom/io/lzh/lzhdecompressionexception.h index 1a3d4f9..1ec380f 100644 --- a/pbom/io/lzh/lzhdecompressionexception.h +++ b/pbom/io/lzh/lzhdecompressionexception.h @@ -2,7 +2,7 @@ #include "util/exception.h" -namespace pboman3 { +namespace pboman3::io { class LzhDecompressionException : public AppException { public: LzhDecompressionException(QString message); diff --git a/pbom/io/pbodatastream.cpp b/pbom/io/pbodatastream.cpp index c7cd9ea..fca1e27 100644 --- a/pbom/io/pbodatastream.cpp +++ b/pbom/io/pbodatastream.cpp @@ -1,6 +1,6 @@ #include "pbodatastream.h" -namespace pboman3 { +namespace pboman3::io { PboDataStream::PboDataStream(PboFile* file) : QDataStream(file), file_(file) { diff --git a/pbom/io/pbodatastream.h b/pbom/io/pbodatastream.h index 293754a..492364f 100644 --- a/pbom/io/pbodatastream.h +++ b/pbom/io/pbodatastream.h @@ -3,7 +3,7 @@ #include "pbofile.h" #include -namespace pboman3 { +namespace pboman3::io { class PboEofException: public QException { }; diff --git a/pbom/io/pboentry.cpp b/pbom/io/pboentry.cpp index ad9cbf4..3969362 100644 --- a/pbom/io/pboentry.cpp +++ b/pbom/io/pboentry.cpp @@ -1,7 +1,7 @@ #include "pboentry.h" #include -namespace pboman3 { +namespace pboman3::io { PboEntry PboEntry::makeSignature() { return PboEntry("", PboPackingMethod::Product, 0, 0, 0, 0); } diff --git a/pbom/io/pboentry.h b/pbom/io/pboentry.h index cf1560c..8ab992b 100644 --- a/pbom/io/pboentry.h +++ b/pbom/io/pboentry.h @@ -2,8 +2,9 @@ #include "domain/pbopath.h" -namespace pboman3 { +namespace pboman3::io { using namespace std; + using namespace domain; enum class PboPackingMethod { Uncompressed = 0x00000000, diff --git a/pbom/io/pbofile.cpp b/pbom/io/pbofile.cpp index 6cd30a7..d607533 100644 --- a/pbom/io/pbofile.cpp +++ b/pbom/io/pbofile.cpp @@ -1,6 +1,6 @@ #include "pbofile.h" -namespace pboman3 { +namespace pboman3::io { PboFile::PboFile(const QString& name) : QFile(name) { } diff --git a/pbom/io/pbofile.h b/pbom/io/pbofile.h index 1e0aabf..314f86a 100644 --- a/pbom/io/pbofile.h +++ b/pbom/io/pbofile.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::io { class PboFile : public QFile { Q_OBJECT public: diff --git a/pbom/io/pboheader.cpp b/pbom/io/pboheader.cpp index d436ee2..6cbe1a0 100644 --- a/pbom/io/pboheader.cpp +++ b/pbom/io/pboheader.cpp @@ -1,6 +1,6 @@ #include "pboheader.h" -namespace pboman3 { +namespace pboman3::io { PboHeader PboHeader::makeBoundary() { return PboHeader(QString(), QString()); } diff --git a/pbom/io/pboheader.h b/pbom/io/pboheader.h index dcf898a..fa34d09 100644 --- a/pbom/io/pboheader.h +++ b/pbom/io/pboheader.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::io { struct PboHeader { static PboHeader makeBoundary(); diff --git a/pbom/main.cpp b/pbom/main.cpp index a85c10a..9a0150a 100644 --- a/pbom/main.cpp +++ b/pbom/main.cpp @@ -60,8 +60,8 @@ namespace pboman3 { ActivateCom(app); LOG(info, "Display the main window") - const auto model = QScopedPointer(new PboModel()); - MainWindow w(nullptr, model.get()); + const auto model = QScopedPointer(new model::PboModel()); + ui::MainWindow w(nullptr, model.get()); w.show(); if (!pboFile.isEmpty()) { @@ -86,7 +86,7 @@ namespace pboman3 { ActivateCom(app); int exitCode; - PackWindow w(nullptr); + ui::PackWindow w(nullptr); if (outputDir.isEmpty()) { exitCode = w.tryPackFoldersWithPrompt(folders) ? QApplication::exec() : 0; } else { @@ -109,7 +109,7 @@ namespace pboman3 { ActivateCom(app); int exitCode; - UnpackWindow w(nullptr); + ui::UnpackWindow w(nullptr); if (outputDir.isEmpty()) { exitCode = w.tryUnpackFilesWithPrompt(files) ? QApplication::exec() : 0; } else { diff --git a/pbom/model/task/packtask.cpp b/pbom/model/task/packtask.cpp index f013c3d..27cd910 100644 --- a/pbom/model/task/packtask.cpp +++ b/pbom/model/task/packtask.cpp @@ -6,7 +6,7 @@ #define LOG(...) LOGGER("model/task/PackTask", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::model { using namespace io; PackTask::PackTask(QString folder, QString outputDir) diff --git a/pbom/model/task/packtask.h b/pbom/model/task/packtask.h index 815fb94..70bf11b 100644 --- a/pbom/model/task/packtask.h +++ b/pbom/model/task/packtask.h @@ -4,7 +4,7 @@ #include "task.h" #include "model/interactionparcel.h" -namespace pboman3 { +namespace pboman3::model { using namespace domain; class PackTask : public Task { diff --git a/pbom/model/task/packwindowmodel.cpp b/pbom/model/task/packwindowmodel.cpp index c033c13..3ebd040 100644 --- a/pbom/model/task/packwindowmodel.cpp +++ b/pbom/model/task/packwindowmodel.cpp @@ -1,7 +1,7 @@ #include "packwindowmodel.h" #include "packtask.h" -namespace pboman3 { +namespace pboman3::model { PackWindowModel::PackWindowModel(const QStringList& folders, const QString& outputDir) { for (const QString& folder : folders) { QSharedPointer task(new PackTask(folder, outputDir)); diff --git a/pbom/model/task/packwindowmodel.h b/pbom/model/task/packwindowmodel.h index ab14f20..f9d1dbf 100644 --- a/pbom/model/task/packwindowmodel.h +++ b/pbom/model/task/packwindowmodel.h @@ -2,7 +2,7 @@ #include "taskwindowmodel.h" -namespace pboman3 { +namespace pboman3::model { class PackWindowModel: public TaskWindowModel{ public: PackWindowModel(const QStringList& folders, const QString& outputDir); diff --git a/pbom/model/task/task.h b/pbom/model/task/task.h index 34a3214..fba7eb3 100644 --- a/pbom/model/task/task.h +++ b/pbom/model/task/task.h @@ -3,7 +3,7 @@ #include #include "util/util.h" -namespace pboman3 { +namespace pboman3::model { class Task : public QObject { Q_OBJECT diff --git a/pbom/model/task/taskwindowmodel.cpp b/pbom/model/task/taskwindowmodel.cpp index 272426f..6d40b20 100644 --- a/pbom/model/task/taskwindowmodel.cpp +++ b/pbom/model/task/taskwindowmodel.cpp @@ -7,7 +7,7 @@ #define LOG(...) LOGGER("model/task/TaskWindowModel", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::model { void TaskWindowModel::start() { const int numThreads = std::min(QThread::idealThreadCount(), static_cast(tasks_.count())); for (int i = 0; i < numThreads; i++) { diff --git a/pbom/model/task/taskwindowmodel.h b/pbom/model/task/taskwindowmodel.h index 0195cf4..bf4f3f2 100644 --- a/pbom/model/task/taskwindowmodel.h +++ b/pbom/model/task/taskwindowmodel.h @@ -6,7 +6,7 @@ #include #include "task.h" -namespace pboman3 { +namespace pboman3::model { typedef qint32 ThreadId; class TaskWindowModel : public QObject { diff --git a/pbom/model/task/unpacktask.cpp b/pbom/model/task/unpacktask.cpp index 1c00518..232d257 100644 --- a/pbom/model/task/unpacktask.cpp +++ b/pbom/model/task/unpacktask.cpp @@ -12,7 +12,7 @@ #define LOG(...) LOGGER("model/task/UnpackTask", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::model { using namespace io; UnpackTask::UnpackTask(QString pboPath, const QString& outputDir) diff --git a/pbom/model/task/unpacktask.h b/pbom/model/task/unpacktask.h index 202a5a5..de13590 100644 --- a/pbom/model/task/unpacktask.h +++ b/pbom/model/task/unpacktask.h @@ -4,7 +4,7 @@ #include "task.h" #include "domain/pbodocument.h" -namespace pboman3 { +namespace pboman3::model { using namespace domain; class UnpackTask : public Task { diff --git a/pbom/model/task/unpackwindowmodel.cpp b/pbom/model/task/unpackwindowmodel.cpp index 3150876..b0e37b6 100644 --- a/pbom/model/task/unpackwindowmodel.cpp +++ b/pbom/model/task/unpackwindowmodel.cpp @@ -1,7 +1,7 @@ #include "unpackwindowmodel.h" #include "unpacktask.h" -namespace pboman3 { +namespace pboman3::model { UnpackWindowModel::UnpackWindowModel(const QStringList& pboFiles, const QString& outputDir) { for (const QString& pboFile : pboFiles) { QSharedPointer task(new UnpackTask(pboFile, outputDir)); diff --git a/pbom/model/task/unpackwindowmodel.h b/pbom/model/task/unpackwindowmodel.h index 533e278..4f98477 100644 --- a/pbom/model/task/unpackwindowmodel.h +++ b/pbom/model/task/unpackwindowmodel.h @@ -2,7 +2,7 @@ #include "taskwindowmodel.h" -namespace pboman3 { +namespace pboman3::model { class UnpackWindowModel: public TaskWindowModel{ public: UnpackWindowModel(const QStringList& pboFiles, const QString& outputDir); diff --git a/pbom/ui/__test__/fscollector__test.cpp b/pbom/ui/__test__/fscollector__test.cpp index dba87fc..2b3cd7b 100644 --- a/pbom/ui/__test__/fscollector__test.cpp +++ b/pbom/ui/__test__/fscollector__test.cpp @@ -3,7 +3,7 @@ #include #include "ui/fscollector.h" -namespace pboman3::test { +namespace pboman3::ui::test { TEST(FsCollectorTest, CollectFiles_Finds_Files_In_All_Folders) { const QString d1 = "f1"; const QString d11 = d1 + QDir::separator() + "f11"; diff --git a/pbom/ui/__test__/updatesdialog__test.cpp b/pbom/ui/__test__/updatesdialog__test.cpp index 29ca935..756a927 100644 --- a/pbom/ui/__test__/updatesdialog__test.cpp +++ b/pbom/ui/__test__/updatesdialog__test.cpp @@ -2,7 +2,7 @@ #include "ui/updatesdialog.h" #include -namespace pboman3::test { +namespace pboman3::ui::test { TEST(SemanticVersionTest, Ctor_Initializes_Without_Params) { const SemanticVersion ver; ASSERT_FALSE(ver.isValid()); diff --git a/pbom/ui/aboutdialog.cpp b/pbom/ui/aboutdialog.cpp index 5e2f50a..aa12ab5 100644 --- a/pbom/ui/aboutdialog.cpp +++ b/pbom/ui/aboutdialog.cpp @@ -3,7 +3,7 @@ #include #include "ui_aboutdialog.h" -namespace pboman3 { +namespace pboman3::ui { AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui_(new Ui::AboutDialog) { diff --git a/pbom/ui/aboutdialog.h b/pbom/ui/aboutdialog.h index c568c92..f6ba14b 100644 --- a/pbom/ui/aboutdialog.h +++ b/pbom/ui/aboutdialog.h @@ -6,7 +6,7 @@ namespace Ui { class AboutDialog; } -namespace pboman3 { +namespace pboman3::ui { class AboutDialog : public QDialog { public: AboutDialog(QWidget* parent = nullptr); diff --git a/pbom/ui/closedialog.cpp b/pbom/ui/closedialog.cpp index 3bcad4f..f88e421 100644 --- a/pbom/ui/closedialog.cpp +++ b/pbom/ui/closedialog.cpp @@ -1,7 +1,6 @@ #include "closedialog.h" -namespace pboman3 { - +namespace pboman3::ui { CloseDialog::CloseDialog(const QFileInfo& file, QWidget* parent) : QDialog(parent), ui_(new Ui::CloseDialog) { diff --git a/pbom/ui/closedialog.h b/pbom/ui/closedialog.h index a9eb5d5..041aed0 100644 --- a/pbom/ui/closedialog.h +++ b/pbom/ui/closedialog.h @@ -8,7 +8,7 @@ namespace Ui { class CloseDialog; } -namespace pboman3 { +namespace pboman3::ui { class CloseDialog : public QDialog { Q_OBJECT public: diff --git a/pbom/ui/compresslist.cpp b/pbom/ui/compresslist.cpp index 242101e..9349f82 100644 --- a/pbom/ui/compresslist.cpp +++ b/pbom/ui/compresslist.cpp @@ -3,7 +3,7 @@ #include #include -namespace pboman3 { +namespace pboman3::ui { constexpr int colTitleIndex = 0; constexpr int colExtensionIndex = 1; constexpr int colCompressIndex = 2; diff --git a/pbom/ui/compresslist.h b/pbom/ui/compresslist.h index 61b4d6a..adc36df 100644 --- a/pbom/ui/compresslist.h +++ b/pbom/ui/compresslist.h @@ -3,7 +3,7 @@ #include #include "model/interactionparcel.h" -namespace pboman3 { +namespace pboman3::ui { using namespace model; class CompressListItem : public QTreeWidgetItem { diff --git a/pbom/ui/conflictslist.cpp b/pbom/ui/conflictslist.cpp index b37cec7..ee6f38a 100644 --- a/pbom/ui/conflictslist.cpp +++ b/pbom/ui/conflictslist.cpp @@ -3,7 +3,7 @@ #include #include -namespace pboman3 { +namespace pboman3::ui { constexpr int colTitleIndex = 0; constexpr int colExtensionIndex = 1; constexpr int colResolutionIndex = 2; diff --git a/pbom/ui/conflictslist.h b/pbom/ui/conflictslist.h index d0a2ed4..253473d 100644 --- a/pbom/ui/conflictslist.h +++ b/pbom/ui/conflictslist.h @@ -3,7 +3,7 @@ #include "QTreeWidget" #include "model/conflictsparcel.h" -namespace pboman3 { +namespace pboman3::ui { using namespace model; class ConflictsListItem : public QTreeWidgetItem { diff --git a/pbom/ui/errordialog.cpp b/pbom/ui/errordialog.cpp index f4e9773..25f622b 100644 --- a/pbom/ui/errordialog.cpp +++ b/pbom/ui/errordialog.cpp @@ -1,6 +1,6 @@ #include "errordialog.h" -namespace pboman3 { +namespace pboman3::ui { ErrorDialog::ErrorDialog(const DiskAccessException& ex, QWidget* parent) : QDialog(parent), ui_(new Ui::ErrorDialog) { diff --git a/pbom/ui/errordialog.h b/pbom/ui/errordialog.h index 3ab3cc6..84f78bb 100644 --- a/pbom/ui/errordialog.h +++ b/pbom/ui/errordialog.h @@ -9,7 +9,7 @@ namespace Ui { class ErrorDialog; } -namespace pboman3 { +namespace pboman3::ui { using namespace io; class ErrorDialog : public QDialog { diff --git a/pbom/ui/fileviewer.h b/pbom/ui/fileviewer.h index e77ea01..b29eb15 100644 --- a/pbom/ui/fileviewer.h +++ b/pbom/ui/fileviewer.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::ui { class FileViewer { public: virtual void previewFile(const QString& path) = 0; diff --git a/pbom/ui/fscollector.cpp b/pbom/ui/fscollector.cpp index 7ce5aa5..d3d634b 100644 --- a/pbom/ui/fscollector.cpp +++ b/pbom/ui/fscollector.cpp @@ -4,7 +4,7 @@ #define LOG(...) LOGGER("ui/FsCollector", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { NodeDescriptors FsCollector::collectFiles(const QList& urls) { LOG(info, "Collecting the files at:", urls) diff --git a/pbom/ui/fscollector.h b/pbom/ui/fscollector.h index 0f9cd41..ed56e1d 100644 --- a/pbom/ui/fscollector.h +++ b/pbom/ui/fscollector.h @@ -3,7 +3,7 @@ #include #include "model/interactionparcel.h" -namespace pboman3 { +namespace pboman3::ui { using namespace model; class FsCollector { diff --git a/pbom/ui/headersdialog.cpp b/pbom/ui/headersdialog.cpp index 9ee49e1..5d8199f 100644 --- a/pbom/ui/headersdialog.cpp +++ b/pbom/ui/headersdialog.cpp @@ -8,7 +8,7 @@ #define LOG(...) LOGGER("ui/HeadersDialog", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { constexpr int colName = 0; constexpr int colValue = 1; constexpr Qt::ItemFlags itemFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; diff --git a/pbom/ui/headersdialog.h b/pbom/ui/headersdialog.h index 29f40aa..87fde35 100644 --- a/pbom/ui/headersdialog.h +++ b/pbom/ui/headersdialog.h @@ -9,7 +9,7 @@ namespace Ui { } -namespace pboman3 { +namespace pboman3::ui { using namespace domain; class HeadersDialog : public QDialog { diff --git a/pbom/ui/iconmgr.h b/pbom/ui/iconmgr.h index f180c84..bd5d74c 100644 --- a/pbom/ui/iconmgr.h +++ b/pbom/ui/iconmgr.h @@ -3,7 +3,7 @@ // ReSharper disable once CppUnusedIncludeDirective #include -namespace pboman3 { +namespace pboman3::ui { class IconMgr { public: virtual ~IconMgr() = default; diff --git a/pbom/ui/insertdialog.cpp b/pbom/ui/insertdialog.cpp index 2184109..41e4266 100644 --- a/pbom/ui/insertdialog.cpp +++ b/pbom/ui/insertdialog.cpp @@ -3,7 +3,7 @@ #define LOG(...) LOGGER("ui/InsertDialog", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { InsertDialog::InsertDialog(QWidget* parent, Mode dialogMode, NodeDescriptors* descriptors, ConflictsParcel* conflicts) : QDialog(parent), diff --git a/pbom/ui/insertdialog.h b/pbom/ui/insertdialog.h index 4c02601..58cacde 100644 --- a/pbom/ui/insertdialog.h +++ b/pbom/ui/insertdialog.h @@ -8,7 +8,7 @@ namespace Ui { class InsertDialog; } -namespace pboman3 { +namespace pboman3::ui { class InsertDialog : public QDialog { Q_OBJECT diff --git a/pbom/ui/insertdialog.ui b/pbom/ui/insertdialog.ui index 6996339..9f3794b 100644 --- a/pbom/ui/insertdialog.ui +++ b/pbom/ui/insertdialog.ui @@ -40,7 +40,7 @@ - + Qt::CustomContextMenu @@ -74,7 +74,7 @@ - + QFrame::Box @@ -99,7 +99,7 @@ - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -109,17 +109,17 @@ - pboman3::CompressList + pboman3::ui::CompressList QTreeView
ui/compresslist.h
- pboman3::ConflictsList + pboman3::ui::ConflictsList QTreeWidget
ui/conflictslist.h
- pboman3::InsertDialogButtons + pboman3::ui::InsertDialogButtons QDialogButtonBox
ui/insertdialogbuttons.h
diff --git a/pbom/ui/insertdialogbuttons.cpp b/pbom/ui/insertdialogbuttons.cpp index 8cc4a9c..6c28005 100644 --- a/pbom/ui/insertdialogbuttons.cpp +++ b/pbom/ui/insertdialogbuttons.cpp @@ -4,7 +4,7 @@ #define LOG(...) LOGGER("ui/InsertDialogButtons", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { InsertDialogButtons::InsertDialogButtons(QWidget* parent) : QDialogButtonBox(StandardButtons(Ok | Cancel), parent), btnNext_(nullptr), diff --git a/pbom/ui/insertdialogbuttons.h b/pbom/ui/insertdialogbuttons.h index 036e894..af38160 100644 --- a/pbom/ui/insertdialogbuttons.h +++ b/pbom/ui/insertdialogbuttons.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::ui { class InsertDialogButtons : public QDialogButtonBox { Q_OBJECT diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index e085939..afd6cc2 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -20,7 +20,7 @@ #define LOG(...) LOGGER("ui/MainWindow", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { MainWindow::MainWindow(QWidget* parent, model::PboModel* model) : QMainWindow(parent), ui_(new Ui::MainWindow), diff --git a/pbom/ui/mainwindow.h b/pbom/ui/mainwindow.h index 236ae12..fa5362d 100644 --- a/pbom/ui/mainwindow.h +++ b/pbom/ui/mainwindow.h @@ -10,7 +10,7 @@ namespace Ui { class MainWindow; } -namespace pboman3 { +namespace pboman3::ui { using namespace model; class MainWindow : public QMainWindow { diff --git a/pbom/ui/mainwindow.ui b/pbom/ui/mainwindow.ui index 104ebc7..ce7111c 100644 --- a/pbom/ui/mainwindow.ui +++ b/pbom/ui/mainwindow.ui @@ -50,7 +50,7 @@ 3 - + true @@ -153,7 +153,7 @@ - + Open.. @@ -325,12 +325,12 @@ - pboman3::TreeWidget + pboman3::ui::TreeWidget QTreeWidget
treewidget/treewidget.h
- pboman3::StatusBar + pboman3::ui::StatusBar QStatusBar
statusbar.h
diff --git a/pbom/ui/packwindow.cpp b/pbom/ui/packwindow.cpp index 868fade..1ed1bab 100644 --- a/pbom/ui/packwindow.cpp +++ b/pbom/ui/packwindow.cpp @@ -2,7 +2,9 @@ #include #include "model/task/packwindowmodel.h" -namespace pboman3 { +namespace pboman3::ui { + using namespace model; + PackWindow::PackWindow(QWidget* parent) : TaskWindow(parent) { QString title = "Pack PBO(s) - "; diff --git a/pbom/ui/packwindow.h b/pbom/ui/packwindow.h index b8cf4b2..fbf42cf 100644 --- a/pbom/ui/packwindow.h +++ b/pbom/ui/packwindow.h @@ -2,7 +2,7 @@ #include "taskwindow.h" -namespace pboman3 { +namespace pboman3::ui { class PackWindow : public TaskWindow { public: PackWindow(QWidget* parent); diff --git a/pbom/ui/progresswidget/progresswidget.cpp b/pbom/ui/progresswidget/progresswidget.cpp index 89b0a6d..d84997d 100644 --- a/pbom/ui/progresswidget/progresswidget.cpp +++ b/pbom/ui/progresswidget/progresswidget.cpp @@ -2,7 +2,7 @@ #include #include -namespace pboman3 { +namespace pboman3::ui { ProgressWidget::ProgressWidget(QWidget* parent) : QWidget(parent) { progress_ = new QProgressBar(this); diff --git a/pbom/ui/progresswidget/progresswidget.h b/pbom/ui/progresswidget/progresswidget.h index 71e655e..7bdfc56 100644 --- a/pbom/ui/progresswidget/progresswidget.h +++ b/pbom/ui/progresswidget/progresswidget.h @@ -4,7 +4,7 @@ #include #include -namespace pboman3 { +namespace pboman3::ui { class ProgressWidget : public QWidget { public: ProgressWidget(QWidget* parent = nullptr); diff --git a/pbom/ui/renamedialog.cpp b/pbom/ui/renamedialog.cpp index d3b0dfd..46db797 100644 --- a/pbom/ui/renamedialog.cpp +++ b/pbom/ui/renamedialog.cpp @@ -6,7 +6,7 @@ #define LOG(...) LOGGER("ui/RenameDialog", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { RenameDialog::RenameDialog(QWidget* parent, PboNode* node) : QDialog(parent), ui_(new Ui::RenameDialog), diff --git a/pbom/ui/renamedialog.h b/pbom/ui/renamedialog.h index fd100ff..1becd17 100644 --- a/pbom/ui/renamedialog.h +++ b/pbom/ui/renamedialog.h @@ -4,7 +4,7 @@ #include "domain/pbonode.h" #include "ui_renamedialog.h" -namespace pboman3 { +namespace pboman3::ui { using namespace domain; class RenameDialog : public QDialog { diff --git a/pbom/ui/signaturedialog.cpp b/pbom/ui/signaturedialog.cpp index 59f2436..4a843c4 100644 --- a/pbom/ui/signaturedialog.cpp +++ b/pbom/ui/signaturedialog.cpp @@ -3,7 +3,7 @@ #define LOG(...) LOGGER("ui/SignatureDialog", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { SignatureDialog::SignatureDialog(const QByteArray* signature, QWidget* parent) : QDialog(parent), ui_(new Ui::SignatureDialog) { diff --git a/pbom/ui/signaturedialog.h b/pbom/ui/signaturedialog.h index 19d5ab0..c240c5d 100644 --- a/pbom/ui/signaturedialog.h +++ b/pbom/ui/signaturedialog.h @@ -7,7 +7,7 @@ namespace Ui { class SignatureDialog; } -namespace pboman3 { +namespace pboman3::ui { class SignatureDialog : public QDialog { Q_OBJECT public: diff --git a/pbom/ui/statusbar.cpp b/pbom/ui/statusbar.cpp index 87c132a..cce1643 100644 --- a/pbom/ui/statusbar.cpp +++ b/pbom/ui/statusbar.cpp @@ -2,7 +2,7 @@ #include "util/exception.h" #include "util/log.h" -namespace pboman3 { +namespace pboman3::ui { StatusBar::StatusBar(QWidget* parent) : QStatusBar(parent) { diff --git a/pbom/ui/statusbar.h b/pbom/ui/statusbar.h index 053055e..0ce148b 100644 --- a/pbom/ui/statusbar.h +++ b/pbom/ui/statusbar.h @@ -6,7 +6,7 @@ #include #include -namespace pboman3 { +namespace pboman3::ui { class StatusBar : public QStatusBar { Q_OBJECT public: diff --git a/pbom/ui/taskwindow.cpp b/pbom/ui/taskwindow.cpp index 1d346ce..0e52ad9 100644 --- a/pbom/ui/taskwindow.cpp +++ b/pbom/ui/taskwindow.cpp @@ -6,7 +6,7 @@ #include "model/task/taskwindowmodel.h" #include "util/exception.h" -namespace pboman3 { +namespace pboman3::ui { TaskWindow::TaskWindow(QWidget* parent) : QMainWindow(parent), ui_(new Ui::TaskWindow), diff --git a/pbom/ui/taskwindow.h b/pbom/ui/taskwindow.h index 21e8f4f..b8d2182 100644 --- a/pbom/ui/taskwindow.h +++ b/pbom/ui/taskwindow.h @@ -10,7 +10,9 @@ namespace Ui { class TaskWindow; } -namespace pboman3 { +namespace pboman3::ui { + using namespace model; + class TaskWindow : public QMainWindow { Q_OBJECT diff --git a/pbom/ui/treewidget/__test__/treewidgetbase__test.cpp b/pbom/ui/treewidget/__test__/treewidgetbase__test.cpp index f4d1056..52cac72 100644 --- a/pbom/ui/treewidget/__test__/treewidgetbase__test.cpp +++ b/pbom/ui/treewidget/__test__/treewidgetbase__test.cpp @@ -3,7 +3,7 @@ #include #include "ui/treewidget/treewidgetbase.h" -namespace pboman3::test { +namespace pboman3::ui::test { namespace treewidgetbase_test { class MockTreeWidgetBase : public TreeWidgetBase { public: diff --git a/pbom/ui/treewidget/__test__/treewidgetbase_test.cpp b/pbom/ui/treewidget/__test__/treewidgetbase_test.cpp index a63a74b..d38ea64 100644 --- a/pbom/ui/treewidget/__test__/treewidgetbase_test.cpp +++ b/pbom/ui/treewidget/__test__/treewidgetbase_test.cpp @@ -3,7 +3,7 @@ #include #include "ui/treewidget/treewidgetbase.h" -namespace pboman3::test { +namespace pboman3::ui::test { namespace treewidgetbase__test { class MockTreeWidgetBase : public TreeWidgetBase { protected: diff --git a/pbom/ui/treewidget/deleteop.cpp b/pbom/ui/treewidget/deleteop.cpp index d8d5182..6b6aa66 100644 --- a/pbom/ui/treewidget/deleteop.cpp +++ b/pbom/ui/treewidget/deleteop.cpp @@ -3,7 +3,7 @@ #define LOG(...) LOGGER("ui/treewidget/DeleteOp", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { void DeleteOp::schedule(QList nodes) { LOG(info, "Schedule delete for", nodes.count(), "nodes") nodes_ = std::move(nodes); diff --git a/pbom/ui/treewidget/deleteop.h b/pbom/ui/treewidget/deleteop.h index fddac61..6092cf3 100644 --- a/pbom/ui/treewidget/deleteop.h +++ b/pbom/ui/treewidget/deleteop.h @@ -2,7 +2,7 @@ #include "domain/pbonode.h" -namespace pboman3 { +namespace pboman3::ui { using namespace domain; class DeleteOp { diff --git a/pbom/ui/treewidget/treewidget.cpp b/pbom/ui/treewidget/treewidget.cpp index 3ab924d..2cf9ba0 100644 --- a/pbom/ui/treewidget/treewidget.cpp +++ b/pbom/ui/treewidget/treewidget.cpp @@ -14,7 +14,7 @@ #define LOG(...) LOGGER("ui/treewidget/TreeWidget", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { #define MIME_TYPE_PBOMAN "application/pboman3" TreeWidget::TreeWidget(QWidget* parent) diff --git a/pbom/ui/treewidget/treewidget.h b/pbom/ui/treewidget/treewidget.h index 8fa8437..17f8a13 100644 --- a/pbom/ui/treewidget/treewidget.h +++ b/pbom/ui/treewidget/treewidget.h @@ -5,7 +5,7 @@ #include "model/pbomodel.h" #include -namespace pboman3 { +namespace pboman3::ui { using namespace model; class TreeWidget : public TreeWidgetBase { diff --git a/pbom/ui/treewidget/treewidgetbase.cpp b/pbom/ui/treewidget/treewidgetbase.cpp index d017af5..2648fe5 100644 --- a/pbom/ui/treewidget/treewidgetbase.cpp +++ b/pbom/ui/treewidget/treewidgetbase.cpp @@ -7,7 +7,7 @@ #define LOG(...) LOGGER("ui/treewidget/TreeWidgetBase", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { TreeWidgetBase::TreeWidgetBase(QWidget* parent) : QTreeWidget(parent), root_(nullptr), diff --git a/pbom/ui/treewidget/treewidgetbase.h b/pbom/ui/treewidget/treewidgetbase.h index 24723b5..aabb7c4 100644 --- a/pbom/ui/treewidget/treewidgetbase.h +++ b/pbom/ui/treewidget/treewidgetbase.h @@ -4,7 +4,7 @@ #include "treewidgetitem.h" #include "domain/pbonode.h" -namespace pboman3 { +namespace pboman3::ui { using namespace domain; class TreeWidgetBase : public QTreeWidget { diff --git a/pbom/ui/treewidget/treewidgetitem.cpp b/pbom/ui/treewidget/treewidgetitem.cpp index 9b4c677..4c4bf8b 100644 --- a/pbom/ui/treewidget/treewidgetitem.cpp +++ b/pbom/ui/treewidget/treewidgetitem.cpp @@ -3,7 +3,7 @@ #include "ui/renamedialog.h" #include "ui/win32/win32iconmgr.h" -namespace pboman3 { +namespace pboman3::ui { TreeWidgetItem::TreeWidgetItem(PboNode* node) : TreeWidgetItem(node, QSharedPointer(new Win32IconMgr)) { } diff --git a/pbom/ui/treewidget/treewidgetitem.h b/pbom/ui/treewidget/treewidgetitem.h index b989fdf..11f1ad8 100644 --- a/pbom/ui/treewidget/treewidgetitem.h +++ b/pbom/ui/treewidget/treewidgetitem.h @@ -4,7 +4,7 @@ #include "domain/pbonode.h" #include "ui/iconmgr.h" -namespace pboman3 { +namespace pboman3::ui { using namespace domain; class TreeWidgetItem : public QTreeWidgetItem, public QObject { diff --git a/pbom/ui/unpackwindow.cpp b/pbom/ui/unpackwindow.cpp index fe63515..320d8c8 100644 --- a/pbom/ui/unpackwindow.cpp +++ b/pbom/ui/unpackwindow.cpp @@ -2,7 +2,7 @@ #include #include "model/task/unpackwindowmodel.h" -namespace pboman3 { +namespace pboman3::ui { UnpackWindow::UnpackWindow(QWidget* parent) : TaskWindow(parent) { QString title = "Unpack PBO(s) - "; diff --git a/pbom/ui/unpackwindow.h b/pbom/ui/unpackwindow.h index b01d10d..315e631 100644 --- a/pbom/ui/unpackwindow.h +++ b/pbom/ui/unpackwindow.h @@ -2,7 +2,7 @@ #include "taskwindow.h" -namespace pboman3 { +namespace pboman3::ui { class UnpackWindow : public TaskWindow { public: UnpackWindow(QWidget* parent); diff --git a/pbom/ui/updatesdialog.cpp b/pbom/ui/updatesdialog.cpp index 626eaee..c5e92a9 100644 --- a/pbom/ui/updatesdialog.cpp +++ b/pbom/ui/updatesdialog.cpp @@ -6,8 +6,7 @@ #define LOG(...) LOGGER("ui/UpdatesDialog", __VA_ARGS__) -namespace pboman3 { - +namespace pboman3::ui { SemanticVersion::SemanticVersion(QString rawVersion) : raw_(std::move(rawVersion)) { const QRegularExpression reg("v?(\\d+)\\.(\\d+)\\.(\\d+)"); diff --git a/pbom/ui/updatesdialog.h b/pbom/ui/updatesdialog.h index 2f364a3..3324619 100644 --- a/pbom/ui/updatesdialog.h +++ b/pbom/ui/updatesdialog.h @@ -8,7 +8,7 @@ namespace Ui { class UpdatesDialog; } -namespace pboman3 { +namespace pboman3::ui { class SemanticVersion { public: explicit SemanticVersion(QString rawVersion); diff --git a/pbom/ui/win32/win32fileviewer.cpp b/pbom/ui/win32/win32fileviewer.cpp index 447d352..5c32481 100644 --- a/pbom/ui/win32/win32fileviewer.cpp +++ b/pbom/ui/win32/win32fileviewer.cpp @@ -6,7 +6,7 @@ #define LOG(...) LOGGER("ui/win32/Win32FileViewer", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { Win32FileViewerException::Win32FileViewerException(QString message) : AppException(std::move(message)) { } diff --git a/pbom/ui/win32/win32fileviewer.h b/pbom/ui/win32/win32fileviewer.h index de0a733..fe9f25f 100644 --- a/pbom/ui/win32/win32fileviewer.h +++ b/pbom/ui/win32/win32fileviewer.h @@ -3,7 +3,7 @@ #include "ui/fileviewer.h" #include "util/exception.h" -namespace pboman3 { +namespace pboman3::ui { class Win32FileViewerException : public AppException { public: Win32FileViewerException(QString message); diff --git a/pbom/ui/win32/win32iconmgr.cpp b/pbom/ui/win32/win32iconmgr.cpp index c83700a..3a5377d 100644 --- a/pbom/ui/win32/win32iconmgr.cpp +++ b/pbom/ui/win32/win32iconmgr.cpp @@ -8,7 +8,7 @@ #define LOG(...) LOGGER("ui/win32/Win32IconMgr", __VA_ARGS__) -namespace pboman3 { +namespace pboman3::ui { Win32IconMgr::Win32IconMgr() { cache_[""] = QIcon(":ifile.png"); cache_[":folder-closed:"] = QIcon(":ifolderclosed.png"); diff --git a/pbom/ui/win32/win32iconmgr.h b/pbom/ui/win32/win32iconmgr.h index a853e00..58c599f 100644 --- a/pbom/ui/win32/win32iconmgr.h +++ b/pbom/ui/win32/win32iconmgr.h @@ -3,7 +3,7 @@ #include "ui/iconmgr.h" #include -namespace pboman3 { +namespace pboman3::ui { class Win32IconMgr: public IconMgr { public: Win32IconMgr(); From 9360f904d13cc96e28dc984c09d79c0157cf2633 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Fri, 19 Nov 2021 20:12:03 +0300 Subject: [PATCH 16/39] Did not like the user experience when the file signature is reset after any file change. Rolled it back. --- pbom/domain/__test__/pbodocument_test.cpp | 81 ----------------------- pbom/domain/pbodocument.cpp | 2 - 2 files changed, 83 deletions(-) diff --git a/pbom/domain/__test__/pbodocument_test.cpp b/pbom/domain/__test__/pbodocument_test.cpp index e9cc383..c6b4bcc 100644 --- a/pbom/domain/__test__/pbodocument_test.cpp +++ b/pbom/domain/__test__/pbodocument_test.cpp @@ -30,29 +30,6 @@ namespace pboman3::domain::test { ASSERT_EQ(count, 1); } - TEST(PboDocumentTest, Public_Ctor_Changed_Clears_Signature_When_Hierarchy_Changes) { - QByteArray signature; - signature.fill('p', 20); - - PboDocument document("file.pbo"); - document.setSignature(std::move(signature)); - - document.root()->createHierarchy(PboPath("f1.txt"), ConflictResolution::Unset); - - ASSERT_EQ(document.signature(), QByteArray()); - } - - TEST(PboDocumentTest, Repository_Ctor_Changed_Clears_Signature_When_Hierarchy_Changes) { - QByteArray signature; - signature.fill('p', 10); - - const PboDocument document("file.pbo", QList>{}, signature); - - document.root()->createHierarchy(PboPath("f1.txt"), ConflictResolution::Unset); - - ASSERT_EQ(document.signature(), QByteArray()); - } - TEST(PboDocumentTest, Public_Ctor_Changed_Fires_When_Headers_Change) { const PboDocument document("file.pbo"); @@ -86,35 +63,6 @@ namespace pboman3::domain::test { ASSERT_EQ(count, 1); } - TEST(PboDocumentTest, Public_Ctor_Changed_Clears_Signature_When_Header_Change) { - QByteArray signature; - signature.fill('p', 20); - - PboDocument document("file.pbo"); - document.setSignature(std::move(signature)); - - QSharedPointer tran = document.headers()->beginTransaction(); - tran->add("h1", "v1"); - tran->commit(); - tran.clear(); - - ASSERT_EQ(document.signature(), QByteArray()); - } - - TEST(PboDocumentTest, Repository_Ctor_Changed_Clears_Signature_When_Header_Change) { - QByteArray signature; - signature.fill('p', 10); - - const PboDocument document("file.pbo", QList>{}, signature); - - QSharedPointer tran = document.headers()->beginTransaction(); - tran->add("h1", "v1"); - tran->commit(); - tran.clear(); - - ASSERT_EQ(document.signature(), QByteArray()); - } - TEST(PboDocumentTest, Public_Ctor_TitleChanged_Fires) { const PboDocument document("file.pbo"); @@ -153,33 +101,4 @@ namespace pboman3::domain::test { ASSERT_EQ(count, 1); ASSERT_EQ(title, "new.pbo"); } - - TEST(PboDocumentTest, Public_Ctor_TitleChanged_Clears_Signature) { - QByteArray signature; - signature.fill('p', 20); - - PboDocument document("file.pbo"); - document.setSignature(std::move(signature)); - - QSharedPointer tran = document.root()->beginTransaction(); - tran->setTitle("new.pbo"); - tran->commit(); - tran.clear(); - - ASSERT_EQ(document.signature(), QByteArray()); - } - - TEST(PboDocumentTest, Repository_Ctor_TitleChanged_Clears_Signature) { - QByteArray signature; - signature.fill('p', 10); - - const PboDocument document("file.pbo", QList>{}, signature); - - QSharedPointer tran = document.root()->beginTransaction(); - tran->setTitle("new.pbo"); - tran->commit(); - tran.clear(); - - ASSERT_EQ(document.signature(), QByteArray()); - } } diff --git a/pbom/domain/pbodocument.cpp b/pbom/domain/pbodocument.cpp index 30ae843..e4de507 100644 --- a/pbom/domain/pbodocument.cpp +++ b/pbom/domain/pbodocument.cpp @@ -37,7 +37,6 @@ namespace pboman3::domain { void PboDocument::setupConnections() { connect(root_.get(), &PboNode::hierarchyChanged, [this] { - signature_.clear(); emit changed(); }); @@ -46,7 +45,6 @@ namespace pboman3::domain { }); connect(headers_.get(), &DocumentHeaders::headersChanged, [this]() { - signature_.clear(); emit changed(); }); } From 496c746a0a9b38e518716193783206b7c6a4ba3e Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 20 Nov 2021 18:04:02 +0300 Subject: [PATCH 17/39] Fix the broken build --- pbom/util/__test__/qpointerlistiterator_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/pbom/util/__test__/qpointerlistiterator_test.cpp b/pbom/util/__test__/qpointerlistiterator_test.cpp index 05054a8..15e757b 100644 --- a/pbom/util/__test__/qpointerlistiterator_test.cpp +++ b/pbom/util/__test__/qpointerlistiterator_test.cpp @@ -3,6 +3,7 @@ #include "io/pboheader.h" namespace pboman3::test { + using namespace io; TEST(QPointerListIteratorTest, Ietrator_Works) { const QSharedPointer h1(new PboHeader("n1", "v1")); const QSharedPointer h2(new PboHeader("n2", "v2")); From 065e0f5f592b3d0a4d951732b92830667c03a1f7 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 20 Nov 2021 18:09:58 +0300 Subject: [PATCH 18/39] Move the generic exceptions from utils to the root namespace --- pbom/CMakeLists.txt | 2 +- pbom/domain/__test__/documentheaderstransaction_test.cpp | 2 +- pbom/domain/__test__/pbonode_test.cpp | 2 +- pbom/domain/__test__/pbonodetransaction_test.cpp | 2 +- pbom/domain/documentheaders.cpp | 2 +- pbom/domain/documentheaderstransaction.cpp | 2 +- pbom/domain/pbonode.cpp | 2 +- pbom/domain/pbonodetransaction.cpp | 2 +- pbom/domain/validationexception.h | 2 +- pbom/{util => }/exception.cpp | 0 pbom/{util => }/exception.h | 0 pbom/io/bb/__test__/nodefilesystem_test.cpp | 2 +- pbom/io/bb/__test__/unpackbackend_test.cpp | 2 +- pbom/io/bb/unpackbackend.cpp | 2 +- pbom/io/diskaccessexception.h | 2 +- pbom/io/lzh/lzhdecompressionexception.h | 2 +- pbom/io/pbofileformatexception.h | 2 +- pbom/main.cpp | 2 +- pbom/model/interactionparcel.cpp | 2 +- pbom/model/pbomodel.cpp | 2 +- pbom/model/task/taskwindowmodel.cpp | 2 +- pbom/ui/errordialog.h | 2 +- pbom/ui/statusbar.cpp | 2 +- pbom/ui/taskwindow.cpp | 2 +- pbom/ui/treewidget/treewidget.cpp | 2 +- pbom/ui/treewidget/treewidgetbase.cpp | 2 +- pbom/ui/win32/win32fileviewer.h | 2 +- pbom/ui/win32/win32iconmgr.cpp | 2 +- pbom/util/CMakeLists.txt | 1 - 29 files changed, 26 insertions(+), 27 deletions(-) rename pbom/{util => }/exception.cpp (100%) rename pbom/{util => }/exception.h (100%) diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index 5facc98..a38dad6 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -21,7 +21,7 @@ add_subdirectory(ui) add_subdirectory(util) qt_add_resources(PROJECT_SOURCES res/res.qrc) -qt_add_executable(pbom ${PROJECT_SOURCES} main.cpp res/winapp.rc) +qt_add_executable(pbom ${PROJECT_SOURCES} exception.cpp main.cpp res/winapp.rc) target_link_libraries(pbom PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network CLI11) target_include_directories(pbom PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pbom/domain/__test__/documentheaderstransaction_test.cpp b/pbom/domain/__test__/documentheaderstransaction_test.cpp index c00add6..88bee6a 100644 --- a/pbom/domain/__test__/documentheaderstransaction_test.cpp +++ b/pbom/domain/__test__/documentheaderstransaction_test.cpp @@ -1,7 +1,7 @@ #include #include "domain/documentheaders.h" #include "domain/documentheaderstransaction.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::domain::test { TEST(DocumentHeadersTransactionTest, Count_Returns_Headers_Count) { diff --git a/pbom/domain/__test__/pbonode_test.cpp b/pbom/domain/__test__/pbonode_test.cpp index fe144f9..567cee1 100644 --- a/pbom/domain/__test__/pbonode_test.cpp +++ b/pbom/domain/__test__/pbonode_test.cpp @@ -5,7 +5,7 @@ #include "domain/pbonodetransaction.h" #include "gmock/gmock.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::domain::test { TEST(PboNodeTest, Ctor_Initializes_Node) { diff --git a/pbom/domain/__test__/pbonodetransaction_test.cpp b/pbom/domain/__test__/pbonodetransaction_test.cpp index 1d3355b..66cb582 100644 --- a/pbom/domain/__test__/pbonodetransaction_test.cpp +++ b/pbom/domain/__test__/pbonodetransaction_test.cpp @@ -1,7 +1,7 @@ #include #include "domain/pbonodetransaction.h" #include "domain/validationexception.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::domain::test { TEST(PboNodeTransactionTest, Title_Returns_Title) { diff --git a/pbom/domain/documentheaders.cpp b/pbom/domain/documentheaders.cpp index d280bd5..bf90571 100644 --- a/pbom/domain/documentheaders.cpp +++ b/pbom/domain/documentheaders.cpp @@ -1,6 +1,6 @@ #include "documentheaders.h" #include "documentheaderstransaction.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::domain { DocumentHeaders::DocumentHeaders() = default; diff --git a/pbom/domain/documentheaderstransaction.cpp b/pbom/domain/documentheaderstransaction.cpp index 3a4864b..775be08 100644 --- a/pbom/domain/documentheaderstransaction.cpp +++ b/pbom/domain/documentheaderstransaction.cpp @@ -1,6 +1,6 @@ #include "documentheaders.h" #include "documentheaderstransaction.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::domain { DocumentHeadersTransaction::DocumentHeadersTransaction(QList> headers, DocumentHeaders* parent) diff --git a/pbom/domain/pbonode.cpp b/pbom/domain/pbonode.cpp index 634fe4a..e031151 100644 --- a/pbom/domain/pbonode.cpp +++ b/pbom/domain/pbonode.cpp @@ -1,6 +1,6 @@ #include "pbonode.h" #include -#include "util/exception.h" +#include "exception.h" #include "validationexception.h" #include "pbonodetransaction.h" diff --git a/pbom/domain/pbonodetransaction.cpp b/pbom/domain/pbonodetransaction.cpp index bafde22..f8472f1 100644 --- a/pbom/domain/pbonodetransaction.cpp +++ b/pbom/domain/pbonodetransaction.cpp @@ -1,6 +1,6 @@ #include "pbonodetransaction.h" #include "validationexception.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::domain { PboNodeTransaction::PboNodeTransaction(PboNode* node) diff --git a/pbom/domain/validationexception.h b/pbom/domain/validationexception.h index 7a18159..755f609 100644 --- a/pbom/domain/validationexception.h +++ b/pbom/domain/validationexception.h @@ -1,6 +1,6 @@ #pragma once -#include "util/exception.h" +#include "exception.h" #include namespace pboman3::domain { diff --git a/pbom/util/exception.cpp b/pbom/exception.cpp similarity index 100% rename from pbom/util/exception.cpp rename to pbom/exception.cpp diff --git a/pbom/util/exception.h b/pbom/exception.h similarity index 100% rename from pbom/util/exception.h rename to pbom/exception.h diff --git a/pbom/io/bb/__test__/nodefilesystem_test.cpp b/pbom/io/bb/__test__/nodefilesystem_test.cpp index b4f7a55..2dc0c69 100644 --- a/pbom/io/bb/__test__/nodefilesystem_test.cpp +++ b/pbom/io/bb/__test__/nodefilesystem_test.cpp @@ -1,5 +1,5 @@ #include "io/bb/nodefilesystem.h" -#include "util/exception.h" +#include "exception.h" #include #include diff --git a/pbom/io/bb/__test__/unpackbackend_test.cpp b/pbom/io/bb/__test__/unpackbackend_test.cpp index dcef5f8..69e313e 100644 --- a/pbom/io/bb/__test__/unpackbackend_test.cpp +++ b/pbom/io/bb/__test__/unpackbackend_test.cpp @@ -5,7 +5,7 @@ #include #include "io/bs/fsrawbinarysource.h" #include "io/bs/pbobinarysource.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::io::test { TEST(UnpackBackendTest, UnpackSync_Extracts_Nodes_To_File_System) { diff --git a/pbom/io/bb/unpackbackend.cpp b/pbom/io/bb/unpackbackend.cpp index 8a7537b..3586452 100644 --- a/pbom/io/bb/unpackbackend.cpp +++ b/pbom/io/bb/unpackbackend.cpp @@ -1,7 +1,7 @@ #include "unpackbackend.h" #include #include "io/diskaccessexception.h" -#include "util/exception.h" +#include "exception.h" #include "util/log.h" #define LOG(...) LOGGER("io/bb/UnpackBackend", __VA_ARGS__) diff --git a/pbom/io/diskaccessexception.h b/pbom/io/diskaccessexception.h index 251d3c3..08825e7 100644 --- a/pbom/io/diskaccessexception.h +++ b/pbom/io/diskaccessexception.h @@ -1,6 +1,6 @@ #pragma once -#include "util/exception.h" +#include "exception.h" namespace pboman3::io { class DiskAccessException : public AppException { diff --git a/pbom/io/lzh/lzhdecompressionexception.h b/pbom/io/lzh/lzhdecompressionexception.h index 1ec380f..27f6470 100644 --- a/pbom/io/lzh/lzhdecompressionexception.h +++ b/pbom/io/lzh/lzhdecompressionexception.h @@ -1,6 +1,6 @@ #pragma once -#include "util/exception.h" +#include "exception.h" namespace pboman3::io { class LzhDecompressionException : public AppException { diff --git a/pbom/io/pbofileformatexception.h b/pbom/io/pbofileformatexception.h index 2b3f53f..641f79c 100644 --- a/pbom/io/pbofileformatexception.h +++ b/pbom/io/pbofileformatexception.h @@ -1,6 +1,6 @@ #pragma once -#include "util/exception.h" +#include "exception.h" namespace pboman3::io { class PboFileFormatException : public AppException { diff --git a/pbom/main.cpp b/pbom/main.cpp index 9a0150a..f23e074 100644 --- a/pbom/main.cpp +++ b/pbom/main.cpp @@ -9,7 +9,7 @@ #include "ui/mainwindow.h" #include "ui/packwindow.h" #include "ui/unpackwindow.h" -#include "util/exception.h" +#include "exception.h" #include "util/log.h" #define LOG(...) LOGGER("Main", __VA_ARGS__) diff --git a/pbom/model/interactionparcel.cpp b/pbom/model/interactionparcel.cpp index 86bdc94..2c9a438 100644 --- a/pbom/model/interactionparcel.cpp +++ b/pbom/model/interactionparcel.cpp @@ -1,7 +1,7 @@ #include "interactionparcel.h" #include #include -#include "util/exception.h" +#include "exception.h" namespace pboman3::model { const QSharedPointer& NodeDescriptor::binarySource() const { diff --git a/pbom/model/pbomodel.cpp b/pbom/model/pbomodel.cpp index 0745f8d..4a58a94 100644 --- a/pbom/model/pbomodel.cpp +++ b/pbom/model/pbomodel.cpp @@ -5,7 +5,7 @@ #include #include "io/documentreader.h" #include "io/documentwriter.h" -#include "util/exception.h" +#include "exception.h" #include "util/log.h" #define LOG(...) LOGGER("model/PboModel", __VA_ARGS__) diff --git a/pbom/model/task/taskwindowmodel.cpp b/pbom/model/task/taskwindowmodel.cpp index 6d40b20..be680af 100644 --- a/pbom/model/task/taskwindowmodel.cpp +++ b/pbom/model/task/taskwindowmodel.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "util/exception.h" +#include "exception.h" #include "util/log.h" #define LOG(...) LOGGER("model/task/TaskWindowModel", __VA_ARGS__) diff --git a/pbom/ui/errordialog.h b/pbom/ui/errordialog.h index 84f78bb..8533a17 100644 --- a/pbom/ui/errordialog.h +++ b/pbom/ui/errordialog.h @@ -3,7 +3,7 @@ #include "ui_errordialog.h" #include "io/diskaccessexception.h" #include "io/pbofileformatexception.h" -#include "util/exception.h" +#include "exception.h" namespace Ui { class ErrorDialog; diff --git a/pbom/ui/statusbar.cpp b/pbom/ui/statusbar.cpp index cce1643..06545ec 100644 --- a/pbom/ui/statusbar.cpp +++ b/pbom/ui/statusbar.cpp @@ -1,5 +1,5 @@ #include "statusbar.h" -#include "util/exception.h" +#include "exception.h" #include "util/log.h" namespace pboman3::ui { diff --git a/pbom/ui/taskwindow.cpp b/pbom/ui/taskwindow.cpp index 0e52ad9..35ffe4f 100644 --- a/pbom/ui/taskwindow.cpp +++ b/pbom/ui/taskwindow.cpp @@ -4,7 +4,7 @@ #include #include "ui_taskwindow.h" #include "model/task/taskwindowmodel.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::ui { TaskWindow::TaskWindow(QWidget* parent) diff --git a/pbom/ui/treewidget/treewidget.cpp b/pbom/ui/treewidget/treewidget.cpp index 2cf9ba0..95783e0 100644 --- a/pbom/ui/treewidget/treewidget.cpp +++ b/pbom/ui/treewidget/treewidget.cpp @@ -5,7 +5,7 @@ #include #include "ui/fscollector.h" #include "ui/insertdialog.h" -#include "util/exception.h" +#include "exception.h" #include #include "io/diskaccessexception.h" #include "ui/errordialog.h" diff --git a/pbom/ui/treewidget/treewidgetbase.cpp b/pbom/ui/treewidget/treewidgetbase.cpp index 2648fe5..e1abca8 100644 --- a/pbom/ui/treewidget/treewidgetbase.cpp +++ b/pbom/ui/treewidget/treewidgetbase.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "util/exception.h" +#include "exception.h" #include "util/log.h" #define LOG(...) LOGGER("ui/treewidget/TreeWidgetBase", __VA_ARGS__) diff --git a/pbom/ui/win32/win32fileviewer.h b/pbom/ui/win32/win32fileviewer.h index fe9f25f..2fccd4b 100644 --- a/pbom/ui/win32/win32fileviewer.h +++ b/pbom/ui/win32/win32fileviewer.h @@ -1,7 +1,7 @@ #pragma once #include "ui/fileviewer.h" -#include "util/exception.h" +#include "exception.h" namespace pboman3::ui { class Win32FileViewerException : public AppException { diff --git a/pbom/ui/win32/win32iconmgr.cpp b/pbom/ui/win32/win32iconmgr.cpp index 3a5377d..d9c2fcb 100644 --- a/pbom/ui/win32/win32iconmgr.cpp +++ b/pbom/ui/win32/win32iconmgr.cpp @@ -1,7 +1,7 @@ #include "win32iconmgr.h" #include #include -#include "util/exception.h" +#include "exception.h" #include #include #include "util/log.h" diff --git a/pbom/util/CMakeLists.txt b/pbom/util/CMakeLists.txt index 1bba3de..1dac322 100644 --- a/pbom/util/CMakeLists.txt +++ b/pbom/util/CMakeLists.txt @@ -1,5 +1,4 @@ list(APPEND PROJECT_SOURCES - "util/exception.cpp" "util/log.cpp" "util/util.cpp") From d50d62ef86794a1edb7174698c2da7ea873a8945 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 20 Nov 2021 18:17:17 +0300 Subject: [PATCH 19/39] Moved the remaining stuff info namespaces --- pbom/domain/binarysource.h | 2 ++ pbom/domain/documentheaders.h | 2 ++ pbom/domain/documentheaderstransaction.h | 2 ++ pbom/io/lzh/lzh.h | 2 ++ pbom/model/task/task.h | 2 ++ pbom/util/__test__/qpointerlistiterator_test.cpp | 2 +- pbom/util/__test__/util_test.cpp | 2 +- pbom/util/log.cpp | 2 +- pbom/util/log.h | 4 ++-- pbom/util/qpointerlistiterator.h | 2 +- pbom/util/util.cpp | 2 +- pbom/util/util.h | 2 +- 12 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pbom/domain/binarysource.h b/pbom/domain/binarysource.h index fe7b78c..76aa1e8 100644 --- a/pbom/domain/binarysource.h +++ b/pbom/domain/binarysource.h @@ -5,6 +5,8 @@ #include "util/util.h" namespace pboman3::domain { + using namespace util; + class BinarySource { protected: diff --git a/pbom/domain/documentheaders.h b/pbom/domain/documentheaders.h index e31bfbc..7fcd0b7 100644 --- a/pbom/domain/documentheaders.h +++ b/pbom/domain/documentheaders.h @@ -4,6 +4,8 @@ #include "util/qpointerlistiterator.h" namespace pboman3::domain { + using namespace util; + class DocumentHeadersTransaction; class DocumentHeaders : public QObject { diff --git a/pbom/domain/documentheaderstransaction.h b/pbom/domain/documentheaderstransaction.h index a56a524..972ed5a 100644 --- a/pbom/domain/documentheaderstransaction.h +++ b/pbom/domain/documentheaderstransaction.h @@ -6,6 +6,8 @@ #include "domain/documentheaders.h" namespace pboman3::domain { + using namespace util; + class DocumentHeadersTransaction : public QObject { Q_OBJECT diff --git a/pbom/io/lzh/lzh.h b/pbom/io/lzh/lzh.h index 3f06841..a4dfeed 100644 --- a/pbom/io/lzh/lzh.h +++ b/pbom/io/lzh/lzh.h @@ -4,6 +4,8 @@ #include "util/util.h" namespace pboman3::io { + using namespace util; + class Lzh { public: static void decompress(QFileDevice* source, QFileDevice* target, int outputLength, const Cancel& cancel); diff --git a/pbom/model/task/task.h b/pbom/model/task/task.h index fba7eb3..91f03d8 100644 --- a/pbom/model/task/task.h +++ b/pbom/model/task/task.h @@ -4,6 +4,8 @@ #include "util/util.h" namespace pboman3::model { + using namespace util; + class Task : public QObject { Q_OBJECT diff --git a/pbom/util/__test__/qpointerlistiterator_test.cpp b/pbom/util/__test__/qpointerlistiterator_test.cpp index 15e757b..759a896 100644 --- a/pbom/util/__test__/qpointerlistiterator_test.cpp +++ b/pbom/util/__test__/qpointerlistiterator_test.cpp @@ -2,7 +2,7 @@ #include "util/qpointerlistiterator.h" #include "io/pboheader.h" -namespace pboman3::test { +namespace pboman3::util::test { using namespace io; TEST(QPointerListIteratorTest, Ietrator_Works) { const QSharedPointer h1(new PboHeader("n1", "v1")); diff --git a/pbom/util/__test__/util_test.cpp b/pbom/util/__test__/util_test.cpp index 826099f..5ba1a88 100644 --- a/pbom/util/__test__/util_test.cpp +++ b/pbom/util/__test__/util_test.cpp @@ -3,7 +3,7 @@ #include "util/util.h" #include -namespace pboman3::test { +namespace pboman3::util::test { struct GetFileExtensionParam { QString fileName; QString expectedResult; diff --git a/pbom/util/log.cpp b/pbom/util/log.cpp index 91eabf1..795be09 100644 --- a/pbom/util/log.cpp +++ b/pbom/util/log.cpp @@ -1,6 +1,6 @@ #include "log.h" -namespace pboman3 { +namespace pboman3::util { LogWorker::LogWorker(QtMessageHandler implementation) : implementation_(implementation) { } diff --git a/pbom/util/log.h b/pbom/util/log.h index 7323421..dfbbfe8 100644 --- a/pbom/util/log.h +++ b/pbom/util/log.h @@ -32,9 +32,9 @@ USE THIS CODE FOR MACRO DEBUGGING #include #include -#define ACTIVATE_ASYNC_LOG_SINK auto logging = QScopedPointer(new LoggingInfrastructure); logging->run(); +#define ACTIVATE_ASYNC_LOG_SINK auto logging = QScopedPointer(new util::LoggingInfrastructure); logging->run(); -namespace pboman3 { +namespace pboman3::util { /*These two guys make standard QT logs be output on a non UI-thread*/ /*as UI-thread output has too big UI responsiveness penalty*/ class LogWorker : public QObject { diff --git a/pbom/util/qpointerlistiterator.h b/pbom/util/qpointerlistiterator.h index bbf21ca..8ac6eb4 100644 --- a/pbom/util/qpointerlistiterator.h +++ b/pbom/util/qpointerlistiterator.h @@ -2,7 +2,7 @@ #include -namespace pboman3 { +namespace pboman3::util { template class QPointerListIterator { public: diff --git a/pbom/util/util.cpp b/pbom/util/util.cpp index e655aba..14fbf86 100644 --- a/pbom/util/util.cpp +++ b/pbom/util/util.cpp @@ -1,6 +1,6 @@ #include "util.h" -namespace pboman3 { +namespace pboman3::util { QString GetFileExtension(const QString& fileName) { const qsizetype extPos = fileName.lastIndexOf("."); QString ext = extPos >= 0 ? fileName.right(fileName.length() - extPos - 1) : ""; diff --git a/pbom/util/util.h b/pbom/util/util.h index 41abe6c..3d6c3d5 100644 --- a/pbom/util/util.h +++ b/pbom/util/util.h @@ -3,7 +3,7 @@ #include #include -namespace pboman3 { +namespace pboman3::util { typedef std::function Cancel; QString GetFileExtension(const QString& fileName); From a5c3efe492fb0d1c265df3355b748ed2314f33b4 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 20 Nov 2021 18:35:25 +0300 Subject: [PATCH 20/39] Doing some renames in the code --- pbom/CMakeLists.txt | 2 +- pbom/io/CMakeLists.txt | 14 ++--- pbom/io/__test__/documentreader_test.cpp | 14 ++--- pbom/io/__test__/pboentry_test.cpp | 52 ------------------- pbom/io/__test__/pboheader_test.cpp | 17 ------ pbom/io/__test__/pboheaderentity_test.cpp | 17 ++++++ pbom/io/__test__/pboheaderio_test.cpp | 16 +++--- pbom/io/__test__/pboheaderreader_test.cpp | 38 +++++++------- pbom/io/__test__/pbonodeentity_test.cpp | 52 +++++++++++++++++++ ...ourcebase.cpp => abstractbinarysource.cpp} | 14 ++--- ...arysourcebase.h => abstractbinarysource.h} | 10 ++-- pbom/io/bs/fsrawbinarysource.cpp | 2 +- pbom/io/bs/fsrawbinarysource.h | 4 +- pbom/io/bs/pbobinarysource.cpp | 2 +- pbom/io/bs/pbobinarysource.h | 4 +- pbom/io/documentreader.cpp | 4 +- pbom/io/documentwriter.cpp | 20 +++---- pbom/io/documentwriter.h | 6 +-- pbom/io/pboheader.cpp | 17 ------ pbom/io/pboheaderentity.cpp | 17 ++++++ pbom/io/{pboheader.h => pboheaderentity.h} | 6 +-- pbom/io/pboheaderio.cpp | 14 ++--- pbom/io/pboheaderio.h | 12 ++--- pbom/io/pboheaderreader.cpp | 8 +-- pbom/io/pboheaderreader.h | 8 +-- pbom/io/{pboentry.cpp => pbonodeentity.cpp} | 42 +++++++-------- pbom/io/{pboentry.h => pbonodeentity.h} | 12 ++--- pbom/model/task/unpacktask.cpp | 2 +- .../__test__/qpointerlistiterator_test.cpp | 18 +++---- 29 files changed, 222 insertions(+), 222 deletions(-) delete mode 100644 pbom/io/__test__/pboentry_test.cpp delete mode 100644 pbom/io/__test__/pboheader_test.cpp create mode 100644 pbom/io/__test__/pboheaderentity_test.cpp create mode 100644 pbom/io/__test__/pbonodeentity_test.cpp rename pbom/io/bs/{binarysourcebase.cpp => abstractbinarysource.cpp} (56%) rename pbom/io/bs/{binarysourcebase.h => abstractbinarysource.h} (64%) delete mode 100644 pbom/io/pboheader.cpp create mode 100644 pbom/io/pboheaderentity.cpp rename pbom/io/{pboheader.h => pboheaderentity.h} (52%) rename pbom/io/{pboentry.cpp => pbonodeentity.cpp} (53%) rename pbom/io/{pboentry.h => pbonodeentity.h} (73%) diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index a38dad6..7c1e8c0 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -57,7 +57,7 @@ install(FILES ${QT_BINARIES_DIR}/../../Tools/OpenSSL/Win_x64/bin/libcrypto-1_1-x64.dll DESTINATION ${CMAKE_INSTALL_BINDIR}) -add_executable(pbom_test ${PROJECT_SOURCES} ${TEST_SOURCES} testmain.cpp) +add_executable(pbom_test ${PROJECT_SOURCES} ${TEST_SOURCES} exception.cpp testmain.cpp) add_test(NAME pbom_test COMMAND pbom_test) target_link_libraries(pbom_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network gtest gmock) target_include_directories(pbom_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pbom/io/CMakeLists.txt b/pbom/io/CMakeLists.txt index 409f19a..a20f292 100644 --- a/pbom/io/CMakeLists.txt +++ b/pbom/io/CMakeLists.txt @@ -6,7 +6,7 @@ list(APPEND PROJECT_SOURCES "io/bb/tempbackend.cpp" "io/bb/unpackbackend.cpp" "io/bb/unpacktaskbackend.cpp" - "io/bs/binarysourcebase.cpp" + "io/bs/abstractbinarysource.cpp" "io/bs/fslzhbinarysource.cpp" "io/bs/fsrawbinarysource.cpp" "io/bs/pbobinarysource.cpp" @@ -19,12 +19,12 @@ list(APPEND PROJECT_SOURCES "io/documentreader.cpp" "io/documentwriter.cpp" "io/pbodatastream.cpp" - "io/pboentry.cpp" "io/pbofile.cpp" "io/pbofileformatexception.cpp" - "io/pboheader.cpp" + "io/pboheaderentity.cpp" "io/pboheaderio.cpp" - "io/pboheaderreader.cpp") + "io/pboheaderreader.cpp" + "io/pbonodeentity.cpp") set(PROJECT_SOURCES ${PROJECT_SOURCES} PARENT_SCOPE) @@ -42,10 +42,10 @@ list(APPEND TEST_SOURCES "io/lzh/__test__/lzh_test.cpp" "io/__test__/documentreader_test.cpp" "io/__test__/documentwriter_test.cpp" - "io/__test__/pboentry_test.cpp" "io/__test__/pbofile_test.cpp" - "io/__test__/pboheader_test.cpp" + "io/__test__/pboheaderentity_test.cpp" "io/__test__/pboheaderio_test.cpp" - "io/__test__/pboheaderreader_test.cpp") + "io/__test__/pboheaderreader_test.cpp" + "io/__test__/pbonodeentity_test.cpp") set(TEST_SOURCES ${TEST_SOURCES} PARENT_SCOPE) diff --git a/pbom/io/__test__/documentreader_test.cpp b/pbom/io/__test__/documentreader_test.cpp index 17fc70a..1ebbcaf 100644 --- a/pbom/io/__test__/documentreader_test.cpp +++ b/pbom/io/__test__/documentreader_test.cpp @@ -16,16 +16,16 @@ namespace pboman3::io::test { p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - const PboEntry e0 = PboEntry::makeSignature(); - const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + const PboNodeEntity e0 = PboNodeEntity::makeSignature(); + const PboNodeEntity e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 5); - const PboEntry e2("f2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, + const PboNodeEntity e2("f2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, 0x07070707, 10); - const PboEntry e3 = PboEntry::makeBoundary(); + const PboNodeEntity e3 = PboNodeEntity::makeBoundary(); - const PboHeader h1("p1", "v1"); - const PboHeader h2("p2", "v2"); - const PboHeader h3 = PboHeader::makeBoundary(); + const PboHeaderEntity h1("p1", "v1"); + const PboHeaderEntity h2("p2", "v2"); + const PboHeaderEntity h3 = PboHeaderEntity::makeBoundary(); const QByteArray signature(20, 5); diff --git a/pbom/io/__test__/pboentry_test.cpp b/pbom/io/__test__/pboentry_test.cpp deleted file mode 100644 index 263b8e6..0000000 --- a/pbom/io/__test__/pboentry_test.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include "io/pboentry.h" - -namespace pboman3::io::test { - TEST(PboEntryTest, Ctor_Functional) { - const PboEntry entry("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); - ASSERT_EQ(entry.fileName(), "some-file"); - ASSERT_EQ(entry.packingMethod(), PboPackingMethod::Packed); - ASSERT_EQ(entry.originalSize(), 1); - ASSERT_EQ(entry.reserved(), 2); - ASSERT_EQ(entry.timestamp(), 3); - ASSERT_EQ(entry.dataSize(), 4); - } - - TEST(PboEntryTest, IsBoundary_Functional) { - const PboEntry entry = PboEntry::makeBoundary(); - ASSERT_TRUE(entry.isBoundary()); - ASSERT_FALSE(entry.isContent()); - } - - TEST(PboEntryTest, IsSignature_Functional) { - const PboEntry entry = PboEntry::makeSignature(); - ASSERT_TRUE(entry.isSignature()); - ASSERT_FALSE(entry.isContent()); - } - - // ReSharper disable once CppInconsistentNaming - class PboEntryTest_IsContent : public testing::TestWithParam { - }; - - TEST_P(PboEntryTest_IsContent, Functional) { - const PboEntry entry("some-file", GetParam(), 100, 0, 0, 100); - ASSERT_FALSE(entry.isSignature()); - ASSERT_FALSE(entry.isBoundary()); - ASSERT_TRUE(entry.isContent()); - } - - INSTANTIATE_TEST_SUITE_P(IsContent, PboEntryTest_IsContent, - testing::Values(PboPackingMethod::Packed, PboPackingMethod::Uncompressed)); - - TEST(PboEntryTest, Size_Functional) { - const PboEntry entry("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); - ASSERT_EQ(entry.size(), entry.fileName().size() + 21); - } - - TEST(PboEntryTest, IsCompressed_Functional) { - const PboEntry entry1("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); - ASSERT_TRUE(entry1.isCompressed()); - const PboEntry entry2("some-file", PboPackingMethod::Uncompressed, 1, 2, 3, 1); - ASSERT_FALSE(entry2.isCompressed()); - } -} diff --git a/pbom/io/__test__/pboheader_test.cpp b/pbom/io/__test__/pboheader_test.cpp deleted file mode 100644 index 4b2262c..0000000 --- a/pbom/io/__test__/pboheader_test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include "io/pboheader.h" - -namespace pboman3::io::test { - TEST(PboHeaderTest, Ctor_Functional) { - const PboHeader header("name1", "value1"); - ASSERT_EQ(header.name, "name1"); - ASSERT_EQ(header.value, "value1"); - - ASSERT_FALSE(header.isBoundary()); - } - - TEST(PboHeaderTest, IsBoundary_Functional) { - const PboHeader header = PboHeader::makeBoundary(); - ASSERT_TRUE(header.isBoundary()); - } -} diff --git a/pbom/io/__test__/pboheaderentity_test.cpp b/pbom/io/__test__/pboheaderentity_test.cpp new file mode 100644 index 0000000..cc593ba --- /dev/null +++ b/pbom/io/__test__/pboheaderentity_test.cpp @@ -0,0 +1,17 @@ +#include +#include "io/pboheaderentity.h" + +namespace pboman3::io::test { + TEST(PboHeaderEntityTest, Ctor_Functional) { + const PboHeaderEntity header("name1", "value1"); + ASSERT_EQ(header.name, "name1"); + ASSERT_EQ(header.value, "value1"); + + ASSERT_FALSE(header.isBoundary()); + } + + TEST(PboHeaderEntityTest, IsBoundary_Functional) { + const PboHeaderEntity header = PboHeaderEntity::makeBoundary(); + ASSERT_TRUE(header.isBoundary()); + } +} diff --git a/pbom/io/__test__/pboheaderio_test.cpp b/pbom/io/__test__/pboheaderio_test.cpp index 88e6938..e221a5b 100644 --- a/pbom/io/__test__/pboheaderio_test.cpp +++ b/pbom/io/__test__/pboheaderio_test.cpp @@ -25,7 +25,7 @@ namespace pboman3::io::test { const PboHeaderIO io(&f); - const QSharedPointer e = io.readNextEntry(); + const QSharedPointer e = io.readNextEntry(); ASSERT_FALSE(e); } @@ -45,7 +45,7 @@ namespace pboman3::io::test { const PboHeaderIO io(&f); - const QSharedPointer e = io.readNextEntry(); + const QSharedPointer e = io.readNextEntry(); ASSERT_TRUE(e); ASSERT_TRUE(e->isBoundary()); } @@ -70,15 +70,15 @@ namespace pboman3::io::test { const PboHeaderIO io(&f); - const QSharedPointer e = io.readNextHeader(); + const QSharedPointer e = io.readNextHeader(); ASSERT_FALSE(e); } INSTANTIATE_TEST_SUITE_P(ReadNextHeader, PboHeaderIOTest_ReadNextHeader, testing::Values(0)); TEST(PboHeaderIOTest, WriteEntry_Writes) { - const PboEntry e1("some-file1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 0x04040404); - const PboEntry e2("some-file2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, 0x07070707, 0x08080808); + const PboNodeEntity e1("some-file1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 0x04040404); + const PboNodeEntity e2("some-file2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, 0x07070707, 0x08080808); QTemporaryFile t; ASSERT_TRUE(t.open()); @@ -122,9 +122,9 @@ namespace pboman3::io::test { } TEST(PboHeaderIOTest, WriteHeader_Writes) { - const PboHeader h1("h1", "v1"); - const PboHeader h2("h2", "v2"); - const PboHeader h3 = PboHeader::makeBoundary(); + const PboHeaderEntity h1("h1", "v1"); + const PboHeaderEntity h2("h2", "v2"); + const PboHeaderEntity h3 = PboHeaderEntity::makeBoundary(); QTemporaryFile t; ASSERT_TRUE(t.open()); diff --git a/pbom/io/__test__/pboheaderreader_test.cpp b/pbom/io/__test__/pboheaderreader_test.cpp index e2f852d..0802950 100644 --- a/pbom/io/__test__/pboheaderreader_test.cpp +++ b/pbom/io/__test__/pboheaderreader_test.cpp @@ -14,9 +14,9 @@ namespace pboman3::io::test { PboFile p(t.fileName()); p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + const PboNodeEntity e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 0x04040404); - const PboEntry e2 = PboEntry::makeBoundary(); + const PboNodeEntity e2 = PboNodeEntity::makeBoundary(); io.writeEntry(e1); io.writeEntry(e2); @@ -53,9 +53,9 @@ namespace pboman3::io::test { PboFile p(t.fileName()); p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + const PboNodeEntity e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 10); - const PboEntry e2 = PboEntry::makeBoundary(); + const PboNodeEntity e2 = PboNodeEntity::makeBoundary(); const QByteArray signature(20, 5); io.writeEntry(e1); @@ -97,16 +97,16 @@ namespace pboman3::io::test { p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - const PboEntry e0 = PboEntry::makeSignature(); - const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + const PboNodeEntity e0 = PboNodeEntity::makeSignature(); + const PboNodeEntity e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 5); - const PboEntry e2("f2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, + const PboNodeEntity e2("f2", PboPackingMethod::Uncompressed, 0x05050505, 0x06060606, 0x07070707, 10); - const PboEntry e3 = PboEntry::makeBoundary(); + const PboNodeEntity e3 = PboNodeEntity::makeBoundary(); - const PboHeader h1("p1", "v1"); - const PboHeader h2("p2", "v2"); - const PboHeader h3 = PboHeader::makeBoundary(); + const PboHeaderEntity h1("p1", "v1"); + const PboHeaderEntity h2("p2", "v2"); + const PboHeaderEntity h3 = PboHeaderEntity::makeBoundary(); const QByteArray signature(20, 5); @@ -165,9 +165,9 @@ namespace pboman3::io::test { PboFile p(t.fileName()); p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + const PboNodeEntity e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 10); - const PboEntry e2 = PboEntry::makeBoundary(); + const PboNodeEntity e2 = PboNodeEntity::makeBoundary(); io.writeEntry(e1); io.writeEntry(e2); @@ -227,8 +227,8 @@ namespace pboman3::io::test { PboFile p(t.fileName()); p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - io.writeEntry(PboEntry::makeSignature()); - io.writeHeader(PboHeader("p1", "v1")); + io.writeEntry(PboNodeEntity::makeSignature()); + io.writeHeader(PboHeaderEntity("p1", "v1")); p.close(); t.close(); @@ -248,12 +248,12 @@ namespace pboman3::io::test { p.open(QIODeviceBase::WriteOnly); const PboHeaderIO io(&p); - const PboEntry e0 = PboEntry::makeSignature(); - const PboEntry e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, + const PboNodeEntity e0 = PboNodeEntity::makeSignature(); + const PboNodeEntity e1("f1", PboPackingMethod::Packed, 0x01010101, 0x02020202, 0x03030303, 0x04040404); - const PboHeader h1("p1", "v1"); - const PboHeader h2 = PboHeader::makeBoundary(); + const PboHeaderEntity h1("p1", "v1"); + const PboHeaderEntity h2 = PboHeaderEntity::makeBoundary(); io.writeEntry(e0); io.writeHeader(h1); diff --git a/pbom/io/__test__/pbonodeentity_test.cpp b/pbom/io/__test__/pbonodeentity_test.cpp new file mode 100644 index 0000000..084db50 --- /dev/null +++ b/pbom/io/__test__/pbonodeentity_test.cpp @@ -0,0 +1,52 @@ +#include +#include "io/pbonodeentity.h" + +namespace pboman3::io::test { + TEST(PboNodeEntityTest, Ctor_Functional) { + const PboNodeEntity entry("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); + ASSERT_EQ(entry.fileName(), "some-file"); + ASSERT_EQ(entry.packingMethod(), PboPackingMethod::Packed); + ASSERT_EQ(entry.originalSize(), 1); + ASSERT_EQ(entry.reserved(), 2); + ASSERT_EQ(entry.timestamp(), 3); + ASSERT_EQ(entry.dataSize(), 4); + } + + TEST(PboNodeEntityTest, IsBoundary_Functional) { + const PboNodeEntity entry = PboNodeEntity::makeBoundary(); + ASSERT_TRUE(entry.isBoundary()); + ASSERT_FALSE(entry.isContent()); + } + + TEST(PboNodeEntityTest, IsSignature_Functional) { + const PboNodeEntity entry = PboNodeEntity::makeSignature(); + ASSERT_TRUE(entry.isSignature()); + ASSERT_FALSE(entry.isContent()); + } + + // ReSharper disable once CppInconsistentNaming + class PboNodeEntityTest_IsContent : public testing::TestWithParam { + }; + + TEST_P(PboNodeEntityTest_IsContent, Functional) { + const PboNodeEntity entry("some-file", GetParam(), 100, 0, 0, 100); + ASSERT_FALSE(entry.isSignature()); + ASSERT_FALSE(entry.isBoundary()); + ASSERT_TRUE(entry.isContent()); + } + + INSTANTIATE_TEST_SUITE_P(IsContent, PboNodeEntityTest_IsContent, + testing::Values(PboPackingMethod::Packed, PboPackingMethod::Uncompressed)); + + TEST(PboNodeEntityTest, Size_Functional) { + const PboNodeEntity entry("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); + ASSERT_EQ(entry.size(), entry.fileName().size() + 21); + } + + TEST(PboNodeEntityTest, IsCompressed_Functional) { + const PboNodeEntity entry1("some-file", PboPackingMethod::Packed, 1, 2, 3, 4); + ASSERT_TRUE(entry1.isCompressed()); + const PboNodeEntity entry2("some-file", PboPackingMethod::Uncompressed, 1, 2, 3, 1); + ASSERT_FALSE(entry2.isCompressed()); + } +} diff --git a/pbom/io/bs/binarysourcebase.cpp b/pbom/io/bs/abstractbinarysource.cpp similarity index 56% rename from pbom/io/bs/binarysourcebase.cpp rename to pbom/io/bs/abstractbinarysource.cpp index 867dc06..6318092 100644 --- a/pbom/io/bs/binarysourcebase.cpp +++ b/pbom/io/bs/abstractbinarysource.cpp @@ -1,31 +1,31 @@ -#include "binarysourcebase.h" +#include "abstractbinarysource.h" #include #include "io/diskaccessexception.h" namespace pboman3::io { - BinarySourceBase::BinarySourceBase(QString path) + AbstractBinarySource::AbstractBinarySource(QString path) : path_(std::move(path)) { file_ = new QFile(path_); } - BinarySourceBase::~BinarySourceBase() { + AbstractBinarySource::~AbstractBinarySource() { delete file_; } - void BinarySourceBase::open() const { + void AbstractBinarySource::open() const { if (!file_->open(QIODeviceBase::ReadOnly)) throw DiskAccessException("Can not open the file. Check you have enough permissions and the file is not locked by another process.", path_); } - void BinarySourceBase::close() const { + void AbstractBinarySource::close() const { file_->close(); } - bool BinarySourceBase::isOpen() const { + bool AbstractBinarySource::isOpen() const { return file_->isOpen(); } - const QString& BinarySourceBase::path() const { + const QString& AbstractBinarySource::path() const { return path_; } } diff --git a/pbom/io/bs/binarysourcebase.h b/pbom/io/bs/abstractbinarysource.h similarity index 64% rename from pbom/io/bs/binarysourcebase.h rename to pbom/io/bs/abstractbinarysource.h index 4fd702b..a977f8d 100644 --- a/pbom/io/bs/binarysourcebase.h +++ b/pbom/io/bs/abstractbinarysource.h @@ -5,11 +5,11 @@ namespace pboman3::io { using namespace domain; - class BinarySourceBase: public BinarySource { + class AbstractBinarySource: public BinarySource { public: - BinarySourceBase(QString path); + AbstractBinarySource(QString path); - virtual ~BinarySourceBase(); + virtual ~AbstractBinarySource(); virtual void writeToPbo(QFileDevice* targetFile, const Cancel& cancel) = 0; @@ -29,8 +29,8 @@ namespace pboman3::io { bool isCompressed() const override = 0; - friend QDebug operator <<(QDebug debug, const BinarySourceBase& bs) { - return debug << "BinarySourceBase(Compressed=" << bs.isCompressed() << ", Path=" << bs.path_ << ")"; + friend QDebug operator <<(QDebug debug, const AbstractBinarySource& bs) { + return debug << "AbstractBinarySource(Compressed=" << bs.isCompressed() << ", Path=" << bs.path_ << ")"; } protected: diff --git a/pbom/io/bs/fsrawbinarysource.cpp b/pbom/io/bs/fsrawbinarysource.cpp index 2107668..467eb40 100644 --- a/pbom/io/bs/fsrawbinarysource.cpp +++ b/pbom/io/bs/fsrawbinarysource.cpp @@ -3,7 +3,7 @@ namespace pboman3::io { FsRawBinarySource::FsRawBinarySource(QString path, qsizetype bufferSize) - : BinarySourceBase(std::move(path)), + : AbstractBinarySource(std::move(path)), bufferSize_(bufferSize) { } diff --git a/pbom/io/bs/fsrawbinarysource.h b/pbom/io/bs/fsrawbinarysource.h index bcf24f7..e3f20b1 100644 --- a/pbom/io/bs/fsrawbinarysource.h +++ b/pbom/io/bs/fsrawbinarysource.h @@ -1,9 +1,9 @@ #pragma once -#include "binarysourcebase.h" +#include "abstractbinarysource.h" namespace pboman3::io { - class FsRawBinarySource : public BinarySourceBase { + class FsRawBinarySource : public AbstractBinarySource { public: FsRawBinarySource(QString path, qsizetype bufferSize = 1024 * 1024); diff --git a/pbom/io/bs/pbobinarysource.cpp b/pbom/io/bs/pbobinarysource.cpp index 42688bd..eb0cf48 100644 --- a/pbom/io/bs/pbobinarysource.cpp +++ b/pbom/io/bs/pbobinarysource.cpp @@ -5,7 +5,7 @@ namespace pboman3::io { PboBinarySource::PboBinarySource(const QString& path, const PboDataInfo& dataInfo, qsizetype bufferSize) - : BinarySourceBase(path), + : AbstractBinarySource(path), dataInfo_(dataInfo), bufferSize_(bufferSize) { } diff --git a/pbom/io/bs/pbobinarysource.h b/pbom/io/bs/pbobinarysource.h index 727ef72..a922504 100644 --- a/pbom/io/bs/pbobinarysource.h +++ b/pbom/io/bs/pbobinarysource.h @@ -1,6 +1,6 @@ #pragma once -#include "binarysourcebase.h" +#include "abstractbinarysource.h" namespace pboman3::io { struct PboDataInfo { @@ -11,7 +11,7 @@ namespace pboman3::io { qint32 compressed; }; - class PboBinarySource : public BinarySourceBase { + class PboBinarySource : public AbstractBinarySource { public: PboBinarySource(const QString& path, const PboDataInfo& dataInfo, qsizetype bufferSize = 1024 * 1024); diff --git a/pbom/io/documentreader.cpp b/pbom/io/documentreader.cpp index f77748c..10feb56 100644 --- a/pbom/io/documentreader.cpp +++ b/pbom/io/documentreader.cpp @@ -18,14 +18,14 @@ namespace pboman3::io { QList> headers; headers.reserve(header.headers.count()); - for (const QSharedPointer& h : header.headers) + for (const QSharedPointer& h : header.headers) headers.append(QSharedPointer(new DocumentHeader(DocumentHeader::InternalData{ h->name, h->value }))); const QFileInfo fi(path_); QSharedPointer document(new PboDocument(fi.fileName(), std::move(headers), std::move(header.signature))); qsizetype entryDataOffset = header.dataBlockStart; - for (const QSharedPointer& e : header.entries) { + for (const QSharedPointer& e : header.entries) { PboNode* node = document->root()->createHierarchy(e->makePath()); PboDataInfo dataInfo{0, 0, 0, 0, 0}; dataInfo.originalSize = e->originalSize(); diff --git a/pbom/io/documentwriter.cpp b/pbom/io/documentwriter.cpp index 328b546..9770810 100644 --- a/pbom/io/documentwriter.cpp +++ b/pbom/io/documentwriter.cpp @@ -2,7 +2,7 @@ #include #include #include "diskaccessexception.h" -#include "pboheader.h" +#include "pboheaderentity.h" #include "pboheaderio.h" namespace pboman3::io { @@ -53,7 +53,7 @@ namespace pboman3::io { if (!body.open()) throw DiskAccessException("Could not create the file.", body.fileName()); - QList> entries; + QList> entries; writeNode(&body, document->root(), entries, cancel); if (cancel()) @@ -84,7 +84,7 @@ namespace pboman3::io { } } - void DocumentWriter::writeNode(QFileDevice* file, PboNode* node, QList>& entries, + void DocumentWriter::writeNode(QFileDevice* file, PboNode* node, QList>& entries, const Cancel& cancel) { for (PboNode* child : *node) { if (cancel()) @@ -98,7 +98,7 @@ namespace pboman3::io { const qint32 originalSize = child->binarySource->readOriginalSize(); const auto dataSize = static_cast(after - before); - QSharedPointer entry(new PboEntry( + QSharedPointer entry(new PboNodeEntity( child->makePath().toString(), child->binarySource->isCompressed() ? PboPackingMethod::Packed : PboPackingMethod::Uncompressed, child->binarySource->readOriginalSize(), @@ -124,24 +124,24 @@ namespace pboman3::io { } void DocumentWriter::writeHeader(PboFile* file, const DocumentHeaders* headers, - const QList>& entries, const Cancel& cancel) { + const QList>& entries, const Cancel& cancel) { const PboHeaderIO io(file); - io.writeEntry(PboEntry::makeSignature()); + io.writeEntry(PboNodeEntity::makeSignature()); for (const DocumentHeader* header : *headers) { if (cancel()) { break; } - io.writeHeader(PboHeader(header->name(), header->value())); + io.writeHeader(PboHeaderEntity(header->name(), header->value())); } if (cancel()) { return; } - io.writeHeader(PboHeader::makeBoundary()); + io.writeHeader(PboHeaderEntity::makeBoundary()); - for (const QSharedPointer& entry : entries) { + for (const QSharedPointer& entry : entries) { if (cancel()) { break; } @@ -152,7 +152,7 @@ namespace pboman3::io { return; } - io.writeEntry(PboEntry::makeBoundary()); + io.writeEntry(PboNodeEntity::makeBoundary()); for (PboNode* key : binarySources_.keys()) { PboDataInfo& existing = binarySources_[key]; diff --git a/pbom/io/documentwriter.h b/pbom/io/documentwriter.h index e196c4d..d9c15b6 100644 --- a/pbom/io/documentwriter.h +++ b/pbom/io/documentwriter.h @@ -2,7 +2,7 @@ #include #include -#include "pboentry.h" +#include "pbonodeentity.h" #include "pbofile.h" #include "bs/pbobinarysource.h" #include "domain/pbodocument.h" @@ -30,9 +30,9 @@ namespace pboman3::io { void writeInternal(PboDocument* document, const QString& path, const Cancel& cancel); - void writeNode(QFileDevice* file, PboNode* node, QList>& entries, const Cancel& cancel); + void writeNode(QFileDevice* file, PboNode* node, QList>& entries, const Cancel& cancel); - void writeHeader(PboFile* file, const DocumentHeaders* headers, const QList>& entries, const Cancel& cancel); + void writeHeader(PboFile* file, const DocumentHeaders* headers, const QList>& entries, const Cancel& cancel); void copyBody(QFileDevice* pbo, QFileDevice* body, const Cancel& cancel); diff --git a/pbom/io/pboheader.cpp b/pbom/io/pboheader.cpp deleted file mode 100644 index 6cbe1a0..0000000 --- a/pbom/io/pboheader.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "pboheader.h" - -namespace pboman3::io { - PboHeader PboHeader::makeBoundary() { - return PboHeader(QString(), QString()); - } - - PboHeader::PboHeader(QString name, QString value) - : name(std::move(name)), - value(std::move(value)) { - } - - bool PboHeader::isBoundary() const { - return name.isEmpty() && value.isEmpty(); - } - -} diff --git a/pbom/io/pboheaderentity.cpp b/pbom/io/pboheaderentity.cpp new file mode 100644 index 0000000..d201a6f --- /dev/null +++ b/pbom/io/pboheaderentity.cpp @@ -0,0 +1,17 @@ +#include "pboheaderentity.h" + +namespace pboman3::io { + PboHeaderEntity PboHeaderEntity::makeBoundary() { + return PboHeaderEntity(QString(), QString()); + } + + PboHeaderEntity::PboHeaderEntity(QString name, QString value) + : name(std::move(name)), + value(std::move(value)) { + } + + bool PboHeaderEntity::isBoundary() const { + return name.isEmpty() && value.isEmpty(); + } + +} diff --git a/pbom/io/pboheader.h b/pbom/io/pboheaderentity.h similarity index 52% rename from pbom/io/pboheader.h rename to pbom/io/pboheaderentity.h index fa34d09..3be2bbe 100644 --- a/pbom/io/pboheader.h +++ b/pbom/io/pboheaderentity.h @@ -3,13 +3,13 @@ #include namespace pboman3::io { - struct PboHeader { - static PboHeader makeBoundary(); + struct PboHeaderEntity { + static PboHeaderEntity makeBoundary(); const QString name; const QString value; - PboHeader(QString name, QString value); + PboHeaderEntity(QString name, QString value); bool isBoundary() const; }; diff --git a/pbom/io/pboheaderio.cpp b/pbom/io/pboheaderio.cpp index ee0befb..d785d03 100644 --- a/pbom/io/pboheaderio.cpp +++ b/pbom/io/pboheaderio.cpp @@ -8,7 +8,7 @@ namespace pboman3::io { : file_(file) { } - QSharedPointer PboHeaderIO::readNextEntry() const { + QSharedPointer PboHeaderIO::readNextEntry() const { PboDataStream data(file_); try { @@ -30,13 +30,13 @@ namespace pboman3::io { qint32 dataSize; data >> dataSize; - return QSharedPointer(new PboEntry(fileName, packingMethod, originalSize, reserved, timeStamp, dataSize)); + return QSharedPointer(new PboNodeEntity(fileName, packingMethod, originalSize, reserved, timeStamp, dataSize)); } catch (PboEofException&) { return nullptr; } } - QSharedPointer PboHeaderIO::readNextHeader() const { + QSharedPointer PboHeaderIO::readNextHeader() const { PboDataStream data(file_); try { @@ -44,18 +44,18 @@ namespace pboman3::io { data >> name; if (name.isEmpty()) - return QSharedPointer(new PboHeader("", "")); + return QSharedPointer(new PboHeaderEntity("", "")); QString value; data >> value; - return QSharedPointer(new PboHeader(name, value)); + return QSharedPointer(new PboHeaderEntity(name, value)); } catch (PboEofException&) { return nullptr; } } - void PboHeaderIO::writeEntry(const PboEntry& entry) const { + void PboHeaderIO::writeEntry(const PboNodeEntity& entry) const { PboDataStream data(file_); data << entry.fileName(); @@ -66,7 +66,7 @@ namespace pboman3::io { data << entry.dataSize(); } - void PboHeaderIO::writeHeader(const PboHeader& header) const { + void PboHeaderIO::writeHeader(const PboHeaderEntity& header) const { PboDataStream data(file_); if (header.isBoundary()) { diff --git a/pbom/io/pboheaderio.h b/pbom/io/pboheaderio.h index 9a01033..30a5239 100644 --- a/pbom/io/pboheaderio.h +++ b/pbom/io/pboheaderio.h @@ -1,8 +1,8 @@ #pragma once #include "pbofile.h" -#include "io/pboentry.h" -#include "io/pboheader.h" +#include "io/pbonodeentity.h" +#include "io/pboheaderentity.h" #include namespace pboman3::io { @@ -12,13 +12,13 @@ namespace pboman3::io { public: explicit PboHeaderIO(PboFile* file); - QSharedPointer readNextEntry() const; + QSharedPointer readNextEntry() const; - QSharedPointer readNextHeader() const; + QSharedPointer readNextHeader() const; - void writeEntry(const PboEntry& entry) const; + void writeEntry(const PboNodeEntity& entry) const; - void writeHeader(const PboHeader& header) const; + void writeHeader(const PboHeaderEntity& header) const; private: PboFile* file_; diff --git a/pbom/io/pboheaderreader.cpp b/pbom/io/pboheaderreader.cpp index 99e97a8..a5f8cea 100644 --- a/pbom/io/pboheaderreader.cpp +++ b/pbom/io/pboheaderreader.cpp @@ -5,11 +5,11 @@ namespace pboman3::io { PboFileHeader PboHeaderReader::readFileHeader(PboFile* file) { - QList> headers; - QList> entries; + QList> headers; + QList> entries; const PboHeaderIO reader(file); - QSharedPointer entry = reader.readNextEntry(); + QSharedPointer entry = reader.readNextEntry(); if (!entry) { throw PboFileFormatException("The file is not a valid PBO."); @@ -17,7 +17,7 @@ namespace pboman3::io { qsizetype dataBlockEnd = 0; if (entry->isSignature()) { - QSharedPointer header = reader.readNextHeader(); + QSharedPointer header = reader.readNextHeader(); while (header && !header->isBoundary()) { headers.append(header); header = reader.readNextHeader(); diff --git a/pbom/io/pboheaderreader.h b/pbom/io/pboheaderreader.h index bdc1522..a1aac3f 100644 --- a/pbom/io/pboheaderreader.h +++ b/pbom/io/pboheaderreader.h @@ -1,14 +1,14 @@ #pragma once #include "pbofile.h" -#include "io/pboentry.h" -#include "io/pboheader.h" +#include "io/pbonodeentity.h" +#include "io/pboheaderentity.h" #include namespace pboman3::io { struct PboFileHeader { - QList> headers; - QList> entries; + QList> headers; + QList> entries; qsizetype dataBlockStart; QByteArray signature; diff --git a/pbom/io/pboentry.cpp b/pbom/io/pbonodeentity.cpp similarity index 53% rename from pbom/io/pboentry.cpp rename to pbom/io/pbonodeentity.cpp index 3969362..d6dd330 100644 --- a/pbom/io/pboentry.cpp +++ b/pbom/io/pbonodeentity.cpp @@ -1,16 +1,16 @@ -#include "pboentry.h" +#include "pbonodeentity.h" #include namespace pboman3::io { - PboEntry PboEntry::makeSignature() { - return PboEntry("", PboPackingMethod::Product, 0, 0, 0, 0); + PboNodeEntity PboNodeEntity::makeSignature() { + return PboNodeEntity("", PboPackingMethod::Product, 0, 0, 0, 0); } - PboEntry PboEntry::makeBoundary() { - return PboEntry("", PboPackingMethod::Uncompressed, 0, 0, 0, 0); + PboNodeEntity PboNodeEntity::makeBoundary() { + return PboNodeEntity("", PboPackingMethod::Uncompressed, 0, 0, 0, 0); } - PboEntry::PboEntry(QString fileName, PboPackingMethod packingMethod, + PboNodeEntity::PboNodeEntity(QString fileName, PboPackingMethod packingMethod, qint32 originalSize, qint32 reserved, qint32 timestamp, qint32 dataSize) : fileName_(std::move(fileName)), @@ -21,60 +21,60 @@ namespace pboman3::io { dataSize_(dataSize) { } - bool PboEntry::isBoundary() const { + bool PboNodeEntity::isBoundary() const { return fileName_.isEmpty(); } - bool PboEntry::isSignature() const { + bool PboNodeEntity::isSignature() const { return packingMethod_ == PboPackingMethod::Product; } - bool PboEntry::isCompressed() const { + bool PboNodeEntity::isCompressed() const { return packingMethod_ == PboPackingMethod::Packed && originalSize_ != dataSize_; } - bool PboEntry::isContent() const { + bool PboNodeEntity::isContent() const { return !isBoundary() && packingMethod_ == PboPackingMethod::Uncompressed || packingMethod_ == PboPackingMethod::Packed; } - int PboEntry::size() const { + int PboNodeEntity::size() const { return static_cast(fileName_.length()) + sizeOfFields; } - PboPath PboEntry::makePath() const { + PboPath PboNodeEntity::makePath() const { return PboPath(fileName_); } //each header entry consists of 5x4 bytes of fields + filename.length + 1 byte zero string terminator - constexpr int PboEntry::sizeOfFields = 21; + constexpr int PboNodeEntity::sizeOfFields = 21; - const QString& PboEntry::fileName() const { + const QString& PboNodeEntity::fileName() const { return fileName_; } - PboPackingMethod PboEntry::packingMethod() const { + PboPackingMethod PboNodeEntity::packingMethod() const { return packingMethod_; } - qint32 PboEntry::originalSize() const { + qint32 PboNodeEntity::originalSize() const { return originalSize_; } - qint32 PboEntry::reserved() const { + qint32 PboNodeEntity::reserved() const { return reserved_; } - qint32 PboEntry::timestamp() const { + qint32 PboNodeEntity::timestamp() const { return timestamp_; } - qint32 PboEntry::dataSize() const { + qint32 PboNodeEntity::dataSize() const { return dataSize_; } - QDebug operator<<(QDebug debug, const PboEntry& entry) { - return debug << "PboEntry(FileName=" << entry.fileName_ << ", PackingMethod=" << + QDebug operator<<(QDebug debug, const PboNodeEntity& entry) { + return debug << "PboNodeEntity(FileName=" << entry.fileName_ << ", PackingMethod=" << static_cast(entry.packingMethod_) << ", OriginalSize=" << entry.originalSize_ << ", TimeStamp=" << entry.timestamp_ << ", DataSize=" << entry.dataSize_ << ")"; } diff --git a/pbom/io/pboentry.h b/pbom/io/pbonodeentity.h similarity index 73% rename from pbom/io/pboentry.h rename to pbom/io/pbonodeentity.h index 8ab992b..b5c1234 100644 --- a/pbom/io/pboentry.h +++ b/pbom/io/pbonodeentity.h @@ -12,17 +12,17 @@ namespace pboman3::io { Product = 0x56657273 }; - class PboEntry { + class PboNodeEntity { public: - static PboEntry makeSignature(); + static PboNodeEntity makeSignature(); - static PboEntry makeBoundary(); + static PboNodeEntity makeBoundary(); - PboEntry(QString fileName, PboPackingMethod packingMethod, + PboNodeEntity(QString fileName, PboPackingMethod packingMethod, qint32 originalSize, qint32 reserved, qint32 timestamp, qint32 dataSize); - virtual ~PboEntry() = default; + virtual ~PboNodeEntity() = default; bool isBoundary() const; @@ -48,7 +48,7 @@ namespace pboman3::io { qint32 dataSize() const; - friend QDebug operator <<(QDebug debug, const PboEntry& entry); + friend QDebug operator <<(QDebug debug, const PboNodeEntity& entry); private: static const int sizeOfFields; diff --git a/pbom/model/task/unpacktask.cpp b/pbom/model/task/unpacktask.cpp index 232d257..7d892c6 100644 --- a/pbom/model/task/unpacktask.cpp +++ b/pbom/model/task/unpacktask.cpp @@ -3,7 +3,7 @@ #include #include "io/bb/unpacktaskbackend.h" #include "io/bs/pbobinarysource.h" -#include "io/pboentry.h" +#include "io/pbonodeentity.h" #include "domain/pbonode.h" #include "io/diskaccessexception.h" #include "io/documentreader.h" diff --git a/pbom/util/__test__/qpointerlistiterator_test.cpp b/pbom/util/__test__/qpointerlistiterator_test.cpp index 759a896..bb21179 100644 --- a/pbom/util/__test__/qpointerlistiterator_test.cpp +++ b/pbom/util/__test__/qpointerlistiterator_test.cpp @@ -1,14 +1,14 @@ #include #include "util/qpointerlistiterator.h" -#include "io/pboheader.h" +#include "io/pboheaderentity.h" namespace pboman3::util::test { using namespace io; TEST(QPointerListIteratorTest, Ietrator_Works) { - const QSharedPointer h1(new PboHeader("n1", "v1")); - const QSharedPointer h2(new PboHeader("n2", "v2")); + const QSharedPointer h1(new PboHeaderEntity("n1", "v1")); + const QSharedPointer h2(new PboHeaderEntity("n2", "v2")); - QList> headers; + QList> headers; headers.append(h1); headers.append(h2); @@ -78,14 +78,14 @@ namespace pboman3::util::test { ASSERT_EQ(it11, it22); ASSERT_EQ(it1, it2); - ASSERT_EQ(static_cast(it1), static_cast(it2)); + ASSERT_EQ(static_cast(it1), static_cast(it2)); } TEST(QPointerListConstIteratorTest, Ietrator_Works) { - const QSharedPointer h1(new PboHeader("n1", "v1")); - const QSharedPointer h2(new PboHeader("n2", "v2")); + const QSharedPointer h1(new PboHeaderEntity("n1", "v1")); + const QSharedPointer h2(new PboHeaderEntity("n2", "v2")); - QList> headers; + QList> headers; headers.append(h1); headers.append(h2); @@ -155,6 +155,6 @@ namespace pboman3::util::test { ASSERT_EQ(it11, it22); ASSERT_EQ(it1, it2); - ASSERT_EQ(static_cast(it1), static_cast(it2)); + ASSERT_EQ(static_cast(it1), static_cast(it2)); } } From e3a16e96f6b17f7a07e88d058af4602e2b164e04 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Mon, 22 Nov 2021 21:34:40 +0300 Subject: [PATCH 21/39] Fixed DocumentWriter was leaving inconsistent files on cancel --- pbom/domain/documentheaders.cpp | 6 +- pbom/domain/documentheaders.h | 7 +- pbom/domain/pbodocument.cpp | 5 ++ pbom/domain/pbodocument.h | 2 + pbom/io/__test__/documentwriter_test.cpp | 48 +++++++++++- pbom/io/documentwriter.cpp | 97 +++++++++++++++++++----- pbom/model/pbomodel.cpp | 6 +- 7 files changed, 144 insertions(+), 27 deletions(-) diff --git a/pbom/domain/documentheaders.cpp b/pbom/domain/documentheaders.cpp index bf90571..2a98f04 100644 --- a/pbom/domain/documentheaders.cpp +++ b/pbom/domain/documentheaders.cpp @@ -1,6 +1,6 @@ #include "documentheaders.h" +#include #include "documentheaderstransaction.h" -#include "exception.h" namespace pboman3::domain { DocumentHeaders::DocumentHeaders() = default; @@ -63,4 +63,8 @@ namespace pboman3::domain { return false; } + + QDebug& operator<<(QDebug& debug, const DocumentHeaders& headers) { + return debug << "DocumentHeaders(Count=" << headers.count() << ")"; + } } diff --git a/pbom/domain/documentheaders.h b/pbom/domain/documentheaders.h index 7fcd0b7..ece1a7d 100644 --- a/pbom/domain/documentheaders.h +++ b/pbom/domain/documentheaders.h @@ -14,7 +14,7 @@ namespace pboman3::domain { public: DocumentHeaders(); - explicit DocumentHeaders(QList> headers);//Repository Ctor + explicit DocumentHeaders(QList> headers); //Repository Ctor qsizetype count() const; @@ -32,6 +32,8 @@ namespace pboman3::domain { friend DocumentHeadersTransaction; + friend QDebug& operator<<(QDebug& debug, const DocumentHeaders& headers); + signals: void headersChanged(); @@ -40,6 +42,7 @@ namespace pboman3::domain { void setHeaders(QList> headers); - static bool areDifferent(const QList>& list1, const QList>& list2); + static bool areDifferent(const QList>& list1, + const QList>& list2); }; } diff --git a/pbom/domain/pbodocument.cpp b/pbom/domain/pbodocument.cpp index e4de507..ebc4c2d 100644 --- a/pbom/domain/pbodocument.cpp +++ b/pbom/domain/pbodocument.cpp @@ -48,4 +48,9 @@ namespace pboman3::domain { emit changed(); }); } + + QDebug& operator<<(QDebug& debug, const PboDocument& document) { + return debug << "PboDocument(Headers=" << *document.headers_ << ", Root=" << *document.root_ << ", Signature=" + << document.signature_.length() << " bytes)"; + } } diff --git a/pbom/domain/pbodocument.h b/pbom/domain/pbodocument.h index dc63e5b..5bedf33 100644 --- a/pbom/domain/pbodocument.h +++ b/pbom/domain/pbodocument.h @@ -25,6 +25,8 @@ namespace pboman3::domain { void titleChanged(const QString& title); + friend QDebug& operator<<(QDebug& debug, const PboDocument& document); + private: QSharedPointer headers_; QSharedPointer root_; diff --git a/pbom/io/__test__/documentwriter_test.cpp b/pbom/io/__test__/documentwriter_test.cpp index d175cf9..6493c62 100644 --- a/pbom/io/__test__/documentwriter_test.cpp +++ b/pbom/io/__test__/documentwriter_test.cpp @@ -94,7 +94,7 @@ namespace pboman3::io::test { class DocumentWriterTest : public testing::TestWithParam {}; - TEST_P(DocumentWriterTest, Write_Cleans_Files_On_Cancel) { + TEST_P(DocumentWriterTest, Write_Cleans_On_Cancel_When_Writing_New_File) { //mock files contents const QByteArray mockContent1(15, 1); QTemporaryFile e1; @@ -122,7 +122,49 @@ namespace pboman3::io::test { ASSERT_FALSE(QFileInfo(filePath + ".b").exists()); } - INSTANTIATE_TEST_SUITE_P(Write_Cleans_On_Cancel, DocumentWriterTest, testing::Range(1, 13)); + INSTANTIATE_TEST_SUITE_P(Write_Cleans_On_Cancel_When_Writing_New_File, DocumentWriterTest, testing::Range(1, 16)); + + TEST_P(DocumentWriterTest, Write_Cleans_On_Cancel_When_Rewriting_Existing_File) { + //mock files contents + const QByteArray mockContent1(15, 1); + QTemporaryFile e1; + e1.open(); + e1.write(mockContent1); + e1.close(); + + //existing pbo file + constexpr int origPboSize = 100; + const QByteArray origPboContent(origPboSize, '1'); + const QTemporaryDir temp; + const QString filePath = temp.filePath("file.pbo"); + QFile origPbo(filePath); + origPbo.open(QIODeviceBase::ReadWrite); + origPbo.write(origPboContent); + origPbo.close(); + + //pbo document with content + PboDocument document("file.pbo"); + PboNode* n1 = document.root()->createHierarchy(PboPath("e1.txt")); + n1->binarySource = QSharedPointer(new FsRawBinarySource(e1.fileName())); + n1->binarySource->open(); + + //call the method + int count = 0; + int expectedHitCount = GetParam();//experimental way + DocumentWriter writer(filePath); + writer.write(&document, [&count, expectedHitCount]() { count++; return count > expectedHitCount - 1; }); + + ASSERT_FALSE(QFileInfo(filePath + ".b").exists());//no temp file + + QFile prevPbo(filePath); + ASSERT_TRUE(prevPbo.exists());//the original file is in place and has its content + prevPbo.open(QIODeviceBase::ReadWrite); + const QByteArray prevPboContent = prevPbo.read(origPboSize); + prevPbo.close(); + ASSERT_EQ(prevPboContent, origPboContent); + } + + INSTANTIATE_TEST_SUITE_P(Write_Cleans_On_Cancel_When_Rewriting_Existing_File, DocumentWriterTest, testing::Range(1, 16)); TEST(DocumentWriterTest, Write_Cleans_Temporary_Files_On_Write) { //mock files contents @@ -179,7 +221,7 @@ namespace pboman3::io::test { ASSERT_TRUE(n1->binarySource->isOpen()); } - INSTANTIATE_TEST_SUITE_P(Write_Leaves_Binary_Sources_Open_If_Cancelled, DocumentWriterTest, testing::Range(1, 13)); + INSTANTIATE_TEST_SUITE_P(Write_Leaves_Binary_Sources_Open_If_Cancelled, DocumentWriterTest, testing::Range(1, 16)); TEST(DocumentWriterTest, Write_Opens_Binary_Sources_After_Write) { //mock files contents diff --git a/pbom/io/documentwriter.cpp b/pbom/io/documentwriter.cpp index 9770810..225723a 100644 --- a/pbom/io/documentwriter.cpp +++ b/pbom/io/documentwriter.cpp @@ -4,6 +4,9 @@ #include "diskaccessexception.h" #include "pboheaderentity.h" #include "pboheaderio.h" +#include "util/log.h" + +#define LOG(...) LOGGER("io/documentwriter", __VA_ARGS__) namespace pboman3::io { DocumentWriter::DocumentWriter(QString path) @@ -14,58 +17,103 @@ namespace pboman3::io { void DocumentWriter::write(PboDocument* document, const Cancel& cancel) { assert(document && "Document must not be null"); - const QString filePath = QFile::exists(path_) ? path_ + ".t" : path_; + LOG(info, "Writing the document to:", path_) + const bool shouldBackup = QFile::exists(path_); + const QString filePath = shouldBackup ? path_ + ".t" : path_; + writeInternal(document, filePath, cancel); + if (cancel()) { + if (!shouldBackup) + QFile::remove(filePath); //don't assert as not critical + LOG(info, "Cancel - clean temp files and return") + return; + } + + LOG(info, "Suspending binary sources") suspendBinarySources(document->root()); - if (filePath != path_ && !cancel()) { - const QString backupPath = path_ + ".bak"; - //LOG(info, "Cleaning up the temporary files") + bool backupMade = false; + const QString backupPath = path_ + ".bak"; + if (shouldBackup && !cancel()) { + LOG(info, "Back up the original file as: ", backupPath) if (QFile::exists(backupPath) && !QFile::remove(backupPath)) { - //LOG(info, "Could not remove the prev backup file - throwing;", backupPath) + LOG(warning, "Could not remove the prev backup file - throwing;", backupPath) resumeBinarySources(document->root()); throw DiskAccessException( "Could not remove the file. Check you have enough permissions and the file is not locked by another process.", backupPath); } if (!QFile::rename(path_, backupPath)) { - //LOG(info, "Could not replace the prev PBO file with a write copy - throwing;", loadedPath_) + LOG(info, "Could not replace the prev PBO file with a write copy - throwing:", path_) resumeBinarySources(document->root()); throw DiskAccessException( "Could not write to the file. Check you have enough permissions and the file is not locked by another process.", path_); } - const bool renamed = QFile::rename(filePath, path_); - assert(renamed); + backupMade = QFile::rename(filePath, path_); + if (!backupMade) { + LOG(warning, "Could not rename file 1 to file 2 - throwing:", filePath, "|", path_) + throw DiskAccessException("Could not rename the file. Normally this must not happen.", filePath); + } } if (cancel()) { + LOG(info, "Cancel - removing the written files") + if (backupMade) { + if (!QFile::remove(path_)) { + LOG(warning, "Could not remove the file - throwing", path_) + throw DiskAccessException("Could not remove the file. Normally this must not happen.", path_); + } + LOG(info, "The original file has been already renamed - renaming back") + if (!QFile::rename(backupPath, path_)) { + LOG(warning, "Could not rename file 1 to file 2 - throwing:", backupPath, "|", path_) + throw DiskAccessException("Could not renames the file. Normally this must not happen.", backupPath); + } + } else { + if (!shouldBackup && !QFile::remove(path_)) { + LOG(warning, "Could not remove the file - throwing", path_) + throw DiskAccessException("Could not remove the file. Normally this must not happen.", path_); + } + } + LOG(info, "Resuming binary sources") resumeBinarySources(document->root()); } else { + LOG(info, "Assigining binary sources") assignBinarySources(document->root()); } } void DocumentWriter::writeInternal(PboDocument* document, const QString& path, const Cancel& cancel) { + LOG(info, "Writing to the file:", path) + QTemporaryFile body; body.setFileName(path + ".b"); - if (!body.open()) + if (!body.open()) { + LOG(warning, "Could not open the body temp file - throwing:", body.fileName()) throw DiskAccessException("Could not create the file.", body.fileName()); + } + LOG(info, "Writing nodes") QList> entries; writeNode(&body, document->root(), entries, cancel); - if (cancel()) + if (cancel()) { + LOG(info, "Cancel - return") return; + } PboFile pbo(path); - if (!pbo.open(QIODeviceBase::ReadWrite)) + if (!pbo.open(QIODeviceBase::ReadWrite)) { + LOG(warning, "Could not open the file - throwing:", path) throw DiskAccessException("Could not create the file.", path); + } + LOG(info, "Writing headers") writeHeader(&pbo, document->headers(), entries, cancel); if (cancel()) { + LOG(info, "Cancel - clean temp files and return") pbo.close(); pbo.remove(); return; @@ -74,11 +122,14 @@ namespace pboman3::io { const bool seek = body.seek(0); assert(seek); + LOG(info, "Copy body bytes") copyBody(&pbo, &body, cancel); + LOG(info, "Calc signature") writeSignature(&pbo, document, cancel); if (cancel()) { + LOG(info, "Cancel - clean temp files") pbo.close(); pbo.remove(); } @@ -87,8 +138,10 @@ namespace pboman3::io { void DocumentWriter::writeNode(QFileDevice* file, PboNode* node, QList>& entries, const Cancel& cancel) { for (PboNode* child : *node) { - if (cancel()) + if (cancel()) { + LOG(info, "Cancel - return") return; + } if (child->nodeType() == PboNodeType::File) { const qint64 before = file->pos(); @@ -136,6 +189,7 @@ namespace pboman3::io { } if (cancel()) { + LOG(info, "Cancel - return") return; } @@ -143,12 +197,14 @@ namespace pboman3::io { for (const QSharedPointer& entry : entries) { if (cancel()) { + LOG(info, "Cancel - break") break; } io.writeEntry(*entry); } if (cancel()) { + LOG(info, "Cancel - return") return; } @@ -174,8 +230,10 @@ namespace pboman3::io { copiedBytes += read; emitCopyBytes(copiedBytes, totalBytes); - if (cancel()) + if (cancel()) { + LOG(info, "Cancel - return") return; + } read = body->read(data.data(), data.size()); } } @@ -199,8 +257,10 @@ namespace pboman3::io { emitCalcHash(processed, total); } - if (cancel()) + if (cancel()) { + LOG(info, "Cancel - return") return; + } document->setSignature(sha1.result()); @@ -212,8 +272,7 @@ namespace pboman3::io { for (PboNode* child : *node) { if (child->nodeType() == PboNodeType::File) { child->binarySource->close(); - } - else { + } else { suspendBinarySources(child); } } @@ -223,8 +282,7 @@ namespace pboman3::io { for (PboNode* child : *node) { if (child->nodeType() == PboNodeType::File) { child->binarySource->open(); - } - else { + } else { resumeBinarySources(child); } } @@ -236,8 +294,7 @@ namespace pboman3::io { const PboDataInfo& existing = binarySources_.take(child); child->binarySource = QSharedPointer(new PboBinarySource(path_, existing)); child->binarySource->open(); - } - else { + } else { assignBinarySources(child); } } diff --git a/pbom/model/pbomodel.cpp b/pbom/model/pbomodel.cpp index 4a58a94..c879343 100644 --- a/pbom/model/pbomodel.cpp +++ b/pbom/model/pbomodel.cpp @@ -26,8 +26,9 @@ namespace pboman3::model { const DocumentReader reader(path); try { document_ = reader.read(); + LOG(info, "Read the document:", *document_); } catch (const AppException& ex) { - LOG(debug, "Could not load the file:", ex) + LOG(info, "Could not load the file:", ex) setLoadedPath(nullptr); throw; } @@ -52,7 +53,10 @@ namespace pboman3::model { writer.write(document_.get(), cancel); if (!cancel()) { + LOG(info, "Write process complete") setLoadedPath(savePath); + } else { + LOG(info, "Write process cancelled") } } From 206295e51c79bfe7e436e5e5b5315130a28c831d Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Mon, 22 Nov 2021 21:35:04 +0300 Subject: [PATCH 22/39] Significantly reduced log spam when opening a file --- pbom/domain/pbodocument.cpp | 2 +- pbom/domain/pbonode.cpp | 7 +++++-- pbom/ui/win32/win32iconmgr.cpp | 9 ++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pbom/domain/pbodocument.cpp b/pbom/domain/pbodocument.cpp index ebc4c2d..09aa2e6 100644 --- a/pbom/domain/pbodocument.cpp +++ b/pbom/domain/pbodocument.cpp @@ -51,6 +51,6 @@ namespace pboman3::domain { QDebug& operator<<(QDebug& debug, const PboDocument& document) { return debug << "PboDocument(Headers=" << *document.headers_ << ", Root=" << *document.root_ << ", Signature=" - << document.signature_.length() << " bytes)"; + << document.signature_.length() << "bytes)"; } } diff --git a/pbom/domain/pbonode.cpp b/pbom/domain/pbonode.cpp index e031151..6622788 100644 --- a/pbom/domain/pbonode.cpp +++ b/pbom/domain/pbonode.cpp @@ -114,7 +114,7 @@ namespace pboman3::domain { } PboNode* PboNode::createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, - bool emitEvents) { + bool emitEvents) { PboNode* node = this; for (qsizetype i = 0; i < entryPath.length() - 1; i++) { PboNode* folder = node->findChild(entryPath.at(i)); @@ -270,6 +270,9 @@ namespace pboman3::domain { } QDebug operator<<(QDebug debug, const PboNode& node) { - return debug << "PboNode(" << node.makePath() << ")"; + return node.parentNode_ + ? debug << "PboNode(Path=" << node.makePath() << ")" + : debug << "PboNode(RootTitle=" << node.title_ << ")"; + } } diff --git a/pbom/ui/win32/win32iconmgr.cpp b/pbom/ui/win32/win32iconmgr.cpp index d9c2fcb..5d3a6c0 100644 --- a/pbom/ui/win32/win32iconmgr.cpp +++ b/pbom/ui/win32/win32iconmgr.cpp @@ -19,13 +19,12 @@ namespace pboman3::ui { if (extension.startsWith(".")) throw AppException("The extension must not start with a \".\" symbol"); - LOG(debug, "Get icon for extension:", extension) - if (cache_.contains(extension)) { - LOG(debug, "Retrieve from cache") return cache_[extension]; } + LOG(info, "Get icon for extension:", extension) + SHFILEINFOW info; const QString fn = "file." + extension; const DWORD_PTR hr = SHGetFileInfoW( @@ -35,7 +34,7 @@ namespace pboman3::ui { sizeof info, SHGFI_ICON | SHGFI_USEFILEATTRIBUTES); - LOG(debug, "Retrieve from the OS:", hr) + LOG(info, "Retrieve from the OS:", hr) if (SUCCEEDED(hr)) { cache_[extension] = QIcon(QPixmap::fromImage(QImage::fromHICON(info.hIcon))); @@ -43,7 +42,7 @@ namespace pboman3::ui { return cache_[extension]; } - LOG(debug, "Retrieve failed - fall back to the default", hr) + LOG(warning, "Retrieve failed - fall back to the default", hr) return cache_[""]; } From 3810b9755c58267ab0f2a713d6e1bab595fa0bda Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Tue, 23 Nov 2021 16:09:17 +0300 Subject: [PATCH 23/39] When opening a file, the current one is unloaded explicitly now --- pbom/ui/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index afd6cc2..deff44a 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -41,21 +41,23 @@ namespace pboman3::ui { } void MainWindow::loadFile(const QString& fileName) { + if (model_->isLoaded()) + unloadFile(); + LOG(info, "Loading the file:", fileName) try { model_->loadFile(fileName); } catch (const PboFileFormatException& ex) { LOG(info, "Error when loading file - show error modal:", ex) UI_HANDLE_ERROR(ex) - if (model_->isLoaded()) unloadFile(); } catch (const DiskAccessException& ex) { LOG(info, "Error when loading file - show error modal:", ex) UI_HANDLE_ERROR(ex) - if (model_->isLoaded()) unloadFile(); } } void MainWindow::unloadFile() { + LOG(info, "Unloading the current file") setHasChanges(false); model_->unloadFile(); } From e5ba2de7a2c4cb4c0309edc0918c01abd31eb679 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Tue, 23 Nov 2021 17:43:19 +0300 Subject: [PATCH 24/39] The main window now disabled when saving a file --- pbom/ui/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index deff44a..b3daa7c 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -317,8 +317,6 @@ namespace pboman3::ui { ui_->actionSelectionExtractContainer->setEnabled(false); ui_->actionSelectionExtractContainer->setVisible(false); } - - } void MainWindow::saveFile(const QString& fileName) { @@ -329,12 +327,16 @@ namespace pboman3::ui { promise.addResult(0); }); + ui_->menubar->setEnabled(false); + ui_->treeWidget->setEnabled(false); ui_->statusBar->progressShow(static_cast>(future)); saveWatcher_.setFuture(future); } void MainWindow::saveComplete() { + ui_->menubar->setEnabled(true); + ui_->treeWidget->setEnabled(true); ui_->statusBar->progressHide(); QFuture future = saveWatcher_.future(); From e61d9e4207dbcc26af58b00405357b9d8a914488 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Thu, 25 Nov 2021 19:04:28 +0300 Subject: [PATCH 25/39] When renaming a pbo node conflicts now checked case-insensitively --- pbom/domain/CMakeLists.txt | 2 + pbom/domain/__test__/func_test.cpp | 79 +++++++++++++++++++++++++++ pbom/domain/__test__/pbonode_test.cpp | 17 ------ pbom/domain/func.cpp | 45 +++++++++++++++ pbom/domain/func.h | 13 +++++ pbom/domain/pbonode.cpp | 32 ++--------- pbom/domain/pbonode.h | 4 -- pbom/domain/pbonodetransaction.cpp | 3 +- pbom/model/pbomodel.cpp | 3 +- pbom/model/task/unpacktask.cpp | 12 +--- pbom/model/task/unpacktask.h | 2 - 11 files changed, 149 insertions(+), 63 deletions(-) create mode 100644 pbom/domain/__test__/func_test.cpp create mode 100644 pbom/domain/func.cpp create mode 100644 pbom/domain/func.h diff --git a/pbom/domain/CMakeLists.txt b/pbom/domain/CMakeLists.txt index 5f9b5dc..eef66ab 100644 --- a/pbom/domain/CMakeLists.txt +++ b/pbom/domain/CMakeLists.txt @@ -3,6 +3,7 @@ list(APPEND PROJECT_SOURCES "domain/documentheader.cpp" "domain/documentheaders.cpp" "domain/documentheaderstransaction.cpp" + "domain/func.cpp" "domain/pbodocument.cpp" "domain/pbonode.cpp" "domain/pbonodetransaction.cpp" @@ -15,6 +16,7 @@ list(APPEND TEST_SOURCES "domain/__test__/documentheader_test.cpp" "domain/__test__/documentheaders_test.cpp" "domain/__test__/documentheaderstransaction_test.cpp" + "domain/__test__/func_test.cpp" "domain/__test__/pbodocument_test.cpp" "domain/__test__/pbonode_test.cpp" "domain/__test__/pbonodetransaction_test.cpp" diff --git a/pbom/domain/__test__/func_test.cpp b/pbom/domain/__test__/func_test.cpp new file mode 100644 index 0000000..6ee0031 --- /dev/null +++ b/pbom/domain/__test__/func_test.cpp @@ -0,0 +1,79 @@ +#include +#include "domain/func.h" +#include "domain/pbonode.h" + +namespace pboman3::domain::test { + TEST(FuncTest, CountFilesInTree_Counts_Files) { + PboNode node("file.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("f1/e1.txt")); + node.createHierarchy(PboPath("f1/e2.txt")); + node.createHierarchy(PboPath("f1/f2/e2.txt")); + node.createHierarchy(PboPath("e2.txt")); + + int count = 0; + CountFilesInTree(node, count); + + ASSERT_EQ(count, 4); + } + + TEST(FuncTest, FindDirectChild_Finds_Child_Case_Insensitively_Const) { + PboNode node("file.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("e1.txt")); + node.createHierarchy(PboPath("e2.txt")); + PboNode* e3 = node.createHierarchy(PboPath("e3.txt")); + + const PboNode* found = FindDirectChild(const_cast(&node), "E3.TxT"); + + ASSERT_EQ(e3, found); + } + + TEST(FuncTest, FindDirectChild_Returns_Null_Const) { + PboNode node("file.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("e1.txt")); + node.createHierarchy(PboPath("e2.txt")); + node.createHierarchy(PboPath("e3.txt")); + + const PboNode* found = FindDirectChild(const_cast(&node), "e4.txt"); + + ASSERT_EQ(found, nullptr); + } + + TEST(FuncTest, FindDirectChild_Finds_Child_Case_Insensitively_NonConst) { + PboNode node("file.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("e1.txt")); + node.createHierarchy(PboPath("e2.txt")); + PboNode* e3 = node.createHierarchy(PboPath("e3.txt")); + + const PboNode* found = FindDirectChild(&node, "E3.TxT"); + + ASSERT_EQ(e3, found); + } + + TEST(FuncTest, FindDirectChild_Returns_Null_NonConst) { + PboNode node("file.pbo", PboNodeType::Container, nullptr); + node.createHierarchy(PboPath("e1.txt")); + node.createHierarchy(PboPath("e2.txt")); + node.createHierarchy(PboPath("e3.txt")); + + const PboNode* found = FindDirectChild(&node, "e4.txt"); + + ASSERT_EQ(found, nullptr); + } + + TEST(FuncTest, IsPathConflict_Functional) { + PboNode root("file-name", PboNodeType::Container, nullptr); + ASSERT_EQ(root.depth(), 0); + + root.createHierarchy(PboPath("e1.txt")); + root.createHierarchy(PboPath("f2/e2.txt")); + + ASSERT_TRUE(IsPathConflict(&root, PboPath("e1.txT"))); + ASSERT_TRUE(IsPathConflict(&root, PboPath("f2"))); + ASSERT_TRUE(IsPathConflict(&root, PboPath("F2/e2.Txt"))); + ASSERT_TRUE(IsPathConflict(&root, PboPath("f2/E2.txt/e4.txt"))); + + ASSERT_FALSE(IsPathConflict(&root, PboPath("e2.txt"))); + ASSERT_FALSE(IsPathConflict(&root, PboPath("f2/e3.txt"))); + ASSERT_FALSE(IsPathConflict(&root, PboPath("f3/e4.txt"))); + } +} diff --git a/pbom/domain/__test__/pbonode_test.cpp b/pbom/domain/__test__/pbonode_test.cpp index 567cee1..6196093 100644 --- a/pbom/domain/__test__/pbonode_test.cpp +++ b/pbom/domain/__test__/pbonode_test.cpp @@ -300,23 +300,6 @@ namespace pboman3::domain::test { ASSERT_EQ(count, 1); } - TEST(PboNodeTest, IsPathConflict_Functional) { - PboNode root("file-name", PboNodeType::Container, nullptr); - ASSERT_EQ(root.depth(), 0); - - root.createHierarchy(PboPath("e1.txt")); - root.createHierarchy(PboPath("f2/e2.txt")); - - ASSERT_TRUE(root.isPathConflict(PboPath("e1.txt"))); - ASSERT_TRUE(root.isPathConflict(PboPath("f2"))); - ASSERT_TRUE(root.isPathConflict(PboPath("f2/e2.txt"))); - ASSERT_TRUE(root.isPathConflict(PboPath("f2/e2.txt/e4.txt"))); - - ASSERT_FALSE(root.isPathConflict(PboPath("e2.txt"))); - ASSERT_FALSE(root.isPathConflict(PboPath("f2/e3.txt"))); - ASSERT_FALSE(root.isPathConflict(PboPath("f3/e4.txt"))); - } - TEST(PboNodeTest, SetTitle_Wont_Emit_If_Title_Not_Changed) { PboNode root("file-name", PboNodeType::Container, nullptr); diff --git a/pbom/domain/func.cpp b/pbom/domain/func.cpp new file mode 100644 index 0000000..16475c5 --- /dev/null +++ b/pbom/domain/func.cpp @@ -0,0 +1,45 @@ +#include "func.h" + +namespace pboman3::domain { + void CountFilesInTree(const PboNode& node, qint32& result) { + if (node.nodeType() == PboNodeType::File) { + result++; + } else { + for (const PboNode* child : node) + CountFilesInTree(*child, result); + } + } + + bool IsPathConflict(const PboNode* node, const PboPath& path) { + const PboNode* n = node; + for (qsizetype i = 0; i < path.length() - 1; i++) { + const PboNode* folder = FindDirectChild(n, path.at(i)); + if (folder) { + if (folder->nodeType() == PboNodeType::File) + return true; + n = folder; + } else { + return false; + } + } + + const PboNode* file = FindDirectChild(n, path.last()); + return file; + } + + const PboNode* FindDirectChild(const PboNode* parent, const QString& title) { + for (const PboNode* child : *parent) { + if (child->title().compare(title, Qt::CaseInsensitive) == 0) + return child; + } + return nullptr; + } + + PboNode* FindDirectChild(PboNode* parent, const QString& title) { + for (PboNode* child : *parent) { + if (child->title().compare(title, Qt::CaseInsensitive) == 0) + return child; + } + return nullptr; + } +} diff --git a/pbom/domain/func.h b/pbom/domain/func.h new file mode 100644 index 0000000..4b3277b --- /dev/null +++ b/pbom/domain/func.h @@ -0,0 +1,13 @@ +#pragma once + +#include "pbonode.h" + +namespace pboman3::domain { + void CountFilesInTree(const PboNode& node, qint32& result); + + bool IsPathConflict(const PboNode* node, const PboPath& path); + + const PboNode* FindDirectChild(const PboNode* parent, const QString& title); + + PboNode* FindDirectChild(PboNode* parent, const QString& title); +} diff --git a/pbom/domain/pbonode.cpp b/pbom/domain/pbonode.cpp index 6622788..4dbf919 100644 --- a/pbom/domain/pbonode.cpp +++ b/pbom/domain/pbonode.cpp @@ -3,6 +3,7 @@ #include "exception.h" #include "validationexception.h" #include "pbonodetransaction.h" +#include "func.h" namespace pboman3::domain { PboNode::PboNode(QString title, PboNodeType nodeType, PboNode* parentNode) @@ -42,23 +43,6 @@ namespace pboman3::domain { p->emitHierarchyChanged(); } - bool PboNode::isPathConflict(const PboPath& path) const { - const PboNode* node = this; - for (qsizetype i = 0; i < path.length() - 1; i++) { - const PboNode* folder = node->findChild(path.at(i)); - if (folder) { - if (folder->nodeType_ == PboNodeType::File) - return true; - node = folder; - } else { - return false; - } - } - - const PboNode* file = node->findChild(path.last()); - return file; - } - const QString& PboNode::title() const { return title_; } @@ -71,7 +55,7 @@ namespace pboman3::domain { PboNode* result = this; auto it = path.begin(); while (it != path.end()) { - result = result->findChild(*it); + result = FindDirectChild(result, *it); if (!result) return nullptr; ++it; @@ -117,7 +101,7 @@ namespace pboman3::domain { bool emitEvents) { PboNode* node = this; for (qsizetype i = 0; i < entryPath.length() - 1; i++) { - PboNode* folder = node->findChild(entryPath.at(i)); + PboNode* folder = FindDirectChild(node, entryPath.at(i)); if (!folder) { folder = node->createChild(entryPath.at(i), PboNodeType::Folder); } else if (folder->nodeType_ == PboNodeType::File) { @@ -141,7 +125,7 @@ namespace pboman3::domain { node = folder; } - PboNode* file = node->findChild(entryPath.last()); + PboNode* file = FindDirectChild(node, entryPath.last()); if (!file) { file = node->createChild(entryPath.last(), PboNodeType::File); if (emitEvents) @@ -170,14 +154,6 @@ namespace pboman3::domain { return file; } - PboNode* PboNode::findChild(const QString& title) const { - for (const QSharedPointer& child : children_) { - if (child->title_ == title) - return child.get(); - } - return nullptr; - } - QString PboNode::pickFolderTitle(const PboNode* parent, const QString& expectedTitle) const { int index = 1; QString attemptTitle = formatFolderTitleCopy(expectedTitle, index); diff --git a/pbom/domain/pbonode.h b/pbom/domain/pbonode.h index 83007c1..b505384 100644 --- a/pbom/domain/pbonode.h +++ b/pbom/domain/pbonode.h @@ -24,8 +24,6 @@ namespace pboman3::domain { void removeFromHierarchy(); - bool isPathConflict(const PboPath& path) const; - const QString& title() const; PboNodeType nodeType() const; @@ -57,8 +55,6 @@ namespace pboman3::domain { PboNode* createHierarchy(const PboPath& entryPath, const ConflictResolution& onConflict, bool emitEvents); - PboNode* findChild(const QString& title) const; - QString pickFolderTitle(const PboNode* parent, const QString& expectedTitle) const; QString pickFileTitle(const PboNode* parent, const QString& expectedTitle) const; diff --git a/pbom/domain/pbonodetransaction.cpp b/pbom/domain/pbonodetransaction.cpp index f8472f1..15ef83a 100644 --- a/pbom/domain/pbonodetransaction.cpp +++ b/pbom/domain/pbonodetransaction.cpp @@ -1,6 +1,7 @@ #include "pbonodetransaction.h" #include "validationexception.h" #include "exception.h" +#include "domain/func.h" namespace pboman3::domain { PboNodeTransaction::PboNodeTransaction(PboNode* node) @@ -39,7 +40,7 @@ namespace pboman3::domain { return "The value can not be empty"; if (!node_->parentNode_) return ""; - PboNode* existing = node_->parentNode_->findChild(title); + PboNode* existing = FindDirectChild(node_->parentNode_, title); return existing && existing != node_ ? "The item with this name already exists" : ""; } } diff --git a/pbom/model/pbomodel.cpp b/pbom/model/pbomodel.cpp index c879343..2393f07 100644 --- a/pbom/model/pbomodel.cpp +++ b/pbom/model/pbomodel.cpp @@ -1,5 +1,6 @@ #include "pbomodel.h" #include "domain/pbonode.h" +#include "domain/func.h" #include #include #include @@ -125,7 +126,7 @@ namespace pboman3::model { ConflictsParcel conflicts; for (const NodeDescriptor& descriptor : descriptors) { - if (parent->isPathConflict(PboPath(descriptor.path()))) { + if (IsPathConflict(parent, PboPath(descriptor.path()))) { LOG(info, "The descriptor is in conflict:", descriptor) conflicts.setResolution(descriptor, ConflictResolution::Copy); } diff --git a/pbom/model/task/unpacktask.cpp b/pbom/model/task/unpacktask.cpp index 7d892c6..b90fbe2 100644 --- a/pbom/model/task/unpacktask.cpp +++ b/pbom/model/task/unpacktask.cpp @@ -5,6 +5,7 @@ #include "io/bs/pbobinarysource.h" #include "io/pbonodeentity.h" #include "domain/pbonode.h" +#include "domain/func.h" #include "io/diskaccessexception.h" #include "io/documentreader.h" #include "io/pbofileformatexception.h" @@ -33,7 +34,7 @@ namespace pboman3::model { constexpr qsizetype startProgress = 0; qint32 endProgress = 0; - countNodeFiles(document->root(), endProgress); + CountFilesInTree(*document->root(), endProgress); emit taskInitialized(pboPath_, startProgress, endProgress); std::function onError = [this](const QString& error) { @@ -110,13 +111,4 @@ namespace pboman3::model { return true; } - - void UnpackTask::countNodeFiles(const PboNode* node, qint32& count) { - if (node->nodeType() == PboNodeType::File) { - count++; - } else { - for (const PboNode* child : *node) - countNodeFiles(child, count); - } - } } diff --git a/pbom/model/task/unpacktask.h b/pbom/model/task/unpacktask.h index de13590..544e48d 100644 --- a/pbom/model/task/unpacktask.h +++ b/pbom/model/task/unpacktask.h @@ -24,7 +24,5 @@ namespace pboman3::model { bool tryCreatePboDir(QDir* dir); bool tryCreateEntryDir(const QDir& pboDir, const QSharedPointer& entry); - - static void countNodeFiles(const PboNode* node, qint32& count); }; } From c288aea74040a5cbaf9d186460d806a5cedd474a Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Thu, 25 Nov 2021 19:09:39 +0300 Subject: [PATCH 26/39] Fixed incorrect menu item title when extracting an item into a container folder --- pbom/ui/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index b3daa7c..eaccfa2 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -41,7 +41,7 @@ namespace pboman3::ui { } void MainWindow::loadFile(const QString& fileName) { - if (model_->isLoaded()) + if (model_->isLoaded()) unloadFile(); LOG(info, "Loading the file:", fileName) @@ -408,7 +408,9 @@ namespace pboman3::ui { } QString MainWindow::makeExtractToTitle(const PboNode* node) const { - return "Extract to ./" + node->title() + return "Extract to ./" + (node->nodeType() == PboNodeType::Container + ? GetFileNameWithoutExtension(node->title()) + : node->title()) + (node->nodeType() == PboNodeType::File ? "" : "/"); } } From a9b1f71942faecd0fc58ac276a017145ce3d213b Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Thu, 25 Nov 2021 20:00:27 +0300 Subject: [PATCH 27/39] Fixed the DiskAccessException was caught as AppException Cleanup io/exceptions --- pbom/io/diskaccessexception.cpp | 4 ++++ pbom/io/diskaccessexception.h | 2 ++ pbom/io/pbofileformatexception.cpp | 8 ++++++-- pbom/io/pbofileformatexception.h | 4 +++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pbom/io/diskaccessexception.cpp b/pbom/io/diskaccessexception.cpp index a55cb41..5fe1320 100644 --- a/pbom/io/diskaccessexception.cpp +++ b/pbom/io/diskaccessexception.cpp @@ -18,4 +18,8 @@ namespace pboman3::io { const QString& DiskAccessException::file() const { return file_; } + + QDebug operator<<(QDebug debug, const DiskAccessException& ex) { + return debug << "DiskAccessException(" << ex.message_ << ")"; + } } diff --git a/pbom/io/diskaccessexception.h b/pbom/io/diskaccessexception.h index 08825e7..1dd7b82 100644 --- a/pbom/io/diskaccessexception.h +++ b/pbom/io/diskaccessexception.h @@ -13,6 +13,8 @@ namespace pboman3::io { const QString& file() const; + friend QDebug operator<<(QDebug debug, const DiskAccessException& ex); + private: QString file_; }; diff --git a/pbom/io/pbofileformatexception.cpp b/pbom/io/pbofileformatexception.cpp index 128e8b3..04b85e0 100644 --- a/pbom/io/pbofileformatexception.cpp +++ b/pbom/io/pbofileformatexception.cpp @@ -6,11 +6,15 @@ namespace pboman3::io { : AppException(std::move(message)) { } - QDebug operator<<(QDebug debug, const PboFileFormatException& ex) { - return debug << "PboFileFormatException(" << ex.message_ << ")"; + void PboFileFormatException::raise() const { + throw *this; } QException* PboFileFormatException::clone() const { return new PboFileFormatException(*this); } + + QDebug operator<<(QDebug debug, const PboFileFormatException& ex) { + return debug << "PboFileFormatException(" << ex.message_ << ")"; + } } diff --git a/pbom/io/pbofileformatexception.h b/pbom/io/pbofileformatexception.h index 641f79c..20795d5 100644 --- a/pbom/io/pbofileformatexception.h +++ b/pbom/io/pbofileformatexception.h @@ -7,8 +7,10 @@ namespace pboman3::io { public: explicit PboFileFormatException(QString message); - friend QDebug operator<<(QDebug debug, const PboFileFormatException& ex); + void raise() const override; QException* clone() const override; + + friend QDebug operator<<(QDebug debug, const PboFileFormatException& ex); }; } From d931fa00504e116f8a58e5229f256f79947c5cd0 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 27 Nov 2021 13:03:49 +0300 Subject: [PATCH 28/39] Fixed the Pack command was incorrectly interpreting the -o option --- pbom/model/task/packtask.cpp | 24 ++++++++++-------------- pbom/model/task/packtask.h | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/pbom/model/task/packtask.cpp b/pbom/model/task/packtask.cpp index 27cd910..570284c 100644 --- a/pbom/model/task/packtask.cpp +++ b/pbom/model/task/packtask.cpp @@ -1,5 +1,4 @@ #include "packtask.h" - #include "io/diskaccessexception.h" #include "io/documentwriter.h" #include "util/log.h" @@ -18,9 +17,8 @@ namespace pboman3::model { LOG(info, "Folder file: ", folder_) LOG(info, "Output dir: ", outputDir_) - const QFileInfo fi(folder_); - - const QString pboFile = QDir(outputDir_).filePath(fi.fileName()).append(".pbo"); + const QDir folder(folder_); + const QString pboFile = QDir(outputDir_).filePath(folder.dirName()).append(".pbo"); LOG(info, "The pbo file name:", pboFile) if (QFileInfo(pboFile).exists()) { LOG(info, "The pbo file already exists") @@ -28,17 +26,17 @@ namespace pboman3::model { return; } - emit taskThinking(fi.absoluteFilePath()); + emit taskThinking(folder.absolutePath()); PboDocument document("root"); - const qint32 filesCount = collectDir(fi, fi.dir(), *document.root(), cancel); + const qint32 filesCount = collectDir(folder, folder, *document.root(), cancel); if (cancel()) return; if (filesCount == 0) { LOG(info, "The Folder was empty") - emit taskMessage("Failure | The folder is empty | " + fi.absolutePath()); + emit taskMessage("Failure | The folder is empty | " + folder.absolutePath()); return; } @@ -59,7 +57,7 @@ namespace pboman3::model { #define WT_BODY 2 #define WT_SIGNATURE 7 - emit taskInitialized(fi.absoluteFilePath(), 0, filesCount * (WT_ENTRIES + WT_BODY + WT_SIGNATURE)); + emit taskInitialized(folder.absolutePath(), 0, filesCount * (WT_ENTRIES + WT_BODY + WT_SIGNATURE)); qint32 progress = 0; connect(&writer, &DocumentWriter::progress, [this, &progress, filesCount](const DocumentWriter::ProgressEvent* evt) { @@ -87,7 +85,7 @@ namespace pboman3::model { LOG(info, "Unpack complete") } catch (const DiskAccessException& ex) { LOG(warning, "Task failed with exception:", ex) - emit taskMessage("Failure | " + ex.message() + " | " + fi.absolutePath()); + emit taskMessage("Failure | " + ex.message() + " | " + folder.absolutePath()); } } @@ -95,23 +93,21 @@ namespace pboman3::model { return debug << "PackTask(Folder=" << task.folder_ << ", OutputDir=" << task.outputDir_ << ")"; } - qint32 PackTask::collectDir(const QFileInfo& dirEntry, const QDir& rootDir, PboNode& rootNode, + qint32 PackTask::collectDir(const QDir& dirEntry, const QDir& rootDir, PboNode& rootNode, const Cancel& cancel) const { LOG(debug, "Collecting the dir:", dirEntry) qint32 count = 0; - const QDir d(dirEntry.filePath() + QDir::separator()); - const QFileInfoList entries = d.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); + const QFileInfoList entries = dirEntry.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); for (const QFileInfo& entry : entries) { if (cancel()) return 0; - if (!entry.isSymLink()) { if (entry.isFile()) count += collectFile(entry, rootDir, rootNode); else if (entry.isDir()) - count += collectDir(entry, rootDir, rootNode, cancel); + count += collectDir(QDir(entry.filePath()), rootDir, rootNode, cancel); } } diff --git a/pbom/model/task/packtask.h b/pbom/model/task/packtask.h index 70bf11b..6e99063 100644 --- a/pbom/model/task/packtask.h +++ b/pbom/model/task/packtask.h @@ -19,7 +19,7 @@ namespace pboman3::model { const QString folder_; const QString outputDir_; - qint32 collectDir(const QFileInfo& dirEntry, const QDir& rootDir, PboNode& rootNode, const Cancel& cancel) const; + qint32 collectDir(const QDir& dirEntry, const QDir& rootDir, PboNode& rootNode, const Cancel& cancel) const; qint32 collectFile(const QFileInfo& fileEntry, const QDir& rootDir, PboNode& rootNode) const; }; From 58f66c96344d6106b08e5685fb415902a4f26ae0 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 27 Nov 2021 13:29:15 +0300 Subject: [PATCH 29/39] Files now loaded in the background thread --- pbom/ui/mainwindow.cpp | 48 ++++++++++++++++++++++++++++++++++-------- pbom/ui/mainwindow.h | 7 ++++++ pbom/ui/statusbar.cpp | 11 +++++----- pbom/ui/statusbar.h | 2 +- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index eaccfa2..ba469e7 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -44,9 +44,30 @@ namespace pboman3::ui { if (model_->isLoaded()) unloadFile(); - LOG(info, "Loading the file:", fileName) - try { + const QFuture future = QtConcurrent::run([this, fileName](QPromise& promise) { + LOG(info, "Loading the file:", fileName) model_->loadFile(fileName); + promise.addResult(0); + }); + + setIsLoading(static_cast>(future), false); + loadWatcher_.setFuture(future); + } + + void MainWindow::loadComplete() { + resetIsLoading(); + + QFuture future = loadWatcher_.future(); + + if (!future.isValid()) { + LOG(info, "File loading was cancelled - exiting") + return; + } + + try { + future.takeResult(); //to get exceptions rethrown + LOG(info, "File loading is complete") + setHasChanges(false); } catch (const PboFileFormatException& ex) { LOG(info, "Error when loading file - show error modal:", ex) UI_HANDLE_ERROR(ex) @@ -71,11 +92,12 @@ namespace pboman3::ui { } void MainWindow::setupConnections() { + connect(&loadWatcher_, &QFutureWatcher::finished, this, &MainWindow::loadComplete); connect(&saveWatcher_, &QFutureWatcher::finished, this, &MainWindow::saveComplete); connect(ui_->treeWidget, &TreeWidget::backgroundOpStarted, this, [this](QFuture f) { ui_->treeWidget->setEnabled(false); - ui_->statusBar->progressShow(f); + ui_->statusBar->progressShow(std::move(f), true); }); connect(ui_->treeWidget, &TreeWidget::backgroundOpStopped, this, [this]() { ui_->treeWidget->setEnabled(true); @@ -327,17 +349,13 @@ namespace pboman3::ui { promise.addResult(0); }); - ui_->menubar->setEnabled(false); - ui_->treeWidget->setEnabled(false); - ui_->statusBar->progressShow(static_cast>(future)); + setIsLoading(static_cast>(future), true); saveWatcher_.setFuture(future); } void MainWindow::saveComplete() { - ui_->menubar->setEnabled(true); - ui_->treeWidget->setEnabled(true); - ui_->statusBar->progressHide(); + resetIsLoading(); QFuture future = saveWatcher_.future(); @@ -407,6 +425,18 @@ namespace pboman3::ui { } } + void MainWindow::setIsLoading(QFuture future, bool supportsCancellation) const { + ui_->menubar->setEnabled(false); + ui_->treeWidget->setEnabled(false); + ui_->statusBar->progressShow(std::move(future), supportsCancellation); + } + + void MainWindow::resetIsLoading() const { + ui_->menubar->setEnabled(true); + ui_->treeWidget->setEnabled(true); + ui_->statusBar->progressHide(); + } + QString MainWindow::makeExtractToTitle(const PboNode* node) const { return "Extract to ./" + (node->nodeType() == PboNodeType::Container ? GetFileNameWithoutExtension(node->title()) diff --git a/pbom/ui/mainwindow.h b/pbom/ui/mainwindow.h index fa5362d..8b059d5 100644 --- a/pbom/ui/mainwindow.h +++ b/pbom/ui/mainwindow.h @@ -30,8 +30,11 @@ namespace pboman3::ui { Ui::MainWindow* ui_; PboModel* model_; QFutureWatcher saveWatcher_; + QFutureWatcher loadWatcher_; bool hasChanges_; + void loadComplete(); + void unloadFile(); void setupConnections(); @@ -70,6 +73,10 @@ namespace pboman3::ui { void updateWindowTitle(); + void setIsLoading(QFuture future, bool supportsCancellation) const; + + void resetIsLoading() const; + QString makeExtractToTitle(const PboNode* node) const; }; } diff --git a/pbom/ui/statusbar.cpp b/pbom/ui/statusbar.cpp index 06545ec..3900f84 100644 --- a/pbom/ui/statusbar.cpp +++ b/pbom/ui/statusbar.cpp @@ -4,9 +4,10 @@ namespace pboman3::ui { StatusBar::StatusBar(QWidget* parent) - : QStatusBar(parent) { + : QStatusBar(parent), + progress_(new QProgressBar(this)), + button_(new QPushButton(this)) { - progress_ = new QProgressBar(this); progress_->setTextVisible(false); progress_->setMinimum(0); progress_->setMaximum(0); @@ -14,7 +15,6 @@ namespace pboman3::ui { progress_->setVisible(false); addWidget(progress_, 1); //ownership transferred! - button_ = new QPushButton(this); button_->setText("&Cancel"); button_->setStyleSheet("padding: 0 5; alignment: center;"); button_->setVisible(false); @@ -22,13 +22,14 @@ namespace pboman3::ui { addWidget(button_); //ownership transferred! } - void StatusBar::progressShow(QFuture future) { + void StatusBar::progressShow(QFuture future, bool supportsCancellation) { if (progress_->isVisible()) throw InvalidOperationException("A new background operation must not begin while the previous is running"); future_ = std::move(future); progress_->setVisible(true); - button_->setVisible(true); + if (supportsCancellation) + button_->setVisible(true); } void StatusBar::progressHide() const { diff --git a/pbom/ui/statusbar.h b/pbom/ui/statusbar.h index 0ce148b..2fe9caf 100644 --- a/pbom/ui/statusbar.h +++ b/pbom/ui/statusbar.h @@ -13,7 +13,7 @@ namespace pboman3::ui { StatusBar(QWidget* parent = nullptr); public slots: - void progressShow(QFuture future); + void progressShow(QFuture future, bool supportsCancellation = true); void progressHide() const; From 5a0b830a681e18a671f8b03c342a87c1a013f0d6 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 27 Nov 2021 16:16:57 +0300 Subject: [PATCH 30/39] Pack and Unpack commands now display progress in the windows taskbar --- Folder.DotSettings | 1 + pbom/ui/CMakeLists.txt | 1 + pbom/ui/taskbarindicator.h | 14 +++++++ pbom/ui/taskwindow.cpp | 34 ++++++++++++++++ pbom/ui/taskwindow.h | 21 ++++++++++ pbom/ui/win32/win32taskbarindicator.cpp | 53 +++++++++++++++++++++++++ pbom/ui/win32/win32taskbarindicator.h | 27 +++++++++++++ 7 files changed, 151 insertions(+) create mode 100644 pbom/ui/taskbarindicator.h create mode 100644 pbom/ui/win32/win32taskbarindicator.cpp create mode 100644 pbom/ui/win32/win32taskbarindicator.h diff --git a/Folder.DotSettings b/Folder.DotSettings index 8d51946..937585e 100644 --- a/Folder.DotSettings +++ b/Folder.DotSettings @@ -63,4 +63,5 @@ True True True + True True diff --git a/pbom/ui/CMakeLists.txt b/pbom/ui/CMakeLists.txt index 21ba616..fff2ac2 100644 --- a/pbom/ui/CMakeLists.txt +++ b/pbom/ui/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND PROJECT_SOURCES "ui/treewidget/treewidgetitem.cpp" "ui/win32/win32iconmgr.cpp" "ui/win32/win32fileviewer.cpp" + "ui/win32/win32taskbarindicator.cpp" "ui/aboutdialog.cpp" "ui/aboutdialog.ui" "ui/closedialog.cpp" diff --git a/pbom/ui/taskbarindicator.h b/pbom/ui/taskbarindicator.h new file mode 100644 index 0000000..7cd3256 --- /dev/null +++ b/pbom/ui/taskbarindicator.h @@ -0,0 +1,14 @@ +#pragma once + +namespace pboman3::ui { + class TaskbarIndicator { + public: + virtual ~TaskbarIndicator() = default; + + virtual void setProgressValue(qint64 value, qint64 maxValue) = 0; + + virtual void setIndeterminate() = 0; + + virtual void setError() = 0; + }; +} diff --git a/pbom/ui/taskwindow.cpp b/pbom/ui/taskwindow.cpp index 35ffe4f..5367b8d 100644 --- a/pbom/ui/taskwindow.cpp +++ b/pbom/ui/taskwindow.cpp @@ -5,6 +5,7 @@ #include "ui_taskwindow.h" #include "model/task/taskwindowmodel.h" #include "exception.h" +#include "win32/win32taskbarindicator.h" namespace pboman3::ui { TaskWindow::TaskWindow(QWidget* parent) @@ -14,6 +15,7 @@ namespace pboman3::ui { log_(nullptr), doneText_("Done") { ui_->setupUi(this); + taskbar_ = QSharedPointer(new TaskbarIndicator(winId())); } TaskWindow::~TaskWindow() { @@ -49,6 +51,8 @@ namespace pboman3::ui { } void TaskWindow::threadThinking(ThreadId threadId, const QString& text) const { + taskbar_->threadThinking(); + const ProgressWidget* progress = progressBars_.value(threadId); progress->setIndeterminate(true); progress->setText(text); @@ -56,6 +60,8 @@ namespace pboman3::ui { void TaskWindow::threadInitialized(ThreadId threadId, const QString& text, qint32 minProgress, qint32 maxProgress) const { + taskbar_->threadInitialized(maxProgress); + const ProgressWidget* progress = progressBars_.value(threadId); progress->setMinimum(minProgress); progress->setMaximum(maxProgress); @@ -64,6 +70,8 @@ namespace pboman3::ui { } void TaskWindow::threadProgress(ThreadId threadId, qint32 progress) const { + taskbar_->threadProgress(threadId, progress); + const ProgressWidget* progressBar = progressBars_.value(threadId); progressBar->setValue(progress); } @@ -75,6 +83,8 @@ namespace pboman3::ui { activeThreadCount_--; if (activeThreadCount_ == 0) { + taskbar_.clear(); + ui_->buttonBox->setEnabled(true); ui_->buttonBox->setStandardButtons(QDialogButtonBox::StandardButton::Close); } @@ -102,4 +112,28 @@ namespace pboman3::ui { close(); } } + + TaskWindow::TaskbarIndicator::TaskbarIndicator(WId windowId) + : maxValue_(0), + currentValue_(0) { + taskbar_ = QSharedPointer(new Win32TaskbarIndicator(windowId)); + } + + void TaskWindow::TaskbarIndicator::threadThinking() const { + if (currentValue_ == 0) + taskbar_->setIndeterminate(); + } + + void TaskWindow::TaskbarIndicator::threadInitialized(qint64 maxProgress) { + maxValue_ += maxProgress; + taskbar_->setProgressValue(currentValue_, maxValue_); + } + + void TaskWindow::TaskbarIndicator::threadProgress(ThreadId threadId, qint32 progress) { + const qint32 threadValue = threadValues_.value(threadId, 0); + const qint32 increment = progress - threadValue; + threadValues_.insert(threadId, progress); + currentValue_ += increment; + taskbar_->setProgressValue(currentValue_, maxValue_); + } } diff --git a/pbom/ui/taskwindow.h b/pbom/ui/taskwindow.h index b8d2182..a417902 100644 --- a/pbom/ui/taskwindow.h +++ b/pbom/ui/taskwindow.h @@ -3,6 +3,7 @@ #include #include #include +#include "taskbarindicator.h" #include "model/task/taskwindowmodel.h" #include "progresswidget/progresswidget.h" @@ -25,12 +26,15 @@ namespace pboman3::ui { void start(const QSharedPointer& model); private: + class TaskbarIndicator; + Ui::TaskWindow* ui_; QSharedPointer model_; int activeThreadCount_; QHash progressBars_; QPlainTextEdit* log_; QString doneText_; + QSharedPointer taskbar_; void threadStarted(ThreadId threadId); @@ -45,5 +49,22 @@ namespace pboman3::ui { void threadMessage(ThreadId threadId, const QString& message); void buttonClicked(QAbstractButton* button); + + class TaskbarIndicator { + public: + TaskbarIndicator(WId windowId); + + void threadThinking() const; + + void threadInitialized(qint64 maxProgress); + + void threadProgress(ThreadId threadId, qint32 progress); + + private: + qint64 maxValue_; + qint64 currentValue_; + QHash threadValues_; + QSharedPointer taskbar_; + }; }; } diff --git a/pbom/ui/win32/win32taskbarindicator.cpp b/pbom/ui/win32/win32taskbarindicator.cpp new file mode 100644 index 0000000..d213e21 --- /dev/null +++ b/pbom/ui/win32/win32taskbarindicator.cpp @@ -0,0 +1,53 @@ +#include "win32taskbarindicator.h" +#include + +namespace pboman3::ui { + Win32TaskbarIndicator::Win32TaskbarIndicator(WId windowId) + : window_(reinterpret_cast(windowId)), // NOLINT(performance-no-int-to-ptr) + isErr_(false) { + const HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, + reinterpret_cast(&progress_)); + if (!SUCCEEDED(hr)) + progress_ = 0; + } + + Win32TaskbarIndicator::~Win32TaskbarIndicator() { + if (progress_) { + progress_->SetProgressState(window_, TBPF_NOPROGRESS); + progress_->Release(); + if (!isErr_) + flashWindow(); + } + } + + void Win32TaskbarIndicator::setProgressValue(qint64 value, qint64 maxValue) { + if (!progress_) + return; + isErr_ = false; + progress_->SetProgressState(window_, TBPF_NORMAL); + progress_->SetProgressValue(window_, value, maxValue); + } + + void Win32TaskbarIndicator::setIndeterminate() { + if (progress_) { + isErr_ = false; + progress_->SetProgressState(window_, TBPF_INDETERMINATE); + } + } + + void Win32TaskbarIndicator::setError() { + if (progress_) { + isErr_ = true; + progress_->SetProgressState(window_, TBPF_ERROR); + } + } + + void Win32TaskbarIndicator::flashWindow() const { + FLASHWINFO fi; + fi.cbSize = sizeof fi; + fi.hwnd = window_; + fi.dwFlags = FLASHW_TRAY | FLASHW_TIMERNOFG; + fi.dwTimeout = 0; + FlashWindowEx(&fi); + } +} diff --git a/pbom/ui/win32/win32taskbarindicator.h b/pbom/ui/win32/win32taskbarindicator.h new file mode 100644 index 0000000..4da4098 --- /dev/null +++ b/pbom/ui/win32/win32taskbarindicator.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include "ui/taskbarindicator.h" + +namespace pboman3::ui { + class Win32TaskbarIndicator : public TaskbarIndicator { + public: + Win32TaskbarIndicator(WId windowId); + + ~Win32TaskbarIndicator() override; + + void setProgressValue(qint64 value, qint64 maxValue) override; + + void setIndeterminate() override; + + void setError() override; + + private: + ITaskbarList3* progress_; + HWND window_; + bool isErr_; + + void flashWindow() const; + }; +} From 26dccb0e3351c2e3d30178cc4605f96a2cff1ab3 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 27 Nov 2021 18:32:56 +0300 Subject: [PATCH 31/39] Background UI operations now display progress in the windows taskbar --- pbom/ui/statusbar.cpp | 10 +++++++++- pbom/ui/statusbar.h | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pbom/ui/statusbar.cpp b/pbom/ui/statusbar.cpp index 3900f84..f7b6fd9 100644 --- a/pbom/ui/statusbar.cpp +++ b/pbom/ui/statusbar.cpp @@ -1,6 +1,7 @@ #include "statusbar.h" #include "exception.h" #include "util/log.h" +#include "ui/win32/win32taskbarindicator.h" namespace pboman3::ui { StatusBar::StatusBar(QWidget* parent) @@ -30,10 +31,17 @@ namespace pboman3::ui { progress_->setVisible(true); if (supportsCancellation) button_->setVisible(true); + + taskbar_ = QSharedPointer(new Win32TaskbarIndicator(effectiveWinId())); + taskbar_->setIndeterminate(); } - void StatusBar::progressHide() const { + void StatusBar::progressHide() { + if (!progress_->isVisible()) + throw InvalidOperationException("There is no background operation in progress"); + progress_->setVisible(false); button_->setVisible(false); + taskbar_.clear(); } } diff --git a/pbom/ui/statusbar.h b/pbom/ui/statusbar.h index 2fe9caf..65ef163 100644 --- a/pbom/ui/statusbar.h +++ b/pbom/ui/statusbar.h @@ -5,6 +5,7 @@ #include #include #include +#include "ui/taskbarindicator.h" namespace pboman3::ui { class StatusBar : public QStatusBar { @@ -15,7 +16,7 @@ namespace pboman3::ui { public slots: void progressShow(QFuture future, bool supportsCancellation = true); - void progressHide() const; + void progressHide(); signals: void cancelRequested(); @@ -24,5 +25,6 @@ namespace pboman3::ui { QFuture future_; QProgressBar* progress_; QPushButton* button_; + QSharedPointer taskbar_; }; } From 7ae4d57a71b3b4a92cf41c8ef29f53e486db8abc Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 27 Nov 2021 18:48:05 +0300 Subject: [PATCH 32/39] Update the build tools and frameworks --- .github/workflows/artifcats.yaml | 6 +++--- .github/workflows/tests.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/artifcats.yaml b/.github/workflows/artifcats.yaml index 7bf7f9f..c36e5c2 100644 --- a/.github/workflows/artifcats.yaml +++ b/.github/workflows/artifcats.yaml @@ -13,7 +13,7 @@ defaults: jobs: release: name: Make artifacts - runs-on: windows-2019 + runs-on: windows-2022 steps: - name: Set Short Sha run: | @@ -33,7 +33,7 @@ jobs: echo "pbom_build_number=${{github.run_number}} (${{env.git_sha}})" >> $env:GITHUB_ENV echo "pbom_installer_version=0.0.${{github.run_number}}" >> $env:GITHUB_ENV - - name: Install MSVC 2019 + - name: Install MSVC 2022 uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 @@ -41,7 +41,7 @@ jobs: - name: Install Qt6 uses: jurplel/install-qt-action@v2 with: - version: '6.1.1' + version: '6.2.1' host: windows target: desktop arch: win64_msvc2019_64 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 329d51e..7cd665f 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,7 +8,7 @@ defaults: jobs: release: name: Run tests - runs-on: windows-2019 + runs-on: windows-2022 steps: - name: Set Short Sha run: | @@ -26,7 +26,7 @@ jobs: echo "pbom_version=$('${{github.ref}}' -replace 'refs/heads/','' -replace '[^\w\d]','_')" >> $env:GITHUB_ENV echo "pbom_build_number=${{github.run_number}} (${{env.git_sha}})" >> $env:GITHUB_ENV - - name: Install MSVC 2019 + - name: Install MSVC 2022 uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 @@ -34,7 +34,7 @@ jobs: - name: Install Qt6 uses: jurplel/install-qt-action@v2 with: - version: '6.1.1' + version: '6.2.1' host: windows target: desktop arch: win64_msvc2019_64 From 1a6ea22f9c9c006130a095b09e9956343a80cd47 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Sat, 27 Nov 2021 23:17:24 +0300 Subject: [PATCH 33/39] Added the new QT modules to support 6.2 --- installer/PBOManager.wxs | 10 ++++++++++ pbom/CMakeLists.txt | 3 +++ 2 files changed, 13 insertions(+) diff --git a/installer/PBOManager.wxs b/installer/PBOManager.wxs index df0ffb0..afb77dc 100644 --- a/installer/PBOManager.wxs +++ b/installer/PBOManager.wxs @@ -61,6 +61,7 @@ +
@@ -75,6 +76,7 @@ + + + + + + + diff --git a/pbom/CMakeLists.txt b/pbom/CMakeLists.txt index 7c1e8c0..f2869e7 100644 --- a/pbom/CMakeLists.txt +++ b/pbom/CMakeLists.txt @@ -52,6 +52,9 @@ install(FILES install(FILES ${QT_BINARIES_DIR}/plugins/styles/qwindowsvistastyle${QT_BINARIES_SUFFIX}.dll DESTINATION ${CMAKE_INSTALL_BINDIR}/styles) +install(FILES + ${QT_BINARIES_DIR}/plugins/tls/qopensslbackend${QT_BINARIES_SUFFIX}.dll + DESTINATION ${CMAKE_INSTALL_BINDIR}/tls) install(FILES ${QT_BINARIES_DIR}/../../Tools/OpenSSL/Win_x64/bin/libssl-1_1-x64.dll ${QT_BINARIES_DIR}/../../Tools/OpenSSL/Win_x64/bin/libcrypto-1_1-x64.dll From a984153518e9fa84570a41073346ff639ee7a6dc Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Tue, 30 Nov 2021 20:39:35 +0300 Subject: [PATCH 34/39] Collecting files from file system is now done in the background --- pbom/ui/__test__/fscollector__test.cpp | 44 +++++++++++++------------- pbom/ui/fscollector.cpp | 36 ++++++++++++--------- pbom/ui/fscollector.h | 6 ++-- pbom/ui/treewidget/treewidget.cpp | 36 +++++++++++++++++---- pbom/ui/treewidget/treewidget.h | 3 ++ 5 files changed, 79 insertions(+), 46 deletions(-) diff --git a/pbom/ui/__test__/fscollector__test.cpp b/pbom/ui/__test__/fscollector__test.cpp index 2b3cd7b..6a90e57 100644 --- a/pbom/ui/__test__/fscollector__test.cpp +++ b/pbom/ui/__test__/fscollector__test.cpp @@ -37,29 +37,29 @@ namespace pboman3::ui::test { f5.open(QIODeviceBase::ReadWrite); f5.close(); - const NodeDescriptors files = FsCollector::collectFiles(QList{ - QUrl::fromLocalFile(tempDir.filePath(d2)), - QUrl::fromLocalFile(f1.fileName()), - QUrl::fromLocalFile(tempDir.filePath(d1)) - }); + const QSharedPointer files = FsCollector::collectFiles(QList{ + QUrl::fromLocalFile(tempDir.filePath(d2)), + QUrl::fromLocalFile(f1.fileName()), + QUrl::fromLocalFile(tempDir.filePath(d1)) + }, []() { return false; }); - ASSERT_EQ(files.length(), 5); + ASSERT_EQ(files->length(), 5); - ASSERT_EQ(files[0].path(), PboPath("f1/f11/f3.txt")); - ASSERT_EQ(files[0].binarySource()->path(), f3.fileName()); - ASSERT_TRUE(files[0].binarySource()->isOpen()); + ASSERT_EQ(files->at(0).path(), PboPath("f1/f11/f3.txt")); + ASSERT_EQ(files->at(0).binarySource()->path(), f3.fileName()); + ASSERT_TRUE(files->at(0).binarySource()->isOpen()); - ASSERT_EQ(files[1].path(), PboPath("f1/f11/f4.txt")); - ASSERT_EQ(files[1].binarySource()->path(), f4.fileName()); + ASSERT_EQ(files->at(1).path(), PboPath("f1/f11/f4.txt")); + ASSERT_EQ(files->at(1).binarySource()->path(), f4.fileName()); - ASSERT_EQ(files[2].path(), PboPath("f1/f2.txt")); - ASSERT_EQ(files[2].binarySource()->path(), f2.fileName()); + ASSERT_EQ(files->at(2).path(), PboPath("f1/f2.txt")); + ASSERT_EQ(files->at(2).binarySource()->path(), f2.fileName()); - ASSERT_EQ(files[3].path(), PboPath("f1.txt")); - ASSERT_EQ(files[3].binarySource()->path(), f1.fileName()); + ASSERT_EQ(files->at(3).path(), PboPath("f1.txt")); + ASSERT_EQ(files->at(3).binarySource()->path(), f1.fileName()); - ASSERT_EQ(files[4].path(), PboPath("f2/f5.txt")); - ASSERT_EQ(files[4].binarySource()->path(), f5.fileName()); + ASSERT_EQ(files->at(4).path(), PboPath("f2/f5.txt")); + ASSERT_EQ(files->at(4).binarySource()->path(), f5.fileName()); } TEST(FsCollectorTest, CollectFiles_Wont_Find_Symlinks) { @@ -86,12 +86,12 @@ namespace pboman3::ui::test { ASSERT_TRUE(QFile::link(tempDir.filePath(d1), tempDir.filePath("d1.lnk"))); //run the code - const NodeDescriptors files = FsCollector::collectFiles(QList{ - QUrl::fromLocalFile(tempDir.absolutePath()) - }); + const QSharedPointer files = FsCollector::collectFiles(QList{ + QUrl::fromLocalFile(tempDir.absolutePath()) + }, []() { return false; }); //ensure symlinks were not collected - ASSERT_EQ(files.count(), 1); - ASSERT_EQ(files[0].path(), PboPath({ tempDir.dirName(), "d1", "f1.txt" })); + ASSERT_EQ(files->count(), 1); + ASSERT_EQ(files->at(0).path(), PboPath({ tempDir.dirName(), "d1", "f1.txt" })); } } diff --git a/pbom/ui/fscollector.cpp b/pbom/ui/fscollector.cpp index d3d634b..4535b95 100644 --- a/pbom/ui/fscollector.cpp +++ b/pbom/ui/fscollector.cpp @@ -5,45 +5,51 @@ #define LOG(...) LOGGER("ui/FsCollector", __VA_ARGS__) namespace pboman3::ui { - NodeDescriptors FsCollector::collectFiles(const QList& urls) { + QSharedPointer FsCollector::collectFiles(const QList& urls, const Cancel& cancel) { LOG(info, "Collecting the files at:", urls) - NodeDescriptors result; - result.reserve(100); + QSharedPointer result(new NodeDescriptors); + result->reserve(100); for (const QUrl& url : urls) { + if (cancel()) + return nullptr; QFileInfo fi(url.toLocalFile()); - if (fi.isFile()) { - collectFile(fi, fi.dir(), result); - } else if (fi.isDir()) { - collectDir(fi, fi.dir(), result); + if (!fi.isSymLink()) { + if (fi.isFile()) { + collectFile(fi, fi.dir(), result.get()); + } else if (fi.isDir()) { + collectDir(fi, fi.dir(), result.get(), cancel); + } } } - std::sort(result.begin(), result.end(), [](const NodeDescriptor& f1, const NodeDescriptor& f2) { + std::sort(result->begin(), result->end(), [](const NodeDescriptor& f1, const NodeDescriptor& f2) { return f1.path() < f2.path(); }); - LOG(info, "Collected files:", result.size()) + LOG(info, "Collected files:", result->size()) return result; } - void FsCollector::collectDir(const QFileInfo& fi, const QDir& parent, NodeDescriptors& descriptors) { + void FsCollector::collectDir(const QFileInfo& fi, const QDir& parent, NodeDescriptors* descriptors, const Cancel& cancel) { LOG(debug, "Collecting the dir:", fi) const QDir d(fi.filePath() + QDir::separator()); const QFileInfoList entries = d.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); for (const QFileInfo& entry : entries) { + if (cancel()) + return; if (!entry.isSymLink()) { if (entry.isFile()) collectFile(entry, parent, descriptors); else if (entry.isDir()) - collectDir(entry, parent, descriptors); + collectDir(entry, parent, descriptors, cancel); } } } - void FsCollector::collectFile(const QFileInfo& fi, const QDir& parent, NodeDescriptors& descriptors) { + void FsCollector::collectFile(const QFileInfo& fi, const QDir& parent, NodeDescriptors* descriptors) { LOG(debug, "Collecting the file:", fi) if (!fi.isShortcut() && !fi.isSymbolicLink()) { @@ -51,10 +57,10 @@ namespace pboman3::ui { const QString pboPath = parent.relativeFilePath(fi.canonicalFilePath()); - const auto bs = QSharedPointer(new FsRawBinarySource(std::move(fsPath))); + const auto bs = QSharedPointer(new FsRawBinarySource(std::move(fsPath))); bs->open(); - - descriptors.append(NodeDescriptor(bs, PboPath(pboPath))); + + descriptors->append(NodeDescriptor(bs, PboPath(pboPath))); LOG(debug, "File collected") } diff --git a/pbom/ui/fscollector.h b/pbom/ui/fscollector.h index ed56e1d..e886dc1 100644 --- a/pbom/ui/fscollector.h +++ b/pbom/ui/fscollector.h @@ -8,11 +8,11 @@ namespace pboman3::ui { class FsCollector { public: - static NodeDescriptors collectFiles(const QList& urls); + static QSharedPointer collectFiles(const QList& urls, const Cancel& cancel); private: - static void collectDir(const QFileInfo& fi, const QDir& parent, NodeDescriptors& descriptors); + static void collectDir(const QFileInfo& fi, const QDir& parent, NodeDescriptors* descriptors, const Cancel& cancel); - static void collectFile(const QFileInfo& fi, const QDir& parent, NodeDescriptors& descriptors); + static void collectFile(const QFileInfo& fi, const QDir& parent, NodeDescriptors* descriptors); }; } diff --git a/pbom/ui/treewidget/treewidget.cpp b/pbom/ui/treewidget/treewidget.cpp index 95783e0..9aa821d 100644 --- a/pbom/ui/treewidget/treewidget.cpp +++ b/pbom/ui/treewidget/treewidget.cpp @@ -26,6 +26,8 @@ namespace pboman3::ui { connect(&cutCopyWatcher_, &QFutureWatcher::finished, this, &TreeWidget::copyOrCutExecute); connect(&openWatcher_, &QFutureWatcher::finished, this, &TreeWidget::openExecute); connect(&extractWatcher_, &QFutureWatcher::finished, this, &TreeWidget::extractExecute); + connect(&fsOpWatcher_, &QFutureWatcher::finished, this, + &TreeWidget::addFilesFromFileSystemExecute); connect(this, &TreeWidget::itemSelectionChanged, this, &TreeWidget::onSelectionChanged); connect(this, &TreeWidget::doubleClicked, this, &TreeWidget::onDoubleClicked); } @@ -375,10 +377,32 @@ namespace pboman3::ui { void TreeWidget::addFilesFromFilesystem(const QList& urls) { LOG(info, "Add files from the file system:", urls) - NodeDescriptors files; + const QFuture> future = QtConcurrent::run([&urls](QPromise>& promise) { + QSharedPointer files = FsCollector::collectFiles(urls, [&promise]() { return promise.isCanceled(); }); + promise.addResult(files); + }); + + emit backgroundOpStarted(static_cast>(future)); + + fsOpWatcher_.setFuture(future); + } + + void TreeWidget::addFilesFromFileSystemExecute() { + LOG(info, "File collecting completed") + + emit backgroundOpStopped(); + + const QFuture> future = fsOpWatcher_.future(); + + if (!future.isValid()) { + LOG(info, "The operation was cancelled - exitig") + return; + } + + QSharedPointer files; try { - files = FsCollector::collectFiles(urls); - LOG(debug, "Collected descriptors:", files) + files = future.result(); + LOG(debug, "Collected descriptors:", *files) } catch (const DiskAccessException& ex) { LOG(info, "Error when collecting - show error modal:", ex) UI_HANDLE_ERROR_RET(ex) @@ -387,13 +411,13 @@ namespace pboman3::ui { PboNode* item = getCurrentFolder(); LOG(info, "Selected node is:", *item) - ConflictsParcel conflicts = model_->checkConflicts(item, files); + ConflictsParcel conflicts = model_->checkConflicts(item, *files); LOG(debug, "The result of conflicts check:", conflicts) - InsertDialog dialog(this, InsertDialog::Mode::ExternalFiles, &files, &conflicts); + InsertDialog dialog(this, InsertDialog::Mode::ExternalFiles, files.get(), &conflicts); if (dialog.exec() == QDialog::DialogCode::Accepted) { LOG(info, "The user accepted the file insert dialog") - model_->createNodeSet(item, files, conflicts); + model_->createNodeSet(item, *files, conflicts); } } } diff --git a/pbom/ui/treewidget/treewidget.h b/pbom/ui/treewidget/treewidget.h index 17f8a13..8e4f3c4 100644 --- a/pbom/ui/treewidget/treewidget.h +++ b/pbom/ui/treewidget/treewidget.h @@ -65,6 +65,7 @@ namespace pboman3::ui { QFutureWatcher cutCopyWatcher_; QFutureWatcher openWatcher_; QFutureWatcher extractWatcher_; + QFutureWatcher> fsOpWatcher_; ActionState actionState_; void onDoubleClicked(); @@ -84,5 +85,7 @@ namespace pboman3::ui { void addFilesFromPbo(PboNode* target, const QMimeData* mimeData); void addFilesFromFilesystem(const QList& urls); + + void addFilesFromFileSystemExecute(); }; } From 93486fa306ee6f325e5d0bfaa89cf7e6574a5d75 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Tue, 30 Nov 2021 20:41:53 +0300 Subject: [PATCH 35/39] Fixed passing parameters by value --- pbom/ui/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pbom/ui/mainwindow.cpp b/pbom/ui/mainwindow.cpp index ba469e7..bde3c9b 100644 --- a/pbom/ui/mainwindow.cpp +++ b/pbom/ui/mainwindow.cpp @@ -44,7 +44,7 @@ namespace pboman3::ui { if (model_->isLoaded()) unloadFile(); - const QFuture future = QtConcurrent::run([this, fileName](QPromise& promise) { + const QFuture future = QtConcurrent::run([this, &fileName](QPromise& promise) { LOG(info, "Loading the file:", fileName) model_->loadFile(fileName); promise.addResult(0); @@ -344,7 +344,7 @@ namespace pboman3::ui { void MainWindow::saveFile(const QString& fileName) { LOG(info, "Saving the file") - const QFuture future = QtConcurrent::run([this, fileName](QPromise& promise) { + const QFuture future = QtConcurrent::run([this, &fileName](QPromise& promise) { model_->saveFile([&promise]() { return promise.isCanceled(); }, fileName); promise.addResult(0); }); From 047aa469fb55b779fb158378b5e187f7408d6bde Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Tue, 30 Nov 2021 21:17:44 +0300 Subject: [PATCH 36/39] Fixed forgotten binary backend cleanup --- pbom/io/bb/binarybackend.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pbom/io/bb/binarybackend.cpp b/pbom/io/bb/binarybackend.cpp index bec0e97..9b2b2a7 100644 --- a/pbom/io/bb/binarybackend.cpp +++ b/pbom/io/bb/binarybackend.cpp @@ -46,5 +46,7 @@ namespace pboman3::io { } void BinaryBackend::clear(const PboNode* node) const { + tempBackend_->clear(node); + execBackend_->clear(node); } } From 762ed2914371ad31344a6b844012867cae71ed99 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Tue, 30 Nov 2021 22:31:00 +0300 Subject: [PATCH 37/39] Fixed the taskbar was blinking when opening files --- pbom/ui/win32/win32taskbarindicator.cpp | 6 +++++- pbom/ui/win32/win32taskbarindicator.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pbom/ui/win32/win32taskbarindicator.cpp b/pbom/ui/win32/win32taskbarindicator.cpp index d213e21..1a3df27 100644 --- a/pbom/ui/win32/win32taskbarindicator.cpp +++ b/pbom/ui/win32/win32taskbarindicator.cpp @@ -15,7 +15,7 @@ namespace pboman3::ui { if (progress_) { progress_->SetProgressState(window_, TBPF_NOPROGRESS); progress_->Release(); - if (!isErr_) + if (!isErr_ && !windowHasFocus()) flashWindow(); } } @@ -42,6 +42,10 @@ namespace pboman3::ui { } } + bool Win32TaskbarIndicator::windowHasFocus() const { + return GetActiveWindow() == window_; + } + void Win32TaskbarIndicator::flashWindow() const { FLASHWINFO fi; fi.cbSize = sizeof fi; diff --git a/pbom/ui/win32/win32taskbarindicator.h b/pbom/ui/win32/win32taskbarindicator.h index 4da4098..98f3c68 100644 --- a/pbom/ui/win32/win32taskbarindicator.h +++ b/pbom/ui/win32/win32taskbarindicator.h @@ -22,6 +22,8 @@ namespace pboman3::ui { HWND window_; bool isErr_; + bool windowHasFocus() const; + void flashWindow() const; }; } From b6053a4db938379f00e6a4c6f71db61397bd93a6 Mon Sep 17 00:00:00 2001 From: Nikita Kobzev Date: Wed, 1 Dec 2021 22:07:21 +0300 Subject: [PATCH 38/39] Fixed installer was not replacing a previous installed version if installing per user --- installer/PBOManager.wxs | 1 + 1 file changed, 1 insertion(+) diff --git a/installer/PBOManager.wxs b/installer/PBOManager.wxs index afb77dc..19bfbc4 100644 --- a/installer/PBOManager.wxs +++ b/installer/PBOManager.wxs @@ -254,6 +254,7 @@ PbomInstallPer = "machine" PbomInstallPer = "user" PbomInstallPer = "machine" + 1 1 Date: Wed, 1 Dec 2021 22:27:02 +0300 Subject: [PATCH 39/39] Updated the changelog for v1.1.0 --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ced5d5..fbfb469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # PBO Manager change log +## Version 1.1.0 + +- [Enhancement] UI no longer freezes when opening a PBO +- [Enhancement] UI no longer freezes when pasting files into a PBO from the file system +- [Enhancement] Progress operations are displayed in the Windows taskbar +- [Enhancement] The application now can check for a new version. See "Help->Check for updates" +- [Fix] If make any changes in a PBO while saving, the PBO got corrupted +- [Fix] If "Cancel" while saving a PBO had a chance to corrupt +- [Fix] The "pack" console command was interpreting "-o" option incorrectly +- [Fix] It was possible to rename a file inside a PBO, if there was a file with the same name in a different casing +- [Fix] The context menu "Extract to " had a superfluous file extension + ## Version 1.0.0 - Support of normal PBOs - Support of Mikero's mangled PBOs -- Windows explorer integration \ No newline at end of file +- Windows explorer integration