#include "config/config.hpp" #include "common/exception.hpp" #include "config/default_conf.hpp" #include "utils/utils.hpp" #define TOML_ENABLE_FORMATTERS 0 // NOLINT #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Config; GlobalConfiguration Config::globalConf{}; std::optional Config::currentConf{}; extern "C" __attribute__((visibility("default"))) void lsfg_get_active_conf(Configuration *cfg) { *cfg = Config::activeConf; } namespace { /// Turn a string into a VkPresentModeKHR enum value. VkPresentModeKHR into_present(const std::string& mode) { if (mode == "fifo" || mode == "vsync") return VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; if (mode == "mailbox") return VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR; if (mode == "immediate") return VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR; return VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; } } void Config::updateConfig( const std::string& file, const std::pair& name) { // process unchecked legacy environment variables if (std::getenv("LSFG_LEGACY")) { const char* dll = std::getenv("LSFG_DLL_PATH"); if (dll) globalConf.dll = std::string(dll); currentConf.emplace(); const char* multiplier = std::getenv("LSFG_MULTIPLIER"); if (multiplier) currentConf->multiplier = std::stoul(multiplier); const char* flow_scale = std::getenv("LSFG_FLOW_SCALE"); if (flow_scale) currentConf->flowScale = std::stof(flow_scale); const char* performance = std::getenv("LSFG_PERFORMANCE_MODE"); if (performance) currentConf->performance = std::string(performance) == "1"; const char* hdr = std::getenv("LSFG_HDR_MODE"); if (hdr) currentConf->hdr = std::string(hdr) == "1"; const char* e_present = std::getenv("LSFG_EXPERIMENTAL_PRESENT_MODE"); if (e_present) currentConf->e_present = into_present(std::string(e_present)); return; } // ensure configuration file exists if (!std::filesystem::exists(file)) { std::cerr << "lsfg-vk: Placing default configuration file at " << file << '\n'; const auto parent = std::filesystem::path(file).parent_path(); if (!std::filesystem::exists(parent)) if (!std::filesystem::create_directories(parent)) throw std::runtime_error("Unable to create configuration directory at " + parent.string()); std::ofstream out(file); if (!out.is_open()) throw std::runtime_error("Unable to create configuration file at " + file); out << DEFAULT_CONFIG; out.close(); } // parse config file toml::table config{}; try { config = toml::parse_file(file); const auto* version = config.get_as>("version"); if (!version || *version != 1) throw std::runtime_error("Configuration file version is not supported, expected 1"); } catch (const std::exception& e) { throw LSFG::rethrowable_error("Unable to parse configuration file", e); } // parse global configuration Config::globalConf = { .config_file = file, .timestamp = std::filesystem::last_write_time(file) }; if (const auto* global = config.get_as("global")) { if (const auto* val = global->get_as>("dll")) globalConf.dll = val->get(); if (const auto* val = global->get_as>("no_fp16")) globalConf.no_fp16 = val->get(); } // parse game-specific configuration std::optional gameConf; if (const auto* games = config["game"].as_array()) { for (auto&& elem : *games) { if (!elem.is_table()) throw std::runtime_error("Invalid game configuration entry"); const auto* game = elem.as_table(); const auto* exe = game->at("exe").value_or("?"); if (!name.first.ends_with(exe) && name.second != exe) continue; gameConf = Config::currentConf; if (!gameConf.has_value()) gameConf.emplace(); if (const auto* val = game->get_as>("multiplier")) gameConf->multiplier = static_cast(val->get()); if (const auto* val = game->get_as>("flow_scale")) gameConf->flowScale = static_cast(val->get()); if (const auto* val = game->get_as>("performance_mode")) gameConf->performance = val->get(); if (const auto* val = game->get_as>("hdr_mode")) gameConf->hdr = val->get(); if (const auto* val = game->get_as>("experimental_present_mode")) gameConf->e_present = into_present(val->get()); break; } } if (!gameConf.has_value()) { Config::currentConf.reset(); std::cerr << "lsfg-vk: Configuration entry disappeared, disabling.\n"; return; } Config::currentConf = *gameConf; // print updated config info std::cerr << "lsfg-vk: Loaded configuration for " << name.first << ":\n"; if (!globalConf.dll.empty()) std::cerr << " Using DLL from: " << globalConf.dll << '\n'; if (globalConf.no_fp16) std::cerr << " FP16 Acceleration: Force-disabled\n"; std::cerr << " Multiplier: " << gameConf->multiplier << '\n'; std::cerr << " Flow Scale: " << gameConf->flowScale << '\n'; std::cerr << " Performance Mode: " << (gameConf->performance ? "Enabled" : "Disabled") << '\n'; std::cerr << " HDR Mode: " << (gameConf->hdr ? "Enabled" : "Disabled") << '\n'; if (gameConf->e_present != 2) std::cerr << " ! Present Mode: " << gameConf->e_present << '\n'; } bool Config::checkStatus() { // check if config is up-to-date auto& globalConf = Config::globalConf; if (globalConf.config_file.empty()) return true; if (!std::filesystem::exists(globalConf.config_file)) return true; // ignore deletion if (std::filesystem::last_write_time(globalConf.config_file) == globalConf.timestamp) return true; // reload config std::cerr << "lsfg-vk: Rereading configuration, as it is no longer valid.\n"; std::this_thread::sleep_for(std::chrono::milliseconds(73)); try { Config::updateConfig(Utils::getConfigFile(), Utils::getProcessName()); } catch (const std::exception& e) { std::cerr << "lsfg-vk: Failed to update configuration, continuing using old:\n"; std::cerr << "- " << e.what() << '\n'; } return false; }