Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
71 changes: 64 additions & 7 deletions Source/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <iterator>
#include <optional>
#include <span>
#include <string>
#include <unordered_set>

#include <SDL_version.h>
#include <expected.hpp>
Expand Down Expand Up @@ -58,6 +60,49 @@

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)
Expand Down Expand Up @@ -158,6 +203,7 @@
void LoadOptions()
{
LoadIni();
DiscoverMods();
Options &options = GetOptions();
for (OptionCategoryBase *pCategory : options.GetCategories()) {
for (OptionEntryBase *pEntry : pCategory->GetEntries()) {
Expand Down Expand Up @@ -215,11 +261,11 @@
SaveIni();
}

std::string_view OptionEntryBase::GetName() const

Check warning on line 264 in Source/options.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/options.cpp:264:35 [readability-identifier-naming]

invalid case style for method 'GetName'
{
return _(name);
}
std::string_view OptionEntryBase::GetDescription() const

Check warning on line 268 in Source/options.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/options.cpp:268:35 [readability-identifier-naming]

invalid case style for method 'GetDescription'
{
return _(description);
}
Expand All @@ -227,7 +273,7 @@
{
return flags;
}
void OptionEntryBase::SetValueChangedCallback(tl::function_ref<void()> callback)

Check warning on line 276 in Source/options.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/options.cpp:276:23 [readability-identifier-naming]

invalid case style for class member 'SetValueChangedCallback'
{
callback_ = callback;
}
Expand Down Expand Up @@ -1478,20 +1524,31 @@
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
@@ -1,6 +1,6 @@
#pragma once

#include <array>

Check failure on line 3 in Source/options.h

View workflow job for this annotation

GitHub Actions / tidy-check

Source/options.h:3:10 [clang-diagnostic-error]

'array' file not found
#include <cstddef>
#include <cstdint>
#include <cstring>
Expand Down Expand Up @@ -830,6 +830,8 @@
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
69 changes: 66 additions & 3 deletions Source/utils/file_util.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
#include "utils/file_util.h"

Check failure on line 1 in Source/utils/file_util.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.cpp:1:10 [clang-diagnostic-error]

'utils/file_util.h' file not found

#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <limits>
#include <string>
#include <string_view>
#include <vector>

#include <SDL.h>

Expand Down Expand Up @@ -43,7 +40,7 @@
// We include sys/stat.h for mkdir where available.
#if !defined(DVL_HAS_FILESYSTEM) && defined(__has_include) && !defined(_WIN32)
#if __has_include(<sys/stat.h>)
#include <sys/stat.h>

Check warning on line 43 in Source/utils/file_util.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.cpp:43:1 [readability-duplicate-include]

duplicate include
#endif
#endif

Expand All @@ -64,7 +61,7 @@
}
#endif

std::string_view Dirname(std::string_view path)

Check warning on line 64 in Source/utils/file_util.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.cpp:64:43 [misc-unused-parameters]

parameter 'path' is unused
{
while (path.size() > 1 && path.back() == DirectorySeparator)
path.remove_suffix(1);
Expand Down Expand Up @@ -445,7 +442,7 @@
std::string name { path };
std::replace(name.begin(), name.end(), '\\', '/');
FILE *f = fopen(name.c_str(), "r+");
if (f != nullptr) {

Check warning on line 445 in Source/utils/file_util.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.cpp:445:2 [bugprone-branch-clone]

if with identical then and else branches
fclose(f);
remove(name.c_str());
f = nullptr;
Expand All @@ -472,4 +469,70 @@
#endif
}

std::vector<std::string> ListDirectories(const char *path)

Check warning on line 472 in Source/utils/file_util.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.cpp:472:54 [misc-unused-parameters]

parameter 'path' is unused
{
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)

Check warning on line 505 in Source/utils/file_util.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.cpp:505:48 [misc-unused-parameters]

parameter 'path' is unused
{
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 changes: 4 additions & 0 deletions Source/utils/file_util.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#pragma once

#include <cstdint>

Check failure on line 3 in Source/utils/file_util.h

View workflow job for this annotation

GitHub Actions / tidy-check

Source/utils/file_util.h:3:10 [clang-diagnostic-error]

'cstdint' file not found
#include <cstdio>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

namespace devilution {

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