Browse Source

improved conversion to/from prusa/orca: now can tell you what options are ignored.

supermerill 1 year ago
parent
commit
46bb95a4ec

+ 10 - 4
src/PrusaSlicer.cpp

@@ -142,7 +142,10 @@ int CLI::run(int argc, char **argv)
         if (! config_substitutions.empty()) {
             boost::nowide::cout << "The following configuration values were substituted when loading \" << file << \":\n";
             for (const ConfigSubstitution &subst : config_substitutions)
-                boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
+                if(subst.opt_def)
+                    boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
+                else
+                    boost::nowide::cout << "\tkey = \"" << subst.old_name << "\"\t can't be loaded (value = \"" << subst.old_value <<"\")\n";
         }
         config.normalize_fdm();
         PrinterTechnology other_printer_technology = get_printer_technology(config);
@@ -192,10 +195,13 @@ int CLI::run(int argc, char **argv)
                     boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
                     return 1;
                 }
-                if (! config_substitutions.substitutions.empty()) {
+                if (! config_substitutions.empty()) {
                     boost::nowide::cout << "The following configuration values were substituted when loading \" << file << \":\n";
-                    for (const ConfigSubstitution& subst : config_substitutions.substitutions)
-                        boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
+                    for (const ConfigSubstitution& subst : config_substitutions.get())
+                        if(subst.opt_def)
+                            boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
+                        else
+                            boost::nowide::cout << "\tkey = \"" << subst.old_name << "\"\t can't be loaded (value = \"" << subst.old_value <<"\")\n";
                 }
                 // config is applied to m_print_config before the current m_config values.
                 config += std::move(m_print_config);

+ 3 - 0
src/libslic3r/AppConfig.cpp

@@ -395,6 +395,9 @@ void AppConfig::set_defaults()
         if (get("check_material_export").empty())
             set("check_material_export", "0");
 
+        if (get("show_unknown_setting").empty())
+            set("show_unknown_setting", "1");
+
         if (get("use_custom_toolbar_size").empty())
             set("use_custom_toolbar_size", "0");
 

+ 89 - 34
src/libslic3r/Config.cpp

@@ -565,12 +565,17 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
 {
     t_config_option_key opt_key = opt_key_src;
     std::string         value   = value_src;
+    //note: should be done BEFORE calling set_deserialize
     // Both opt_key and value may be modified by handle_legacy().
     // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
     this->handle_legacy(opt_key, value);
-    if (opt_key.empty())
+    if (opt_key.empty()) {
+        assert(false);
         // Ignore the option.
         return true;
+    }
+    assert(opt_key == opt_key_src);
+    assert(value == value_src);
     return this->set_deserialize_raw(opt_key, value, substitutions_ctxt, append);
 }
 
@@ -601,7 +606,7 @@ void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const s
                 if (optdef == nullptr)
                     throw UnknownOptionException(opt_key_src);
             }
-            substitutions_ctxt.substitutions.push_back(ConfigSubstitution{ optdef, value_src, ConfigOptionUniquePtr(optdef->default_value->clone()) });
+            substitutions_ctxt.add(ConfigSubstitution{ optdef, value_src, ConfigOptionUniquePtr(optdef->default_value->clone()) });
         }
     }
 }
@@ -694,12 +699,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
 
         if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable ||
                             substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)) {
-            // Log the substitution.
-            ConfigSubstitution config_substitution;
-            config_substitution.opt_def   = optdef;
-            config_substitution.old_value = value;
-            config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
-            substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution));
+            substitutions_ctxt.emplace(optdef, std::string(value), ConfigOptionUniquePtr(opt->clone()));
         }
     }
     //set phony status
@@ -937,9 +937,20 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
     for (const boost::property_tree::ptree::value_type &v : tree) {
         t_config_option_key opt_key = v.first;
         try {
-            this->set_deserialize(opt_key, v.second.get_value<std::string>(), substitutions_ctxt);
+            std::string value = v.second.get_value<std::string>();
+            PrintConfigDef::handle_legacy(opt_key, value, false);
+            if (!opt_key.empty()) {
+                if (!PrintConfigDef::is_defined(opt_key)) {
+                    if (substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) {
+                        substitutions_ctxt.add(ConfigSubstitution(v.first, value));
+                    }
+                } else {
+                    this->set_deserialize(opt_key, value, substitutions_ctxt);
+                }
+            }
         } catch (UnknownOptionException & /* e */) {
             // ignore
+            assert(false);
         } catch (BadOptionValueException & e) {
             if (compatibility_rule == ForwardCompatibilitySubstitutionRule::Disable)
                 throw e;
@@ -947,41 +958,44 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
             const ConfigDef* def = this->def();
             if (def == nullptr) throw e;
             const ConfigOptionDef* optdef = def->get(opt_key);
-            substitutions_ctxt.substitutions.emplace_back(optdef, v.second.get_value<std::string>(), ConfigOptionUniquePtr(optdef->default_value->clone()));
+            substitutions_ctxt.emplace(optdef, v.second.get_value<std::string>(), ConfigOptionUniquePtr(optdef->default_value->clone()));
         }
     }
-    return std::move(substitutions_ctxt.substitutions);
+    return std::move(substitutions_ctxt).data();
 }
 
 // Load the config keys from the given string.
-size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
+std::map<t_config_option_key, std::string> ConfigBase::load_gcode_string_legacy(const char* str)
 {
+    std::map<t_config_option_key, std::string> opt_key_values;
     if (str == nullptr)
-        return 0;
+        return opt_key_values;
 
     // Walk line by line in reverse until a non-configuration key appears.
     const char *data_start = str;
     // boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
-    const char *end = data_start + strlen(str);
-    size_t num_key_value_pairs = 0;
+    const char *end                 = data_start + strlen(str);
     for (;;) {
         // Extract next line.
-        for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end);
+        for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end)
+            ;
         if (end == data_start)
             break;
-        const char *start = end ++;
-        for (; start > data_start && *start != '\r' && *start != '\n'; --start);
+        const char *start = end++;
+        for (; start > data_start && *start != '\r' && *start != '\n'; --start)
+            ;
         if (start == data_start)
             break;
         // Extracted a line from start to end. Extract the key = value pair.
