initial config file

This commit is contained in:
PancakeTAS 2025-07-16 20:32:23 +02:00 committed by Pancake
parent 455e114aef
commit a5ea1893ab
8 changed files with 145 additions and 15 deletions

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "thirdparty/dxbc"]
path = thirdparty/dxbc
url = https://github.com/PancakeTAS/dxbc.git
[submodule "thirdparty/toml11"]
path = thirdparty/toml11
url = https://github.com/ToruNiina/toml11

View file

@ -16,8 +16,10 @@ add_compile_options(-fPIC
add_subdirectory(thirdparty/dxbc EXCLUDE_FROM_ALL)
add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL)
add_subdirectory(thirdparty/toml11 EXCLUDE_FROM_ALL)
add_subdirectory(framegen)
# main project
project(lsfg-vk
VERSION 0.0.1
@ -41,7 +43,10 @@ set_target_properties(lsfg-vk PROPERTIES
target_include_directories(lsfg-vk
PRIVATE include)
target_link_libraries(lsfg-vk PRIVATE
lsfg-vk-framegen pe-parse dxbc vulkan)
lsfg-vk-framegen pe-parse dxbc toml11 vulkan)
get_target_property(TOML11_INCLUDE_DIRS toml11 INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(lsfg-vk SYSTEM PRIVATE ${TOML11_INCLUDE_DIRS})
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set_target_properties(lsfg-vk PROPERTIES

View file

@ -2,6 +2,7 @@
#include <vulkan/vulkan_core.h>
#include <exception>
#include <stdexcept>
#include <string>
@ -31,4 +32,31 @@ namespace LSFG {
VkResult result;
};
/// Simple exception class for stacking errors.
class rethrowable_error : public std::runtime_error {
public:
///
/// Construct a new rethrowable_error with a message.
///
/// @param message The error message.
/// @param exe The original exception to rethrow.
///
explicit rethrowable_error(const std::string& message,
const std::exception& exe);
/// Get the exception as a string.
[[nodiscard]] const char* what() const noexcept override {
return message.c_str();
}
// Trivially copyable, moveable and destructible
rethrowable_error(const rethrowable_error&) = default;
rethrowable_error(rethrowable_error&&) = default;
rethrowable_error& operator=(const rethrowable_error&) = default;
rethrowable_error& operator=(rethrowable_error&&) = default;
~rethrowable_error() noexcept override;
private:
std::string message;
};
}

View file

@ -2,6 +2,7 @@
#include <vulkan/vulkan_core.h>
#include <exception>
#include <stdexcept>
#include <cstdint>
#include <format>
@ -14,3 +15,10 @@ vulkan_error::vulkan_error(VkResult result, const std::string& message)
result(result) {}
vulkan_error::~vulkan_error() noexcept = default;
rethrowable_error::rethrowable_error(const std::string& message, const std::exception& exe)
: std::runtime_error(message) {
this->message = std::format("{}\n- {}", message, exe.what());
}
rethrowable_error::~rethrowable_error() noexcept = default;

View file

@ -8,7 +8,7 @@
namespace Config {
/// lsfg-vk configuration.
/// lsfg-vk configuration
struct Configuration {
/// Whether lsfg-vk should be loaded in the first place.
bool enable{false};

View file

@ -1,15 +1,99 @@
#include "config/config.hpp"
#include "common/exception.hpp"
#include <filesystem>
#include <optional>
#include <toml11/find.hpp>
#include <toml11/parser.hpp>
#include <toml.hpp>
#include <unordered_map>
#include <string_view>
#include <exception>
#include <stdexcept>
#include <cstddef>
#include <utility>
#include <atomic>
#include <memory>
#include <string>
using namespace Config;
const Configuration defaultConf{
.enable = false
};
namespace {
Configuration globalConf{};
std::optional<std::unordered_map<std::string, Configuration>> gameConfs;
}
bool Config::loadAndWatchConfig(const std::string& file) {
// parse config file
toml::value toml{};
if (std::filesystem::exists(file)) {
try {
toml = toml::parse(file);
} catch (const std::exception& e) {
throw LSFG::rethrowable_error("Unable to parse configuration file", e);
}
}
// parse global configuration
auto& global = globalConf;
const toml::value globalTable = toml::find_or_default<toml::table>(toml, "global");
global.enable = toml::find_or(globalTable, "enable", false);
global.dll = toml::find_or(globalTable, "dll", std::string());
global.multiplier = toml::find_or(globalTable, "multiplier", size_t(2));
global.flowScale = toml::find_or(globalTable, "flow_scale", 1.0F);
global.performance = toml::find_or(globalTable, "performance_mode", false);
global.hdr = toml::find_or(globalTable, "hdr_mode", false);
global.valid = std::make_shared<std::atomic_bool>(true);
// validate global configuration
if (global.multiplier < 2)
throw std::runtime_error("Multiplier cannot be less than 2");
if (global.flowScale < 0.25F || global.flowScale > 1.0F)
throw std::runtime_error("Flow scale must be between 0.25 and 1.0");
// parse game-specific configuration
auto& games = gameConfs.emplace();
const toml::value gamesList = toml::find_or_default<toml::array>(toml, "game");
for (const auto& gameTable : gamesList.as_array()) {
if (!gameTable.is_table())
throw std::runtime_error("Invalid game configuration entry");
if (!gameTable.contains("exe"))
throw std::runtime_error("Game override missing 'exe' field");
const std::string exe = toml::find<std::string>(gameTable, "exe");
Configuration game{
.enable = toml::find_or(gameTable, "enable", global.enable),
.dll = toml::find_or(gameTable, "dll", global.dll),
.multiplier = toml::find_or(gameTable, "multiplier", global.multiplier),
.flowScale = toml::find_or(gameTable, "flow_scale", global.flowScale),
.performance = toml::find_or(gameTable, "performance_mode", global.performance),
.hdr = toml::find_or(gameTable, "hdr_mode", global.hdr),
.valid = global.valid // only need a single validity flag
};
// validate the configuration
if (game.multiplier < 2)
throw std::runtime_error("Multiplier cannot be less than 2");
if (game.flowScale < 0.25F || game.flowScale > 1.0F)
throw std::runtime_error("Flow scale must be between 0.25 and 1.0");
games[exe] = std::move(game);
}
// prepare config watcher
// (TODO)
return false;
}
Configuration Config::getConfig(std::string_view name) {
return defaultConf;
if (name.empty() || !gameConfs.has_value())
return globalConf;
const auto& games = *gameConfs;
auto it = games.find(std::string(name));
if (it != games.end())
return it->second;
return globalConf;
}

View file

@ -46,8 +46,8 @@ namespace {
return{configFile};
const char* homePath = std::getenv("HOME");
if (homePath && *homePath != '\0')
return std::string(homePath) + "/.config/lsfg-vk.conf";
return "/etc/lsfg-vk.conf";
return std::string(homePath) + "/.config/lsfg-vk.toml";
return "/etc/lsfg-vk.toml";
}
__attribute__((constructor)) void lsfgvk_init() {
@ -57,6 +57,7 @@ namespace {
exit(0);
}
// TODO: health check, maybe?
std::cerr << "lsfg-vk: This library is not meant to be preloaded, unless you are running a benchmark.\n";
exit(1);
}
@ -66,8 +67,8 @@ namespace {
try {
Config::loadAndWatchConfig(file);
} catch (const std::exception& e) {
std::cerr << "lsfg-vk: Unable to read configuration file, exiting." << '\n';
std::cerr << e.what() << '\n';
std::cerr << "lsfg-vk: An error occured while trying to parse the configuration, exiting:" << '\n';
std::cerr << "- " << e.what() << '\n';
exit(0);
}
@ -87,10 +88,10 @@ namespace {
// print config
std::cerr << "lsfg-vk: Loaded configuration for " << name << ":\n";
std::cerr << "lsfg-vk: Using DLL from: " << conf.dll << '\n';
std::cerr << "lsfg-vk: Multiplier: " << conf.multiplier << '\n';
std::cerr << "lsfg-vk: Flow Scale: " << conf.flowScale << '\n';
std::cerr << "lsfg-vk: Performance Mode: " << (conf.performance ? "Enabled" : "Disabled") << '\n';
std::cerr << "lsfg-vk: HDR: " << (conf.hdr ? "Enabled" : "Disabled") << '\n';
if (!conf.dll.empty()) std::cerr << " Using DLL from: " << conf.dll << '\n';
std::cerr << " Multiplier: " << conf.multiplier << '\n';
std::cerr << " Flow Scale: " << conf.flowScale << '\n';
std::cerr << " Performance Mode: " << (conf.performance ? "Enabled" : "Disabled") << '\n';
std::cerr << " HDR Mode: " << (conf.hdr ? "Enabled" : "Disabled") << '\n';
}
}

1
thirdparty/toml11 vendored Submodule

@ -0,0 +1 @@
Subproject commit be08ba2be2a964edcdb3d3e3ea8d100abc26f286