Merge pull request #5388 from Sonicadvance1/123

Config: Finish wiring up Regex app overrides
This commit is contained in:
Tony Wasserka authored and GitHub committed 2026-04-02 11:00:37 +02:00
commit c6d2ce043f
7 files changed
+133 -31

No files matched your search

+2 -1
View File
@@ -6,7 +6,8 @@ set(FEXCORE_BASE_SRCS
Utils/FileLoading.cpp
Utils/ForcedAssert.cpp
Utils/LogManager.cpp
Utils/SpinWaitLock.cpp)
Utils/SpinWaitLock.cpp
Utils/WildcardMatcher.cpp)
if (NOT MINGW)
list(APPEND FEXCORE_BASE_SRCS
+23
View File
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
#include <FEXCore/Utils/WildcardMatcher.h>
namespace FEXCore::Utils::Wildcard {
static bool matchHelper(std::string_view pattern, std::string_view text, size_t p_idx, size_t t_idx) {
if (p_idx == pattern.size()) {
// Pattern exhausted
return (t_idx == text.size());
} else if (pattern[p_idx] == '*') {
// Wildcard: Try matching zero characters, or one or more characters
return matchHelper(pattern, text, p_idx + 1, t_idx) || (t_idx < text.size() && matchHelper(pattern, text, p_idx, t_idx + 1));
} else {
// Match normally
return (t_idx < text.size() && pattern[p_idx] == text[t_idx] && matchHelper(pattern, text, p_idx + 1, t_idx + 1));
}
}
bool Matches(std::string_view pattern, std::string_view text) {
return matchHelper(pattern, text, 0, 0);
}
} // namespace FEXCore::Utils::Wildcard
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <string_view>
namespace FEXCore::Utils::Wildcard {
bool Matches(std::string_view pattern, std::string_view text);
} // namespace FEXCore::Utils::Wildcard
+48 -27
View File
@@ -7,11 +7,12 @@
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/map.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/vector.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/FileLoading.h>
#include <FEXCore/Utils/WildcardMatcher.h>
#include <FEXHeaderUtils/Filesystem.h>
#include <FEXHeaderUtils/SymlinkChecks.h>
#include <cstring>
#include <fmt/format.h>
#include <functional>
@@ -28,7 +29,8 @@
namespace FEX::Config {
namespace JSON {
static void LoadJSonConfig(const fextl::string& Config, std::function<void(const char* Name, const char* ConfigSring)> Func) {
static void LoadJSonConfig(const fextl::string& Config, std::optional<fextl::string> AppName,
std::function<void(const char* Name, const char* ConfigString)> Func) {
fextl::vector<char> Data;
if (!FEXCore::FileLoading::LoadFile(Data, Config)) {
return;
@@ -48,21 +50,35 @@ namespace JSON {
return;
}
for (const json_t* ConfigItem = json_getChild(ConfigList); ConfigItem != nullptr; ConfigItem = json_getSibling(ConfigItem)) {
const char* ConfigName = json_getName(ConfigItem);
const char* ConfigString = json_getValue(ConfigItem);
fextl::vector<const json_t*> ConfigBlocks;
ConfigBlocks.push_back(ConfigList);
if (!ConfigName) {
LogMan::Msg::EFmt("JSON file '{}': Couldn't get config name for an item", Config);
return;
if (AppName) {
const json_t* OverrideList = json_getProperty(json, "AppOverrides");
if (OverrideList) {
for (const json_t* Item = json_getChild(OverrideList); Item != nullptr; Item = json_getSibling(Item)) {
const char* AppPattern = json_getName(Item);
// Find the first match, then break
if (FEXCore::Utils::Wildcard::Matches(AppPattern, *AppName)) {
ConfigBlocks.push_back(Item);
break;
}
}
}
}
if (!ConfigString) {
LogMan::Msg::EFmt("JSON file '{}': Couldn't get value for config item '{}'", Config, ConfigName);
return;
for (auto ConfigBlock : ConfigBlocks) {
for (const json_t* ConfigItem = json_getChild(ConfigBlock); ConfigItem != nullptr; ConfigItem = json_getSibling(ConfigItem)) {
const char* ConfigName = json_getName(ConfigItem);
const char* ConfigString = json_getValue(ConfigItem);
if (!ConfigString) {
LogMan::Msg::EFmt("JSON file '{}': Couldn't get value for config item '{}'", Config, ConfigName);
return;
}
Func(ConfigName, ConfigString);
}
Func(ConfigName, ConfigString);
}
}
} // namespace JSON
@@ -167,22 +183,24 @@ protected:
class MainLoader final : public OptionMapper {
public:
explicit MainLoader(FEXCore::Config::LayerType Type);
explicit MainLoader(fextl::string ConfigFile);
explicit MainLoader(FEXCore::Config::LayerType Type, std::optional<fextl::string> AppName = std::nullopt);
explicit MainLoader(fextl::string ConfigFile, std::optional<fextl::string> AppName = std::nullopt);
explicit MainLoader(FEXCore::Config::LayerType Type, std::string_view ConfigFile);
void Load() override;
private:
std::optional<fextl::string> AppName;
fextl::string Config;
};
class AppLoader final : public OptionMapper {
public:
explicit AppLoader(const fextl::string& Filename, FEXCore::Config::LayerType Type);
explicit AppLoader(const fextl::string& AppName, FEXCore::Config::LayerType Type);
void Load();
private:
const fextl::string AppName;
fextl::string Config;
};
@@ -221,12 +239,14 @@ void OptionMapper::MapNameToOption(const char* ConfigName, const char* ConfigStr
#include <FEXCore/Config/ConfigOptions.inl>
}
MainLoader::MainLoader(FEXCore::Config::LayerType Type)
MainLoader::MainLoader(FEXCore::Config::LayerType Type, std::optional<fextl::string> AppName)
: OptionMapper(Type)
, AppName {AppName}
, Config {FEXCore::Config::GetConfigFileLocation(Type == FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN)} {}
MainLoader::MainLoader(fextl::string ConfigFile)
MainLoader::MainLoader(fextl::string ConfigFile, std::optional<fextl::string> AppName)
: OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
, AppName {AppName}
, Config {std::move(ConfigFile)} {}
@@ -236,13 +256,14 @@ MainLoader::MainLoader(FEXCore::Config::LayerType Type, std::string_view ConfigF
void MainLoader::Load() {
SetCurrentConfigFile(Config);
JSON::LoadJSonConfig(Config, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
JSON::LoadJSonConfig(Config, AppName, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
}
AppLoader::AppLoader(const fextl::string& Filename, FEXCore::Config::LayerType Type)
: OptionMapper(Type) {
AppLoader::AppLoader(const fextl::string& AppName, FEXCore::Config::LayerType Type)
: OptionMapper(Type)
, AppName {AppName} {
const bool Global = Type == FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP || Type == FEXCore::Config::LayerType::LAYER_GLOBAL_APP;
Config = FEXCore::Config::GetApplicationConfig(Filename, Global);
Config = FEXCore::Config::GetApplicationConfig(AppName, Global);
// Immediately load so we can reload the meta layer
Load();
@@ -250,7 +271,7 @@ AppLoader::AppLoader(const fextl::string& Filename, FEXCore::Config::LayerType T
void AppLoader::Load() {
SetCurrentConfigFile(Config);
JSON::LoadJSonConfig(Config, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
JSON::LoadJSonConfig(Config, AppName, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
}
EnvLoader::EnvLoader(char* const _envp[])
@@ -320,11 +341,11 @@ fextl::unique_ptr<FEXCore::Config::Layer> CreateGlobalMainLayer() {
return fextl::make_unique<MainLoader>(FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN);
}
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File) {
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File, std::optional<fextl::string> AppName) {
if (File) {
return fextl::make_unique<MainLoader>(*File);
return fextl::make_unique<MainLoader>(*File, std::move(AppName));
} else {
return fextl::make_unique<MainLoader>(FEXCore::Config::LayerType::LAYER_MAIN);
return fextl::make_unique<MainLoader>(FEXCore::Config::LayerType::LAYER_MAIN, std::move(AppName));
}
}
@@ -461,7 +482,7 @@ void LoadConfig(fextl::string ProgramName, char** const envp, const PortableInfo
if (!IsPortable) {
FEXCore::Config::AddLayer(CreateGlobalMainLayer());
}
FEXCore::Config::AddLayer(CreateMainLayer());
FEXCore::Config::AddLayer(CreateMainLayer(nullptr, ProgramName.empty() ? std::nullopt : std::optional {ProgramName}));
if (!ProgramName.empty()) {
if (!IsPortable) {
+1 -1
View File
@@ -81,7 +81,7 @@ fextl::unique_ptr<FEXCore::Config::Layer> CreateGlobalMainLayer();
*
* @return unique_ptr for that layer
*/
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File = nullptr);
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File = nullptr, std::optional<fextl::string> AppName = std::nullopt);
fextl::unique_ptr<FEXCore::Config::Layer> CreateUserOverrideLayer(std::string_view AppConfig);
/**
+3 -2
View File
@@ -6,9 +6,10 @@ set(TESTS
FileMappingBaseAddress
Filesystem
InterruptableConditionVariable
StringUtils)
StringUtils
WildcardMatcher)
list(APPEND LIBS Common FEXCore JemallocLibs)
list(APPEND LIBS Common FEXCore FEXCore_Base JemallocLibs)
foreach(API_TEST ${TESTS})
add_executable(${API_TEST} ${API_TEST}.cpp)
+48
View File
@@ -0,0 +1,48 @@
#include <FEXCore/fextl/string.h>
#include <FEXCore/Utils/Allocator.h>
#include <FEXCore/Utils/WildcardMatcher.h>
#include <catch2/catch_test_macros.hpp>
using namespace FEXCore::Utils::Wildcard;
TEST_CASE("Singular regex") {
CHECK(Matches("a", "a"));
CHECK(Matches("a*", "a*"));
CHECK(Matches("a*", "aaaaaaa"));
}
TEST_CASE("Concat regex") {
CHECK(Matches("aaa", "aaa"));
CHECK(Matches("ab", "ab"));
CHECK(!Matches("a", "ab"));
CHECK(!Matches("ab", "a"));
}
TEST_CASE("Wildcard beginning end") {
CHECK(Matches("a*", "a"));
CHECK(Matches("*a", "a"));
CHECK(Matches("*a*", "a"));
}
TEST_CASE("Wildcard middle") {
CHECK(Matches("test*pattern", "test__pattern"));
}
TEST_CASE("Wildcard mult") {
CHECK(Matches("test*pattern*more", "test__pattern__more"));
CHECK(Matches("test**pattern", "test_pattern"));
}
TEST_CASE("Wildcard regex simple") {
CHECK(Matches("*", ""));
CHECK(Matches("*", "setup.json"));
CHECK(Matches("test*pattern", "test__pattern"));
CHECK(!Matches("setup.*", "setupjson"));
CHECK(Matches("setup*", "setup.json"));
CHECK(Matches("setup*", "setup/setup.json"));
CHECK(Matches("*setup*", "setup/setup.json"));
}
TEST_CASE("FEX regex") {
CHECK(Matches("*Config*", "/home/ubuntu/.fex-emu/Config.json"));
CHECK(Matches("*Config.json", "/home/ubuntu/.fex-emu/Config.json"));
}