Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions Source/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
#include <iterator>
#include <optional>
#include <span>
#include <string>
#include <unordered_set>

#include <SDL.h>
Comment thread
StephenCWills marked this conversation as resolved.
Outdated
#include <SDL_version.h>
#include <expected.hpp>
#include <fmt/format.h>
Expand Down Expand Up @@ -58,6 +61,44 @@ namespace devilution {

namespace {

void LoadUserMods()
{
const std::string modsPath = paths::BasePath() + "assets/lua/mods/";
Comment thread
AJenbo marked this conversation as resolved.
Outdated

// Check if the mods directory exists.
if (!DirectoryExists(modsPath.c_str()))
return;

// Use our helper function to get a list of directory names.
std::vector<std::string> modFolders = ListDirectories(modsPath.c_str());
std::unordered_set<std::string> modsInFolder;

// Iterate through each found mod folder.
for (const std::string &modFolder : modFolders) {
// Build the full path to the expected init.lua file.
std::string modScriptPath = modsPath + modFolder + DIRECTORY_SEPARATOR_STR + "init.lua";
// Only consider this folder if the init.lua file exists.
if (!FileExists(modScriptPath.c_str()))
continue;

// Record that this mod folder is present.
modsInFolder.insert(modFolder);

// Get the list of mods currently stored in the INI.
auto existingMods = GetOptions().Mods.GetModList();
// If the mod isn’t already registered, add it.
if (std::find(existingMods.begin(), existingMods.end(), modFolder) == existingMods.end())
GetOptions().Mods.AddModEntry(modFolder);
}

// Remove mods that are in the INI but no longer in the mods folder.
auto existingMods = GetOptions().Mods.GetModList();
for (const std::string_view &mod : existingMods) {
if (modsInFolder.find(std::string(mod)) == modsInFolder.end())
GetOptions().Mods.RemoveModEntry(std::string(mod));
}
}

std::optional<Ini> ini;

#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
Expand Down Expand Up @@ -158,6 +199,7 @@ bool HardwareCursorSupported()
void LoadOptions()
{
LoadIni();
LoadUserMods();
Options &options = GetOptions();
for (OptionCategoryBase *pCategory : options.GetCategories()) {
for (OptionEntryBase *pEntry : pCategory->GetEntries()) {
Expand Down Expand Up @@ -1478,20 +1520,31 @@ 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)
return *modEntries;

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);
}
Comment thread
AJenbo marked this conversation as resolved.

std::forward_list<ModOptions::ModEntry> &newModEntries = modEntries.emplace();
for (auto &modName : modNames) {
newModEntries.emplace_front(modName);
Expand Down
2 changes: 2 additions & 0 deletions Source/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
39 changes: 36 additions & 3 deletions Source/utils/file_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
#include <cstdint>
#include <cstring>
#include <limits>
#include <string>
#include <string_view>
#include <vector>

#include <SDL.h>

Expand Down Expand Up @@ -47,6 +44,12 @@
#endif
#endif

#ifdef NXDK
#include <windows.h> // For WIN32_FIND_DATAA, FindFirstFileA, etc.
#else
#include <filesystem>
#endif

namespace devilution {

#if defined(_WIN32) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR)
Expand Down Expand Up @@ -472,4 +475,34 @@ FILE *OpenFile(const char *path, const char *mode)
#endif
}

std::vector<std::string> ListDirectories(const char *path)
{
std::vector<std::string> dirs;
#ifdef NXDK
// Use the NXDK Windows API.
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
// For platforms that support std::filesystem.
for (const auto &entry : std::filesystem::directory_iterator(path)) {
Comment thread
AJenbo marked this conversation as resolved.
Outdated
if (entry.is_directory())
dirs.push_back(entry.path().filename().string());
}
Comment thread
AJenbo marked this conversation as resolved.
Outdated
#endif
return dirs;
}

} // namespace devilution
3 changes: 3 additions & 0 deletions Source/utils/file_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <memory>
#include <string>
#include <string_view>
#include <vector>

namespace devilution {

Expand Down Expand Up @@ -46,4 +47,6 @@ 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);

} // namespace devilution