diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6098e1f --- /dev/null +++ b/.clang-format @@ -0,0 +1,41 @@ +--- +# We'll use defaults from the LLVM style, but with 4 columns indentation. +BasedOnStyle: LLVM +IndentWidth: 2 +--- +Language: Cpp +DeriveLineEnding: false +UseCRLF: true +DerivePointerAlignment: false +PointerAlignment: Left +AlignConsecutiveAssignments: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Empty +AlwaysBreakTemplateDeclarations: Yes +AccessModifierOffset: -2 +AlignTrailingComments: true +SpacesBeforeTrailingComments: 2 +NamespaceIndentation: Inner +MaxEmptyLinesToKeep: 1 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +ColumnLimit: 88 +ForEachMacros: ['Q_FOREACH', 'foreach'] diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..d0c3535 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +60e7642364b534d9cb405acfa750b7bbe73869a9 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f869712 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.cpp text eol=crlf +*.h text eol=crlf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..97b78c6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,16 @@ +name: Build Installer BAIN Plugin + +on: + push: + branches: master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + runs-on: windows-2022 + steps: + - name: Build Installer BAIN Plugin + uses: ModOrganizer2/build-with-mob-action@master + with: + mo2-dependencies: cmake_common uibase diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..75c294e --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,16 @@ +name: Lint Installer BAIN Plugin + +on: + push: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check format + uses: ModOrganizer2/check-formatting-action@master + with: + check-path: "." diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 74fcf6f..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,40 +0,0 @@ -version: 1.0.{build} -skip_branch_with_pr: true -image: Visual Studio 2019 -environment: - WEBHOOK_URL: - secure: gOKbXaZM9ImtMD5XrYITvdyZUW/az082G9OIN1EC1Vbg57wBaeLhi49uGjxPw5GVujHku6kxN6ab89zhbS5GVeluR76GM83IbKV4Sh7udXzoYZZdg6YudtYHzdhCgUeiedpswbuczTq9ceIkkfSEWZuh/lMAAVVwvcGsJAnoPFw= -build_script: -- pwsh: >- - $ErrorActionPreference = 'Stop' - - git clone --depth=1 --no-single-branch https://github.com/ModOrganizer2/modorganizer-umbrella.git c:\projects\modorganizer-umbrella - - New-Item -ItemType Directory -Path c:\projects\modorganizer-build - - cd c:\projects\modorganizer-umbrella - - ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH -eq $null) ? ($branch = $env:APPVEYOR_REPO_BRANCH) : ($branch = $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) - - git checkout $(git show-ref --verify --quiet refs/remotes/origin/${branch} || echo '-b') ${branch} - - C:\Python37-x64\python.exe unimake.py -d c:\projects\modorganizer-build -s Appveyor_Build=True ${env:APPVEYOR_PROJECT_NAME} - - if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode ) } -artifacts: - - path: vsbuild\src\RelWithDebInfo\installer_bain.dll - name: installer_bain_dll - - path: vsbuild\src\RelWithDebInfo\installer_bain.pdb - name: installer_bain_pdb - - path: vsbuild\src\RelWithDebInfo\installer_bain.lib - name: installer_bain_lib -on_success: - - ps: Set-Location -Path $env:APPVEYOR_BUILD_FOLDER - - ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1 - - ps: ./send.ps1 success $env:WEBHOOK_URL -on_failure: - - ps: Set-Location -Path $env:APPVEYOR_BUILD_FOLDER - - ps: Push-AppveyorArtifact ${env:APPVEYOR_BUILD_FOLDER}\stdout.log - - ps: Push-AppveyorArtifact ${env:APPVEYOR_BUILD_FOLDER}\stderr.log - - ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1 - - ps: ./send.ps1 failure $env:WEBHOOK_URL \ No newline at end of file diff --git a/src/baincomplexinstallerdialog.cpp b/src/baincomplexinstallerdialog.cpp index 7bea3f6..a2a0ec5 100644 --- a/src/baincomplexinstallerdialog.cpp +++ b/src/baincomplexinstallerdialog.cpp @@ -1,126 +1,120 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "baincomplexinstallerdialog.h" -#include "textviewer.h" -#include "ui_baincomplexinstallerdialog.h" - -#include - - -using namespace MOBase; - - -BainComplexInstallerDialog::BainComplexInstallerDialog( - const std::vector> &subpackages, const GuessedValue &modName, - const QStringList& defaultOptions, const QString &packageTXT, QWidget *parent) - : TutorableDialog("BainInstaller", parent), ui(new Ui::BainComplexInstallerDialog), m_Manual(false), - m_PackageTXT(packageTXT) -{ - ui->setupUi(this); - - for (auto iter = modName.variants().begin(); iter != modName.variants().end(); ++iter) { - ui->nameCombo->addItem(*iter); - } - - ui->nameCombo->setCurrentIndex(ui->nameCombo->findText(modName)); - - for (const auto& subpackage : subpackages) { - - auto name = subpackage->name(); - - QListWidgetItem *item = new QListWidgetItem(name, ui->optionsList); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - - if (name.mid(0, 2) == "00" || defaultOptions.contains(name, Qt::CaseInsensitive)) { - item->setCheckState(Qt::Checked); - } - else { - item->setCheckState(Qt::Unchecked); - } - - ui->optionsList->addItem(item); - } - - ui->packageBtn->setEnabled(!packageTXT.isEmpty()); - ui->nameCombo->completer()->setCaseSensitivity(Qt::CaseSensitive); -} - - -BainComplexInstallerDialog::~BainComplexInstallerDialog() -{ - delete ui; -} - - -QString BainComplexInstallerDialog::getName() const -{ - return ui->nameCombo->currentText(); -} - -QStringList BainComplexInstallerDialog::updateTree(std::shared_ptr &tree) -{ - // Retrieve the list of selected names: - std::set selectedNames; - for (int i = 0; i < ui->optionsList->count(); ++i) { - QListWidgetItem* item = ui->optionsList->item(i); - if (item->checkState() == Qt::Checked) { - selectedNames.insert(item->text()); - } - } - - // Create a new empty tree and merge all the selected folder in it: - auto newTree = tree->createOrphanTree(); - for (auto& entry : *tree) { - if (entry->isDir() && selectedNames.count(entry->name()) > 0) { - newTree->merge(entry->astree()); - } - } - - tree = newTree; - - return QStringList(selectedNames.begin(), selectedNames.end()); -} - - -void BainComplexInstallerDialog::on_okBtn_clicked() -{ - this->accept(); -} - - -void BainComplexInstallerDialog::on_cancelBtn_clicked() -{ - this->reject(); -} - - -void BainComplexInstallerDialog::on_manualBtn_clicked() -{ - m_Manual = true; - this->reject(); -} - -void BainComplexInstallerDialog::on_packageBtn_clicked() -{ - TextViewer viewer(m_PackageTXT, this); - viewer.setDescription(""); - viewer.addFile(m_PackageTXT, false); - viewer.exec(); -} +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "baincomplexinstallerdialog.h" +#include "textviewer.h" +#include "ui_baincomplexinstallerdialog.h" + +#include + +using namespace MOBase; + +BainComplexInstallerDialog::BainComplexInstallerDialog( + const std::vector>& subpackages, + const GuessedValue& modName, const QStringList& defaultOptions, + const QString& packageTXT, QWidget* parent) + : TutorableDialog("BainInstaller", parent), ui(new Ui::BainComplexInstallerDialog), + m_Manual(false), m_PackageTXT(packageTXT) +{ + ui->setupUi(this); + + for (auto iter = modName.variants().begin(); iter != modName.variants().end(); + ++iter) { + ui->nameCombo->addItem(*iter); + } + + ui->nameCombo->setCurrentIndex(ui->nameCombo->findText(modName)); + + for (const auto& subpackage : subpackages) { + + auto name = subpackage->name(); + + QListWidgetItem* item = new QListWidgetItem(name, ui->optionsList); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + + if (name.mid(0, 2) == "00" || defaultOptions.contains(name, Qt::CaseInsensitive)) { + item->setCheckState(Qt::Checked); + } else { + item->setCheckState(Qt::Unchecked); + } + + ui->optionsList->addItem(item); + } + + ui->packageBtn->setEnabled(!packageTXT.isEmpty()); + ui->nameCombo->completer()->setCaseSensitivity(Qt::CaseSensitive); +} + +BainComplexInstallerDialog::~BainComplexInstallerDialog() +{ + delete ui; +} + +QString BainComplexInstallerDialog::getName() const +{ + return ui->nameCombo->currentText(); +} + +QStringList BainComplexInstallerDialog::updateTree(std::shared_ptr& tree) +{ + // Retrieve the list of selected names: + std::set selectedNames; + for (int i = 0; i < ui->optionsList->count(); ++i) { + QListWidgetItem* item = ui->optionsList->item(i); + if (item->checkState() == Qt::Checked) { + selectedNames.insert(item->text()); + } + } + + // Create a new empty tree and merge all the selected folder in it: + auto newTree = tree->createOrphanTree(); + for (auto& entry : *tree) { + if (entry->isDir() && selectedNames.count(entry->name()) > 0) { + newTree->merge(entry->astree()); + } + } + + tree = newTree; + + return QStringList(selectedNames.begin(), selectedNames.end()); +} + +void BainComplexInstallerDialog::on_okBtn_clicked() +{ + this->accept(); +} + +void BainComplexInstallerDialog::on_cancelBtn_clicked() +{ + this->reject(); +} + +void BainComplexInstallerDialog::on_manualBtn_clicked() +{ + m_Manual = true; + this->reject(); +} + +void BainComplexInstallerDialog::on_packageBtn_clicked() +{ + TextViewer viewer(m_PackageTXT, this); + viewer.setDescription(""); + viewer.addFile(m_PackageTXT, false); + viewer.exec(); +} diff --git a/src/baincomplexinstallerdialog.h b/src/baincomplexinstallerdialog.h index d305208..a88cb11 100644 --- a/src/baincomplexinstallerdialog.h +++ b/src/baincomplexinstallerdialog.h @@ -1,95 +1,95 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef BAINCOMPLEXINSTALLERDIALOG_H -#define BAINCOMPLEXINSTALLERDIALOG_H - -#include -#include "ifiletree.h" - -#include "tutorabledialog.h" - - -namespace Ui { -class BainComplexInstallerDialog; -} - - -/** - * @brief Dialog to choose from options offered by a (complex) bain package - **/ -class BainComplexInstallerDialog : public MOBase::TutorableDialog -{ - Q_OBJECT - -public: - /** - * @brief Constructor - * - * @param tree the directory tree of the archive. The caller is resonsible to verify this actually qualifies as a bain installer - * @param modName proposed name for the mod. The dialog allows the user to change this - * @param defaultOptions The default options to select. Can contains options not actually present. - * @param packageTXT path to the extracted package.txt file or an empty string if there is none - * @param parent parent widget - **/ - explicit BainComplexInstallerDialog(const std::vector> &subpackages, - const MOBase::GuessedValue &modName, - const QStringList &defaultOptions, - const QString &packageTXT, QWidget *parent); - ~BainComplexInstallerDialog(); - - /** - * @return bool true if the user requested the manual dialog - **/ - bool manualRequested() const { return m_Manual; } - - /** - * @return QString the (user-modified) name to be used for the mod - **/ - QString getName() const; - - /** - * @brief Remove from the given tree the option not selected by the user. - * - * @param tree The input tree. - * - * @return the list of options selected by the user. - **/ - QStringList updateTree(std::shared_ptr &tree); - -private slots: - - void on_manualBtn_clicked(); - - void on_okBtn_clicked(); - - void on_cancelBtn_clicked(); - - void on_packageBtn_clicked(); - -private: - - Ui::BainComplexInstallerDialog *ui; - - bool m_Manual; - QString m_PackageTXT; - -}; - -#endif // BAINCOMPLEXINSTALLERDIALOG_H +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef BAINCOMPLEXINSTALLERDIALOG_H +#define BAINCOMPLEXINSTALLERDIALOG_H + +#include "ifiletree.h" +#include + +#include "tutorabledialog.h" + +namespace Ui +{ +class BainComplexInstallerDialog; +} + +/** + * @brief Dialog to choose from options offered by a (complex) bain package + **/ +class BainComplexInstallerDialog : public MOBase::TutorableDialog +{ + Q_OBJECT + +public: + /** + * @brief Constructor + * + * @param tree the directory tree of the archive. The caller is resonsible to verify + *this actually qualifies as a bain installer + * @param modName proposed name for the mod. The dialog allows the user to change this + * @param defaultOptions The default options to select. Can contains options not + *actually present. + * @param packageTXT path to the extracted package.txt file or an empty string if + *there is none + * @param parent parent widget + **/ + explicit BainComplexInstallerDialog( + const std::vector>& subpackages, + const MOBase::GuessedValue& modName, const QStringList& defaultOptions, + const QString& packageTXT, QWidget* parent); + ~BainComplexInstallerDialog(); + + /** + * @return bool true if the user requested the manual dialog + **/ + bool manualRequested() const { return m_Manual; } + + /** + * @return QString the (user-modified) name to be used for the mod + **/ + QString getName() const; + + /** + * @brief Remove from the given tree the option not selected by the user. + * + * @param tree The input tree. + * + * @return the list of options selected by the user. + **/ + QStringList updateTree(std::shared_ptr& tree); + +private slots: + + void on_manualBtn_clicked(); + + void on_okBtn_clicked(); + + void on_cancelBtn_clicked(); + + void on_packageBtn_clicked(); + +private: + Ui::BainComplexInstallerDialog* ui; + + bool m_Manual; + QString m_PackageTXT; +}; + +#endif // BAINCOMPLEXINSTALLERDIALOG_H diff --git a/src/installerbain.cpp b/src/installerbain.cpp index 3605f16..67dad6d 100644 --- a/src/installerbain.cpp +++ b/src/installerbain.cpp @@ -1,232 +1,224 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "installerbain.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "baincomplexinstallerdialog.h" - - -using namespace MOBase; - - -InstallerBAIN::InstallerBAIN() - : m_MOInfo(nullptr) -{ -} - - -bool InstallerBAIN::init(IOrganizer* moInfo) -{ - m_MOInfo = moInfo; - return true; -} - -QString InstallerBAIN::name() const -{ - return "BAIN Installer"; -} - -QString InstallerBAIN::localizedName() const -{ - return tr("BAIN Installer"); -} - - -QString InstallerBAIN::author() const -{ - return "Tannin"; -} - -QString InstallerBAIN::description() const -{ - return tr("Installer for BAIN archives (originally targeting Wrye Bash)"); -} - -VersionInfo InstallerBAIN::version() const -{ - return VersionInfo(1, 1, 0, VersionInfo::RELEASE_FINAL); -} - -QList InstallerBAIN::settings() const -{ - return QList(); -} - -unsigned int InstallerBAIN::priority() const -{ - return 40; -} - -bool InstallerBAIN::isManualInstaller() const -{ - return false; -} - -void InstallerBAIN::onInstallationStart(QString const& archive, bool reinstallation, IModInterface* currentMod) -{ - // We reset some field and fetch the previously installed options: - m_InstallerUsed = false; - m_PreviousOptions.clear(); - m_SelectedOptions.clear(); - - if (currentMod) { - auto settings = currentMod->pluginSettings(name()); - for (auto& [name, value] : settings) { - if (name.startsWith("option")) { - m_PreviousOptions.append(value.toString()); - } - } - } -} - -void InstallerBAIN::onInstallationEnd(EInstallResult result, IModInterface* newMod) -{ - if (result == EInstallResult::RESULT_SUCCESS && m_InstallerUsed) { - newMod->clearPluginSettings(name()); - for (auto i = 0; i < m_SelectedOptions.size(); ++i) { - newMod->setPluginSetting(name(), QString("option%1").arg(i), m_SelectedOptions[i]); - } - } -} - -std::vector> InstallerBAIN::findSubpackages( - std::shared_ptr tree, std::size_t* invalidFolders) const -{ - // Folders that can be present but do not impact the installation: - static const std::set IGNORED_FOLDERS{ - "fomod", "omod conversion data", "images", "screenshots", "docs" - }; - - auto checker = m_MOInfo->gameFeatures()->gameFeature(); - - if (!checker) { - return {}; - } - - // each directory would have to serve as a top-level directory - std::vector> subpackages; - std::size_t ninvalids = 0; - for (auto entry : *tree) { - - if (!entry->isDir()) { - continue; - } - - // ignore fomod in case of combined fomod/bain packages. - // dirs starting with -- are supposed to be ignored - if (IGNORED_FOLDERS.contains(entry->name()) || - entry->name().startsWith("--")) { - continue; - } - - if (checker->dataLooksValid(entry->astree()) == ModDataChecker::CheckReturn::VALID) { - subpackages.push_back(entry); - } - else { - ninvalids++; - } - } - - if (invalidFolders) { - *invalidFolders = ninvalids; - } - - return subpackages; -} - -bool InstallerBAIN::isArchiveSupported(std::shared_ptr tree) const -{ - auto checker = m_MOInfo->gameFeatures()->gameFeature(); - - if (!checker) { - return false; - } - - std::size_t numInvalidDirs = 0; - auto subpackages = findSubpackages(tree, &numInvalidDirs); - - if (subpackages.size() < 2) { - // a complex bain package contains at least 2 directories to choose from - return false; - } - else if (numInvalidDirs == 0) { - return true; - } - else { - return QMessageBox::question(parentWidget(), tr("May be BAIN installer"), - tr("This installer looks like it may contain a BAIN installer but I'm not sure. " - "Install as BAIN installer?"), - QMessageBox::Yes | QMessageBox::No) - == QMessageBox::Yes; - } - - return true; -} - -IPluginInstaller::EInstallResult InstallerBAIN::install(GuessedValue& modName, std::shared_ptr& tree, - QString&, int&) -{ - auto entry = tree->find("package.txt", FileTreeEntry::FILE); - - QString packageTXT; - if (entry != nullptr) { - packageTXT = manager()->extractFile(entry); - } - - auto subpackages = findSubpackages(tree); - - BainComplexInstallerDialog dialog(subpackages, modName, m_PreviousOptions, packageTXT, parentWidget()); - - int res = dialog.exec(); - - if (res == QDialog::Accepted) { - modName.update(dialog.getName(), GUESS_USER); - - // Update the tree: - m_SelectedOptions = dialog.updateTree(tree); - m_InstallerUsed = true; - - return IPluginInstaller::RESULT_SUCCESS; - } - else { - if (dialog.manualRequested()) { - modName.update(dialog.getName(), GUESS_USER); - return IPluginInstaller::RESULT_MANUALREQUESTED; - } - else { - return IPluginInstaller::RESULT_CANCELED; - } - } -} - - -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) -Q_EXPORT_PLUGIN2(installerBAIN, InstallerBAIN) -#endif +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "installerbain.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "baincomplexinstallerdialog.h" + +using namespace MOBase; + +InstallerBAIN::InstallerBAIN() : m_MOInfo(nullptr) {} + +bool InstallerBAIN::init(IOrganizer* moInfo) +{ + m_MOInfo = moInfo; + return true; +} + +QString InstallerBAIN::name() const +{ + return "BAIN Installer"; +} + +QString InstallerBAIN::localizedName() const +{ + return tr("BAIN Installer"); +} + +QString InstallerBAIN::author() const +{ + return "Tannin"; +} + +QString InstallerBAIN::description() const +{ + return tr("Installer for BAIN archives (originally targeting Wrye Bash)"); +} + +VersionInfo InstallerBAIN::version() const +{ + return VersionInfo(1, 1, 0, VersionInfo::RELEASE_FINAL); +} + +QList InstallerBAIN::settings() const +{ + return QList(); +} + +unsigned int InstallerBAIN::priority() const +{ + return 40; +} + +bool InstallerBAIN::isManualInstaller() const +{ + return false; +} + +void InstallerBAIN::onInstallationStart(QString const& archive, bool reinstallation, + IModInterface* currentMod) +{ + // We reset some field and fetch the previously installed options: + m_InstallerUsed = false; + m_PreviousOptions.clear(); + m_SelectedOptions.clear(); + + if (currentMod) { + auto settings = currentMod->pluginSettings(name()); + for (auto& [name, value] : settings) { + if (name.startsWith("option")) { + m_PreviousOptions.append(value.toString()); + } + } + } +} + +void InstallerBAIN::onInstallationEnd(EInstallResult result, IModInterface* newMod) +{ + if (result == EInstallResult::RESULT_SUCCESS && m_InstallerUsed) { + newMod->clearPluginSettings(name()); + for (auto i = 0; i < m_SelectedOptions.size(); ++i) { + newMod->setPluginSetting(name(), QString("option%1").arg(i), + m_SelectedOptions[i]); + } + } +} + +std::vector> +InstallerBAIN::findSubpackages(std::shared_ptr tree, + std::size_t* invalidFolders) const +{ + // Folders that can be present but do not impact the installation: + static const std::set IGNORED_FOLDERS{ + "fomod", "omod conversion data", "images", "screenshots", "docs"}; + + auto checker = m_MOInfo->gameFeatures()->gameFeature(); + + if (!checker) { + return {}; + } + + // each directory would have to serve as a top-level directory + std::vector> subpackages; + std::size_t ninvalids = 0; + for (auto entry : *tree) { + + if (!entry->isDir()) { + continue; + } + + // ignore fomod in case of combined fomod/bain packages. + // dirs starting with -- are supposed to be ignored + if (IGNORED_FOLDERS.contains(entry->name()) || entry->name().startsWith("--")) { + continue; + } + + if (checker->dataLooksValid(entry->astree()) == + ModDataChecker::CheckReturn::VALID) { + subpackages.push_back(entry); + } else { + ninvalids++; + } + } + + if (invalidFolders) { + *invalidFolders = ninvalids; + } + + return subpackages; +} + +bool InstallerBAIN::isArchiveSupported(std::shared_ptr tree) const +{ + auto checker = m_MOInfo->gameFeatures()->gameFeature(); + + if (!checker) { + return false; + } + + std::size_t numInvalidDirs = 0; + auto subpackages = findSubpackages(tree, &numInvalidDirs); + + if (subpackages.size() < 2) { + // a complex bain package contains at least 2 directories to choose from + return false; + } else if (numInvalidDirs == 0) { + return true; + } else { + return QMessageBox::question(parentWidget(), tr("May be BAIN installer"), + tr("This installer looks like it may contain a BAIN " + "installer but I'm not sure. " + "Install as BAIN installer?"), + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes; + } + + return true; +} + +IPluginInstaller::EInstallResult +InstallerBAIN::install(GuessedValue& modName, std::shared_ptr& tree, + QString&, int&) +{ + auto entry = tree->find("package.txt", FileTreeEntry::FILE); + + QString packageTXT; + if (entry != nullptr) { + packageTXT = manager()->extractFile(entry); + } + + auto subpackages = findSubpackages(tree); + + BainComplexInstallerDialog dialog(subpackages, modName, m_PreviousOptions, packageTXT, + parentWidget()); + + int res = dialog.exec(); + + if (res == QDialog::Accepted) { + modName.update(dialog.getName(), GUESS_USER); + + // Update the tree: + m_SelectedOptions = dialog.updateTree(tree); + m_InstallerUsed = true; + + return IPluginInstaller::RESULT_SUCCESS; + } else { + if (dialog.manualRequested()) { + modName.update(dialog.getName(), GUESS_USER); + return IPluginInstaller::RESULT_MANUALREQUESTED; + } else { + return IPluginInstaller::RESULT_CANCELED; + } + } +} + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +Q_EXPORT_PLUGIN2(installerBAIN, InstallerBAIN) +#endif diff --git a/src/installerbain.h b/src/installerbain.h index 94e770b..bcfaaa0 100644 --- a/src/installerbain.h +++ b/src/installerbain.h @@ -1,81 +1,80 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef INSTALLERBAIN_H -#define INSTALLERBAIN_H - - -#include - - -class InstallerBAIN : public MOBase::IPluginInstallerSimple -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple) -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) - Q_PLUGIN_METADATA(IID "org.tannin.InstallerBAIN" FILE "installerbain.json") -#endif - -public: - - InstallerBAIN(); - - virtual bool init(MOBase::IOrganizer* moInfo) override; - virtual QString name() const override; - virtual QString localizedName() const override; - virtual QString author() const override; - virtual QString description() const override; - virtual MOBase::VersionInfo version() const override; - virtual QList settings() const override; - - virtual unsigned int priority() const; - virtual bool isManualInstaller() const; - - virtual void onInstallationStart(QString const& archive, bool reinstallation, MOBase::IModInterface* currentMod) override; - virtual void onInstallationEnd(EInstallResult result, MOBase::IModInterface* newMod) override; - - virtual bool isArchiveSupported(std::shared_ptr tree) const; - virtual EInstallResult install(MOBase::GuessedValue &modName, std::shared_ptr &tree, - QString &version, int &modID); - -protected: - - /** - * @brief Retrieve the entries corresponding to subpackages in the given tree. - * - * @param tree The tree to check for subpackages. - * @param invalidFolders If not null, will contain the number of invalid folders. - * - * @return the list of entries corresponding to subpackages. - */ - std::vector> findSubpackages( - std::shared_ptr tree, std::size_t *invalidFolders = nullptr) const; - -private: - - const MOBase::IOrganizer *m_MOInfo; - - // Indicates if the installer was used: - bool m_InstallerUsed; - - // List of options currently installed and being installed: - QStringList m_PreviousOptions, m_SelectedOptions; -}; - -#endif // INSTALLERBAIN_H +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef INSTALLERBAIN_H +#define INSTALLERBAIN_H + +#include + +class InstallerBAIN : public MOBase::IPluginInstallerSimple +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple) +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + Q_PLUGIN_METADATA(IID "org.tannin.InstallerBAIN" FILE "installerbain.json") +#endif + +public: + InstallerBAIN(); + + virtual bool init(MOBase::IOrganizer* moInfo) override; + virtual QString name() const override; + virtual QString localizedName() const override; + virtual QString author() const override; + virtual QString description() const override; + virtual MOBase::VersionInfo version() const override; + virtual QList settings() const override; + + virtual unsigned int priority() const; + virtual bool isManualInstaller() const; + + virtual void onInstallationStart(QString const& archive, bool reinstallation, + MOBase::IModInterface* currentMod) override; + virtual void onInstallationEnd(EInstallResult result, + MOBase::IModInterface* newMod) override; + + virtual bool isArchiveSupported(std::shared_ptr tree) const; + virtual EInstallResult install(MOBase::GuessedValue& modName, + std::shared_ptr& tree, + QString& version, int& modID); + +protected: + /** + * @brief Retrieve the entries corresponding to subpackages in the given tree. + * + * @param tree The tree to check for subpackages. + * @param invalidFolders If not null, will contain the number of invalid folders. + * + * @return the list of entries corresponding to subpackages. + */ + std::vector> + findSubpackages(std::shared_ptr tree, + std::size_t* invalidFolders = nullptr) const; + +private: + const MOBase::IOrganizer* m_MOInfo; + + // Indicates if the installer was used: + bool m_InstallerUsed; + + // List of options currently installed and being installed: + QStringList m_PreviousOptions, m_SelectedOptions; +}; + +#endif // INSTALLERBAIN_H