summaryrefslogtreecommitdiff
path: root/src/instancemanager.cpp
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-11-06 13:56:04 -0500
committerisanae <14251494+isanae@users.noreply.github.com>2020-11-07 20:16:25 -0500
commitdc2ada2a4249917f538938298deb193ed1dd6bb9 (patch)
tree9f5564aae12004e4a55ed63f1ce294c0c75d7816 /src/instancemanager.cpp
parentb7cb63ddb1e2b263d5e485c97faea527c7c0af44 (diff)
added ... to the "Delete Instance" button to make it less scary
merged InstanceInfo into Instance, most of it was redundant added logging before deleting instance added Instance::readFromIni(), contains stuff that used to be in setup(), used by instance dialog replaced QDir with QString in a few places, I hate QDir
Diffstat (limited to 'src/instancemanager.cpp')
-rw-r--r--src/instancemanager.cpp278
1 files changed, 248 insertions, 30 deletions
diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp
index e7c2a611..fbafc6e8 100644
--- a/src/instancemanager.cpp
+++ b/src/instancemanager.cpp
@@ -38,7 +38,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
-Instance::Instance(QDir dir, bool portable, QString profileName) :
+Instance::Instance(QString dir, bool portable, QString profileName) :
m_dir(std::move(dir)), m_portable(portable), m_plugin(nullptr),
m_profile(std::move(profileName))
{
@@ -49,7 +49,7 @@ QString Instance::name() const
if (isPortable())
return QObject::tr("Portable");
else
- return m_dir.dirName();
+ return QDir(m_dir).dirName();
}
QString Instance::gameName() const
@@ -62,11 +62,16 @@ QString Instance::gameDirectory() const
return m_gameDir;
}
-QDir Instance::directory() const
+QString Instance::directory() const
{
return m_dir;
}
+QString Instance::baseDirectory() const
+{
+ return m_baseDir;
+}
+
MOBase::IPluginGame* Instance::gamePlugin() const
{
return m_plugin;
@@ -87,13 +92,29 @@ bool Instance::isPortable() const
return m_portable;
}
-Instance::SetupResults Instance::setup(PluginContainer& plugins)
+bool Instance::isActive() const
+{
+ auto& m = InstanceManager::singleton();
+
+ if (auto i=m.currentInstance())
+ {
+ if (m_portable) {
+ return i->isPortable();
+ } else {
+ return (i->name() == name());
+ }
+ }
+
+ return false;
+}
+
+bool Instance::readFromIni()
{
Settings s(iniPath());
if (s.iniStatus() != QSettings::NoError) {
log::error("can't read ini {}", iniPath());
- return SetupResults::BadIni;
+ return false;
}
// game name and directory are from ini unless overridden by setGame()
@@ -107,27 +128,61 @@ Instance::SetupResults Instance::setup(PluginContainer& plugins)
m_gameDir = *v;
}
- // getting game plugin
- const auto r = getGamePlugin(plugins);
- if (r != SetupResults::Ok) {
- return r;
- }
-
- // getting game variant, error if it's missing and required by the plugin
if (m_gameVariant.isEmpty()) {
if (auto v=s.game().edition()) {
m_gameVariant = *v;
}
}
+ if (m_baseDir.isEmpty()) {
+ m_baseDir = s.paths().base();
+ }
+
+ // figuring out profile from ini if it's missing
+ getProfile(s);
+
+ return true;
+}
+
+Instance::SetupResults Instance::setup(PluginContainer& plugins)
+{
+ // read initial values from the ini
+ if (!readFromIni()) {
+ return SetupResults::BadIni;
+ }
+
+ // getting game plugin
+ const auto r = getGamePlugin(plugins);
+ if (r != SetupResults::Ok) {
+ return r;
+ }
+
+ // error if the variant missing and required by the plugin
if (m_gameVariant.isEmpty() && m_plugin->gameVariants().size() > 1) {
return SetupResults::MissingVariant;
} else {
m_plugin->setGameVariant(m_gameVariant);
}
- // figuring out profile from ini if it's missing
- getProfile(s);
+ // update the ini in case anything was missing
+ updateIni();
+
+ // the game directory may be different than what the plugin detected, the user
+ // can change it in the settings and might have multiple versions of the game
+ // installed
+ m_plugin->setGamePath(m_gameDir);
+
+ return SetupResults::Ok;
+}
+
+void Instance::updateIni()
+{
+ Settings s(iniPath());
+
+ if (s.iniStatus() != QSettings::NoError) {
+ log::error("can't open ini {}", iniPath());
+ return;
+ }
// updating the settings since some of these values might have been missing
s.game().setName(m_gameName);
@@ -138,13 +193,6 @@ Instance::SetupResults Instance::setup(PluginContainer& plugins)
// don't write a variant to the ini if the plugin doesn't require one
s.game().setEdition(m_gameVariant);
}
-
- // the game directory may be different than what the plugin detected, the user
- // can change it in the settings and might have multiple versions of the game
- // installed
- m_plugin->setGamePath(m_gameDir);
-
- return SetupResults::Ok;
}
void Instance::setGame(const QString& name, const QString& dir)
@@ -272,7 +320,6 @@ Instance::SetupResults Instance::getGamePlugin(PluginContainer& plugins)
}
}
-
void Instance::getProfile(const Settings& s)
{
if (!m_profile.isEmpty()) {
@@ -294,6 +341,176 @@ void Instance::getProfile(const Settings& s)
iniPath(), m_profile);
}
+// returns a list of files and folders that must be deleted when deleting
+// this instance
+//
+std::vector<Instance::Object> Instance::objectsForDeletion() const
+{
+ // native separators and ending slash
+ auto prettyDir = [](auto s) {
+ if (!s.endsWith("/") || !s.endsWith("\\")) {
+ s += "/";
+ }
+
+ return QDir::toNativeSeparators(s);
+ };
+
+ // native separators
+ auto prettyFile = [](auto s) {
+ return QDir::toNativeSeparators(s);
+ };
+
+
+ // lowercase, native separators and ending slash
+ auto canonicalDir = [](QString s) {
+ s = s.toLower();
+
+ if (!s.endsWith("/") || !s.endsWith("\\")) {
+ s += "/";
+ }
+
+ return QDir::toNativeSeparators(s);
+ };
+
+ // lower and native separators
+ auto canonicalFile = [](auto s) {
+ return QDir::toNativeSeparators(s.toLower());
+ };
+
+
+
+ // whether the given directory is contained in the root
+ auto dirInRoot = [&](const QString& root, const QString& dir) {
+ return canonicalDir(dir).startsWith(canonicalDir(root));
+ };
+
+ // whether the given file is contained in the root
+ auto fileInRoot = [&](const QString& root, const QString& file) {
+ return canonicalFile(file).startsWith(canonicalDir(root));
+ };
+
+
+ Settings settings(iniPath());
+
+ if (settings.iniStatus() != QSettings::NoError) {
+ log::error("can't read ini {}", iniPath());
+ return {};
+ }
+
+
+
+ const QString loc = directory();
+ const QString base = settings.paths().base();
+
+
+ // directories that might contain the individual files and directories
+ // set in the path settings
+ std::vector<Object> roots;
+
+ // a portable instance has its location in the installation directory,
+ // don't delete that
+ if (!isPortable()) {
+ if (QDir(loc).exists()) {
+ roots.push_back({loc, true});
+ }
+ }
+
+ // the base directory is the location directory by default, don't add it
+ // if it's the same
+ if (canonicalDir(base) != canonicalDir(loc)) {
+ if (QDir(base).exists()) {
+ roots.push_back({base, false});
+ }
+ }
+
+
+ // all the directories that are part of an instance; none of them are
+ // mandatory for deletion
+ const std::vector<Object> dirs = {
+ settings.paths().downloads(),
+ settings.paths().mods(),
+ settings.paths().cache(),
+ settings.paths().profiles(),
+ settings.paths().overwrite(),
+ QDir(m_dir).filePath(QString::fromStdWString(AppConfig::dumpsDir())),
+ QDir(m_dir).filePath(QString::fromStdWString(AppConfig::logPath())),
+ };
+
+ // all the files that are part of an instance
+ const std::vector<Object> files = {
+ {iniPath(), true}, // the ini file must be deleted
+ };
+
+
+ // this will contain the root directories, plus all the individual
+ // directories that are not inside these roots
+ std::vector<Object> cleanDirs;
+
+ for (const auto& f : dirs) {
+ bool inRoots = false;
+
+ for (const auto& root : roots) {
+ if (dirInRoot(root.path, f.path)) {
+ inRoots = true;
+ break;
+ }
+ }
+
+ if (!inRoots) {
+ // not in roots, this is a path that was changed by the user; make
+ // sure it exists
+ if (QDir(f.path).exists()) {
+ cleanDirs.push_back({prettyDir(f.path), f.mandatoryDelete});
+ }
+ }
+ }
+
+ // prepending the roots
+ for (auto itor=roots.rbegin(); itor!=roots.rend(); ++itor) {
+ cleanDirs.insert(
+ cleanDirs.begin(),
+ {prettyDir(itor->path), itor->mandatoryDelete});
+ }
+
+
+
+ // this will contain the individual files that are not inside the roots;
+ // not that this only contains the INI file for now, so most of this is
+ // useless
+ std::vector<Object> cleanFiles;
+
+ for (const auto& f : files) {
+ bool inRoots = false;
+
+ for (const auto& root : roots) {
+ if (fileInRoot(root.path, f.path)) {
+ inRoots = true;
+ break;
+ }
+ }
+
+ if (!inRoots) {
+ // not in roots, this is a path that was changed by the user; make
+ // sure it exists
+ if (QFileInfo(f.path).exists()) {
+ cleanFiles.push_back({prettyFile(f.path), f.mandatoryDelete});
+ }
+ }
+ }
+
+
+ // contains all the directories and files to be deleted
+ std::vector<Object> all;
+ all.insert(all.end(), cleanDirs.begin(), cleanDirs.end());
+ all.insert(all.end(), cleanFiles.begin(), cleanFiles.end());
+
+ // mandatory on top
+ std::stable_sort(all.begin(), all.end());
+
+ return all;
+}
+
+
InstanceManager::InstanceManager()
{
@@ -327,20 +544,20 @@ std::optional<Instance> InstanceManager::currentInstance() const
if (!allowedToChangeInstance()) {
// force portable instance
- return Instance(QDir(portablePath()), true, profile);
+ return Instance(portablePath(), true, profile);
}
if (name.isEmpty()) {
if (portableInstanceExists()) {
// use portable
- return Instance(QDir(portablePath()), true, profile);
+ return Instance(portablePath(), true, profile);
} else {
// no instance set
return {};
}
}
- return Instance(QDir(instancePath(name)), false, profile);
+ return Instance(instancePath(name), false, profile);
}
void InstanceManager::clearCurrentInstance()
@@ -365,12 +582,13 @@ QString InstanceManager::globalInstancesRootPath() const
QStandardPaths::writableLocation(QStandardPaths::DataLocation));
}
-QString InstanceManager::iniPath(const QDir& instanceDir) const
+QString InstanceManager::iniPath(const QString& instanceDir) const
{
- return instanceDir.filePath(QString::fromStdWString(AppConfig::iniFileName()));
+ return QDir(instanceDir).filePath(
+ QString::fromStdWString(AppConfig::iniFileName()));
}
-std::vector<QDir> InstanceManager::globalInstancePaths() const
+std::vector<QString> InstanceManager::globalInstancePaths() const
{
const std::set<QString> ignore = {
"cache", "qtwebengine",
@@ -379,7 +597,7 @@ std::vector<QDir> InstanceManager::globalInstancePaths() const
const QDir root(globalInstancesRootPath());
const auto dirs = root.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- std::vector<QDir> list;
+ std::vector<QString> list;
for (auto&& d : dirs) {
if (!ignore.contains(QFileInfo(d).fileName().toLower())) {
@@ -416,7 +634,7 @@ bool InstanceManager::allowedToChangeInstance() const
}
const MOBase::IPluginGame* InstanceManager::gamePluginForDirectory(
- const QDir& instanceDir, const PluginContainer& plugins) const
+ const QString& instanceDir, const PluginContainer& plugins) const
{
const QString ini = iniPath(instanceDir);