DUP-01: pipeline unificat de presets PostFX i CrtPi a options.cpp

Afegits `loadPresetsFromFile<T>` i `savePresetsToFile<T>` a l'anonymous
namespace; els quatre entry-points passen a ser one-liners que injecten
parse/write/defaults per tipus. Abans eren dues còpies del mateix codi
diferenciades només pel tipus concret de preset. Els YAML regenerats són
byte-a-byte idèntics als anteriors (el formatter i els comentaris es
preserven).
This commit is contained in:
JailDesigner committed 2026-04-20 17:37:58 +02:00
1 parent ab7a79dc50
commit 505c20dabc
2 files changed
+95 -88

No files matched your search

+2 -2
View File
@@ -20,7 +20,7 @@ Els IDs es mantenen estables una vegada assignats. Cada entrada apunta al fitxer
|----|-------|-------| |----|-------|-------|
| HC-03 | Geometria del mapa cablejada a `map.hpp` (bloqueja "pirámide a pirámide") | DONE | | HC-03 | Geometria del mapa cablejada a `map.hpp` (bloqueja "pirámide a pirámide") | DONE |
| COUP-01 | `core/` inclou `game/ui/notification_presets.hpp` (dependència invertida) | DONE | | COUP-01 | `core/` inclou `game/ui/notification_presets.hpp` (dependència invertida) | DONE |
| DUP-01 | PostFX i CrtPi són dos pipelines duplicats a `options.cpp` | PENDING | | DUP-01 | PostFX i CrtPi són dos pipelines duplicats a `options.cpp` | DONE |
| PATCH-04 | Reescriure silenciosament `config.yaml` al fallar parseig o versió | PENDING | | PATCH-04 | Reescriure silenciosament `config.yaml` al fallar parseig o versió | PENDING |
| HC-01 | `SPEED_PX_PER_S`, caixa de col·lisió i rutes d'assets cablejades | PENDING | | HC-01 | `SPEED_PX_PER_S`, caixa de col·lisió i rutes d'assets cablejades | PENDING |
@@ -30,7 +30,7 @@ Els IDs es mantenen estables una vegada assignats. Cada entrada apunta al fitxer
| ID | Fitxer:línia | Troballa | Severitat | Estat | Notes | | ID | Fitxer:línia | Troballa | Severitat | Estat | Notes |
|----|--------------|----------|-----------|-------|-------| |----|--------------|----------|-----------|-------|-------|
| DUP-01 | `options.cpp:152-335,443-547` | PostFX i CrtPi tenen `default*Presets/parse*Preset/write*Preset/load*FromFile/save*ToFile/set*File` duplicats | Importante | PENDING | Template `PresetStore<T>` amb serialitzadors per camp | | DUP-01 | `options.cpp:152-335,443-547` | PostFX i CrtPi tenen `default*Presets/parse*Preset/write*Preset/load*FromFile/save*ToFile/set*File` duplicats | Importante | DONE | Afegits `loadPresetsFromFile<T>` i `savePresetsToFile<T>` a l'anonymous namespace; els 4 entry-points passen a ser one-liners que injecten parse/write/defaults per tipus. Els fitxers `postfx.yaml`/`crtpi.yaml` regenerats són idèntics a l'anterior. `std::cerr` manté el comportament — INCONS-02 el corregirà |
| DUP-02 | `player.cpp:100-140` | `snapToHorizontalLane` i `snapToVerticalLane` són el mateix algorisme amb eixos permutats | Importante | PENDING | Unificar en `snapToLane(Axis, delta_time)` | | DUP-02 | `player.cpp:100-140` | `snapToHorizontalLane` i `snapToVerticalLane` són el mateix algorisme amb eixos permutats | Importante | PENDING | Unificar en `snapToLane(Axis, delta_time)` |
| DUP-03 | `options.cpp:14-62` | Tres parelles `enumToString`/`enumFromString` amb la mateixa forma | Menor | PENDING | Taula `constexpr` + helpers genèrics | | DUP-03 | `options.cpp:14-62` | Tres parelles `enumToString`/`enumFromString` amb la mateixa forma | Menor | PENDING | Taula `constexpr` + helpers genèrics |
| DUP-04 | `options.cpp:79-149` | Cada `parse*` repeteix `if (node.contains) { ... get_value<T> }` | Menor | PENDING | Helper `readField<T>(node, key, dst)` | | DUP-04 | `options.cpp:79-149` | Cada `parse*` repeteix `if (node.contains) { ... get_value<T> }` | Menor | PENDING | Helper `readField<T>(node, key, dst)` |
+93 -86
View File
@@ -334,6 +334,73 @@ namespace {
out << " enable_sharper: " << (preset.enable_sharper ? "true" : "false") << "\n"; out << " enable_sharper: " << (preset.enable_sharper ? "true" : "false") << "\n";
} }
// Pipeline genèric de càrrega de presets: si el fitxer no existeix o és
// invàlid (no deserialitza, manca `presets:`, no és seqüència, queda
// buit), es restauren els defaults via `defaults_fn` i es reescriu via
// `save_fn`. Unifica PostFX i CrtPi — abans eren dues còpies del mateix
// codi que només diferien en el tipus concret de preset.
template <typename T>
auto loadPresetsFromFile(
const std::string& path,
std::vector<T>& presets,
std::vector<T> (*defaults_fn)(),
void (*parse_fn)(const fkyaml::node&, T&),
bool (*save_fn)(),
const char* kind_label) -> bool {
if (path.empty()) { return false; }
const std::string CONTENT = readFileOrEmpty(path);
if (CONTENT.empty()) {
presets = defaults_fn();
return save_fn();
}
try {
auto yaml = fkyaml::node::deserialize(CONTENT);
if (!yaml.contains("presets") || !yaml["presets"].is_sequence()) {
presets = defaults_fn();
return save_fn();
}
presets.clear();
for (const auto& preset_node : yaml["presets"]) {
T preset;
parse_fn(preset_node, preset);
presets.push_back(preset);
}
if (presets.empty()) {
presets = defaults_fn();
return save_fn();
}
return true;
} catch (const std::exception& e) {
std::cerr << "Options: error parsejant " << kind_label << ": " << e.what() << '\n';
std::cerr << "Options: reescrivint el fitxer amb presets per defecte.\n";
presets = defaults_fn();
return save_fn();
}
}
// Pipeline genèric d'escriptura: capçalera de comentari + `presets:` +
// una entrada per preset via `write_fn`. `config_key_note` és la segona
// línia de comentari que recorda amb quin camp de config.yaml han de
// coincidir els noms.
template <typename T>
auto savePresetsToFile(
const std::string& path,
const std::vector<T>& presets,
void (*write_fn)(std::ostringstream&, const T&),
const char* header_comment,
const char* config_key_note) -> bool {
if (path.empty()) { return false; }
std::ostringstream out;
out << "# " << header_comment << "\n";
out << "# " << config_key_note << "\n";
out << "presets:\n";
for (const auto& preset : presets) {
write_fn(out, preset);
}
return writeFile(path, out.str());
}
} // namespace } // namespace
namespace Options { namespace Options {
@@ -445,52 +512,22 @@ namespace Options {
} }
auto loadPostFXFromFile() -> bool { auto loadPostFXFromFile() -> bool {
if (postfx_file_path.empty()) { return false; } return loadPresetsFromFile<PostFXPreset>(
postfx_file_path,
const std::string CONTENT = readFileOrEmpty(postfx_file_path); postfx_presets,
if (CONTENT.empty()) { &defaultPostFXPresets,
postfx_presets = defaultPostFXPresets(); &parsePostFXPreset,
return savePostFXToFile(); &savePostFXToFile,
} "postfx.yaml");
try {
auto yaml = fkyaml::node::deserialize(CONTENT);
if (!yaml.contains("presets") || !yaml["presets"].is_sequence()) {
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
postfx_presets.clear();
for (const auto& preset_node : yaml["presets"]) {
PostFXPreset preset;
parsePostFXPreset(preset_node, preset);
postfx_presets.push_back(preset);
}
if (postfx_presets.empty()) {
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
return true;
} catch (const std::exception& e) {
std::cerr << "Options: error parsejant postfx.yaml: " << e.what() << '\n';
std::cerr << "Options: reescrivint el fitxer amb presets per defecte.\n";
postfx_presets = defaultPostFXPresets();
return savePostFXToFile();
}
} }
auto savePostFXToFile() -> bool { auto savePostFXToFile() -> bool {
if (postfx_file_path.empty()) { return false; } return savePresetsToFile<PostFXPreset>(
postfx_file_path,
std::ostringstream out; postfx_presets,
out << "# Presets PostFX per a AEE 2026. Editable a mà.\n"; &writePostFXPreset,
out << "# Els noms han de coincidir amb config.yaml > video.shader.current_postfx_preset.\n"; "Presets PostFX per a AEE 2026. Editable a mà.",
out << "presets:\n"; "Els noms han de coincidir amb config.yaml > video.shader.current_postfx_preset.");
for (const auto& preset : postfx_presets) {
writePostFXPreset(out, preset);
}
return writeFile(postfx_file_path, out.str());
} }
void setCrtPiFile(const std::string& path) { void setCrtPiFile(const std::string& path) {
@@ -498,52 +535,22 @@ namespace Options {
} }
auto loadCrtPiFromFile() -> bool { auto loadCrtPiFromFile() -> bool {
if (crtpi_file_path.empty()) { return false; } return loadPresetsFromFile<CrtPiPreset>(
crtpi_file_path,
const std::string CONTENT = readFileOrEmpty(crtpi_file_path); crtpi_presets,
if (CONTENT.empty()) { &defaultCrtPiPresets,
crtpi_presets = defaultCrtPiPresets(); &parseCrtPiPreset,
return saveCrtPiToFile(); &saveCrtPiToFile,
} "crtpi.yaml");
try {
auto yaml = fkyaml::node::deserialize(CONTENT);
if (!yaml.contains("presets") || !yaml["presets"].is_sequence()) {
crtpi_presets = defaultCrtPiPresets();
return saveCrtPiToFile();
}
crtpi_presets.clear();
for (const auto& preset_node : yaml["presets"]) {
CrtPiPreset preset;
parseCrtPiPreset(preset_node, preset);
crtpi_presets.push_back(preset);
}
if (crtpi_presets.empty()) {
crtpi_presets = defaultCrtPiPresets();
return saveCrtPiToFile();
}
return true;
} catch (const std::exception& e) {
std::cerr << "Options: error parsejant crtpi.yaml: " << e.what() << '\n';
std::cerr << "Options: reescrivint el fitxer amb presets per defecte.\n";
crtpi_presets = defaultCrtPiPresets();
return saveCrtPiToFile();
}
} }
auto saveCrtPiToFile() -> bool { auto saveCrtPiToFile() -> bool {
if (crtpi_file_path.empty()) { return false; } return savePresetsToFile<CrtPiPreset>(
crtpi_file_path,
std::ostringstream out; crtpi_presets,
out << "# Presets CRT-Pi per a AEE 2026. Editable a mà.\n"; &writeCrtPiPreset,
out << "# Els noms han de coincidir amb config.yaml > video.shader.current_crtpi_preset.\n"; "Presets CRT-Pi per a AEE 2026. Editable a mà.",
out << "presets:\n"; "Els noms han de coincidir amb config.yaml > video.shader.current_crtpi_preset.");
for (const auto& preset : crtpi_presets) {
writeCrtPiPreset(out, preset);
}
return writeFile(crtpi_file_path, out.str());
} }
auto buildActiveShader() -> Rendering::ShaderType { auto buildActiveShader() -> Rendering::ShaderType {