From d582428d4abde3be2174113ef21b08487dff0dc6 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 2 Nov 2014 13:03:39 +0000 Subject: [PATCH] Improved checking if a file is a plugin. Closes #322. --- src/backend/game.cpp | 34 +++++++++++++++++++++++++++++++++- src/backend/game.h | 2 ++ src/backend/graph.cpp | 6 ++---- src/backend/metadata.cpp | 12 ++---------- src/backend/metadata.h | 2 -- src/backend/parsers.h | 6 +++--- src/tests/api/api.h | 4 +--- 7 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/backend/game.cpp b/src/backend/game.cpp index e85473e4..3616972d 100644 --- a/src/backend/game.cpp +++ b/src/backend/game.cpp @@ -676,7 +676,7 @@ namespace loot { std::vector groupPlugins; //First calculate the mean plugin size. Store it temporarily in a map to reduce filesystem lookups and file size recalculation. for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) { - if (fs::is_regular_file(it->status()) && IsPlugin(it->path().string())) { + if (fs::is_regular_file(it->status()) && this->IsValidPlugin(it->path().filename().string())) { uintmax_t fileSize = fs::file_size(it->path()); meanFileSize += fileSize; @@ -741,6 +741,38 @@ namespace loot { return false; } + bool Game::IsValidPlugin(const std::string& name) const { + BOOST_LOG_TRIVIAL(trace) << "Checking to see if \"" << name << "\" is a valid plugin."; + // Rather than just checking the extension, try also parsing the file header, and see if it fails. + if (!boost::iends_with(name, ".esm") && !boost::iends_with(name, ".esp") && !boost::iends_with(name, ".esm.ghost") && !boost::iends_with(name, ".esp.ghost")) { + BOOST_LOG_TRIVIAL(warning) << "The .es(p|m) file \"" << name << "\" is not a valid plugin."; + return false; + } + + try { + string filepath = (this->DataPath() / name).string(); + if (fs::exists(this->DataPath() / fs::path(name + ".ghost"))) + filepath += ".ghost"; + + espm::File * file = nullptr; + if (this->Id() == LIBLO_GAME_TES4) + file = new espm::tes4::File(filepath, this->espm_settings, false, true); + else if (this->Id() == LIBLO_GAME_TES5) + file = new espm::tes5::File(filepath, this->espm_settings, false, true); + else if (this->Id() == LIBLO_GAME_FO3) + file = new espm::fo3::File(filepath, this->espm_settings, false, true); + else + file = new espm::fonv::File(filepath, this->espm_settings, false, true); + + delete file; + } + catch (std::exception& /*e*/) { + BOOST_LOG_TRIVIAL(warning) << "The .es(p|m) file \"" << name << "\" is not a valid plugin."; + return false; + } + return true; + } + void Game::CreateLOOTGameFolder() { //Make sure that the LOOT game path exists. try { diff --git a/src/backend/game.h b/src/backend/game.h index d5b52129..e3249f53 100644 --- a/src/backend/game.h +++ b/src/backend/game.h @@ -141,6 +141,8 @@ namespace loot { void LoadPlugins(bool headersOnly); //Loads all installed plugins. bool HasBeenLoaded(); // Checks if the game's plugins have already been loaded. + bool IsValidPlugin(const std::string& name) const; + std::list Sort(const unsigned int language, std::function progressCallback); //Caches for condition results, active plugins and CRCs. diff --git a/src/backend/graph.cpp b/src/backend/graph.cpp index c1617f14..44982c9e 100644 --- a/src/backend/graph.cpp +++ b/src/backend/graph.cpp @@ -179,8 +179,7 @@ namespace loot { BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for requirements."; set fileset(graph[*vit].Reqs()); for (const auto &file : fileset) { - if (loot::IsPlugin(file.Name()) && - loot::GetVertexByName(graph, file.Name(), parentVertex) && + if (loot::GetVertexByName(graph, file.Name(), parentVertex) && !boost::edge(parentVertex, *vit, graph).second) { BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex].Name() << "\" to \"" << graph[*vit].Name() << "\"."; @@ -196,8 +195,7 @@ namespace loot { BOOST_LOG_TRIVIAL(trace) << "Adding in-edges for 'load after's."; fileset = graph[*vit].LoadAfter(); for (const auto &file : fileset) { - if (loot::IsPlugin(file.Name()) && - loot::GetVertexByName(graph, file.Name(), parentVertex) && + if (loot::GetVertexByName(graph, file.Name(), parentVertex) && !boost::edge(parentVertex, *vit, graph).second) { BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex].Name() << "\" to \"" << graph[*vit].Name() << "\"."; diff --git a/src/backend/metadata.cpp b/src/backend/metadata.cpp index 4cd03989..c38d2f1d 100644 --- a/src/backend/metadata.cpp +++ b/src/backend/metadata.cpp @@ -862,13 +862,13 @@ namespace loot { } } for (const auto &req : requirements) { - if (!boost::filesystem::exists(game.DataPath() / req.Name()) && !(IsPlugin(req.Name()) && boost::filesystem::exists(game.DataPath() / (req.Name() + ".ghost")))) { + if (!boost::filesystem::exists(game.DataPath() / req.Name()) && !((boost::iends_with(req.Name(), ".esp") || boost::iends_with(req.Name(), ".esm")) && boost::filesystem::exists(game.DataPath() / (req.Name() + ".ghost")))) { BOOST_LOG_TRIVIAL(error) << "\"" << name << "\" requires \"" << req.Name() << "\", but it is missing."; messages.push_back(loot::Message(messageType, (boost::format(boost::locale::translate("This plugin requires \"%1%\" to be installed, but it is missing.")) % req.Name()).str())); } } for (const auto &inc : incompatibilities) { - if (boost::filesystem::exists(game.DataPath() / inc.Name()) || (IsPlugin(inc.Name()) && boost::filesystem::exists(game.DataPath() / (inc.Name() + ".ghost")))) { + if (boost::filesystem::exists(game.DataPath() / inc.Name()) || ((boost::iends_with(inc.Name(), ".esp") || boost::iends_with(inc.Name(), ".esm")) && boost::filesystem::exists(game.DataPath() / (inc.Name() + ".ghost")))) { if (!game.IsActive(inc.Name())) messageType = loot::Message::warn; BOOST_LOG_TRIVIAL(error) << "\"" << name << "\" is incompatible with \"" << inc.Name() << "\", but both are present."; @@ -928,12 +928,4 @@ namespace loot { bool operator == (const Plugin& lhs, const std::string& rhs) { return rhs == lhs; } - - bool IsPlugin(const std::string& file) { - if (boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm") - || boost::iends_with(file, ".esp.ghost") || boost::iends_with(file, ".esm.ghost")) - return true; - else - return false; - } } diff --git a/src/backend/metadata.h b/src/backend/metadata.h index 43869930..9dc75db3 100644 --- a/src/backend/metadata.h +++ b/src/backend/metadata.h @@ -273,8 +273,6 @@ namespace loot { bool operator == (const Plugin& lhs, const File& rhs); bool operator == (const std::string& lhs, const Plugin& rhs); - - bool IsPlugin(const std::string& file); } namespace std { diff --git a/src/backend/parsers.h b/src/backend/parsers.h index cfd6159f..3417d37b 100644 --- a/src/backend/parsers.h +++ b/src/backend/parsers.h @@ -540,7 +540,7 @@ namespace loot { throw loot::error(loot::error::invalid_args, boost::locale::translate("Invalid file path:").str() + " " + file); } - if (IsPlugin(file)) + if (boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm")) result = boost::filesystem::exists(_game->DataPath() / file) || boost::filesystem::exists(_game->DataPath() / (file + ".ghost")); else result = boost::filesystem::exists(_game->DataPath() / file); @@ -642,7 +642,7 @@ namespace loot { crc = GetCrc32(boost::filesystem::absolute("LOOT.exe")); if (boost::filesystem::exists(_game->DataPath() / file)) crc = GetCrc32(_game->DataPath() / file); - else if (IsPlugin(file) && boost::filesystem::exists(_game->DataPath() / (file + ".ghost"))) + else if ((boost::iends_with(file, ".esp") || boost::iends_with(file, ".esm")) && boost::filesystem::exists(_game->DataPath() / (file + ".ghost"))) crc = GetCrc32(_game->DataPath() / (file + ".ghost")); else { result = false; @@ -673,7 +673,7 @@ namespace loot { Version trueVersion; if (file == "LOOT") trueVersion = Version(boost::filesystem::absolute("LOOT.exe")); - else if (IsPlugin(file)) { + else if (_game->IsValidPlugin(file)) { Plugin plugin(*_game, file, true); trueVersion = Version(plugin.Version()); } diff --git a/src/tests/api/api.h b/src/tests/api/api.h index 93128e7c..fa3a1974 100644 --- a/src/tests/api/api.h +++ b/src/tests/api/api.h @@ -209,8 +209,6 @@ TEST_F(OblivionAPIOperationsTest, SortPlugins) { "Oblivion.esm", "Blank - Different.esm", "Blank - Different Master Dependent.esm", - "NotAPlugin.esm", - "EmptyFile.esm", "Blank - Master Dependent.esp", "Blank.esp", "Blank - Plugin Dependent.esp", @@ -222,7 +220,7 @@ TEST_F(OblivionAPIOperationsTest, SortPlugins) { for (size_t i = 0; i < numPlugins; ++i) { actualOrder.push_back(sortedPlugins[i]); } - EXPECT_EQ(13, numPlugins); + EXPECT_EQ(11, numPlugins); EXPECT_EQ(expectedOrder, actualOrder); } #endif