diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c2de138..3384ca6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ set(LIBGIT2_LIBRARIES "${BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_STATIC_LIBRARY_ ExternalProject_Add(libloadorder PREFIX "external" DEPENDS libespm - URL "https://github.com/WrinklyNinja/libloadorder/archive/8.0.1.tar.gz" + URL "https://github.com/WrinklyNinja/libloadorder/archive/9.0.0.tar.gz" CMAKE_ARGS -DBOOST_INCLUDEDIR=${Boost_INCLUDE_DIR} -DPROJECT_STATIC_RUNTIME=${PROJECT_STATIC_RUNTIME} # BUILD_COMMAND ${CMAKE_COMMAND} --build . --target loadorder --config $(CONFIGURATION) INSTALL_COMMAND "") diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index a261d85b..a427e425 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -912,6 +912,23 @@ namespace loot { PluginSorter sorter; list plugins = sorter.Sort(_lootState.CurrentGame(), _lootState.getLanguage().Code()); + // If TESV or FO4, check if load order has been changed. + if ((_lootState.CurrentGame().Id() == Game::tes5 || _lootState.CurrentGame().Id() == Game::fo4) + && equal(begin(plugins), end(plugins), begin(_lootState.CurrentGame().GetLoadOrder()))) { + // Load order has not been changed, set it without asking for + // user input because there are no changes to accept and some + // plugins' positions may only be inferred and not written to + // loadorder.txt/plugins.txt. + std::list newLoadOrder; + std::transform(begin(plugins), + end(plugins), + back_inserter(newLoadOrder), + [](const Plugin& plugin) { + return plugin.Name(); + }); + _lootState.CurrentGame().SetLoadOrder(newLoadOrder); + } + YAML::Node node; // Store global messages in case they have changed. diff --git a/src/tests/backend/game/game_test.h b/src/tests/backend/game/game_test.h index e4dd63c1..52150702 100644 --- a/src/tests/backend/game/game_test.h +++ b/src/tests/backend/game/game_test.h @@ -173,16 +173,16 @@ namespace loot { game.SetGamePath(dataPath.parent_path()); game.Init(false, localPath); - std::vector loadOrder = getInitialLoadOrder(); + std::vector> loadOrder = getInitialLoadOrder(); // First set reverse timestamps to be sure. time_t time = boost::filesystem::last_write_time(dataPath / masterFile); for (size_t i = 1; i < loadOrder.size(); ++i) { - if (!boost::filesystem::exists(dataPath / loadOrder[i])) - loadOrder[i] += ".ghost"; + if (!boost::filesystem::exists(dataPath / loadOrder[i].first)) + loadOrder[i].first += ".ghost"; - boost::filesystem::last_write_time(dataPath / loadOrder[i], time - i * 60); - ASSERT_EQ(time - i * 60, boost::filesystem::last_write_time(dataPath / loadOrder[i])); + boost::filesystem::last_write_time(dataPath / loadOrder[i].first, time - i * 60); + ASSERT_EQ(time - i * 60, boost::filesystem::last_write_time(dataPath / loadOrder[i].first)); } EXPECT_NO_THROW(game.RedatePlugins()); @@ -191,7 +191,7 @@ namespace loot { if (GetParam() != Game::tes5) interval *= -1; for (size_t i = 0; i < loadOrder.size(); ++i) { - EXPECT_EQ(time + i * interval, boost::filesystem::last_write_time(dataPath / loadOrder[i])); + EXPECT_EQ(time + i * interval, boost::filesystem::last_write_time(dataPath / loadOrder[i].first)); } } diff --git a/src/tests/backend/game/load_order_handler_test.h b/src/tests/backend/game/load_order_handler_test.h index 5832e168..3b385db9 100644 --- a/src/tests/backend/game/load_order_handler_test.h +++ b/src/tests/backend/game/load_order_handler_test.h @@ -145,8 +145,11 @@ namespace loot { blankDifferentMasterDependentEsp, blankPluginDependentEsp, }); - EXPECT_NO_THROW(loh.SetLoadOrder(loadOrder)); + + if (GetParam() == GameSettings::fo4) + loadOrder.erase(begin(loadOrder)); + EXPECT_EQ(loadOrder, getLoadOrder()); } } diff --git a/src/tests/base_game_test.h b/src/tests/base_game_test.h index eb2d9035..bfa73a72 100644 --- a/src/tests/base_game_test.h +++ b/src/tests/base_game_test.h @@ -81,7 +81,6 @@ namespace loot { // Set initial load order and active plugins. setLoadOrder(getInitialLoadOrder()); - setActivePlugins(getInitialActivePlugins()); // Ghost a plugin. ASSERT_FALSE(boost::filesystem::exists(dataPath / (blankMasterDependentEsm + ".ghost"))); @@ -116,7 +115,7 @@ namespace loot { for (const auto& plugin : loadOrder) actual.push_back(plugin.second); } - else { + else if (GetParam() == GameSettings::tes5) { boost::filesystem::ifstream in(localPath / "loadorder.txt"); while (in) { std::string line; @@ -126,31 +125,37 @@ namespace loot { actual.push_back(line); } } + else { + boost::filesystem::ifstream in(localPath / "plugins.txt"); + while (in) { + std::string line; + std::getline(in, line); + + if (!line.empty()) { + if (line[0] == '*') + line = line.substr(1); + + actual.push_back(line); + } + } + } return actual; } - inline std::vector getInitialLoadOrder() const { - return std::vector({ - masterFile, - blankEsm, - blankDifferentEsm, - blankMasterDependentEsm, - blankDifferentMasterDependentEsm, - blankEsp, - blankDifferentEsp, - blankMasterDependentEsp, - blankDifferentMasterDependentEsp, - blankPluginDependentEsp, - blankDifferentPluginDependentEsp, - }); - } - - inline std::unordered_set getInitialActivePlugins() const { - return std::unordered_set({ - masterFile, - blankEsm, - blankDifferentMasterDependentEsp, + inline std::vector> getInitialLoadOrder() const { + return std::vector>({ + {masterFile, true}, + {blankEsm, true}, + {blankDifferentEsm, false}, + {blankMasterDependentEsm, false}, + {blankDifferentMasterDependentEsm, false}, + {blankEsp, false}, + {blankDifferentEsp, false}, + {blankMasterDependentEsp, false}, + {blankDifferentMasterDependentEsp, true}, + {blankPluginDependentEsp, false}, + {blankDifferentPluginDependentEsp, false}, }); } @@ -208,32 +213,36 @@ namespace loot { return 0x187BE342; } - inline void setLoadOrder(const std::vector& loadOrder) { + inline void setLoadOrder(const std::vector>& loadOrder) const { + boost::filesystem::ofstream out(localPath / "plugins.txt"); + for (const auto &plugin : loadOrder) { + if (GetParam() == GameSettings::fo4 && plugin.second) + out << '*'; + else if (GetParam() != GameSettings::fo4 && !plugin.second) + continue; + + out << plugin.first << std::endl; + } + if (isLoadOrderTimestampBased(GetParam())) { time_t modificationTime = time(NULL); // Current time. for (const auto &plugin : loadOrder) { - if (boost::filesystem::exists(dataPath / boost::filesystem::path(plugin + ".ghost"))) { - boost::filesystem::last_write_time(dataPath / boost::filesystem::path(plugin + ".ghost"), modificationTime); + if (boost::filesystem::exists(dataPath / boost::filesystem::path(plugin.first + ".ghost"))) { + boost::filesystem::last_write_time(dataPath / boost::filesystem::path(plugin.first + ".ghost"), modificationTime); } else { - boost::filesystem::last_write_time(dataPath / plugin, modificationTime); + boost::filesystem::last_write_time(dataPath / plugin.first, modificationTime); } modificationTime += 60; } } - else { + else if (GetParam() == GameSettings::tes5) { boost::filesystem::ofstream out(localPath / "loadorder.txt"); for (const auto &plugin : loadOrder) - out << plugin << std::endl; + out << plugin.first << std::endl; } } - inline void setActivePlugins(const std::unordered_set& activePlugins) { - boost::filesystem::ofstream out(localPath / "plugins.txt"); - for (const auto &plugin : activePlugins) - out << plugin << std::endl; - } - inline static bool isLoadOrderTimestampBased(unsigned int gameId) { return gameId == GameSettings::tes4 || gameId == GameSettings::fo3 || gameId == GameSettings::fonv; }