Skip to content

Commit c6d2ce0

Browse files
authored
Merge pull request #5388 from Sonicadvance1/123
Config: Finish wiring up Regex app overrides
2 parents 5c34c57 + 4018d23 commit c6d2ce0

7 files changed

Lines changed: 133 additions & 31 deletions

File tree

FEXCore/Source/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ set(FEXCORE_BASE_SRCS
66
Utils/FileLoading.cpp
77
Utils/ForcedAssert.cpp
88
Utils/LogManager.cpp
9-
Utils/SpinWaitLock.cpp)
9+
Utils/SpinWaitLock.cpp
10+
Utils/WildcardMatcher.cpp)
1011

1112
if (NOT MINGW)
1213
list(APPEND FEXCORE_BASE_SRCS
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
#include <FEXCore/Utils/WildcardMatcher.h>
4+
5+
namespace FEXCore::Utils::Wildcard {
6+
static bool matchHelper(std::string_view pattern, std::string_view text, size_t p_idx, size_t t_idx) {
7+
if (p_idx == pattern.size()) {
8+
// Pattern exhausted
9+
return (t_idx == text.size());
10+
} else if (pattern[p_idx] == '*') {
11+
// Wildcard: Try matching zero characters, or one or more characters
12+
return matchHelper(pattern, text, p_idx + 1, t_idx) || (t_idx < text.size() && matchHelper(pattern, text, p_idx, t_idx + 1));
13+
} else {
14+
// Match normally
15+
return (t_idx < text.size() && pattern[p_idx] == text[t_idx] && matchHelper(pattern, text, p_idx + 1, t_idx + 1));
16+
}
17+
}
18+
19+
bool Matches(std::string_view pattern, std::string_view text) {
20+
return matchHelper(pattern, text, 0, 0);
21+
}
22+
23+
} // namespace FEXCore::Utils::Wildcard
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
#pragma once
4+
#include <string_view>
5+
6+
namespace FEXCore::Utils::Wildcard {
7+
bool Matches(std::string_view pattern, std::string_view text);
8+
} // namespace FEXCore::Utils::Wildcard

Source/Common/Config.cpp

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
#include <FEXCore/fextl/fmt.h>
88
#include <FEXCore/fextl/map.h>
99
#include <FEXCore/fextl/string.h>
10+
#include <FEXCore/fextl/vector.h>
1011
#include <FEXCore/Utils/Allocator.h>
1112
#include <FEXCore/Utils/FileLoading.h>
13+
#include <FEXCore/Utils/WildcardMatcher.h>
1214
#include <FEXHeaderUtils/Filesystem.h>
1315
#include <FEXHeaderUtils/SymlinkChecks.h>
14-
1516
#include <cstring>
1617
#include <fmt/format.h>
1718
#include <functional>
@@ -28,7 +29,8 @@
2829

2930
namespace FEX::Config {
3031
namespace JSON {
31-
static void LoadJSonConfig(const fextl::string& Config, std::function<void(const char* Name, const char* ConfigSring)> Func) {
32+
static void LoadJSonConfig(const fextl::string& Config, std::optional<fextl::string> AppName,
33+
std::function<void(const char* Name, const char* ConfigString)> Func) {
3234
fextl::vector<char> Data;
3335
if (!FEXCore::FileLoading::LoadFile(Data, Config)) {
3436
return;
@@ -48,21 +50,35 @@ namespace JSON {
4850
return;
4951
}
5052

51-
for (const json_t* ConfigItem = json_getChild(ConfigList); ConfigItem != nullptr; ConfigItem = json_getSibling(ConfigItem)) {
52-
const char* ConfigName = json_getName(ConfigItem);
53-
const char* ConfigString = json_getValue(ConfigItem);
53+
fextl::vector<const json_t*> ConfigBlocks;
54+
ConfigBlocks.push_back(ConfigList);
5455

55-
if (!ConfigName) {
56-
LogMan::Msg::EFmt("JSON file '{}': Couldn't get config name for an item", Config);
57-
return;
58-
}
56+
if (AppName) {
57+
const json_t* OverrideList = json_getProperty(json, "AppOverrides");
58+
if (OverrideList) {
59+
for (const json_t* Item = json_getChild(OverrideList); Item != nullptr; Item = json_getSibling(Item)) {
60+
const char* AppPattern = json_getName(Item);
5961

60-
if (!ConfigString) {
61-
LogMan::Msg::EFmt("JSON file '{}': Couldn't get value for config item '{}'", Config, ConfigName);
62-
return;
62+
// Find the first match, then break
63+
if (FEXCore::Utils::Wildcard::Matches(AppPattern, *AppName)) {
64+
ConfigBlocks.push_back(Item);
65+
break;
66+
}
67+
}
6368
}
69+
}
6470

65-
Func(ConfigName, ConfigString);
71+
for (auto ConfigBlock : ConfigBlocks) {
72+
for (const json_t* ConfigItem = json_getChild(ConfigBlock); ConfigItem != nullptr; ConfigItem = json_getSibling(ConfigItem)) {
73+
const char* ConfigName = json_getName(ConfigItem);
74+
const char* ConfigString = json_getValue(ConfigItem);
75+
76+
if (!ConfigString) {
77+
LogMan::Msg::EFmt("JSON file '{}': Couldn't get value for config item '{}'", Config, ConfigName);
78+
return;
79+
}
80+
Func(ConfigName, ConfigString);
81+
}
6682
}
6783
}
6884
} // namespace JSON
@@ -167,22 +183,24 @@ class OptionMapper : public FEXCore::Config::Layer {
167183

168184
class MainLoader final : public OptionMapper {
169185
public:
170-
explicit MainLoader(FEXCore::Config::LayerType Type);
171-
explicit MainLoader(fextl::string ConfigFile);
186+
explicit MainLoader(FEXCore::Config::LayerType Type, std::optional<fextl::string> AppName = std::nullopt);
187+
explicit MainLoader(fextl::string ConfigFile, std::optional<fextl::string> AppName = std::nullopt);
172188
explicit MainLoader(FEXCore::Config::LayerType Type, std::string_view ConfigFile);
173189

174190
void Load() override;
175191

176192
private:
193+
std::optional<fextl::string> AppName;
177194
fextl::string Config;
178195
};
179196

180197
class AppLoader final : public OptionMapper {
181198
public:
182-
explicit AppLoader(const fextl::string& Filename, FEXCore::Config::LayerType Type);
199+
explicit AppLoader(const fextl::string& AppName, FEXCore::Config::LayerType Type);
183200
void Load();
184201

185202
private:
203+
const fextl::string AppName;
186204
fextl::string Config;
187205
};
188206

@@ -221,12 +239,14 @@ void OptionMapper::MapNameToOption(const char* ConfigName, const char* ConfigStr
221239
#include <FEXCore/Config/ConfigOptions.inl>
222240
}
223241

224-
MainLoader::MainLoader(FEXCore::Config::LayerType Type)
242+
MainLoader::MainLoader(FEXCore::Config::LayerType Type, std::optional<fextl::string> AppName)
225243
: OptionMapper(Type)
244+
, AppName {AppName}
226245
, Config {FEXCore::Config::GetConfigFileLocation(Type == FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN)} {}
227246

228-
MainLoader::MainLoader(fextl::string ConfigFile)
247+
MainLoader::MainLoader(fextl::string ConfigFile, std::optional<fextl::string> AppName)
229248
: OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
249+
, AppName {AppName}
230250
, Config {std::move(ConfigFile)} {}
231251

232252

@@ -236,21 +256,22 @@ MainLoader::MainLoader(FEXCore::Config::LayerType Type, std::string_view ConfigF
236256

237257
void MainLoader::Load() {
238258
SetCurrentConfigFile(Config);
239-
JSON::LoadJSonConfig(Config, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
259+
JSON::LoadJSonConfig(Config, AppName, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
240260
}
241261

242-
AppLoader::AppLoader(const fextl::string& Filename, FEXCore::Config::LayerType Type)
243-
: OptionMapper(Type) {
262+
AppLoader::AppLoader(const fextl::string& AppName, FEXCore::Config::LayerType Type)
263+
: OptionMapper(Type)
264+
, AppName {AppName} {
244265
const bool Global = Type == FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP || Type == FEXCore::Config::LayerType::LAYER_GLOBAL_APP;
245-
Config = FEXCore::Config::GetApplicationConfig(Filename, Global);
266+
Config = FEXCore::Config::GetApplicationConfig(AppName, Global);
246267

247268
// Immediately load so we can reload the meta layer
248269
Load();
249270
}
250271

251272
void AppLoader::Load() {
252273
SetCurrentConfigFile(Config);
253-
JSON::LoadJSonConfig(Config, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
274+
JSON::LoadJSonConfig(Config, AppName, [this](const char* Name, const char* ConfigString) { MapNameToOption(Name, ConfigString); });
254275
}
255276

256277
EnvLoader::EnvLoader(char* const _envp[])
@@ -320,11 +341,11 @@ fextl::unique_ptr<FEXCore::Config::Layer> CreateGlobalMainLayer() {
320341
return fextl::make_unique<MainLoader>(FEXCore::Config::LayerType::LAYER_GLOBAL_MAIN);
321342
}
322343

323-
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File) {
344+
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File, std::optional<fextl::string> AppName) {
324345
if (File) {
325-
return fextl::make_unique<MainLoader>(*File);
346+
return fextl::make_unique<MainLoader>(*File, std::move(AppName));
326347
} else {
327-
return fextl::make_unique<MainLoader>(FEXCore::Config::LayerType::LAYER_MAIN);
348+
return fextl::make_unique<MainLoader>(FEXCore::Config::LayerType::LAYER_MAIN, std::move(AppName));
328349
}
329350
}
330351

@@ -461,7 +482,7 @@ void LoadConfig(fextl::string ProgramName, char** const envp, const PortableInfo
461482
if (!IsPortable) {
462483
FEXCore::Config::AddLayer(CreateGlobalMainLayer());
463484
}
464-
FEXCore::Config::AddLayer(CreateMainLayer());
485+
FEXCore::Config::AddLayer(CreateMainLayer(nullptr, ProgramName.empty() ? std::nullopt : std::optional {ProgramName}));
465486

466487
if (!ProgramName.empty()) {
467488
if (!IsPortable) {

Source/Common/Config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ fextl::unique_ptr<FEXCore::Config::Layer> CreateGlobalMainLayer();
8181
*
8282
* @return unique_ptr for that layer
8383
*/
84-
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File = nullptr);
84+
fextl::unique_ptr<FEXCore::Config::Layer> CreateMainLayer(const fextl::string* File = nullptr, std::optional<fextl::string> AppName = std::nullopt);
8585
fextl::unique_ptr<FEXCore::Config::Layer> CreateUserOverrideLayer(std::string_view AppConfig);
8686

8787
/**

unittests/APITests/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ set(TESTS
66
FileMappingBaseAddress
77
Filesystem
88
InterruptableConditionVariable
9-
StringUtils)
9+
StringUtils
10+
WildcardMatcher)
1011

11-
list(APPEND LIBS Common FEXCore JemallocLibs)
12+
list(APPEND LIBS Common FEXCore FEXCore_Base JemallocLibs)
1213

1314
foreach(API_TEST ${TESTS})
1415
add_executable(${API_TEST} ${API_TEST}.cpp)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <FEXCore/fextl/string.h>
2+
#include <FEXCore/Utils/Allocator.h>
3+
#include <FEXCore/Utils/WildcardMatcher.h>
4+
#include <catch2/catch_test_macros.hpp>
5+
6+
using namespace FEXCore::Utils::Wildcard;
7+
8+
TEST_CASE("Singular regex") {
9+
CHECK(Matches("a", "a"));
10+
CHECK(Matches("a*", "a*"));
11+
CHECK(Matches("a*", "aaaaaaa"));
12+
}
13+
14+
TEST_CASE("Concat regex") {
15+
CHECK(Matches("aaa", "aaa"));
16+
CHECK(Matches("ab", "ab"));
17+
CHECK(!Matches("a", "ab"));
18+
CHECK(!Matches("ab", "a"));
19+
}
20+
TEST_CASE("Wildcard beginning end") {
21+
CHECK(Matches("a*", "a"));
22+
CHECK(Matches("*a", "a"));
23+
CHECK(Matches("*a*", "a"));
24+
}
25+
TEST_CASE("Wildcard middle") {
26+
CHECK(Matches("test*pattern", "test__pattern"));
27+
}
28+
29+
TEST_CASE("Wildcard mult") {
30+
CHECK(Matches("test*pattern*more", "test__pattern__more"));
31+
CHECK(Matches("test**pattern", "test_pattern"));
32+
}
33+
34+
TEST_CASE("Wildcard regex simple") {
35+
CHECK(Matches("*", ""));
36+
CHECK(Matches("*", "setup.json"));
37+
CHECK(Matches("test*pattern", "test__pattern"));
38+
CHECK(!Matches("setup.*", "setupjson"));
39+
CHECK(Matches("setup*", "setup.json"));
40+
CHECK(Matches("setup*", "setup/setup.json"));
41+
CHECK(Matches("*setup*", "setup/setup.json"));
42+
}
43+
44+
45+
TEST_CASE("FEX regex") {
46+
CHECK(Matches("*Config*", "/home/ubuntu/.fex-emu/Config.json"));
47+
CHECK(Matches("*Config.json", "/home/ubuntu/.fex-emu/Config.json"));
48+
}

0 commit comments

Comments
 (0)