#include "game/options.hpp" #include #include // Para ifstream, ofstream #include // Para cout, cerr #include // Para string #include "external/fkyaml_node.hpp" // Para fkyaml::node #include "game/defaults.hpp" // Para GameDefaults::VERSION namespace Options { // Crea e inicializa las opciones del programa void init() { #ifdef _DEBUG console = true; #else console = false; #endif } // Establece la ruta del fichero de configuración void setConfigFile(const std::string& path) { config_file_path_ = path; } // Carga las opciones desde el fichero configurado auto loadFromFile() -> bool { // Versión esperada del fichero const std::string CONFIG_VERSION = GameDefaults::VERSION; version = ""; // Intenta abrir y leer el fichero std::ifstream file(config_file_path_); if (!file.good()) { if (console) { std::cout << "Config file not found, creating default: " << config_file_path_ << '\n'; } saveToFile(); return true; } // Lee todo el contenido del fichero std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); try { if (console) { std::cout << "Reading config file: " << config_file_path_ << '\n'; } // Parsea el YAML auto yaml = fkyaml::node::deserialize(content); // Lee la versión if (yaml.contains("version")) { version = yaml["version"].get_value(); } // Si la versión no coincide, crea un fichero nuevo con valores por defecto if (CONFIG_VERSION != version) { if (console) { std::cout << "Config version mismatch (expected: " << CONFIG_VERSION << ", got: " << version << "), creating new config\n"; } init(); saveToFile(); return true; } // Lee window if (yaml.contains("window")) { const auto& win = yaml["window"]; if (win.contains("zoom")) { int val = win["zoom"].get_value(); window.zoom = (val > 0) ? val : GameDefaults::WINDOW_ZOOM; } } // Lee video if (yaml.contains("video")) { const auto& vid = yaml["video"]; if (vid.contains("mode")) { video.fullscreen = vid["mode"].get_value(); } if (vid.contains("filter")) { int val = vid["filter"].get_value(); if (val == static_cast(Screen::Filter::NEAREST) || val == static_cast(Screen::Filter::LINEAR)) { video.filter = static_cast(val); } } if (vid.contains("shaders")) { video.shaders = vid["shaders"].get_value(); } if (vid.contains("vertical_sync")) { video.vertical_sync = vid["vertical_sync"].get_value(); } if (vid.contains("integer_scale")) { video.integer_scale = vid["integer_scale"].get_value(); } if (vid.contains("keep_aspect")) { video.keep_aspect = vid["keep_aspect"].get_value(); } if (vid.contains("palette")) { video.palette = vid["palette"].get_value(); } // Lee border if (vid.contains("border")) { const auto& border = vid["border"]; if (border.contains("enabled")) { video.border.enabled = border["enabled"].get_value(); } if (border.contains("width")) { float val = border["width"].get_value(); video.border.width = (val > 0) ? val : GameDefaults::BORDER_WIDTH; } if (border.contains("height")) { float val = border["height"].get_value(); video.border.height = (val > 0) ? val : GameDefaults::BORDER_HEIGHT; } } } // Lee controls if (yaml.contains("controls")) { const auto& ctrl = yaml["controls"]; if (ctrl.contains("left")) { int val = ctrl["left"].get_value(); controls.key_left = static_cast(val); } if (ctrl.contains("right")) { int val = ctrl["right"].get_value(); controls.key_right = static_cast(val); } if (ctrl.contains("jump")) { int val = ctrl["jump"].get_value(); controls.key_jump = static_cast(val); } } // Lee gamepad_controls if (yaml.contains("gamepad_controls")) { const auto& gp = yaml["gamepad_controls"]; if (gp.contains("left")) { gamepad_controls.button_left = gp["left"].get_value(); } if (gp.contains("right")) { gamepad_controls.button_right = gp["right"].get_value(); } if (gp.contains("jump")) { gamepad_controls.button_jump = gp["jump"].get_value(); } } if (console) { std::cout << "Config file loaded successfully\n\n"; } return true; } catch (const fkyaml::exception& e) { if (console) { std::cerr << "Error parsing YAML config: " << e.what() << '\n'; std::cerr << "Creating new config with defaults\n"; } init(); saveToFile(); return true; } } // Guarda las opciones al fichero configurado auto saveToFile() -> bool { // Crea el nodo YAML raíz como mapping fkyaml::node yaml(fkyaml::node_type::MAPPING); // Establece la versión yaml["version"] = GameDefaults::VERSION; // Window fkyaml::node window_node(fkyaml::node_type::MAPPING); window_node["zoom"] = window.zoom; yaml["window"] = window_node; // Video fkyaml::node video_node(fkyaml::node_type::MAPPING); video_node["mode"] = video.fullscreen; video_node["filter"] = static_cast(video.filter); video_node["shaders"] = video.shaders; video_node["vertical_sync"] = video.vertical_sync; video_node["integer_scale"] = video.integer_scale; video_node["keep_aspect"] = video.keep_aspect; video_node["palette"] = video.palette; // Video border fkyaml::node border_node(fkyaml::node_type::MAPPING); border_node["enabled"] = video.border.enabled; border_node["width"] = video.border.width; border_node["height"] = video.border.height; video_node["border"] = border_node; yaml["video"] = video_node; // Controls fkyaml::node controls_node(fkyaml::node_type::MAPPING); controls_node["left"] = static_cast(controls.key_left); controls_node["right"] = static_cast(controls.key_right); controls_node["jump"] = static_cast(controls.key_jump); yaml["controls"] = controls_node; // Gamepad controls fkyaml::node gamepad_node(fkyaml::node_type::MAPPING); gamepad_node["left"] = gamepad_controls.button_left; gamepad_node["right"] = gamepad_controls.button_right; gamepad_node["jump"] = gamepad_controls.button_jump; yaml["gamepad_controls"] = gamepad_node; // Serializa a string std::string yaml_content = fkyaml::node::serialize(yaml); // Abre el fichero para escritura std::ofstream file(config_file_path_); if (!file.is_open()) { if (console) { std::cerr << "Error: Unable to open file " << config_file_path_ << " for writing\n"; } return false; } if (console) { std::cout << "Writing config file: " << config_file_path_ << '\n'; } // Escribe el encabezado con comentarios file << "# JailDoctor's Dilemma - Configuration File\n"; file << "# This file is automatically generated and managed by the game\n"; file << "#\n"; file << "# Video filters: 0 = Nearest (pixel perfect), 1 = Linear (smooth)\n"; file << "# SDL_Scancode values for keyboard controls (see SDL documentation)\n"; file << "# Gamepad button values: 0-20+ = SDL_GamepadButton, 100 = L2, 101 = R2, 200+ = Axes\n"; file << "\n"; // Escribe el contenido YAML file << yaml_content; file.close(); if (console) { std::cout << "Config file saved successfully\n\n"; } return true; } } // namespace Options