Config.yaml + Screen::Config desacoblat via Options (v0.3.0)
This commit is contained in:
1 parent
de5a670fdb
commit
0348acb0eb
8 files changed
+335
-16
No files matched your search
@@ -0,0 +1,183 @@
|
||||
#include "game/options.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "external/fkyaml_node.hpp"
|
||||
#include "utils/defines.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
auto scalingModeToString(Defaults::ScalingMode mode) -> const char* {
|
||||
switch (mode) {
|
||||
case Defaults::ScalingMode::DISABLED:
|
||||
return "disabled";
|
||||
case Defaults::ScalingMode::STRETCH:
|
||||
return "stretch";
|
||||
case Defaults::ScalingMode::LETTERBOX:
|
||||
return "letterbox";
|
||||
case Defaults::ScalingMode::OVERSCAN:
|
||||
return "overscan";
|
||||
case Defaults::ScalingMode::INTEGER:
|
||||
return "integer";
|
||||
}
|
||||
return "letterbox";
|
||||
}
|
||||
|
||||
auto scalingModeFromString(const std::string& text) -> Defaults::ScalingMode {
|
||||
if (text == "disabled") { return Defaults::ScalingMode::DISABLED; }
|
||||
if (text == "stretch") { return Defaults::ScalingMode::STRETCH; }
|
||||
if (text == "overscan") { return Defaults::ScalingMode::OVERSCAN; }
|
||||
if (text == "integer") { return Defaults::ScalingMode::INTEGER; }
|
||||
return Defaults::ScalingMode::LETTERBOX;
|
||||
}
|
||||
|
||||
auto filterToString(Defaults::TextureFilter filter) -> const char* {
|
||||
return filter == Defaults::TextureFilter::LINEAR ? "linear" : "nearest";
|
||||
}
|
||||
|
||||
auto filterFromString(const std::string& text) -> Defaults::TextureFilter {
|
||||
return text == "linear" ? Defaults::TextureFilter::LINEAR : Defaults::TextureFilter::NEAREST;
|
||||
}
|
||||
|
||||
auto readFileOrEmpty(const std::string& path) -> std::string {
|
||||
std::ifstream file(path);
|
||||
if (!file) { return {}; }
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
auto writeFile(const std::string& path, const std::string& content) -> bool {
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
if (!file) { return false; }
|
||||
file.write(content.data(), static_cast<std::streamsize>(content.size()));
|
||||
return file.good();
|
||||
}
|
||||
|
||||
void parseWindow(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("window")) { return; }
|
||||
const auto& node = yaml["window"];
|
||||
if (node.contains("zoom")) { Options::window.zoom = node["zoom"].get_value<int>(); }
|
||||
if (node.contains("fullscreen")) { Options::window.fullscreen = node["fullscreen"].get_value<bool>(); }
|
||||
}
|
||||
|
||||
void parseVideo(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("video")) { return; }
|
||||
const auto& node = yaml["video"];
|
||||
if (node.contains("vsync")) { Options::video.vsync = node["vsync"].get_value<bool>(); }
|
||||
if (node.contains("scaling_mode")) {
|
||||
Options::video.scaling_mode = scalingModeFromString(node["scaling_mode"].get_value<std::string>());
|
||||
}
|
||||
if (node.contains("texture_filter")) {
|
||||
Options::video.texture_filter = filterFromString(node["texture_filter"].get_value<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
void parseMusic(const fkyaml::node& audio_node) {
|
||||
if (!audio_node.contains("music")) { return; }
|
||||
const auto& node = audio_node["music"];
|
||||
if (node.contains("enabled")) { Options::audio.music.enabled = node["enabled"].get_value<bool>(); }
|
||||
if (node.contains("volume")) { Options::audio.music.volume = node["volume"].get_value<float>(); }
|
||||
}
|
||||
|
||||
void parseSound(const fkyaml::node& audio_node) {
|
||||
if (!audio_node.contains("sound")) { return; }
|
||||
const auto& node = audio_node["sound"];
|
||||
if (node.contains("enabled")) { Options::audio.sound.enabled = node["enabled"].get_value<bool>(); }
|
||||
if (node.contains("volume")) { Options::audio.sound.volume = node["volume"].get_value<float>(); }
|
||||
}
|
||||
|
||||
void parseAudio(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("audio")) { return; }
|
||||
const auto& node = yaml["audio"];
|
||||
if (node.contains("enabled")) { Options::audio.enabled = node["enabled"].get_value<bool>(); }
|
||||
if (node.contains("volume")) { Options::audio.volume = node["volume"].get_value<float>(); }
|
||||
parseMusic(node);
|
||||
parseSound(node);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Options {
|
||||
|
||||
void setConfigFile(const std::string& path) {
|
||||
config_file_path = path;
|
||||
}
|
||||
|
||||
auto loadFromFile() -> bool {
|
||||
if (config_file_path.empty()) { return false; }
|
||||
|
||||
const std::string CONTENT = readFileOrEmpty(config_file_path);
|
||||
if (CONTENT.empty()) {
|
||||
// Primera execució: creem el fitxer amb els valors per defecte.
|
||||
return saveToFile();
|
||||
}
|
||||
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(CONTENT);
|
||||
|
||||
std::string file_version;
|
||||
if (yaml.contains("version")) {
|
||||
file_version = yaml["version"].get_value<std::string>();
|
||||
}
|
||||
// Sense migració per ara: si la versió no coincideix, reescrivim
|
||||
// amb els defaults de la versió nova.
|
||||
if (file_version != Texts::VERSION) { return saveToFile(); }
|
||||
config_version = file_version;
|
||||
|
||||
parseWindow(yaml);
|
||||
parseVideo(yaml);
|
||||
parseAudio(yaml);
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Options: error parsejant config.yaml: " << e.what() << '\n';
|
||||
std::cerr << "Options: reescrivint el fitxer amb defaults.\n";
|
||||
return saveToFile();
|
||||
}
|
||||
}
|
||||
|
||||
auto saveToFile() -> bool {
|
||||
if (config_file_path.empty()) { return false; }
|
||||
|
||||
config_version = Texts::VERSION;
|
||||
|
||||
std::ostringstream out;
|
||||
out << "# Configuració d'AEE 2026. Generat automàticament; editable a mà.\n";
|
||||
out << "version: \"" << config_version << "\"\n\n";
|
||||
|
||||
out << "window:\n";
|
||||
out << " zoom: " << window.zoom << "\n";
|
||||
out << " fullscreen: " << (window.fullscreen ? "true" : "false") << "\n\n";
|
||||
|
||||
out << "video:\n";
|
||||
out << " vsync: " << (video.vsync ? "true" : "false") << "\n";
|
||||
out << " scaling_mode: \"" << scalingModeToString(video.scaling_mode) << "\"\n";
|
||||
out << " texture_filter: \"" << filterToString(video.texture_filter) << "\"\n\n";
|
||||
|
||||
out << "audio:\n";
|
||||
out << " enabled: " << (audio.enabled ? "true" : "false") << "\n";
|
||||
out << " volume: " << audio.volume << "\n";
|
||||
out << " music:\n";
|
||||
out << " enabled: " << (audio.music.enabled ? "true" : "false") << "\n";
|
||||
out << " volume: " << audio.music.volume << "\n";
|
||||
out << " sound:\n";
|
||||
out << " enabled: " << (audio.sound.enabled ? "true" : "false") << "\n";
|
||||
out << " volume: " << audio.sound.volume << "\n";
|
||||
|
||||
return writeFile(config_file_path, out.str());
|
||||
}
|
||||
|
||||
auto buildScreenConfig() -> Screen::Config {
|
||||
Screen::Config config;
|
||||
config.zoom = window.zoom;
|
||||
config.fullscreen = window.fullscreen;
|
||||
config.vsync = video.vsync;
|
||||
config.scaling_mode = video.scaling_mode;
|
||||
config.texture_filter = video.texture_filter;
|
||||
return config;
|
||||
}
|
||||
|
||||
} // namespace Options
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/rendering/screen.hpp"
|
||||
#include "utils/defaults.hpp"
|
||||
|
||||
// Gestor de configuració del joc. És l'ÚNIC lloc que coneix YAML i la ruta
|
||||
// del config.yaml. Els subsistemes (Screen, Audio, ...) reben structs tipats
|
||||
// a través dels build*Config(), de manera que no queden acoblats a aquest
|
||||
// namespace ni al format concret del fitxer.
|
||||
namespace Options {
|
||||
|
||||
struct Window {
|
||||
int zoom{Defaults::Window::ZOOM};
|
||||
bool fullscreen{Defaults::Window::FULLSCREEN};
|
||||
};
|
||||
|
||||
struct Video {
|
||||
bool vsync{Defaults::Video::VSYNC};
|
||||
Defaults::ScalingMode scaling_mode{Defaults::Video::SCALING_MODE};
|
||||
Defaults::TextureFilter texture_filter{Defaults::Video::TEXTURE_FILTER};
|
||||
};
|
||||
|
||||
struct Music {
|
||||
bool enabled{Defaults::Audio::MUSIC_ENABLED};
|
||||
float volume{Defaults::Audio::MUSIC_VOLUME};
|
||||
};
|
||||
|
||||
struct Sound {
|
||||
bool enabled{Defaults::Audio::SOUND_ENABLED};
|
||||
float volume{Defaults::Audio::SOUND_VOLUME};
|
||||
};
|
||||
|
||||
struct Audio {
|
||||
bool enabled{Defaults::Audio::ENABLED};
|
||||
float volume{Defaults::Audio::VOLUME};
|
||||
Music music{};
|
||||
Sound sound{};
|
||||
};
|
||||
|
||||
// --- Estat global (inline vars per a que una sola definició viva a tot
|
||||
// el programa sense necessitar un .cpp extra per a cada una) ---
|
||||
inline std::string config_version{};
|
||||
inline std::string config_file_path{};
|
||||
inline Window window{};
|
||||
inline Video video{};
|
||||
inline Audio audio{};
|
||||
|
||||
// --- API pública ---
|
||||
void setConfigFile(const std::string& path);
|
||||
auto loadFromFile() -> bool;
|
||||
auto saveToFile() -> bool;
|
||||
|
||||
// --- Builders per als subsistemes ---
|
||||
[[nodiscard]] auto buildScreenConfig() -> Screen::Config;
|
||||
|
||||
} // namespace Options
|
||||
Reference in new issue
Block a user