Browse Source

Dynamic mod detection

Co-authored-by: Anders Jenbo <anders@jenbo.dk>
Co-authored-by: Stephen C. Wills <staphen@gmail.com>
pull/7890/head
Eric Robinson 12 months ago committed by GitHub
parent
commit
071303357e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 71
      Source/options.cpp
  2. 2
      Source/options.h
  3. 69
      Source/utils/file_util.cpp
  4. 4
      Source/utils/file_util.h

71
Source/options.cpp

@ -14,6 +14,8 @@
#include <iterator>
#include <optional>
#include <span>
#include <string>
#include <unordered_set>
#include <SDL_version.h>
#include <expected.hpp>
@ -58,6 +60,49 @@ namespace devilution {
namespace {
void DiscoverMods()
{
// Add mods available by default:
std::unordered_set<std::string> modNames = { "clock" };
// Check if the mods directory exists.
const std::string modsPath = StrCat(paths::PrefPath(), "mods");
if (DirectoryExists(modsPath.c_str())) {
// Find unpacked mods
for (const std::string &modFolder : ListDirectories(modsPath.c_str())) {
// Only consider this folder if the init.lua file exists.
std::string modScriptPath = modsPath + modFolder + DIRECTORY_SEPARATOR_STR + "init.lua";
if (!FileExists(modScriptPath.c_str()))
continue;
modNames.insert(modFolder);
}
// Find packed mods
for (const std::string &modMpq : ListFiles(modsPath.c_str())) {
if (!modMpq.ends_with(".mpq"))
continue;
modNames.insert(modMpq.substr(0, modMpq.size() - 4));
}
}
// Get the list of mods currently stored in the INI.
std::vector<std::string_view> existingMods = GetOptions().Mods.GetModList();
// Add new mods.
for (const std::string &modName : modNames) {
if (std::find(existingMods.begin(), existingMods.end(), modName) == existingMods.end())
GetOptions().Mods.AddModEntry(modName);
}
// Remove mods that are no longer installed.
for (const std::string_view &modName : existingMods) {
if (modNames.find(std::string(modName)) == modNames.end())
GetOptions().Mods.RemoveModEntry(std::string(modName));
}
}
std::optional<Ini> ini;
#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
@ -158,6 +203,7 @@ bool HardwareCursorSupported()
void LoadOptions()
{
LoadIni();
DiscoverMods();
Options &options = GetOptions();
for (OptionCategoryBase *pCategory : options.GetCategories()) {
for (OptionEntryBase *pEntry : pCategory->GetEntries()) {
@ -1478,6 +1524,24 @@ std::vector<OptionEntryBase *> ModOptions::GetEntries()
return optionEntries;
}
void ModOptions::AddModEntry(const std::string &modName)
{
auto &entries = GetModEntries();
entries.emplace_front(modName);
}
void ModOptions::RemoveModEntry(const std::string &modName)
{
if (!modEntries) {
return;
}
auto &entries = *modEntries;
entries.remove_if([&](const ModEntry &entry) {
return entry.name == modName;
});
}
std::forward_list<ModOptions::ModEntry> &ModOptions::GetModEntries()
{
if (modEntries)
@ -1485,13 +1549,6 @@ std::forward_list<ModOptions::ModEntry> &ModOptions::GetModEntries()
std::vector<std::string> modNames = ini->getKeys(key);
// Add mods available by default:
for (const std::string_view modName : { "clock" }) {
if (c_find(modNames, modName) != modNames.end()) continue;
ini->set(key, modName, false);
modNames.emplace_back(modName);
}
std::forward_list<ModOptions::ModEntry> &newModEntries = modEntries.emplace();
for (auto &modName : modNames) {
newModEntries.emplace_front(modName);

2
Source/options.h

@ -830,6 +830,8 @@ struct ModOptions : OptionCategoryBase {
std::vector<std::string_view> GetActiveModList();
std::vector<std::string_view> GetModList();
std::vector<OptionEntryBase *> GetEntries() override;
void AddModEntry(const std::string &modName);
void RemoveModEntry(const std::string &modName);
private:
struct ModEntry {

69
Source/utils/file_util.cpp

@ -5,9 +5,6 @@
#include <cstdint>
#include <cstring>
#include <limits>
#include <string>
#include <string_view>
#include <vector>
#include <SDL.h>
@ -472,4 +469,70 @@ FILE *OpenFile(const char *path, const char *mode)
#endif
}
std::vector<std::string> ListDirectories(const char *path)
{
std::vector<std::string> dirs;
#ifdef DVL_HAS_FILESYSTEM
std::error_code ec;
for (const auto &entry : std::filesystem::directory_iterator(reinterpret_cast<const char8_t *>(path), ec)) {
if (!entry.is_directory())
continue;
std::u8string filename = entry.path().filename().u8string();
dirs.emplace_back(filename.begin(), filename.end());
}
#elif defined(_WIN32)
WIN32_FIND_DATAA findData;
// Construct the search path by appending the directory separator and wildcard.
std::string searchPath = std::string(path) + DIRECTORY_SEPARATOR_STR + "*";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE)
return dirs;
do {
std::string folder = findData.cFileName;
// Skip the special entries "." and ".."
if (folder == "." || folder == "..")
continue;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
dirs.push_back(folder);
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
#else
static_assert(false, "ListDirectories not implemented for the current platform");
#endif
return dirs;
}
std::vector<std::string> ListFiles(const char *path)
{
std::vector<std::string> files;
#ifdef DVL_HAS_FILESYSTEM
std::error_code ec;
for (const auto &entry : std::filesystem::directory_iterator(reinterpret_cast<const char8_t *>(path), ec)) {
if (!entry.is_regular_file())
continue;
std::u8string filename = entry.path().filename().u8string();
files.emplace_back(filename.begin(), filename.end());
}
#elif defined(_WIN32)
WIN32_FIND_DATAA findData;
// Construct the search path by appending the directory separator and wildcard.
std::string searchPath = std::string(path) + DIRECTORY_SEPARATOR_STR + "*";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE)
return files;
do {
std::string file = findData.cFileName;
// Skip the special entries "." and ".."
if (file == "." || file == "..")
continue;
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
files.push_back(file);
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
#else
static_assert(false, "ListFiles not implemented for the current platform");
#endif
return files;
}
} // namespace devilution

4
Source/utils/file_util.h

@ -5,6 +5,7 @@
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace devilution {
@ -46,4 +47,7 @@ FILE *OpenFile(const char *path, const char *mode);
std::unique_ptr<wchar_t[]> ToWideChar(std::string_view path);
#endif
std::vector<std::string> ListDirectories(const char *path);
std::vector<std::string> ListFiles(const char *path);
} // namespace devilution

Loading…
Cancel
Save