/*
Copyright (C) 2020 Holt59. All rights reserved.
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
#include "csharp_interface.h"
#include "installer_fomod_csharp.h"
#include "installer_fomod_predialog.h"
#include "xml_info_reader.h"
using namespace MOBase;
bool InstallerFomodCSharp::init(IOrganizer* moInfo)
{
m_MOInfo = moInfo;
CSharp::init(moInfo);
return true;
}
std::shared_ptr
InstallerFomodCSharp::findFomodDirectory(std::shared_ptr tree) const
{
auto entry = tree->find("fomod", FileTreeEntry::DIRECTORY);
if (entry != nullptr) {
return entry->astree();
}
if (tree->empty()) {
return nullptr;
}
// We need at least a directory:
if (!tree->at(0)->isDir()) {
return nullptr;
}
// But not two:
if (tree->size() > 1 && tree->at(1)->isDir()) {
return nullptr;
}
return findFomodDirectory(tree->at(0)->astree());
}
std::shared_ptr
InstallerFomodCSharp::findScriptFile(std::shared_ptr tree) const
{
auto fomodDirectory = findFomodDirectory(tree);
if (fomodDirectory == nullptr) {
return nullptr;
}
for (auto e : *fomodDirectory) {
if (e->isFile() && e->suffix().compare("cs", Qt::CaseInsensitive) == 0) {
return e;
}
}
return nullptr;
}
std::shared_ptr
InstallerFomodCSharp::findInfoFile(std::shared_ptr tree) const
{
auto fomodDirectory = findFomodDirectory(tree);
if (fomodDirectory == nullptr) {
return nullptr;
}
for (auto e : *fomodDirectory) {
if (e->isFile() && e->compare("info.xml") == 0) {
return e;
}
}
return nullptr;
}
bool InstallerFomodCSharp::isArchiveSupported(
std::shared_ptr tree) const
{
return findScriptFile(tree) != nullptr;
}
InstallerFomodCSharp::EInstallResult
InstallerFomodCSharp::install(MOBase::GuessedValue& modName,
std::shared_ptr& tree,
QString& version, int& modID)
{
static std::set imageSuffixes{"png", "jpg", "jpeg",
"gif", "bmp"};
// Extract the script file:
auto scriptFile = findScriptFile(tree);
if (scriptFile == nullptr) {
return EInstallResult::RESULT_NOTATTEMPTED;
}
// Check if there is a info.xml:
auto infoFile = findInfoFile(tree);
// Set containing everything to extract except the script and the info file:
std::set> toExtractSet{scriptFile};
if (infoFile != nullptr) {
toExtractSet.insert(infoFile);
}
// Extract all the images:
tree->walk([&](const QString&, auto entry) {
if (entry->isFile() && imageSuffixes.count(entry->suffix()) > 0) {
toExtractSet.insert(entry);
}
return IFileTree::WalkReturn::CONTINUE;
});
// Extract everything from the fomod/ folder:
auto fomodFolder = findFomodDirectory(tree);
fomodFolder->walk([&](const QString&, auto entry) {
if (entry->isFile()) {
toExtractSet.insert(entry);
}
return IFileTree::WalkReturn::CONTINUE;
});
// Convert to vector:
std::vector toExtract(std::begin(toExtractSet), std::end(toExtractSet));
QStringList paths(manager()->extractFiles(toExtract));
// If user cancelled:
if (toExtract.size() != static_cast(paths.size())) {
return EInstallResult::RESULT_CANCELED;
}
// Create a map from entry to file path:
std::map, QString> entryToPath;
for (std::size_t i = 0; i < toExtract.size(); ++i) {
entryToPath[toExtract[i]] = paths[i];
}
if (infoFile != nullptr) {
QFile file(entryToPath[infoFile]);
if (file.open(QIODevice::ReadOnly)) {
auto info = FomodInfoReader::readXml(file, &FomodInfoReader::parseInfo);
if (!std::get<0>(info).isEmpty()) {
modName.update(std::get<0>(info), GUESS_META);
}
if (std::get<1>(info) != -1) {
modID = std::get<1>(info);
}
if (!std::get<2>(info).isEmpty()) {
version = std::get<2>(info);
}
}
}
// Show the dialog:
InstallerFomodPredialog dialog(modName, parentWidget());
if (dialog.exec() != QDialog::Accepted) {
if (dialog.manualRequested()) {
modName.update(dialog.getName(), GUESS_USER);
return EInstallResult::RESULT_MANUALREQUESTED;
} else {
return EInstallResult::RESULT_CANCELED;
}
}
modName.update(dialog.getName(), GUESS_USER);
// Run the C# script:
const QString scriptPath = entryToPath[scriptFile];
CSharp::beforeInstall(
this, manager(), parentWidget(),
std::const_pointer_cast(scriptFile->parent()->parent()),
std::move(entryToPath));
return CSharp::executeCSharpScript(scriptPath, tree);
}