From 7e7f0e35fc4050ff47a3400ebd8c845e8de70413 Mon Sep 17 00:00:00 2001 From: packerlschupfer <83344883+packerlschupfer@users.noreply.github.com> Date: Thu, 10 Sep 2026 19:46:55 +0200 Subject: [PATCH] CLI: translate the 3MF's renamed compatibility keys before the compat check The 3MF fallback fed the project config to is_compatible_with_printer() as-is, but a project config does not carry compatible_printers or compatible_printers_condition. PresetBundle::construct_full_config() erases both and re-emits them as print_compatible_printers and compatible_machine_expression_group; they are renamed back only on the PresetBundle load path, which the CLI does not take. The check therefore saw no list and no condition, read that as 'no constraint' and accepted every printer. That is not just a wrong accept. An early true skips the !process_compatible block that sets machine_switch, so the new printer is never appended to print_compatible_printers and the exported 3MF stays marked compatible only with the printer it came from -- which is exactly what that block exists to prevent. Translate the two keys back before the check. Index 0 of the expression group is the print preset; the group is filled print, filaments, printer. Also note in the comment that profiles/BBL/{process,machine}_full/ are gitignored and generated by nothing in-tree, so current_*_full_config is always empty and this fallback is the only live path -- not the rare non-BBL case the original comment implied. Reported with measurements by HanifKoh in review of #15449. Preset: add a config-level is_compatible_with_printer() overload The CLI holds resolved DynamicPrintConfigs, not Presets, so it wrapped them in throwaway Preset shells at the call site. Moving that into Preset.cpp puts the compatibility policy -- including the documented fail-open on a malformed compatible_printers_condition -- in one place for the GUI and the CLI, rather than leaving a second copy of the plumbing in OrcaSlicer.cpp to drift. Purely additive: neither existing overload changes, so no GUI behaviour moves. Requested by HanifKoh in review of #15449. (cherry picked from commit 14ca1972ef4d3c7d90935d159423013a40a6bd70) --- src/OrcaSlicer.cpp | 40 ++++++++++++++++++++++++++++------------ src/libslic3r/Preset.cpp | 14 ++++++++++++++ src/libslic3r/Preset.hpp | 5 +++++ 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 22c5f8cc2d..5e51376bcb 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -2611,13 +2611,25 @@ int CLI::run(int argc, char **argv) auto check_compat = [](const DynamicPrintConfig &process_cfg, const DynamicPrintConfig &printer_cfg, const std::string &printer_name) -> bool { - Preset process_preset(Preset::TYPE_PRINT, std::string("__cli_process_check")); - process_preset.config = process_cfg; - Preset printer_preset(Preset::TYPE_PRINTER, printer_name); - printer_preset.config = printer_cfg; - PresetWithVendorProfile process_pwvp(process_preset, nullptr); - PresetWithVendorProfile printer_pwvp(printer_preset, nullptr); - return is_compatible_with_printer(process_pwvp, printer_pwvp); + return is_compatible_with_printer(process_cfg, Preset::TYPE_PRINT, printer_cfg, printer_name); + }; + + //ORCA: a 3MF's project config does not carry compatible_printers / compatible_printers_condition. + // PresetBundle::construct_full_config() erases both and re-emits them as + // print_compatible_printers and compatible_machine_expression_group; they are renamed back + // only on the PresetBundle load path, which the CLI does not take. Feeding the project config + // to the check as-is therefore presents no list and no condition, and + // is_compatible_with_printer() reads that as "no constraint" and accepts every printer. + // Translate the two keys back. Index 0 of the expression group is the print preset -- the + // group is filled print, filaments, printer (PresetBundle.cpp). + auto cli_process_compat_config = [](const DynamicPrintConfig &project_cfg) -> DynamicPrintConfig { + DynamicPrintConfig cfg = project_cfg; + if (const auto *list = project_cfg.option("print_compatible_printers")) + cfg.set_key_value("compatible_printers", new ConfigOptionStrings(list->values)); + const auto *group = project_cfg.option("compatible_machine_expression_group"); + if (group != nullptr && !group->values.empty()) + cfg.set_key_value("compatible_printers_condition", new ConfigOptionString(group->values.front())); + return cfg; }; if (!new_printer_name.empty()) { if (!new_process_name.empty()) { @@ -2628,12 +2640,16 @@ int CLI::run(int argc, char **argv) } else { //3MF-embedded process vs new printer. current_process_full_config is only populated from - //profiles/BBL/process_full/, i.e. for BBL profiles; for every other vendor fall back to the - //3MF's own project config, which is already merged into m_print_config above and carries - //compatible_printers_condition. Without this a 3MF built from a condition-only process is - //rejected when re-sliced with the very printer it was made for. + //profiles/BBL/process_full/, so for every other vendor fall back to the 3MF's own project + //config in m_print_config, with its renamed compatibility keys translated back (see + //cli_process_compat_config above). Without this a 3MF built from a condition-only process + //is rejected when re-sliced with the very printer it was made for. { - const DynamicPrintConfig &process_cfg = current_process_full_config.empty() ? m_print_config : current_process_full_config; + //ORCA: profiles/BBL/{process,machine}_full/ are gitignored and not generated in-tree, + // so current_*_full_config is always empty and this fallback is the only live path. + const DynamicPrintConfig process_cfg = current_process_full_config.empty() + ? cli_process_compat_config(m_print_config) + : current_process_full_config; process_compatible = check_compat(process_cfg, load_machine_config, new_printer_system_name); } BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%") diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 304c8957d5..3c31e427c2 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -867,6 +867,20 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre return is_compatible_with_printer(preset, active_printer, &config); } +// ORCA: see the header. The CLI resolves --load-settings into bare DynamicPrintConfigs and has no +// Preset objects to hand; without this it would have to reimplement the policy or build the shells +// at every call site. +bool is_compatible_with_printer(const DynamicPrintConfig &preset_config, Preset::Type preset_type, + const DynamicPrintConfig &printer_config, const std::string &printer_name) +{ + Preset preset(preset_type, std::string("__compat_check")); + preset.config = preset_config; + Preset printer(Preset::TYPE_PRINTER, printer_name); + printer.config = printer_config; + return is_compatible_with_printer(PresetWithVendorProfile(preset, nullptr), + PresetWithVendorProfile(printer, nullptr)); +} + void Preset::set_visible_from_appconfig(const AppConfig &app_config) { //BBS: add config related log diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index c9b3197a6f..13d283fd6a 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -459,6 +459,11 @@ protected: bool is_compatible_with_print (const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print, const PresetWithVendorProfile &active_printer); bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config); bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer); +// ORCA: same check for callers that hold raw configs rather than Presets (the CLI). Wraps them in +// throwaway Preset shells and delegates, so the compatibility policy -- including the fail-open on a +// malformed compatible_printers_condition -- lives in one place for the GUI and the CLI alike. +bool is_compatible_with_printer(const DynamicPrintConfig &preset_config, Preset::Type preset_type, + const DynamicPrintConfig &printer_config, const std::string &printer_name); // Where a preset is being loaded from. `Auto` lets load_presets() infer from the directory path. struct PresetOrigin {