-        if (end - (++ start) < 10 || start[0] != ';' || start[1] != ' ')
+        if (end - (++start) < 10 || start[0] != ';' || start[1] != ' ')
             break;
         const char *key = start + 2;
         if (!((*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z')))
             // A key must start with a letter.
             break;
         const char *sep = key;
-        for (; sep != end && *sep != '='; ++ sep) ;
+        for (; sep != end && *sep != '='; ++sep)
+            ;
         if (sep == end || sep[-1] != ' ' || sep[1] != ' ')
             break;
         const char *value = sep + 2;
@@ -991,29 +1005,58 @@ size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char*
         if (key_end - key < 3)
             break;
         // The key may contain letters, digits and underscores.
-        for (const char *c = key; c != key_end; ++ c)
+        for (const char *c = key; c != key_end; ++c)
             if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
                 key = nullptr;
                 break;
             }
         if (key == nullptr)
             break;
+        opt_key_values.emplace(std::string(key, key_end), std::string(value, end));
+        end = start;
+    }
+    return opt_key_values;
+}
+
+    
+size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions)
+{
+    if (str == nullptr)
+        return 0;
+
+    // Walk line by line in reverse until a non-configuration key appears.
+    const char *data_start = str;
+    // boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
+    const char *end = data_start + strlen(str);
+    size_t num_key_value_pairs = 0;
+    for (auto [key, value] : load_gcode_string_legacy(str)) {
         try {
-            config.set_deserialize(std::string(key, key_end), std::string(value, end), substitutions);
-            ++num_key_value_pairs;
+            std::string opt_key = key;
+            PrintConfigDef::handle_legacy(opt_key, value, false);
+            if (!opt_key.empty()) {
+                if (!PrintConfigDef::is_defined(opt_key)) {
+                    if (substitutions.rule != ForwardCompatibilitySubstitutionRule::Disable) {
+                        substitutions.add(ConfigSubstitution(key, value));
+                    }
+                } else {
+                    config.set_deserialize(opt_key, value, substitutions);
+                    ++num_key_value_pairs;
+                }
+            }
         }
         catch (UnknownOptionException & /* e */) {
-            // ignore
+            // log & ignore
+            if (substitutions.rule != ForwardCompatibilitySubstitutionRule::Disable)
+                substitutions.add(ConfigSubstitution(key, value));
         } catch (BadOptionValueException & e) {
             if (substitutions.rule == ForwardCompatibilitySubstitutionRule::Disable)
                 throw e;
             // log the error
             const ConfigDef* def = config.def();
             if (def == nullptr) throw e;
-            const ConfigOptionDef* optdef = def->get(std::string(key, key_end));
-            substitutions.substitutions.emplace_back(optdef, std::string(value, end), ConfigOptionUniquePtr(optdef->default_value->clone()));
+            const ConfigOptionDef* optdef = def->get(key);
+            substitutions.emplace(optdef, std::move(value), ConfigOptionUniquePtr(optdef->default_value->clone()));
         }
-        end = start;
     }
 
     return num_key_value_pairs;
@@ -1166,10 +1209,21 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
                 boost::trim(key);
                 boost::trim(value);
                 try {
-                    this->set_deserialize(key, value, substitutions_ctxt);
-                    ++ key_value_pairs;
+                    std::string opt_key = key;
+                    PrintConfigDef::handle_legacy(opt_key, value, false);
+                    if (!opt_key.empty()) {
+                        if (!PrintConfigDef::is_defined(opt_key)) {
+                            if (substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) {
+                                substitutions_ctxt.add(ConfigSubstitution(key, value));
+                            }
+                        } else {
+                            this->set_deserialize(opt_key, value, substitutions_ctxt);
+                            ++key_value_pairs;
+                        }
+                    }
                 } catch (UnknownOptionException & /* e */) {
                     // ignore
+                    assert(false);
                 }
             }
         }
@@ -1192,7 +1246,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
 
     if (key_value_pairs < 80)
         throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
-    return std::move(substitutions_ctxt.substitutions);
+    return std::move(substitutions_ctxt).data();
 }
 
 void ConfigBase::save(const std::string &file, bool to_prusa) const
@@ -1404,10 +1458,11 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
             // Just bail out if the configuration value is not understood.
             ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Disable);
             // Any scalar value of a type different from Bool and String.
-            if (! this->set_deserialize_nothrow(opt_key, value, context, false)) {
-				boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
-				return false;
-			}
+            // here goes int options, like loglevel.
+            if (!this->set_deserialize_nothrow(opt_key, value, context, false)) {
+                boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
+                return false;
+            }
         }
     }
     return true;

+ 16 - 3
src/libslic3r/Config.hpp

@@ -354,10 +354,12 @@ using  ConfigOptionUniquePtr = std::unique_ptr<ConfigOption, ConfigOptionDeleter
 // This structure serves to inform the user about the substitutions having been done during file import.
 struct ConfigSubstitution {
     const ConfigOptionDef   *opt_def { nullptr };
+    std::string              old_name; // for when opt_def is nullptr (option not defined in this version)
     std::string              old_value;
     ConfigOptionUniquePtr    new_value;
     ConfigSubstitution() = default;
-    ConfigSubstitution(const ConfigOptionDef* def, std::string old, ConfigOptionUniquePtr&& new_v) : opt_def(def), old_value(old), new_value(std::move(new_v)) {}
+    ConfigSubstitution(const ConfigOptionDef* def, std::string old, ConfigOptionUniquePtr&& new_v) : opt_def(def), old_name(), old_value(old), new_value(std::move(new_v)) {}
+    ConfigSubstitution(std::string bad_key, std::string value) : opt_def(nullptr), old_name(bad_key), old_value(value), new_value() {}
 };
 
 using  ConfigSubstitutions = std::vector<ConfigSubstitution>;
@@ -367,10 +369,20 @@ using  ConfigSubstitutions = std::vector<ConfigSubstitution>;
 struct ConfigSubstitutionContext
 {
     ConfigSubstitutionContext(ForwardCompatibilitySubstitutionRule rl) : rule(rl) {}
-    bool empty() const throw() { return substitutions.empty(); }
 
     ForwardCompatibilitySubstitutionRule 	rule;
-    ConfigSubstitutions					    substitutions;
+    
+    bool empty() const throw() { return m_substitutions.empty(); }
+    const ConfigSubstitutions &get() const { return m_substitutions; }
+    ConfigSubstitutions data() && { return std::move(m_substitutions); }
+    void add(ConfigSubstitution&& substitution) { m_substitutions.push_back(std::move(substitution)); }
+    void emplace(std::string &&key, std::string &&value) { m_substitutions.emplace_back(std::move(key), std::move(value)); }
+    void emplace(const ConfigOptionDef* def, std::string &&old_value, ConfigOptionUniquePtr&& new_v) { m_substitutions.emplace_back(def, std::move(old_value), std::move(new_v)); }
+    void clear() { m_substitutions.clear(); }
+    void sort_and_remove_duplicates() { sort_remove_duplicates(m_substitutions); }
+
+private:
+    ConfigSubstitutions					    m_substitutions;
 };
 
 // A generic value of a configuration option.
