bool InstallationManager::ensureValidModName(GuessedValue<QString> &name) const
{
  while (name->isEmpty()) {
    bool ok;
    name.update(QInputDialog::getText(m_ParentWidget, tr("Invalid name"),
                                      tr("The name you entered is invalid, please enter a different one."),
                                      QLineEdit::Normal, "", &ok),
                GUESS_USER);
    if (!ok) {
      return false;
    }
  }
  return true;
}
IPluginInstaller::EInstallResult InstallerFomod::install(GuessedValue<QString> &modName, DirectoryTree &tree,
                                                         QString &version, int &modID)
{
  QStringList installerFiles = buildFomodTree(tree);
  manager()->extractFiles(installerFiles, false);

  try {
    const DirectoryTree *fomodTree = findFomodDirectory(&tree);

    QString fomodPath = fomodTree->getParent()->getFullPath();
    FomodInstallerDialog dialog(modName, fomodPath, std::bind(&InstallerFomod::fileState, this, std::placeholders::_1));
    dialog.initData(m_MOInfo);
    if (!dialog.getVersion().isEmpty()) {
      version = dialog.getVersion();
    }
    if (dialog.getModID() != -1) {
      modID = dialog.getModID();
    }

    manager()->setURL(dialog.getURL());

    if (!dialog.hasOptions() || (dialog.exec() == QDialog::Accepted)) {
      modName.update(dialog.getName(), GUESS_USER);
      DirectoryTree *newTree = dialog.updateTree(&tree);
      tree = *newTree;
      delete newTree;

      return IPluginInstaller::RESULT_SUCCESS;
    } else {
      if (dialog.manualRequested()) {
        modName.update(dialog.getName(), GUESS_USER);
        return IPluginInstaller::RESULT_MANUALREQUESTED;
      } else {
        return IPluginInstaller::RESULT_FAILED;
      }
    }
  } catch (const std::exception &e) {
    reportError(tr("Installation as fomod failed: %1").arg(e.what()));
    return IPluginInstaller::RESULT_MANUALREQUESTED;
  }
}
bool InstallationManager::testOverwrite(GuessedValue<QString> &modName, bool *merge) const
{
  QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory + "\\" + modName);
  while (QDir(targetDirectory).exists()) {
    Settings &settings(Settings::instance());
    bool backup = settings.directInterface().value("backup_install", false).toBool();
    QueryOverwriteDialog overwriteDialog(m_ParentWidget,
                                         backup ? QueryOverwriteDialog::BACKUP_YES : QueryOverwriteDialog::BACKUP_NO);
    if (overwriteDialog.exec()) {
      settings.directInterface().setValue("backup_install", overwriteDialog.backup());
      if (overwriteDialog.backup()) {
        QString backupDirectory = generateBackupName(targetDirectory);
        if (!copyDir(targetDirectory, backupDirectory, false)) {
          reportError(tr("failed to create backup"));
          return false;
        }
      }
      if (merge != nullptr) {
        *merge = (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE);
      }
      if (overwriteDialog.action() == QueryOverwriteDialog::ACT_RENAME) {
        bool ok = false;
        QString name = QInputDialog::getText(m_ParentWidget, tr("Mod Name"), tr("Name"),
                                             QLineEdit::Normal, modName, &ok);
        if (ok && !name.isEmpty()) {
          modName.update(name, GUESS_USER);
          if (!ensureValidModName(modName)) {
            return false;
          }
          targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory) + "/" + modName;
        }
      } else if (overwriteDialog.action() == QueryOverwriteDialog::ACT_REPLACE) {
        // save original settings like categories. Because it makes sense
        QString metaFilename = targetDirectory + "/meta.ini";
        QFile settingsFile(metaFilename);
        QByteArray originalSettings;
        if (settingsFile.open(QIODevice::ReadOnly)) {
          originalSettings = settingsFile.readAll();
          settingsFile.close();
        }

        // remove the directory with all content, then recreate it empty
        shellDelete(QStringList(targetDirectory));
        if (!QDir().mkdir(targetDirectory)) {
          // windows may keep the directory around for a moment, preventing its re-creation. Not sure
          // if this still happens with shellDelete
          Sleep(100);
          QDir().mkdir(targetDirectory);
        }
        // restore the saved settings
        if (settingsFile.open(QIODevice::WriteOnly)) {
          settingsFile.write(originalSettings);
          settingsFile.close();
        } else {
          qCritical("failed to restore original settings: %s", metaFilename.toUtf8().constData());
        }
        return true;
      } else if (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE) {
        return true;
      }
    } else {
      return false;
    }
  }

  QDir().mkdir(targetDirectory);

  return true;
}