@@ -2364,6 +2376,7 @@ public:
 	// Set all the nullable values to nils.
     void null_nullables();
 
+    static std::map<t_config_option_key, std::string> load_gcode_string_legacy(const char* str);
     static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);
 
 private:

+ 53 - 42
src/libslic3r/Format/3mf.cpp

@@ -443,6 +443,7 @@ namespace Slic3r {
         // Version of the 3mf file
         unsigned int m_version;
         bool m_check_version;
+        bool m_trying_read_prusa = false;
 
         // Semantic version of PrusaSlicer, that generated this 3MF.
         boost::optional<Semver> m_prusaslicer_generator_version;
@@ -504,7 +505,7 @@ namespace Slic3r {
 
         void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
         bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
-
+        
         // handlers to parse the .model file
         void _handle_start_model_xml_element(const char* name, const char** attributes);
         void _handle_end_model_xml_element(const char* name);
@@ -571,7 +572,7 @@ namespace Slic3r {
         bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
         bool _handle_end_config_metadata();
 
-        bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions, DynamicPrintConfig& config_not_used_remove_plz);
+        bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions, DynamicPrintConfig& global_config);
 
         // callbacks to parse the .model file
         static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
@@ -600,6 +601,7 @@ namespace Slic3r {
         _destroy_xml_parser();
     }
 
+
     bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version)
     {
         m_version = 0;
@@ -769,7 +771,9 @@ namespace Slic3r {
 
                     if (boost::algorithm::iequals(name, PRUSA_LAYER_CONFIG_RANGES_FILE)) {
                         // extract slic3r layer config ranges file from a bad named file
-                        _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
+                            m_trying_read_prusa = true;
+                            _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
+                            m_trying_read_prusa = false;
                         break;
                     }
                 }
@@ -779,30 +783,24 @@ namespace Slic3r {
         //parsed superslicer/prusa files if slic3r not found
         //note that is we successfully read one of the config file, then the other ones should also have the same name
         auto read_from_other_storage = [this, &print_config_parsed, num_entries, &archive, &stat, &config, &model, &filename, &config_substitutions]
-                (const std::string &print_config_name, const std::string& model_config_name, const std::string& layer_config_name, bool from_prusa) -> bool {
-            for (mz_uint i = 0; i < num_entries; ++i)
-            {
-                if (mz_zip_reader_file_stat(&archive, i, &stat))
-                {
+                (const std::string &print_config_name, const std::string& model_config_name, const std::string& layer_config_name) -> bool {
+            for (mz_uint i = 0; i < num_entries; ++i) {
+                if (mz_zip_reader_file_stat(&archive, i, &stat)) {
                     std::string name(stat.m_filename);
                     std::replace(name.begin(), name.end(), '\\', '/');
 
-                    //TODO use special methods to convert them better?
+                    // TODO use special methods to convert them better?
 
-                    if (boost::algorithm::iequals(name, layer_config_name))
-                    {
+                    if (boost::algorithm::iequals(name, layer_config_name)) {
                         // extract slic3r layer config ranges file
                         _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
-                    } else if (boost::algorithm::iequals(name, print_config_name))
-                    {
+                    } else if (boost::algorithm::iequals(name, print_config_name)) {
                         // extract slic3r print config file
                         _extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
                         print_config_parsed = true;
-                    } else if (boost::algorithm::iequals(name, model_config_name))
-                    {
+                    } else if (boost::algorithm::iequals(name, model_config_name)) {
                         // extract slic3r model config file
-                        if (!_extract_model_config_from_archive(archive, stat, model))
-                        {
+                        if (!_extract_model_config_from_archive(archive, stat, model)) {
                             close_zip_reader(&archive);
                             add_error("Archive does not contain a valid model config");
                             return false;
@@ -815,17 +813,19 @@ namespace Slic3r {
         };
         bool use_prusa_config = false;
         if (!print_config_parsed) {
-            if (!read_from_other_storage(SUPER_PRINT_CONFIG_FILE, SUPER_MODEL_CONFIG_FILE, SUPER_LAYER_CONFIG_RANGES_FILE, false))
+            if (!read_from_other_storage(SUPER_PRINT_CONFIG_FILE, SUPER_MODEL_CONFIG_FILE, SUPER_LAYER_CONFIG_RANGES_FILE))
                 return false;
         }
         if (!print_config_parsed) {
-            if (!read_from_other_storage(PRUSA_PRINT_CONFIG_FILE, PRUSA_MODEL_CONFIG_FILE, PRUSA_LAYER_CONFIG_RANGES_FILE, true))
+            m_trying_read_prusa = true;
+            if (!read_from_other_storage(PRUSA_PRINT_CONFIG_FILE, PRUSA_MODEL_CONFIG_FILE, PRUSA_LAYER_CONFIG_RANGES_FILE))
                 return false;
             if (print_config_parsed) {
                 //succeed to read prusa
                 use_prusa_config = true;
                 config.convert_from_prusa(true);
             }
+            m_trying_read_prusa = false;
         }
 
         close_zip_reader(&archive);
@@ -917,12 +917,15 @@ namespace Slic3r {
                 // config data has been found, this model was saved using slic3r pe
 
                 // apply object's name and config data
-                for (const Metadata& metadata : obj_metadata->second.metadata) {
-                        if (metadata.key == "name")
-                            model_object->name = metadata.value;
-                        else
-                            model_object->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
+                std::map<t_config_option_key, std::string> opt_key_to_value;
+                for (const Metadata &metadata : obj_metadata->second.metadata) {
+                    if (metadata.key == "name") {
+                        model_object->name = metadata.value;
+                    } else {
+                        opt_key_to_value.emplace(metadata.key, metadata.value);
                     }
+                }
+                deserialize_maybe_from_prusa(opt_key_to_value, model_object->config, config, config_substitutions, true, m_trying_read_prusa);
 
                 // select object's detected volumes
                 volumes_ptr = &obj_metadata->second.volumes;
@@ -1062,7 +1065,8 @@ namespace Slic3r {
             // parsing 3MFs from before PrusaSlicer 2.0.0 (which can have duplicated entries in the INI.
             // See https://github.com/prusa3d/PrusaSlicer/issues/7155. We'll revert it for now.
             //config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule);
-            ConfigBase::load_from_gcode_string_legacy(config, buffer.data(), config_substitutions);
+            //ConfigBase::load_from_gcode_string_legacy(config, buffer.data(), config_substitutions);
+            deserialize_maybe_from_prusa(ConfigBase::load_gcode_string_legacy(buffer.data()), config, config_substitutions, true, m_trying_read_prusa);
         }
     }
 
@@ -1167,7 +1171,7 @@ namespace Slic3r {
 
                     // get Z range information
                     DynamicPrintConfig config;
-
+                    std::map<t_config_option_key, std::string> opt_key_to_value;
                     for (const auto& option : range_tree) {
                         if (option.first != "option")
                             continue;
@@ -1175,14 +1179,16 @@ namespace Slic3r {
                         std::string value = option.second.data();
                         if (value.empty() && opt_key.find("pattern") != std::string::npos) {
                             add_error("Error while reading '"+ opt_key +"': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.");
-                            ConfigSubstitution config_substitution;
-                            config_substitution.opt_def = config.get_option_def(opt_key);
-                            config_substitution.old_value = "Error while reading '" + opt_key + "': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.";
-                            config_substitution.new_value = ConfigOptionUniquePtr(config_substitution.opt_def->default_value->clone());
-                            config_substitutions.substitutions.emplace_back(std::move(config_substitution));
-                        }else
-                            config.set_deserialize(opt_key, value, config_substitutions);
+                            std::string old_value = "Error while reading '" + opt_key + "': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.";
+                            const ConfigOptionDef *opt_def = config.get_option_def(opt_key);
+                            config_substitutions.emplace(opt_def,
+                                std::move(old_value),
+                                ConfigOptionUniquePtr(opt_def->default_value->clone()));
+                        } else {
+                            opt_key_to_value.emplace(opt_key, value);
+                        }
                     }
+                    deserialize_maybe_from_prusa(opt_key_to_value, config, config_substitutions, true, m_trying_read_prusa);
 
                     config_ranges[{ min_z, max_z }].assign_config(std::move(config));
                 }
@@ -2084,7 +2090,7 @@ namespace Slic3r {
         return true;
     }
 
-    bool _3MF_Importer::_generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions, DynamicPrintConfig& config_not_used_remove_plz)
+    bool _3MF_Importer::_generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions, DynamicPrintConfig& global_config)
     {
         if (!object.volumes.empty()) {
             add_error("Found invalid volumes count");
@@ -2189,6 +2195,7 @@ namespace Slic3r {
             volume->mmu_segmentation_facets.shrink_to_fit();
 
             // apply the remaining volume's metadata
+            std::map<t_config_option_key, std::string> opt_key_to_value;
             for (const Metadata& metadata : volume_data.metadata) {
                 if (metadata.key == NAME_KEY)
                     volume->name = metadata.value;
@@ -2212,17 +2219,21 @@ namespace Slic3r {
                     volume->source.is_converted_from_inches = metadata.value == "1";
                 else if (metadata.key == SOURCE_IN_METERS)
                     volume->source.is_converted_from_meters = metadata.value == "1";
+                else if (metadata.key == MATRIX_KEY)
+                    ;//already parsed
                 else
                     if (metadata.value.empty() && metadata.key.find("pattern") != std::string::npos) {
-                        add_error("Error while reading '" + metadata.key + "': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.");
-                        ConfigSubstitution config_substitution;
-                        config_substitution.opt_def = config_not_used_remove_plz.get_option_def(metadata.key);
-                        config_substitution.old_value = "Error while reading '" + metadata.key + "': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.";
-                        config_substitution.new_value = ConfigOptionUniquePtr(config_substitution.opt_def->default_value->clone());
-                        config_substitutions.substitutions.emplace_back(std::move(config_substitution));
-                    } else
-                        volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
+                            add_error("Error while reading '"+  metadata.key +"': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.");
+                            std::string old_value = "Error while reading '" + metadata.key + "': no value. If you are the one who created this project file, please open an issue and put the ERROR_FILE_TO_SEND_TO_MERILL_PLZZZZ.txt file created next to the executable for debugging.";
+                            const ConfigOptionDef *opt_def = global_config.get_option_def(metadata.key);
+                            config_substitutions.emplace(opt_def,
+                                std::move(old_value),
+                                ConfigOptionUniquePtr(opt_def->default_value->clone()));
+                    } else {
+                        opt_key_to_value.emplace(metadata.key, metadata.value);
+                    }
             }
+            deserialize_maybe_from_prusa(opt_key_to_value, volume->config, global_config, config_substitutions, true, m_trying_read_prusa);
 
             // this may happen for 3mf saved by 3rd part softwares
             if (volume->name.empty()) {

+ 28 - 18
src/libslic3r/Format/AMF.cpp

@@ -734,8 +734,8 @@ void AMFParserContext::endElement(const char * /* name */)
             ConfigBase::load_from_gcode_string_legacy(*m_config, std::move(m_value[1].c_str()), *m_config_substitutions);
         }
         else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
-            const char *opt_key = m_value[0].c_str() + 7;
-            if (print_config_def.options.find(opt_key) != print_config_def.options.end()) {
+            const char *key = m_value[0].c_str() + 7;
+            if (print_config_def.options.find(key) != print_config_def.options.end()) {
                 ModelConfig *config = nullptr;
                 if (m_path.size() == 3) {
                     if (m_path[1] == NODE_TYPE_MATERIAL && m_material)
@@ -749,9 +749,19 @@ void AMFParserContext::endElement(const char * /* name */)
                     auto it  = --m_object->layer_config_ranges.end();
                     config = &it->second;
                 }
-                if (config)
-                    config->set_deserialize(opt_key, m_value[1], *m_config_substitutions);
-            } else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
+                if (config) {
+                    std::string opt_key = key;
+                    std::string value = m_value[1];
+                    PrintConfigDef::handle_legacy(opt_key, value, true);
+                    if (opt_key.empty()) {
+                        if (m_config_substitutions->rule != ForwardCompatibilitySubstitutionRule::Disable) {
+                            m_config_substitutions->emplace(std::string(key), std::move(value));
+                        }
+                    } else {
+                        config->set_deserialize(opt_key, value, *m_config_substitutions);
+                    }
+                }
+            } else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(key, "layer_height_profile") == 0) {
                 // Parse object's layer height profile, a semicolon separated list of floats.
                 char *p = m_value[1].data();
                 std::vector<coordf_t> data;
@@ -766,7 +776,7 @@ void AMFParserContext::endElement(const char * /* name */)
                 }
                 m_object->layer_height_profile.set(std::move(data));
             }
-            else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) {
+            else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(key, "sla_support_points") == 0) {
                 // Parse object's layer height profile, a semicolon separated list of floats.
                 unsigned char coord_idx = 0;
                 Eigen::Matrix<float, 5, 1, Eigen::DontAlign> point(Eigen::Matrix<float, 5, 1, Eigen::DontAlign>::Zero());
@@ -788,7 +798,7 @@ void AMFParserContext::endElement(const char * /* name */)
                 m_object->sla_points_status = sla::PointsStatus::UserModified;
             }
             else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE && 
-                     m_object && strcmp(opt_key, "layer_height_range") == 0) {
+                     m_object && strcmp(key, "layer_height_range") == 0) {
                 // Parse object's layer_height_range, a semicolon separated doubles.
                 char* p = m_value[1].data();
                 char* end = strchr(p, ';');
@@ -798,38 +808,38 @@ void AMFParserContext::endElement(const char * /* name */)
                 m_object->layer_config_ranges[range];
             }
             else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
-                if (strcmp(opt_key, "modifier") == 0) {
+                if (strcmp(key, "modifier") == 0) {
                     // Is this volume a modifier volume?
                     // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
 					m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART);
-                } else if (strcmp(opt_key, "volume_type") == 0) {
+                } else if (strcmp(key, "volume_type") == 0) {
                     m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
                 }
-                else if (strcmp(opt_key, "matrix") == 0) {
+                else if (strcmp(key, "matrix") == 0) {
                     m_volume_transform = Slic3r::Geometry::transform3d_from_string(m_value[1]);
                 }
-                else if (strcmp(opt_key, "source_file") == 0) {
+                else if (strcmp(key, "source_file") == 0) {
                     m_volume->source.input_file = m_value[1];
                 }
-                else if (strcmp(opt_key, "source_object_id") == 0) {
+                else if (strcmp(key, "source_object_id") == 0) {
                     m_volume->source.object_idx = ::atoi(m_value[1].c_str());
                 }
-                else if (strcmp(opt_key, "source_volume_id") == 0) {
+                else if (strcmp(key, "source_volume_id") == 0) {
                     m_volume->source.volume_idx = ::atoi(m_value[1].c_str());
                 }
-                else if (strcmp(opt_key, "source_offset_x") == 0) {
+                else if (strcmp(key, "source_offset_x") == 0) {
                     m_volume->source.mesh_offset(0) = ::atof(m_value[1].c_str());
                 }
-                else if (strcmp(opt_key, "source_offset_y") == 0) {
+                else if (strcmp(key, "source_offset_y") == 0) {
                     m_volume->source.mesh_offset(1) = ::atof(m_value[1].c_str());
                 }
-                else if (strcmp(opt_key, "source_offset_z") == 0) {
+                else if (strcmp(key, "source_offset_z") == 0) {
                     m_volume->source.mesh_offset(2) = ::atof(m_value[1].c_str());
                 }
-                else if (strcmp(opt_key, "source_in_inches") == 0) {
+                else if (strcmp(key, "source_in_inches") == 0) {
                     m_volume->source.is_converted_from_inches = m_value[1] == "1";
                 }
-                else if (strcmp(opt_key, "source_in_meters") == 0) {
+                else if (strcmp(key, "source_in_meters") == 0) {
                     m_volume->source.is_converted_from_meters = m_value[1] == "1";
                 }
             }

+ 201 - 12
src/libslic3r/Format/BBConfig.cpp

@@ -44,6 +44,17 @@ namespace BBConfiguration {
 
 std::map<std::string, std::string> key_translation_map;
 std::map<std::string, std::map<std::string, std::string>> value_translation_map;
+std::vector<std::pair<std::string, std::string>> custom_gcode_replace; // vector<pair>, as i want to keep the ordering
+enum BBSettingType: uint8_t{
+    bbstFFF_PRINT = 0<<0,
+    bbstFFF_FILAMENT = 0<<1,
+    bbstFFF_PRINTER = 0<<2,
+    bbstSLA_PRINT = 0<<3,
+    bbstSLA_MATERIAL = 0<<4,
+    bbstSLA_PRINTER = 0<<5,
+
+};
+std::map<std::string, BBSettingType> key_custom_settings_translation_map;
 // std::map<std::string, std::function<void(std::map<std::string, std::string>&, std::map<std::string,
 // std::vector<std::string>>&, const std::string&, const std::string&)>> transform_complicated; std::map<std::string,
 // std::function<void(std::map<std::string, std::string>&, std::map<std::string, std::vector<std::string>>&, const
@@ -52,11 +63,30 @@ bool is_init = false;
 
 void init()
 {
-    //list from https://github.com/theophile/SuperSlicer_to_Orca_scripts/blob/main/superslicer_to_orca.pl
+    key_translation_map["version"] = "";
+    key_translation_map["from"] = "";
+    key_translation_map["name"] = "";
+
+    //list (partially) from https://github.com/theophile/SuperSlicer_to_Orca_scripts/blob/main/superslicer_to_orca.pl
+    key_translation_map["extra_perimeters_on_overhangs"]    = "extra_perimeters_overhangs";
+    key_translation_map["internal_solid_infill_pattern"]    = "solid_fill_pattern";
+    key_translation_map["overhang_speed_classic"]           = "overhangs_speed";
+    key_translation_map["preferred_orientation"]            = "init_z_rotate";
+    key_translation_map["solid_infill_filament"]            = "solid_infill_extruder";
+    key_translation_map["support_filament"]                 = "support_material_extruder";
+    key_translation_map["support_interface_filament"]       = "support_material_interface_extruder";
+    key_translation_map["sparse_infill_filament"]           = "infill_extruder";
+    key_translation_map["wall_filament"]                    = "perimeter_extruder";
+    key_translation_map["first_layer_filament"]             = "first_layer_extruder";
+    key_translation_map["spiral_mode"]                      = "spiral_vase";
+    
     // print
+    key_translation_map["alternate_extra_wall"]             = "extra_perimeters_odd_layers";
+    key_translation_map["is_infill_first"]                          = "infill_first";
     key_translation_map["enable_arc_fitting"]                       = "arc_fitting";
     key_translation_map["bottom_shell_layers"]                      = "bottom_solid_layers";
     key_translation_map["bottom_shell_thickness"]                   = "bottom_solid_min_thickness";
+    key_translation_map["bottom_solid_infill_flow_ratio"]   = "first_layer_flow_ratio ";
     //key_translation_map["bridge_acceleration"]                      = "bridge_acceleration";
     //key_translation_map["bridge_angle"]                             = "bridge_angle";
     key_translation_map["bridge_density"]                           = "bridge_overlap_min";
@@ -74,7 +104,8 @@ void init()
     key_translation_map["detect_overhang_wall"]                     = "overhangs"; // will go to handle_legacy
     key_translation_map["detect_thin_wall"]                         = "thin_walls";
     //key_translation_map["draft_shield"]                             = "draft_shield";
-    //key_translation_map["elefant_foot_compensation"]  // will go to handle_legacy               = "first_layer_size_compensation";
+    key_translation_map["elefant_foot_compensation"]                = "elefant_foot_compensation";  // will go to handle_legacy
+    key_translation_map["elefant_foot_compensation_layers"]         = "first_layer_size_compensation_layers";
     key_translation_map["enable_overhang_speed"]                    = "enable_dynamic_overhang_speeds"; //2.7
     //key_translation_map["extra_perimeters_on_overhangs"]            = "extra_perimeters_on_overhangs";
     key_translation_map["enable_prime_tower"]                       = "wipe_tower";
@@ -153,7 +184,7 @@ void init()
     key_translation_map["support_line_width"]                       = "support_material_extrusion_width";
     key_translation_map["support_on_build_plate_only"]              = "support_material_buildplate_only";
     key_translation_map["support_threshold_angle"]                  = "support_material_threshold";
-    //key_translation_map["thick_bridges"]                            = "thick_bridges"; //handled by from_prusa
+    key_translation_map["thick_bridges"]                            = "thick_bridges"; //handled by from_prusa
     key_translation_map["top_shell_layers"]                         = "top_solid_layers";
     key_translation_map["top_shell_thickness"]                      = "top_solid_min_thickness";
     key_translation_map["top_surface_acceleration"]                 = "top_solid_infill_acceleration";
@@ -195,9 +226,9 @@ void init()
     key_translation_map["bottom_solid_infill_flow_ratio"]           = "initial_layer_flow_ratio";
     key_translation_map["infill_combination"]                       = "infill_every_layers";
     key_translation_map["print_sequence"]                           = "complete_objects";
-    //key_translation_map["brim_type"]                                = "brim_type"; //handled by from_prusa
+    key_translation_map["brim_type"]                                = "brim_type"; //handled by from_prusa
     //key_translation_map["notes"]                                    = "notes";
-    //key_translation_map["support_material_style"]                   = "support_material_style";
+    key_translation_map["support_style"]                   = "support_material_style";
     //key_translation_map["ironing"]                                  = "ironing";
     //key_translation_map["ironing_type"]                             = "ironing_type";
     //key_translation_map["ironing_angle"]                            = "ironing_angle";
@@ -283,6 +314,7 @@ void init()
     //key_translation_map["default_filament_profile"]            = "default_filament_profile";
     //key_translation_map["default_print_profile"]               = "default_print_profile";
     key_translation_map["deretraction_speed"]                  = "deretract_speed";
+    key_translation_map["emit_machine_limits_to_gcode"]       = "machine_limits_usage";
     //key_translation_map["gcode_flavor"]                        = "gcode_flavor";
     //key_translation_map["inherits"]                            = "inherits";
     key_translation_map["layer_change_gcode"]                  = "layer_gcode";
@@ -418,12 +450,36 @@ void init()
     value_translation_map["support_material_style"]["normal"] = "grid";
     value_translation_map["support_material_style"]["default"] = "grid";
     value_translation_map["support_material_style"]["tree"] = "snug"; // organic in 2.7
+    value_translation_map["support_material_style"]["tree_slim"] = "snug"; // organic in 2.7
+    value_translation_map["support_material_style"]["tree_strong"] = "snug"; // organic in 2.7
+    value_translation_map["support_material_style"]["tree_hybrid"] = "snug"; // organic in 2.7
     value_translation_map["support_material_style"]["organic"] = "snug"; // organic in 2.7
     value_translation_map["retract_lift_top"]["Bottom Only"] = "Not on top";
     value_translation_map["retract_lift_top"]["Top Only"] = "Only on top";
     value_translation_map["thumbnails_format"]["BTT_TFT"] = "BIQU";
     value_translation_map["complete_objects"]["by layer"] = "0";
     value_translation_map["complete_objects"]["by object"] = "1";
+    value_translation_map["machine_limits_usage"]["0"] = "time_estimate_only";
+    value_translation_map["machine_limits_usage"]["1"] = "emit_to_gcode";
+
+
+
+/// GCODE
+    custom_gcode_replace.emplace_back("[bed_temperature_initial_layer_single]", "{first_layer_bed_temperature[initial_extruder]}");
+    custom_gcode_replace.emplace_back("bed_temperature_initial_layer_single", "first_layer_bed_temperature[initial_extruder]");
+    custom_gcode_replace.emplace_back("initial_extruder_id", "initial_extruder");
+    custom_gcode_replace.emplace_back("bbl_bed_temperature_gcode", "false");
+    custom_gcode_replace.emplace_back("bed_temperature_initial_layer", "first_layer_bed_temperature");
+    custom_gcode_replace.emplace_back("bed_temperature_initial_layer_vector", "\"\"");
+    custom_gcode_replace.emplace_back("[temperature_initial_layer]", "{first_layer_temperature[initial_extruder]}");
+    custom_gcode_replace.emplace_back("temperature_initial_layer", "first_layer_temperature[initial_extruder]");
+    //custom_gcode_replace.emplace_back("overall_chamber_temperature", "chamber_temperature"); //fixme: it's a max.
+
+    //if plate_name, then add plate_name as custom setting
+    key_custom_settings_translation_map["print_custom_variables"] = BBSettingType(bbstFFF_PRINT | bbstSLA_PRINT);
+    key_custom_settings_translation_map["filament_custom_variables"] = BBSettingType(bbstFFF_FILAMENT | bbstSLA_MATERIAL);
+    key_custom_settings_translation_map["printer_custom_variables"] = BBSettingType(bbstFFF_PRINTER | bbstSLA_PRINTER);
+    key_custom_settings_translation_map["plate_name"] = BBSettingType(bbstFFF_PRINT | bbstSLA_PRINT);
 }
 
 void complicated_convert(t_config_option_key &opt_key, std::string &value, const std::map<std::string, std::string> &input, std::map<std::string, std::string> &output)
@@ -434,11 +490,109 @@ void complicated_convert(t_config_option_key &opt_key, std::string &value, const
         output["ironing"] = "0";
         assert(input.find("ironing") == input.end() || input.find("ironing")->second == "0");
     }
+    if ("brim_type" == opt_key && "brim_ears" == value) {
+        opt_key = "brim_ears";
+        value = "1";
+    }
+    if ("disable_m73" == opt_key) {
+        output["remaining_times_type"] = "m73";
+        opt_key = "remaining_times";
+        if ("1" == value) {
+            value = "0";
+        } else {
+            value = "1";
+        }
+    }
+    if ("enable_overhang_speed") {
+    
+    }
 }
 
 //settings from orca that I can't convert
 // ironing_pattern
 
+bool push_into_custom_variable(DynamicPrintConfig &print_config,
+                               const std::string & opt_key,
+                               const std::string & opt_value)
+{
+    if (auto it = key_custom_settings_translation_map.find(opt_key); it != key_custom_settings_translation_map.end()) {
+        if ((it->second & bbstFFF_PRINT) != 0 || (it->second & bbstSLA_PRINT) != 0) {
+            if (print_config.opt<ConfigOptionStrings>("print_custom_variables") == nullptr)
+                print_config.set_deserialize("print_custom_variables", "");
+            std::string &value = print_config.opt<ConfigOptionString>("print_custom_variables")->value;
+            if (value.find(opt_key) == std::string::npos)
+                value += opt_key + std::string("=") + opt_value + std::string("\n");
+        }
+        if ((it->second & bbstFFF_FILAMENT) != 0 || (it->second & bbstSLA_MATERIAL) != 0) {
+            if (print_config.opt<ConfigOptionStrings>("filament_custom_variables") == nullptr)
+                print_config.set_deserialize("filament_custom_variables", "");
+            const std::string &val = print_config.opt<ConfigOptionStrings>("filament_custom_variables")->get_at(0);
+            if (val.find(opt_key) == std::string::npos)
+                print_config.opt<ConfigOptionStrings>("filament_custom_variables")
+                    ->set_at(val + opt_key + std::string("=") + opt_value + std::string("\n"), 0);
+        }
+        if ((it->second & bbstFFF_PRINTER) != 0 || (it->second & bbstSLA_PRINTER) != 0) {
+            if (print_config.opt<ConfigOptionStrings>("printer_custom_variables") == nullptr)
+                print_config.set_deserialize("printer_custom_variables", "");
+            std::string &value = print_config.opt<ConfigOptionString>("printer_custom_variables")->value;
+            if (value.find(opt_key) == std::string::npos)
+                value += opt_key + std::string("=") + opt_value + std::string("\n");
+        }
+        return true;
+    }
+    return false;
+}
+
+bool push_into_custom_variables(DynamicPrintConfig &            print_config,
+                                const std::string &             opt_key,
+                                const std::vector<std::string> &opt_value)
+{
+    if (auto it = key_custom_settings_translation_map.find(opt_key); it != key_custom_settings_translation_map.end()) {
+        if ((it->second & bbstFFF_FILAMENT) != 0 || (it->second & bbstSLA_MATERIAL) != 0) {
+            for (int i = 0; i < opt_value.size(); ++i) {
+                if (print_config.opt<ConfigOptionStrings>("filament_custom_variables") == nullptr)
+                    print_config.set_deserialize("filament_custom_variables", "");
+                const std::string &val = print_config.opt<ConfigOptionStrings>("filament_custom_variables")->get_at(i);
+                if (val.find(opt_key) == std::string::npos)
+                    print_config.opt<ConfigOptionStrings>("filament_custom_variables")
+                        ->set_at(val + opt_key + std::string("=") + opt_value[i] + std::string("\n"), i);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+//TODO: ensure it's inside '{' '[' script section, reliably
+void custom_gcode_transform(DynamicPrintConfig &print_config)
+{
+    for (std::string opt_key : {"template_custom_gcode", "toolchange_gcode", "before_layer_gcode",
+                                "between_objects_gcode", "end_gcode", "layer_gcode", "feature_gcode", "start_gcode",
+                                "color_change_gcode", "pause_print_gcode", "toolchange_gcode"}) {
+        auto opt = print_config.opt<ConfigOptionString>(opt_key);
+        if (opt != nullptr) {
+            std::string &custom_gcode = opt->value;
+            // check & replace setting name
+            for (auto &entry : key_translation_map) { boost::replace_all(custom_gcode, entry.first, entry.second); }
+            // check & replace special things
+            for (auto &entry : custom_gcode_replace) { boost::replace_all(custom_gcode, entry.first, entry.second); }
+        }
+    }
+    for (std::string opt_key : {"end_filament_gcode", "start_filament_gcode"}) {
+        auto opt = print_config.opt<ConfigOptionStrings>(opt_key);
+        if (opt != nullptr)
+            for (std::string &custom_gcode : opt->values) {
+                // check & replace setting name
+                for (auto &entry : key_translation_map) {
+                    boost::replace_all(custom_gcode, entry.first, entry.second);
+                }
+                // check & replace special things
+                for (auto &entry : custom_gcode_replace) {
+                    boost::replace_all(custom_gcode, entry.first, entry.second);
+                }
+            }
+    }
+}
 
 } // BBconfiguration
 
@@ -536,9 +690,13 @@ bool read_json_file_bambu(const std_path &temp_file,
     for (auto &entry : key_values) {
         t_config_option_key opt_key = entry.first;
         std::string         value   = entry.second;
+
+        if (push_into_custom_variable(config, opt_key, value))
+            continue;
+
         if (auto it = key_translation_map.find(opt_key); it != key_translation_map.end())
             opt_key = it->second;
-        PrintConfigDef::handle_legacy(opt_key, value);
+        PrintConfigDef::handle_legacy(opt_key, value, false);
 
         complicated_convert(opt_key, value, key_values, good_key_values);
 
@@ -548,18 +706,24 @@ bool read_json_file_bambu(const std_path &temp_file,
 
         if (!opt_key.empty())
             good_key_values[opt_key] = value;
+        //else
+        //    config_substitutions.substitutions.push_back(ConfigSubstitution{ nullptr, entry.first+std::string(" : ")+value, nullptr});
     }
 
     for (auto &entry : key_vector_values) {
         t_config_option_key      key    = entry.first;
         std::vector<std::string> values = entry.second;
+        if (push_into_custom_variables(config, key, values))
+            continue;
         if (auto it = key_translation_map.find(key); it != key_translation_map.end())
             key = it->second;
         std::string check_val = values[0];
-        PrintConfigDef::handle_legacy(key, values[0]);
+        PrintConfigDef::handle_legacy(key, values[0], false);
         assert(check_val == values[0]); // can't change a vec value, sadly.
         if (!key.empty())
             good_key_vector_values[key] = values;
+        //else
+        //    config_substitutions.substitutions.push_back(ConfigSubstitution{ nullptr, entry.first+std::string(" : ")+(values.empty()?"":values.front()), nullptr});
     }
 
     // check how to serialize the array (string use ';', others ',')
@@ -608,16 +772,27 @@ bool read_json_file_bambu(const std_path &temp_file,
         }
         if (valid)
             good_key_values[opt_key] = value_str;
+        else if(optdef)
+            config_substitutions.add(ConfigSubstitution( optdef, value_str, ConfigOptionUniquePtr(optdef->default_value->clone()) ));
+        else
+            config_substitutions.add(ConfigSubstitution(entry.first, value_str));
+
     }
 
     // push these into config
+
     for (auto &entry : good_key_values) {
-        config.set_deserialize(entry.first, entry.second, config_substitutions);
+        if(config_def->has(entry.first))
+            config.set_deserialize(entry.first, entry.second, config_substitutions);
+        else
+            config_substitutions.add(ConfigSubstitution(entry.first, entry.second));
     }
 
     // final transform
     config.convert_from_prusa(with_phony);
 
+    custom_gcode_transform(config);
+
     return true;
 }
 
@@ -650,9 +825,13 @@ bool convert_settings_from_bambu(std::map<std::string, std::string> bambu_settin
     for (auto &entry : bambu_settings_serialized) {
         t_config_option_key opt_key = entry.first;
         std::string         value   = entry.second;
+
+        if (push_into_custom_variable(print_config, opt_key, value))
+            continue;
+
         if (auto it = key_translation_map.find(opt_key); it != key_translation_map.end())
             opt_key = it->second;
-        PrintConfigDef::handle_legacy(opt_key, value);
+        PrintConfigDef::handle_legacy(opt_key, value, false);
         
         complicated_convert(opt_key, value, bambu_settings_serialized, good_key_values);
 
@@ -666,12 +845,18 @@ bool convert_settings_from_bambu(std::map<std::string, std::string> bambu_settin
     
 
     // push these into config
+    const ConfigDef *config_def = print_config.def();
     for (auto &entry : good_key_values) {
-        print_config.set_deserialize(entry.first, entry.second, config_substitutions);
+        if(config_def->has(entry.first))
+            print_config.set_deserialize(entry.first, entry.second, config_substitutions);
+        else
+            config_substitutions.add(ConfigSubstitution(entry.first, entry.second));
     }
     
     // final transform
     print_config.convert_from_prusa(with_phony);
+    custom_gcode_transform(print_config);
+
     return true;
 }
 
@@ -692,7 +877,7 @@ bool convert_settings_from_bambu(std::map<std::string, std::string> bambu_settin
         std::string         value   = entry.second;
         if (auto it = key_translation_map.find(opt_key); it != key_translation_map.end())
             opt_key = it->second;
-        PrintConfigDef::handle_legacy(opt_key, value);
+        PrintConfigDef::handle_legacy(opt_key, value, false);
         
         complicated_convert(opt_key, value, bambu_settings_serialized, good_key_values);
 
@@ -706,8 +891,12 @@ bool convert_settings_from_bambu(std::map<std::string, std::string> bambu_settin
     
 
     // push these into config
+    const ConfigDef *config_def = object_config.get().def();
     for (auto &entry : good_key_values) {
-        object_config.set_deserialize(entry.first, entry.second, config_substitutions);
+        if(config_def->has(entry.first))
+            object_config.set_deserialize(entry.first, entry.second, config_substitutions);
+        else
+            config_substitutions.add(ConfigSubstitution(entry.first, entry.second));
     }
     
     // final transform

+ 19 - 0
src/libslic3r/Format/bbs_3mf.cpp

@@ -1409,6 +1409,25 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
         is_bbl_3mf = m_is_bbl_3mf;
         if (m_bambuslicer_generator_version)
             file_version = *m_bambuslicer_generator_version;
+        if (result && plate_data_list.size() > 0) {
+            if(config.opt<ConfigOptionBool>("gcode_label_objects") == nullptr)
+                config.set_key_value("gcode_label_objects", config.get_option_def("gcode_label_objects")->default_value->clone());
+            bool has_label_objests = config.opt<ConfigOptionBool>("gcode_label_objects")->value;
+            for (PlateData *plate : plate_data_list)
+                has_label_objests = has_label_objests || plate->is_label_object_enabled;
+            config.opt<ConfigOptionBool>("gcode_label_objects")->value = has_label_objests;
+            std::string print_vars = config.opt<ConfigOptionString>("print_custom_variables") == nullptr ? "" : config.opt<ConfigOptionString>("print_custom_variables")->value;
+            if (print_vars.find("plate_name") == std::string::npos && plate_data_list.front()) {
+                std::string plate_name = "";
+                for (PlateData *plate : plate_data_list)
+                    plate_name += plate_name.empty() ? plate->plate_name : std::string(" ; ") + plate->plate_name;
+                boost::replace_all(plate_name, "\"", "'");
+                if (!print_vars.empty() && print_vars.back() != '\n')
+                    print_vars += std::string("\n");
+                print_vars += std::string("plate_name=\"") + plate_name + std::string("\"\n");
+                config.set_key_value("print_custom_variables", new ConfigOptionString(print_vars));
+            }
+        }
         // save for restore
         //if (result && m_load_aux && !m_load_restore) {
         //    save_string_file(model.get_backup_path() + "/origin.txt", filename);

+ 1 - 1
src/libslic3r/GCode/GCodeProcessor.cpp

@@ -1592,7 +1592,7 @@ ConfigSubstitutions load_from_superslicer_gcode_file(const std::string& filename
     if (key_value_pairs < 80)
         throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs));
 
-    return std::move(substitutions_ctxt.substitutions);
+    return std::move(substitutions_ctxt).data();
 }
 
 void GCodeProcessor::apply_config_superslicer(const std::string& filename)

+ 1 - 1
src/libslic3r/Model.cpp

@@ -167,7 +167,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
     CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
     CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
 
-    sort_remove_duplicates(config_substitutions->substitutions);
+    config_substitutions->sort_and_remove_duplicates();
     return model;
 }
 

Some files were not shown because too many files changed in this diff