From ecb1a23edde9dc1f1dacc4928cfc6103f14c4d7e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Jul 2023 13:18:04 +0200 Subject: [PATCH 01/48] SPE-1784: New compressed (binary) gcode format integration 1st installment as part of tech ENABLE_BINARIZED_GCODE Still missing GCode Block save/load --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/GCode.cpp | 303 +++++++-- src/libslic3r/GCode.hpp | 7 + src/libslic3r/GCode/GCodeBinarizer.cpp | 909 +++++++++++++++++++++++++ src/libslic3r/GCode/GCodeBinarizer.hpp | 330 +++++++++ src/libslic3r/GCode/GCodeProcessor.cpp | 264 ++++++- src/libslic3r/GCode/GCodeProcessor.hpp | 16 + src/libslic3r/GCode/Thumbnails.hpp | 32 + src/libslic3r/Preset.cpp | 4 + src/libslic3r/Print.cpp | 3 + src/libslic3r/PrintConfig.cpp | 8 + src/libslic3r/PrintConfig.hpp | 3 + src/libslic3r/Technologies.hpp | 9 + src/slic3r/GUI/Tab.cpp | 3 + 14 files changed, 1818 insertions(+), 75 deletions(-) create mode 100644 src/libslic3r/GCode/GCodeBinarizer.cpp create mode 100644 src/libslic3r/GCode/GCodeBinarizer.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..2c4a22fc5d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -162,6 +162,8 @@ set(SLIC3R_SOURCES GCode/WipeTower.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp + GCode/GCodeBinarizer.cpp + GCode/GCodeBinarizer.hpp GCode/AvoidCrossingPerimeters.cpp GCode/AvoidCrossingPerimeters.hpp GCode.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1f306c83c6..6f148cc695 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -19,7 +19,10 @@ #include "ClipperUtils.hpp" #include "libslic3r.h" #include "LocalesUtils.hpp" -#include "libslic3r/format.hpp" +#include "format.hpp" +#if ENABLE_BINARIZED_GCODE +#include "libslic3r_version.h" +#endif // ENABLE_BINARIZED_GCODE #include #include @@ -837,6 +840,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); +#if ENABLE_BINARIZED_GCODE + m_processor.get_binary_data().reset(); +#endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); @@ -969,35 +975,47 @@ namespace DoExport { } // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. +#if ENABLE_BINARIZED_GCODE static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, - const WipeTowerData &wipe_tower_data, + const WipeTowerData &wipe_tower_data, + const FullPrintConfig &config, + const std::vector &extruders, + unsigned int initial_extruder_id, + PrintStatistics &print_statistics, + bool export_binary_data, + BinaryGCode::BinaryData &binary_data) +#else + static std::string update_print_stats_and_format_filament_stats( + const bool has_wipe_tower, + const WipeTowerData &wipe_tower_data, const FullPrintConfig &config, - const std::vector &extruders, + const std::vector &extruders, unsigned int initial_extruder_id, PrintStatistics &print_statistics) +#endif // ENABLE_BINARIZED_GCODE { - std::string filament_stats_string_out; + std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { + if (! extruders.empty()) { std::pair out_filament_used_mm ("; filament used [mm] = ", 0); std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); std::pair out_filament_used_g ("; filament used [g] = ", 0); std::pair out_filament_cost ("; filament cost = ", 0); for (const Extruder &extruder : extruders) { - print_statistics.printing_extruders.emplace_back(extruder.id()); - filament_types.emplace_back(config.filament_type.get_at(extruder.id())); + print_statistics.printing_extruders.emplace_back(extruder.id()); + filament_types.emplace_back(config.filament_type.get_at(extruder.id())); double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { - assert(is_decimal_separator_point()); + auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { + assert(is_decimal_separator_point()); while (dst.second < extruder.id()) { // Fill in the non-printing extruders with zeros. dst.first += (dst.second > 0) ? ", 0" : "0"; @@ -1006,18 +1024,47 @@ namespace DoExport { if (dst.second > 0) dst.first += ", "; char buf[64]; - sprintf(buf, tmpl, value); + sprintf(buf, tmpl, value); dst.first += buf; ++ dst.second; - }; - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); - if (filament_weight > 0.) { + }; +#if ENABLE_BINARIZED_GCODE + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", used_filament); + binary_data.print_metadata.raw_data.push_back({ "filament used [mm]", std::string(buf) }); + sprintf(buf, "%.2lf", extruded_volume * 0.001); + binary_data.print_metadata.raw_data.push_back({ "filament used [cm3]", std::string(buf) }); + } + else { +#endif // ENABLE_BINARIZED_GCODE + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE + if (filament_weight > 0.) { print_statistics.total_weight = print_statistics.total_weight + filament_weight; - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { +#if ENABLE_BINARIZED_GCODE + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_weight); + binary_data.print_metadata.raw_data.push_back({ "filament used [g]", std::string(buf) }); + } + else +#endif // ENABLE_BINARIZED_GCODE + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { print_statistics.total_cost = print_statistics.total_cost + filament_cost; - append(out_filament_cost, "%.2lf", filament_cost); +#if ENABLE_BINARIZED_GCODE + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_cost); + binary_data.print_metadata.raw_data.push_back({ "filament cost", std::string(buf) }); + } + else +#endif // ENABLE_BINARIZED_GCODE + append(out_filament_cost, "%.2lf", filament_cost); } } print_statistics.total_used_filament += used_filament; @@ -1026,18 +1073,18 @@ namespace DoExport { print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; } filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; - print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); - std::sort(filament_types.begin(), filament_types.end()); - print_statistics.printing_filament_types = filament_types.front(); - for (size_t i = 1; i < filament_types.size(); ++ i) { - print_statistics.printing_filament_types += ","; - print_statistics.printing_filament_types += filament_types[i]; - } + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; + print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); + std::sort(filament_types.begin(), filament_types.end()); + print_statistics.printing_filament_types = filament_types.front(); + for (size_t i = 1; i < filament_types.size(); ++ i) { + print_statistics.printing_filament_types += ","; + print_statistics.printing_filament_types += filament_types[i]; + } } return filament_stats_string_out; } @@ -1082,8 +1129,42 @@ std::vector sort_object_instances_by_model_order(const Pri void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { +#if ENABLE_BINARIZED_GCODE + const bool export_to_binary_gcode = print.full_print_config().option("gcode_binary")->value; + // if exporting gcode in binary format: + // we generate here the data to be passed to the post-processor, who is responsible to export them to file + // 1) generate the thumbnails + // 2) collect the config data + if (export_to_binary_gcode) { + BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + + // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // If "thumbnails_format" is not defined, export to PNG. + if (const auto [thumbnails, thumbnails_format] = std::make_pair( + print.full_print_config().option("thumbnails"), + print.full_print_config().option>("thumbnails_format")); + thumbnails) { + GCodeThumbnails::generate_binary_thumbnails( + thumbnail_cb, binary_data.thumbnails, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + [&print]() { print.throw_if_canceled(); }); + } + + // file data + binary_data.file_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); + // add here other key/value pairs + + // config data + binary_data.slicer_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + encode_full_config(print, binary_data.slicer_metadata.raw_data); + } + // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); +#else + // modifies m_silent_time_estimator_enabled + DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); +#endif // ENABLE_BINARIZED_GCODE if (! print.config().gcode_substitutions.values.empty()) { m_find_replace = make_unique(print.config()); @@ -1138,16 +1219,23 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); - // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". - // If "thumbnails_format" is not defined, export to PNG. - if (const auto [thumbnails, thumbnails_format] = std::make_pair( - print.full_print_config().option("thumbnails"), - print.full_print_config().option>("thumbnails_format")); - thumbnails) - GCodeThumbnails::export_thumbnails_to_file( - thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, - [&file](const char* sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); +#if ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, generate the thumbnails here + if (!export_to_binary_gcode) { +#endif // ENABLE_BINARIZED_GCODE + // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // If "thumbnails_format" is not defined, export to PNG. + if (const auto [thumbnails, thumbnails_format] = std::make_pair( + print.full_print_config().option("thumbnails"), + print.full_print_config().option>("thumbnails_format")); + thumbnails) + GCodeThumbnails::export_thumbnails_to_file( + thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + [&file](const char* sz) { file.write(sz); }, + [&print]() { print.throw_if_canceled(); }); +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE // Write notes (content of the Print Settings tab -> Notes) { @@ -1169,20 +1257,26 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato const double layer_height = first_object->config().layer_height.value; assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; - for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { - const PrintRegion ®ion = print.get_print_region(region_id); - file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); - file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); - file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); - file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); - file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); - if (print.has_support_material()) - file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); - if (print.config().first_layer_extrusion_width.value > 0) - file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); - file.write_format("\n"); +#if ENABLE_BINARIZED_GCODE + if (!export_to_binary_gcode) { +#endif // ENABLE_BINARIZED_GCODE + for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { + const PrintRegion ®ion = print.get_print_region(region_id); + file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); + file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); + file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); + file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); + file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); + if (print.has_support_material()) + file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); + if (print.config().first_layer_extrusion_width.value > 0) + file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); + file.write_format("\n"); + } + print.throw_if_canceled(); +#if ENABLE_BINARIZED_GCODE } - print.throw_if_canceled(); +#endif // ENABLE_BINARIZED_GCODE // adds tags for time estimators if (print.config().remaining_times.value) @@ -1487,31 +1581,66 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.throw_if_canceled(); // Get filament stats. +#if ENABLE_BINARIZED_GCODE file.write(DoExport::update_print_stats_and_format_filament_stats( - // Const inputs + // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), m_writer.extruders(), initial_extruder_id, // Modifies - print.m_print_statistics)); - file.write("\n"); - file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); - file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); - if (print.m_print_statistics.total_toolchanges > 0) - file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); - file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + print.m_print_statistics, + export_to_binary_gcode, + m_processor.get_binary_data() + )); - // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. - // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. - { - file.write("\n; prusaslicer_config = begin\n"); - std::string full_config; - append_full_config(print, full_config); - if (!full_config.empty()) - file.write(full_config); - file.write("; prusaslicer_config = end\n"); + if (export_to_binary_gcode) { + BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + char buf[128]; + sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); + binary_data.print_metadata.raw_data.push_back({ "total filament used [g]", std::string(buf) }); + sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); + binary_data.print_metadata.raw_data.push_back({ "total filament cost", std::string(buf) }); + if (print.m_print_statistics.total_toolchanges > 0) + binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } + else { +#else + file.write(DoExport::update_print_stats_and_format_filament_stats( + // Const inputs + has_wipe_tower, print.wipe_tower_data(), + this->config(), + m_writer.extruders(), + initial_extruder_id, + // Modifies + print.m_print_statistics)); +#endif // ENABLE_BINARIZED_GCODE +#if ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, statistics export is done here +#endif // ENABLE_BINARIZED_GCODE + file.write("\n"); + file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); + file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); + if (print.m_print_statistics.total_toolchanges > 0) + file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + +#if ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, config export is done here +#endif // ENABLE_BINARIZED_GCODE + // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. + // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. + { + file.write("\n; prusaslicer_config = begin\n"); + std::string full_config; + append_full_config(print, full_config); + if (!full_config.empty()) + file.write(full_config); + file.write("; prusaslicer_config = end\n"); + } +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE print.throw_if_canceled(); } @@ -2571,6 +2700,13 @@ void GCode::apply_print_config(const PrintConfig &print_config) void GCode::append_full_config(const Print &print, std::string &str) { +#if ENABLE_BINARIZED_GCODE + std::vector> config; + encode_full_config(print, config); + for (const auto& [key, value] : config) { + str += "; " + key + " = " + value + "\n"; + } +#else const DynamicPrintConfig &cfg = print.full_print_config(); // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. static constexpr auto banned_keys = { @@ -2588,8 +2724,35 @@ void GCode::append_full_config(const Print &print, std::string &str) for (const std::string &key : cfg.keys()) if (! is_banned(key) && ! cfg.option(key)->is_nil()) str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; +#endif // ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE +void GCode::encode_full_config(const Print& print, std::vector>& config) +{ + const DynamicPrintConfig& cfg = print.full_print_config(); + // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. + static constexpr auto banned_keys = { + "compatible_printers"sv, + "compatible_prints"sv, + //FIXME The print host keys should not be exported to full_print_config anymore. The following keys may likely be removed. + "print_host"sv, + "printhost_apikey"sv, + "printhost_cafile"sv + }; + assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); + auto is_banned = [](const std::string& key) { + return std::binary_search(banned_keys.begin(), banned_keys.end(), key); + }; + config.reserve(config.size() + cfg.keys().size()); + for (const std::string& key : cfg.keys()) { + if (!is_banned(key) && !cfg.option(key)->is_nil()) + config.emplace_back(key, cfg.opt_serialize(key)); + } + config.shrink_to_fit(); +} +#endif // ENABLE_BINARIZED_GCODE + void GCode::set_extruders(const std::vector &extruder_ids) { m_writer.set_extruders(extruder_ids); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 346ececba7..7358c15058 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -21,6 +21,9 @@ #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" #include "GCode/ThumbnailData.hpp" +#if ENABLE_BINARIZED_GCODE +#include "GCode/GCodeBinarizer.hpp" +#endif // ENABLE_BINARIZED_GCODE #include #include @@ -187,6 +190,10 @@ public: // append full config to the given string static void append_full_config(const Print& print, std::string& str); +#if ENABLE_BINARIZED_GCODE + // translate full config into a list of items + static void encode_full_config(const Print& print, std::vector>& config); +#endif // ENABLE_BINARIZED_GCODE // Object and support extrusions of the same PrintObject at the same print_z. // public, so that it could be accessed by free helper functions from GCode.cpp diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp new file mode 100644 index 0000000000..9a205f5327 --- /dev/null +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -0,0 +1,909 @@ +#include "GCodeBinarizer.hpp" + +#if ENABLE_BINARIZED_GCODE_DEBUG +#define NOMINMAX +#include +#include +#endif // ENABLE_BINARIZED_GCODE_DEBUG + +#include +#include + +namespace BinaryGCode { + +static size_t g_checksum_max_cache_size = 65536; + +std::string translate_result(BinaryGCode::EResult result) +{ + switch (result) + { + case BinaryGCode::EResult::Success: { return "Success"; } + case BinaryGCode::EResult::ReadError: { return "Read error"; } + case BinaryGCode::EResult::WriteError: { return "Write error"; } + case BinaryGCode::EResult::InvalidMagicNumber: { return "Invalid magic number"; } + case BinaryGCode::EResult::InvalidVersionNumber: { return "Invalid version number"; } + case BinaryGCode::EResult::InvalidChecksumType: { return "Invalid checksum type"; } + case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } + case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } + case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } + case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } + case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } + case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } + case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } + case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } + case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } + case BinaryGCode::EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } + case BinaryGCode::EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } + case BinaryGCode::EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } + } + return std::string(); +} + +size_t get_checksum_max_cache_size() { return g_checksum_max_cache_size; } +void set_checksum_max_cache_size(size_t size) { g_checksum_max_cache_size = size; } + +static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } +static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } +static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } +static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } +static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } + +static bool write_to_file(FILE& file, const void* data, size_t data_size) +{ + fwrite(data, 1, data_size, &file); + return !ferror(&file); +} + +static bool read_from_file(FILE& file, void* data, size_t data_size) +{ + fread(data, 1, data_size, &file); + return !ferror(&file); +} + +static bool encode_metadata(const std::vector>& data_in, std::vector& data_out, + EMetadataEncodingType encoding_type) +{ + for (const auto& [key, value] : data_in) { + switch (encoding_type) + { + case EMetadataEncodingType::INI: + { + data_out.insert(data_out.end(), key.begin(), key.end()); + data_out.emplace_back('='); + data_out.insert(data_out.end(), value.begin(), value.end()); + data_out.emplace_back('\n'); + break; + } + } + } + + return true; +} + +static bool decode_metadata(const std::vector& data_in, std::vector>& data_out, + EMetadataEncodingType encoding_type) +{ + switch (encoding_type) + { + case EMetadataEncodingType::INI: + { + auto start_it = data_in.begin(); + auto end_it = data_in.begin(); + while (end_it != data_in.end()) { + while (end_it != data_in.end() && *end_it != '\n') { + ++end_it; + } + const std::string item(start_it, end_it); + const size_t pos = item.find_first_of('='); + if (pos != std::string::npos) { + data_out.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); + start_it = ++end_it; + } + } + break; + } + } + + return true; +} + +static bool compress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +{ + return true; +} + +static bool uncompress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +{ + return true; +} + +static uint32_t crc32_sw(const uint8_t* buffer, uint32_t length, uint32_t crc) +{ + uint32_t value = crc ^ 0xFFFFFFFF; + while (length--) { + value ^= (uint32_t)*buffer++; + for (int bit = 0; bit < 8; bit++) { + if (value & 1) + value = (value >> 1) ^ 0xEDB88320; + else + value >>= 1; + } + } + value ^= 0xFFFFFFFF; + return value; +} + +std::vector encode(const void* data, size_t data_size) +{ + std::vector ret(data_size); + memcpy(ret.data(), data, data_size); + return ret; +} + +Checksum::Checksum(EChecksumType type) +: m_type(type) +{ + if (m_type != EChecksumType::None) + m_checksum = std::vector(checksum_size(m_type), '\0'); +} + +EChecksumType Checksum::get_type() const +{ + return m_type; +} + +void Checksum::append(const std::vector& data) +{ + size_t remaining_data_size = std::distance(data.begin(), data.end()); + auto it_begin = data.begin(); + while (remaining_data_size + m_cache.size() > g_checksum_max_cache_size) { + update(); + if (remaining_data_size > g_checksum_max_cache_size) { + m_cache.insert(m_cache.end(), it_begin, it_begin + g_checksum_max_cache_size); + it_begin += g_checksum_max_cache_size; + remaining_data_size -= g_checksum_max_cache_size; + } + } + + m_cache.insert(m_cache.end(), it_begin, data.end()); +} + +bool Checksum::matches(Checksum& other) +{ + update(); + other.update(); + return m_checksum == other.m_checksum; +} + +EResult Checksum::write(FILE& file) +{ + if (m_type != EChecksumType::None) { + update(); + if (!write_to_file(file, (const void*)m_checksum.data(), m_checksum.size())) + return EResult::WriteError; + } + return EResult::Success; +} + +EResult Checksum::read(FILE& file) +{ + if (m_type != EChecksumType::None) { + if (!read_from_file(file, (void*)m_checksum.data(), m_checksum.size())) + return EResult::ReadError; + } + return EResult::Success; +} + +void Checksum::update() +{ + if (m_cache.empty()) + return; + + switch (m_type) + { + case EChecksumType::None: + { + break; + } + case EChecksumType::CRC32: + { + const uint32_t old_crc = *(uint32_t*)m_checksum.data(); + const uint32_t new_crc = crc32_sw(m_cache.data(), (uint32_t)m_cache.size(), old_crc); + *(uint32_t*)m_checksum.data() = new_crc; + break; + } + } + + m_cache.clear(); +} + +EResult FileHeader::write(FILE& file) const +{ + if (magic != *(uint32_t*)(MAGIC.data())) + return EResult::InvalidMagicNumber; + if (checksum_type >= checksum_types_count()) + return EResult::InvalidChecksumType; + + if (!write_to_file(file, (const void*)&magic, sizeof(magic))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&version, sizeof(version))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&checksum_type, sizeof(checksum_type))) + return EResult::WriteError; + + return EResult::Success; +} + +EResult FileHeader::read(FILE& file, const uint32_t* const max_version) +{ + if (!read_from_file(file, (void*)&magic, sizeof(magic))) + return EResult::ReadError; + if (magic != *(uint32_t*)(MAGIC.data())) + return EResult::InvalidMagicNumber; + + if (!read_from_file(file, (void*)&version, sizeof(version))) + return EResult::ReadError; + if (max_version != nullptr && version > *max_version) + return EResult::InvalidVersionNumber; + + if (!read_from_file(file, (void*)&checksum_type, sizeof(checksum_type))) + return EResult::ReadError; + if (checksum_type >= checksum_types_count()) + return EResult::InvalidChecksumType; + + return EResult::Success; +} + +void BlockHeader::update_checksum(Checksum& checksum) const +{ + checksum.append(encode((const void*)&type, sizeof(type))); + checksum.append(encode((const void*)&compression, sizeof(compression))); + checksum.append(encode((const void*)&uncompressed_size, sizeof(uncompressed_size))); + if (compression != (uint16_t)ECompressionType::None) + checksum.append(encode((const void*)&compressed_size, sizeof(compressed_size))); +} + +EResult BlockHeader::write(FILE& file) const +{ + if (!write_to_file(file, (const void*)&type, sizeof(type))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&compression, sizeof(compression))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&uncompressed_size, sizeof(uncompressed_size))) + return EResult::WriteError; + if (compression != (uint16_t)ECompressionType::None) { + if (!write_to_file(file, (const void*)&compressed_size, sizeof(compressed_size))) + return EResult::WriteError; + } + return EResult::Success; +} + +EResult BlockHeader::read(FILE& file) +{ + if (!read_from_file(file, (void*)&type, sizeof(type))) + return EResult::ReadError; + if (type >= block_types_count()) + return EResult::InvalidBlockType; + + if (!read_from_file(file, (void*)&compression, sizeof(compression))) + return EResult::ReadError; + if (compression >= compression_types_count()) + return EResult::InvalidCompressionType; + + if (!read_from_file(file, (void*)&uncompressed_size, sizeof(uncompressed_size))) + return EResult::ReadError; + if (compression != (uint16_t)ECompressionType::None) { + if (!read_from_file(file, (void*)&compressed_size, sizeof(compressed_size))) + return EResult::ReadError; + } + + return EResult::Success; +} + +EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const +{ + if (encoding_type > metadata_encoding_types_count()) + return EResult::InvalidMetadataEncodingType; + + BlockHeader block_header = { (uint16_t)block_type, (uint16_t)compression_type, (uint32_t)0 }; + std::vector out_data; + if (!raw_data.empty()) { + // process payload encoding + std::vector uncompressed_data; + if (!encode_metadata(raw_data, uncompressed_data, (EMetadataEncodingType)encoding_type)) + return EResult::MetadataEncodingError; + // process payload compression + block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); + std::vector compressed_data; + if (compression_type != ECompressionType::None) { + if (!compress(uncompressed_data, compressed_data, compression_type)) + return EResult::DataCompressionError; + block_header.compressed_size = (uint32_t)compressed_data.size(); + } + out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); + } + + // write block header + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) + return EResult::WriteError; + if (!out_data.empty()) { + if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) + return EResult::WriteError; + } + + if (checksum.get_type() != EChecksumType::None) { + // update checksum with block header + block_header.update_checksum(checksum); + // update checksum with block payload + checksum.append(encode((const void*)&encoding_type, sizeof(encoding_type))); + if (!out_data.empty()) + checksum.append(out_data); + } + + return EResult::Success; +} + +EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header) +{ + const ECompressionType compression_type = (ECompressionType)block_header.compression; + + if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) + return EResult::ReadError; + if (encoding_type > metadata_encoding_types_count()) + // Found invalid metadata encoding type + return EResult::InvalidMetadataEncodingType; + + std::vector data; + const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; + if (data_size > 0) { + data.resize(data_size); + if (!read_from_file(file, (void*)data.data(), data_size)) + return EResult::ReadError; + } + + std::vector uncompressed_data; + if (compression_type != ECompressionType::None) { + if (!uncompress(data, uncompressed_data, compression_type)) + return EResult::DataUncompressionError; + } + + if (!decode_metadata((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EMetadataEncodingType)encoding_type)) + return EResult::MetadataDecodingError; + + return EResult::Success; +} + +EResult FileMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::FileMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult FileMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult ThumbnailBlock::write(FILE& file, EChecksumType checksum_type) const +{ + if (format >= thumbnail_formats_count()) + return EResult::InvalidThumbnailFormat; + if (width == 0) + return EResult::InvalidThumbnailWidth; + if (height == 0) + return EResult::InvalidThumbnailHeight; + if (data.size() == 0) + return EResult::InvalidThumbnailDataSize; + + // write block header + const BlockHeader block_header = { (uint16_t)EBlockType::Thumbnail, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&format, sizeof(format))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&width, sizeof(width))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&height, sizeof(height))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)data.data(), data.size())) + return EResult::WriteError; + + if (checksum_type != EChecksumType::None) { + Checksum cs(checksum_type); + // update checksum with block header + block_header.update_checksum(cs); + // update checksum with block payload + update_checksum(cs); + // write block checksum + res = cs.write(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + if (!read_from_file(file, (void*)&format, sizeof(format))) + return EResult::ReadError; + if (format >= thumbnail_formats_count()) + return EResult::InvalidThumbnailFormat; + if (!read_from_file(file, (void*)&width, sizeof(width))) + return EResult::ReadError; + if (width == 0) + return EResult::InvalidThumbnailWidth; + if (!read_from_file(file, (void*)&height, sizeof(height))) + return EResult::ReadError; + if (height == 0) + return EResult::InvalidThumbnailHeight; + if (block_header.uncompressed_size == 0) + return EResult::InvalidThumbnailDataSize; + data.resize(block_header.uncompressed_size); + if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) + return EResult::ReadError; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + const EResult res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +void ThumbnailBlock::update_checksum(Checksum& checksum) const +{ + checksum.append(encode((const void*)&format, sizeof(format))); + checksum.append(encode((const void*)&width, sizeof(width))); + checksum.append(encode((const void*)&height, sizeof(height))); + checksum.append(data); +} + +EResult PrinterMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::PrinterMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult PrinterMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult PrintMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::PrintMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult PrintMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult SlicerMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::SlicerMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + return EResult::Success; +} + +EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + return EResult::Success; +} + +#if ENABLE_CHECKSUM_BLOCK +EResult ChecksumBlock::write(FILE& file) const +{ + if (!data.empty()) { + const BlockHeader block_header = { (uint16_t)EBlockType::Checksum, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; + // write block header + const EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + // write block payload + if (!write_to_file(file, (const void*)data.data(), data.size())) + return EResult::WriteError; + } + + return EResult::Success; +} + +EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) +{ + if (block_header.uncompressed_size > 0) { + data.resize(block_header.uncompressed_size); + if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) + return EResult::ReadError; + } + else + data.clear(); + + return EResult::Success; +} +#endif // ENABLE_CHECKSUM_BLOCK + +EResult Binarizer::initialize(FILE& file, EChecksumType checksum_type) +{ + if (!m_enabled) + return EResult::Success; + + // initialize checksum + m_checksum_type = checksum_type; +#if ENABLE_CHECKSUM_BLOCK + m_checksum = ChecksumBlock(); +#endif // ENABLE_CHECKSUM_BLOCK + + // save header + FileHeader file_header; + file_header.checksum_type = (uint16_t)m_checksum_type; + EResult res = file_header.write(file); + if (res != EResult::Success) + return res; + + // save file metadata block + res = m_binary_data.file_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + + // save printer metadata block + res = m_binary_data.printer_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + + // save thumbnail blocks + for (const ThumbnailBlock& block : m_binary_data.thumbnails) { + res = block.write(file, m_checksum_type); + if (res != EResult::Success) + return res; + } + + // save slicer metadata block + res = m_binary_data.slicer_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + + // save gcode block + + return EResult::Success; +} + +EResult Binarizer::finalize(FILE& file) +{ + if (!m_enabled) + return EResult::Success; + + // save print metadata block + EResult res = m_binary_data.print_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + +#if ENABLE_CHECKSUM_BLOCK + if (m_checksum_type != EChecksumType::None) { + // save checksum + // dummy checksum until it is not properly implemented + switch (m_checksum_type) + { + case EChecksumType::CRC32: + case EChecksumType::MD5: + { + m_checksum.data.clear(); + break; + } + } + + res = m_checksum.write(file); + if (res != EResult::Success) + return res; + } +#endif // ENABLE_CHECKSUM_BLOCK + + return EResult::Success; +} + +bool is_valid_binary_gcode(FILE& file) +{ + // cache file position + const long curr_pos = ftell(&file); + rewind(&file); + + std::array magic; + fread((void*)magic.data(), 1, magic.size(), &file); + if (ferror(&file)) + return false; + else { + // restore file position + fseek(&file, curr_pos, SEEK_SET); + return magic == MAGIC; + } +} + +EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version) +{ + rewind(&file); + return header.read(file, max_version); +} + +static EResult checksums_match(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // cache file position + const long curr_pos = ftell(&file); + + Checksum curr_cs((EChecksumType)file_header.checksum_type); + // update block checksum block header + block_header.update_checksum(curr_cs); + + // read block payload + size_t remaining_payload_size = block_payload_size(block_header); + while (remaining_payload_size > 0) { + const size_t size_to_read = std::min(remaining_payload_size, g_checksum_max_cache_size); + std::vector payload(size_to_read); + if (!read_from_file(file, payload.data(), payload.size())) + return EResult::ReadError; + curr_cs.append(payload); + remaining_payload_size -= size_to_read; + } + + // read checksum + Checksum read_cs((EChecksumType)file_header.checksum_type); + EResult res = read_cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + + // Verify checksum + if (!curr_cs.matches(read_cs)) + return EResult::InvalidChecksum; + + // restore file position + fseek(&file, curr_pos, SEEK_SET); + + return EResult::Success; +} + +EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum) +{ + if (verify_checksum && (EChecksumType)file_header.checksum_type != EChecksumType::None) { + const EResult res = block_header.read(file); + if (res != EResult::Success) + // propagate error + return res; + + return checksums_match(file, file_header, block_header); + } + else + return block_header.read(file); +} + +EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum) +{ + // cache file position + const long curr_pos = ftell(&file); + + do { + EResult res = read_next_block_header(file, file_header, block_header, false); + if (res != EResult::Success) + // propagate error + return res; + else if (feof(&file)) { + // block not found + // restore file position + fseek(&file, curr_pos, SEEK_SET); + return EResult::BlockNotFound; + } + else if ((EBlockType)block_header.type == type) { + // block found + if (verify_checksum) { + res = checksums_match(file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + else + break; + } + } + + if (!feof(&file)) { + res = skip_block_content(file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + } + } while (true); + + return EResult::Success; +} + +EResult skip_block_payload(FILE& file, const BlockHeader& block_header) +{ + fseek(&file, (long)block_payload_size(block_header), SEEK_CUR); + return ferror(&file) ? EResult::ReadError : EResult::Success; +} + +EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + fseek(&file, (long)block_content_size(file_header, block_header), SEEK_CUR); + return ferror(&file) ? EResult::ReadError : EResult::Success; +} + +size_t block_parameters_size(EBlockType type) +{ + switch (type) + { + case EBlockType::FileMetadata: { return FileMetadataBlock::get_parameters_size(); } + case EBlockType::GCode: { return GCodeBlock::get_parameters_size(); } + case EBlockType::SlicerMetadata: { return SlicerMetadataBlock::get_parameters_size(); } + case EBlockType::PrinterMetadata: { return PrinterMetadataBlock::get_parameters_size(); } + case EBlockType::PrintMetadata: { return PrintMetadataBlock::get_parameters_size(); } + case EBlockType::Thumbnail: { return ThumbnailBlock::get_parameters_size(); } + } + return 0; +} + +size_t block_payload_size(const BlockHeader& block_header) +{ + size_t ret = block_parameters_size((EBlockType)block_header.type); + ret += ((ECompressionType)block_header.compression == ECompressionType::None) ? + block_header.uncompressed_size : block_header.compressed_size; + return ret; +} + +size_t checksum_size(EChecksumType type) +{ + switch (type) + { + case EChecksumType::None: { return 0; } + case EChecksumType::CRC32: { return 4; } + } + return 0; +} + +extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header) +{ +#if ENABLE_CHECKSUM_BLOCK + return ((EBlockType)block_header.type == EBlockType::Checksum) ? + block_payload_size(block_header) : block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); +#else + return block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); +#endif // ENABLE_CHECKSUM_BLOCK +} + +} // namespace BinaryGCode + diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp new file mode 100644 index 0000000000..058cabd475 --- /dev/null +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -0,0 +1,330 @@ +#ifndef slic3r_GCode_GCodeBinarizer_hpp_ +#define slic3r_GCode_GCodeBinarizer_hpp_ + +#ifdef _WIN32 +#define ENABLE_BINARIZED_GCODE_DEBUG 1 +#endif // _WIN32 + +#define ENABLE_CHECKSUM_BLOCK 0 + +#include +#include +#include +#include +#include + +namespace BinaryGCode { + +static const std::array MAGIC{ 'G', 'C', 'D', 'E' }; +static const uint32_t VERSION = 1; + +enum class EResult : uint16_t +{ + Success, + ReadError, + WriteError, + InvalidMagicNumber, + InvalidVersionNumber, + InvalidChecksumType, + InvalidBlockType, + InvalidCompressionType, + InvalidMetadataEncodingType, + DataCompressionError, + DataUncompressionError, + MetadataEncodingError, + MetadataDecodingError, + BlockNotFound, + InvalidChecksum, + InvalidThumbnailFormat, + InvalidThumbnailWidth, + InvalidThumbnailHeight, + InvalidThumbnailDataSize +}; + +// Returns a string description of the given result +extern std::string translate_result(BinaryGCode::EResult result); + +enum class EChecksumType : uint16_t +{ + None, + CRC32 +}; + +class Checksum +{ +public: + // Constructs a checksum of the given type. + // The checksum data are sized accordingly. + explicit Checksum(EChecksumType type); + + EChecksumType get_type() const; + + // Appends the given data to the cache and performs a checksum update if + // the size of the cache exceeds the max checksum cache size. + void append(const std::vector& data); + // Returns true if the given checksum is equal to this one + bool matches(Checksum& other); + + EResult write(FILE& file); + EResult read(FILE& file); + +private: + EChecksumType m_type; + std::vector m_cache; + std::vector m_checksum; + + void update(); +}; + +struct FileHeader +{ + uint32_t magic{ *(uint32_t*)(MAGIC.data()) }; + uint32_t version{ VERSION }; + uint16_t checksum_type{ (uint16_t)EChecksumType::None }; + + EResult write(FILE& file) const; + EResult read(FILE& file, const uint32_t* const max_version); +}; + +enum class EBlockType : uint16_t +{ +#if ENABLE_CHECKSUM_BLOCK + Checksum, +#endif // ENABLE_CHECKSUM_BLOCK + FileMetadata, + GCode, + SlicerMetadata, + PrinterMetadata, + PrintMetadata, + Thumbnail +}; + +enum class ECompressionType : uint16_t +{ + None, +}; + +struct BlockHeader +{ + uint16_t type{ 0 }; + uint16_t compression{ 0 }; + uint32_t uncompressed_size{ 0 }; + uint32_t compressed_size{ 0 }; + + // Updates the given checksum with the data of this BlockHeader + void update_checksum(Checksum& checksum) const; + + EResult write(FILE& file) const; + EResult read(FILE& file); +}; + +enum class EMetadataEncodingType : uint16_t +{ + INI, +}; + +struct BaseMetadataBlock +{ + // type of data encoding + uint16_t encoding_type{ 0 }; + // data in key/value form + std::vector> raw_data; + + // write block header and data in encoded format + EResult write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const; + // read block data in encoded format + EResult read_data(FILE& file, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(encoding_type); } +}; + +struct FileMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +enum class EThumbnailFormat : uint16_t +{ + PNG, + JPG, + QOI +}; + +struct ThumbnailBlock +{ + uint16_t format{ 0 }; + uint16_t width{ 0 }; + uint16_t height{ 0 }; + std::vector data; + + // write block header and data + EResult write(FILE& file, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(format) + sizeof(width) + sizeof(height); } + +private: + void update_checksum(Checksum& checksum) const; +}; + +struct PrinterMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +struct PrintMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +struct SlicerMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +struct GCodeBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +#if ENABLE_CHECKSUM_BLOCK +struct ChecksumBlock +{ + std::vector data; + + // write block header and data + EResult write(FILE& file) const; + // read block data + EResult read_data(FILE& file, const BlockHeader& block_header); +}; +#endif // ENABLE_CHECKSUM_BLOCK + +//===================================================================================================================================== +// +// PRUSASLICER INTERFACE +// +//===================================================================================================================================== + +struct BinaryData +{ + FileMetadataBlock file_metadata; + PrinterMetadataBlock printer_metadata; + std::vector thumbnails; + SlicerMetadataBlock slicer_metadata; + PrintMetadataBlock print_metadata; + + void reset() { + file_metadata.raw_data.clear(); + printer_metadata.raw_data.clear(); + thumbnails.clear(); + slicer_metadata.raw_data.clear(); + print_metadata.raw_data.clear(); + } +}; + +class Binarizer +{ +public: + bool is_enabled() const { return m_enabled; } + void set_enabled(bool enable) { m_enabled = enable; } + void set_compression_type(ECompressionType type) { m_compression_type = type; } + + BinaryData& get_binary_data() { return m_binary_data; } + const BinaryData& get_binary_data() const { return m_binary_data; } + + EResult initialize(FILE& file, EChecksumType checksum_type); + EResult finalize(FILE& file); + +private: + bool m_enabled{ false }; + + EChecksumType m_checksum_type{ EChecksumType::None }; + ECompressionType m_compression_type{ ECompressionType::None }; + + BinaryData m_binary_data; +#if ENABLE_CHECKSUM_BLOCK + ChecksumBlock m_checksum; +#endif // ENABLE_CHECKSUM_BLOCK +}; + +//===================================================================================================================================== +// +// FIRMWARE INTERFACE +// +//===================================================================================================================================== + +// Get the max size of the cache used to calculate checksums, in bytes +size_t get_checksum_max_cache_size(); +// Set the max size of the cache used to calculate checksums, in bytes +void set_checksum_max_cache_size(size_t size); + +// Returns true if the given file is a valid binary gcode +// Does not modify the file position +extern bool is_valid_binary_gcode(FILE& file); + +// Reads the file header. +// If max_version is not null, version is checked against the passed value +// If return == EResult::Success: +// - header will contain the file header +// - file position will be set at the start of the 1st block header +extern EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version); + +// Reads next block header from the current file position. +// File position must be at the start of a block header. +// If return == EResult::Success: +// - block_header will contain the header of the block +// - file position will be set at the start of the block parameters data +extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum); + +// Searches and reads next block header with the given type from the current file position. +// File position must be at the start of a block header. +// If return == EResult::Success: +// - block_header will contain the header of the block with the required type +// - file position will be set at the start of the block parameters data +// otherwise: +// - file position will keep the current value +extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum); + +// Skips the payload (parameters + data) of the block with the given block header. +// File position must be at the start of the block parameters. +// If return == EResult::Success: +// - file position will be set at the start of the block checksum, if present, or of next block header +extern EResult skip_block_payload(FILE& file, const BlockHeader& block_header); + +// Skips the content (parameters + data + checksum) of the block with the given block header. +// File position must be at the start of the block parameters. +// If return == EResult::Success: +// - file position will be set at the start of the next block header +extern EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + +// Returns the size of the parameters of the given block type, in bytes. +extern size_t block_parameters_size(EBlockType type); + +// Returns the size of the payload (parameters + data) of the block with the given header, in bytes. +extern size_t block_payload_size(const BlockHeader& block_header); + +// Returns the size of the checksum of the given type, in bytes. +extern size_t checksum_size(EChecksumType type); + +// Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. +extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); + +} // namespace BinaryGCode + +#endif // slic3r_GCode_GCodeBinarizer_hpp_ diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f7fbb52a69..e6d97a3f8c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -25,6 +25,10 @@ #endif #include +#if ENABLE_BINARIZED_GCODE_DEBUG +#include +#include +#endif // ENABLE_BINARIZED_GCODE_DEBUG static const float DEFAULT_TOOLPATH_WIDTH = 0.4f; static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f; @@ -554,6 +558,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); +#if ENABLE_BINARIZED_GCODE + m_binarizer.set_enabled(config.gcode_binary); +#endif // ENABLE_BINARIZED_GCODE + m_producer = EProducer::PrusaSlicer; m_flavor = config.gcode_flavor; @@ -1023,6 +1031,23 @@ static inline const char* remove_eols(const char *begin, const char *end) { // Load a G-code into a stand-alone G-code viewer. // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) +#if ENABLE_BINARIZED_GCODE +{ + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + + const bool is_binary = BinaryGCode::is_valid_binary_gcode(*file); + fclose(file); + + if (is_binary) + process_binary_file(filename, cancel_callback); + else + process_ascii_file(filename, cancel_callback); +} + +void GCodeProcessor::process_ascii_file(const std::string& filename, std::function cancel_callback) +#endif // ENABLE_BINARIZED_GCODE { CNumericLocalesSetter locales_setter; @@ -1090,6 +1115,163 @@ void GCodeProcessor::process_file(const std::string& filename, std::functionfinalize(false); } +#if ENABLE_BINARIZED_GCODE +void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) +{ + class ScopedFile + { + public: + explicit ScopedFile(FILE* file) : m_file(file) {} + ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } + private: + FILE* m_file{ nullptr }; + }; + + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); + + ScopedFile scoped_file(file); + + BinaryGCode::set_checksum_max_cache_size(1024); + + // read file header + BinaryGCode::FileHeader file_header; + BinaryGCode::EResult res = BinaryGCode::read_header(*file, file_header, &BinaryGCode::VERSION); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + BinaryGCode::translate_result(res) + "\n"); + + const long first_block_header_position = ftell(file); + const bool verify_checksum = true; + + // read file metadata block + BinaryGCode::BlockHeader block_header; + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::FileMetadata) + throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); + BinaryGCode::FileMetadataBlock file_metadata_block; + res = file_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), + [](const std::pair& item) { return item.first == "Producer"; }); + if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) + m_producer = EProducer::PrusaSlicer; + else + m_producer = EProducer::Unknown; + + // read printer metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrinterMetadata) + throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); + BinaryGCode::PrinterMetadataBlock printer_metadata_block; + res = printer_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Printer metadata:\n"); + for (const auto& [key, value] : printer_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + + // read thumbnail blocks + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + + while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::Thumbnail) { + BinaryGCode::ThumbnailBlock thumbnail_block; + res = thumbnail_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + +#if ENABLE_BINARIZED_GCODE_DEBUG + if (thumbnail_block.data.size() > 0) { + auto format_filename = [](const std::string& stem, const BinaryGCode::ThumbnailBlock& block) { + std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); + switch ((BinaryGCode::EThumbnailFormat)block.format) + { + case BinaryGCode::EThumbnailFormat::PNG: { ret += ".png"; break; } + case BinaryGCode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case BinaryGCode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + } + return ret; + }; + + const boost::filesystem::path path(filename); + const std::string out_path = path.parent_path().string(); + const std::string out_filename = out_path + "\\" + format_filename(path.stem().string(), thumbnail_block); + FILE* outfile = boost::nowide::fopen(out_filename.c_str(), "wb"); + if (outfile != nullptr) { + fwrite((const void*)thumbnail_block.data.data(), 1, thumbnail_block.data.size(), outfile); + fclose(outfile); + } + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + } + + // read slicer metadata block + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) + throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); + BinaryGCode::SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Slicer metadata:\n"); + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + std::string str; + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + str += key + " = " + value + "\n"; + } + // Silently substitute unknown values by new ones for loading configurations from PrusaSlicer's own G-code. + // Showing substitution log or errors may make sense, but we are not really reading many values from the G-code config, + // thus a probability of incorrect substitution is low and the G-code viewer is a consumer-only anyways. + config.load_from_ini_string(str, ForwardCompatibilitySubstitutionRule::EnableSilent); + apply_config(config); + + // read print metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); + BinaryGCode::PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Print metadata:\n"); + for (const auto& [key, value] : print_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG +} +#endif // ENABLE_BINARIZED_GCODE + void GCodeProcessor::initialize(const std::string& filename) { assert(is_decimal_separator_point()); @@ -3439,6 +3621,15 @@ void GCodeProcessor::post_process() throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); } +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::set_checksum_max_cache_size(4096); + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EChecksumType::CRC32); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); + } +#endif // ENABLE_BINARIZED_GCODE + auto time_in_minutes = [](float time_in_seconds) { assert(time_in_seconds >= 0.f); return int((time_in_seconds + 0.5f) / 60.0f); @@ -3555,13 +3746,26 @@ void GCodeProcessor::post_process() size_t m_curr_g1_id{ 0 }; size_t m_out_file_pos{ 0 }; +#if ENABLE_BINARIZED_GCODE + BinaryGCode::Binarizer& m_binarizer; +#endif // ENABLE_BINARIZED_GCODE + public: +#if ENABLE_BINARIZED_GCODE + ExportLines(BinaryGCode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) +#ifndef NDEBUG + : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} +#else + : m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} +#endif // NDEBUG +#else ExportLines(EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_write_type(type), m_machine(machine) {} #else : m_write_type(type), m_machine(machine) {} #endif // NDEBUG +#endif // ENABLE_BINARIZED_GCODE void update(size_t lines_counter, size_t g1_lines_counter) { m_gcode_lines_map.push_back({ lines_counter, 0 }); @@ -3707,12 +3911,18 @@ void GCodeProcessor::post_process() private: void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path) { if (!out_string.empty()) { - fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); - if (ferror(out.f)) { - out.close(); - boost::nowide::remove(out_path.c_str()); - throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); +#if ENABLE_BINARIZED_GCODE + if (!m_binarizer.is_enabled()) { +#endif // ENABLE_BINARIZED_GCODE + fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); + if (ferror(out.f)) { + out.close(); + boost::nowide::remove(out_path.c_str()); + throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); + } +#if ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); @@ -3722,7 +3932,11 @@ void GCodeProcessor::post_process() } }; +#if ENABLE_BINARIZED_GCODE + ExportLines export_lines(m_binarizer, m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); +#else ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); +#endif // ENABLE_BINARIZED_GCODE // replace placeholder lines with the proper final value // gcode_line is in/out parameter, to reduce expensive memory allocation @@ -4035,6 +4249,46 @@ void GCodeProcessor::post_process() export_lines.flush(out, m_result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + // update print metadata + auto update_value = [](std::string& value, const std::vector& values) { + char buf[1024]; + value.clear(); + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); + value += buf; + } + }; + + // update binary data + BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + for (auto& [key, value] : binary_data.print_metadata.raw_data) { + if (key == "filament used [mm]") update_value(value, filament_mm); + else if (key == "filament used [g]") update_value(value, filament_g); + else if (key == "total filament used [g]") update_value(value, { filament_total_g }); + else if (key == "filament used [cm3]") update_value(value, filament_cm3); + else if (key == "filament cost") update_value(value, filament_cost); + else if (key == "total filament cost") update_value(value, { filament_total_cost }); + } + + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); + binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + } + } + + const BinaryGCode::EResult res = m_binarizer.finalize(*out.f); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); + } +#endif // ENABLE_BINARIZED_GCODE + out.close(); in.close(); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fcba4321ae..fe2db07f99 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -7,6 +7,10 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" +#if ENABLE_BINARIZED_GCODE +#include "GCodeBinarizer.hpp" +#endif // ENABLE_BINARIZED_GCODE + #include #include #include @@ -525,6 +529,9 @@ namespace Slic3r { private: GCodeReader m_parser; +#if ENABLE_BINARIZED_GCODE + BinaryGCode::Binarizer m_binarizer; +#endif // ENABLE_BINARIZED_GCODE EUnits m_units; EPositioningType m_global_positioning_type; @@ -622,6 +629,10 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } +#if ENABLE_BINARIZED_GCODE + BinaryGCode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const BinaryGCode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } +#endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { @@ -664,6 +675,11 @@ namespace Slic3r { void apply_config_kissslicer(const std::string& filename); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); +#if ENABLE_BINARIZED_GCODE + void process_ascii_file(const std::string& filename, std::function cancel_callback = nullptr); + void process_binary_file(const std::string& filename, std::function cancel_callback = nullptr); +#endif // ENABLE_BINARIZED_GCODE + // Process tags embedded into comments void process_tags(const std::string_view comment, bool producers_enabled); bool process_producers_tags(const std::string_view comment); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 30bb6b653b..b2b5d60cb1 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -4,6 +4,9 @@ #include "../Point.hpp" #include "../PrintConfig.hpp" #include "ThumbnailData.hpp" +#if ENABLE_BINARIZED_GCODE +#include "GCode/GCodeBinarizer.hpp" +#endif // ENABLE_BINARIZED_GCODE #include #include @@ -55,6 +58,35 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, } } +#if ENABLE_BINARIZED_GCODE +template +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, + const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) +{ + out_thumbnails.clear(); + if (thumbnail_cb != nullptr) { + ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true }); + for (const ThumbnailData& data : thumbnails) { + if (data.is_valid()) { + auto compressed = compress_thumbnail(data, format); + if (compressed->data != nullptr && compressed->size > 0) { + BinaryGCode::ThumbnailBlock& block = out_thumbnails.emplace_back(BinaryGCode::ThumbnailBlock()); + block.width = (uint16_t)data.width; + block.height = (uint16_t)data.height; + switch (format) { + case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::QOI; break; } + } + block.data.resize(compressed->size); + memcpy(block.data.data(), compressed->data, compressed->size); + } + } + } + } +} +#endif // ENABLE_BINARIZED_GCODE + } // namespace Slic3r::GCodeThumbnails #endif // slic3r_GCodeThumbnails_hpp_ diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f42b3f7708..5b1e86c321 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -449,7 +449,11 @@ static std::vector s_Preset_print_options { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", +#if ENABLE_BINARIZED_GCODE + "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "gcode_binary", "perimeter_extruder", +#else "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", +#endif // ENABLE_BINARIZED_GCODE "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8d53dbb5a8..2bd92340d8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -117,6 +117,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "perimeter_acceleration", "post_process", "gcode_substitutions", +#if ENABLE_BINARIZED_GCODE + "gcode_binary", +#endif // ENABLE_BINARIZED_GCODE "printer_notes", "retract_before_travel", "retract_before_wipe", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3da804066b..dd9e4f0578 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1431,6 +1431,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionStrings()); +#if ENABLE_BINARIZED_GCODE + def = this->add("gcode_binary", coBool); + def->label = L("Export as binary G-code"); + def->tooltip = L("Exports the G-code in binary format."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(0)); +#endif // ENABLE_BINARIZED_GCODE + def = this->add("high_current_on_filament_swap", coBool); def->label = L("High extruder current on filament swap"); def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 54a835fe79..806cdc500f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -698,6 +698,9 @@ PRINT_CONFIG_CLASS_DEFINE( // i - case insensitive // w - whole word ((ConfigOptionStrings, gcode_substitutions)) +//#if ENABLE_BINARIZED_GCODE + ((ConfigOptionBool, gcode_binary)) +//#endif // ENABLE_BINARIZED_GCODE ((ConfigOptionString, layer_gcode)) ((ConfigOptionFloat, max_print_speed)) ((ConfigOptionFloat, max_volumetric_speed)) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 602654633c..92b6f8578c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -62,4 +62,13 @@ #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1) +//==================== +// 2.6.1.alpha1 techs +//==================== +#define ENABLE_2_6_1_ALPHA1 1 + +// Enable export of binarized gcode +#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) + + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ad93afa879..5a3152df4c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1673,6 +1673,9 @@ void TabPrint::build() Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); +#if ENABLE_BINARIZED_GCODE + optgroup->append_single_option_line("gcode_binary"); +#endif // ENABLE_BINARIZED_GCODE optgroup = page->new_optgroup(L("Other")); From 3f5de75bae62a08da849b875640d5b616f818158 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 Jul 2023 14:20:00 +0200 Subject: [PATCH 02/48] SPE-1784: New compressed (binary) gcode format integration Added GCode Block save/load with no encoding Changed blocks order to: File metadata|Printer metadata|Thumbnails[]|Print metadata|Slicer metadata|GCode[] --- src/libslic3r/GCode.cpp | 188 ++++++++++--------- src/libslic3r/GCode/GCodeBinarizer.cpp | 243 ++++++++++++++++++++++--- src/libslic3r/GCode/GCodeBinarizer.hpp | 24 ++- src/libslic3r/GCode/GCodeProcessor.cpp | 208 +++++++++++++++------ src/libslic3r/Print.cpp | 22 +++ src/libslic3r/Print.hpp | 17 ++ 6 files changed, 533 insertions(+), 169 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6f148cc695..f6b772e946 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -997,94 +997,107 @@ namespace DoExport { { std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { - std::pair out_filament_used_mm ("; filament used [mm] = ", 0); - std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); - std::pair out_filament_used_g ("; filament used [g] = ", 0); - std::pair out_filament_cost ("; filament cost = ", 0); - for (const Extruder &extruder : extruders) { - print_statistics.printing_extruders.emplace_back(extruder.id()); - filament_types.emplace_back(config.filament_type.get_at(extruder.id())); + if (! extruders.empty()) { +#if ENABLE_BINARIZED_GCODE + std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0); + std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0); + std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0); + std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0); +#else + std::pair out_filament_used_mm("; filament used [mm] = ", 0); + std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); + std::pair out_filament_used_g ("; filament used [g] = ", 0); + std::pair out_filament_cost("; filament cost = ", 0); +#endif // ENABLE_BINARIZED_GCODE + for (const Extruder &extruder : extruders) { + print_statistics.printing_extruders.emplace_back(extruder.id()); + filament_types.emplace_back(config.filament_type.get_at(extruder.id())); - double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter - double filament_weight = extruded_volume * extruder.filament_density() * 0.001; - double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { - assert(is_decimal_separator_point()); - while (dst.second < extruder.id()) { - // Fill in the non-printing extruders with zeros. - dst.first += (dst.second > 0) ? ", 0" : "0"; - ++ dst.second; - } - if (dst.second > 0) - dst.first += ", "; - char buf[64]; - sprintf(buf, tmpl, value); - dst.first += buf; - ++ dst.second; - }; + double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { + assert(is_decimal_separator_point()); + while (dst.second < extruder.id()) { + // Fill in the non-printing extruders with zeros. + dst.first += (dst.second > 0) ? ", 0" : "0"; + ++ dst.second; + } + if (dst.second > 0) + dst.first += ", "; + char buf[64]; + sprintf(buf, tmpl, value); + dst.first += buf; + ++ dst.second; + }; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", used_filament); - binary_data.print_metadata.raw_data.push_back({ "filament used [mm]", std::string(buf) }); - sprintf(buf, "%.2lf", extruded_volume * 0.001); - binary_data.print_metadata.raw_data.push_back({ "filament used [cm3]", std::string(buf) }); - } - else { + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", used_filament); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, std::string(buf) }); + sprintf(buf, "%.2lf", extruded_volume * 0.001); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, std::string(buf) }); + } + else { #endif // ENABLE_BINARIZED_GCODE - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); #if ENABLE_BINARIZED_GCODE - } + } #endif // ENABLE_BINARIZED_GCODE - if (filament_weight > 0.) { - print_statistics.total_weight = print_statistics.total_weight + filament_weight; + if (filament_weight > 0.) { + print_statistics.total_weight = print_statistics.total_weight + filament_weight; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_weight); - binary_data.print_metadata.raw_data.push_back({ "filament used [g]", std::string(buf) }); - } - else + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_weight); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, std::string(buf) }); + } + else #endif // ENABLE_BINARIZED_GCODE - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { - print_statistics.total_cost = print_statistics.total_cost + filament_cost; + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { + print_statistics.total_cost = print_statistics.total_cost + filament_cost; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_cost); - binary_data.print_metadata.raw_data.push_back({ "filament cost", std::string(buf) }); - } - else + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_cost); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, std::string(buf) }); + } + else #endif // ENABLE_BINARIZED_GCODE - append(out_filament_cost, "%.2lf", filament_cost); - } - } - print_statistics.total_used_filament += used_filament; - print_statistics.total_extruded_volume += extruded_volume; - print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; - print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; - } - filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; - print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); - std::sort(filament_types.begin(), filament_types.end()); - print_statistics.printing_filament_types = filament_types.front(); - for (size_t i = 1; i < filament_types.size(); ++ i) { - print_statistics.printing_filament_types += ","; - print_statistics.printing_filament_types += filament_types[i]; - } + append(out_filament_cost, "%.2lf", filament_cost); + } + } + print_statistics.total_used_filament += used_filament; + print_statistics.total_extruded_volume += extruded_volume; + print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + } +#if ENABLE_BINARIZED_GCODE + if (!export_binary_data) { +#endif // ENABLE_BINARIZED_GCODE + filament_stats_string_out += out_filament_used_mm.first; + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE + print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); + std::sort(filament_types.begin(), filament_types.end()); + print_statistics.printing_filament_types = filament_types.front(); + for (size_t i = 1; i < filament_types.size(); ++ i) { + print_statistics.printing_filament_types += ","; + print_statistics.printing_filament_types += filament_types[i]; + } } return filament_stats_string_out; } @@ -1216,8 +1229,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->m_avoid_crossing_curled_overhangs.init_bed_shape(get_bed_shape(print.config())); } - // Write information on the generator. - file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); +#if ENABLE_BINARIZED_GCODE + if (!export_to_binary_gcode) +#endif // ENABLE_BINARIZED_GCODE + // Write information on the generator. + file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); #if ENABLE_BINARIZED_GCODE // if exporting gcode in ascii format, generate the thumbnails here @@ -1582,7 +1598,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Get filament stats. #if ENABLE_BINARIZED_GCODE - file.write(DoExport::update_print_stats_and_format_filament_stats( + const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats( // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), @@ -1592,15 +1608,18 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.m_print_statistics, export_to_binary_gcode, m_processor.get_binary_data() - )); + ); + + if (!export_to_binary_gcode) + file.write(filament_stats_string_out); if (export_to_binary_gcode) { BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); char buf[128]; sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); - binary_data.print_metadata.raw_data.push_back({ "total filament used [g]", std::string(buf) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, std::string(buf) }); sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); - binary_data.print_metadata.raw_data.push_back({ "total filament cost", std::string(buf) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, std::string(buf) }); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } @@ -1619,8 +1638,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // if exporting gcode in ascii format, statistics export is done here #endif // ENABLE_BINARIZED_GCODE file.write("\n"); +#if ENABLE_BINARIZED_GCODE + file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight); + file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost); +#else file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); +#endif // ENABLE_BINARIZED_GCODE if (print.m_print_statistics.total_toolchanges > 0) file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 9a205f5327..ddb1816368 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,6 +12,7 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; +static const size_t MAX_GCODE_CACHE_SIZE = 65536; std::string translate_result(BinaryGCode::EResult result) { @@ -26,10 +27,13 @@ std::string translate_result(BinaryGCode::EResult result) case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case BinaryGCode::EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } + case BinaryGCode::EResult::GCodeEncodingError: { return "GCode encoding error"; } + case BinaryGCode::EResult::GCodeDecodingError: { return "GCode decoding error"; } case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } @@ -48,6 +52,7 @@ static uint16_t block_types_count() { return 1 + (uint16_t)EBlockTyp static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } +static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPack; } static bool write_to_file(FILE& file, const void* data, size_t data_size) { @@ -61,43 +66,60 @@ static bool read_from_file(FILE& file, void* data, size_t data_size) return !ferror(&file); } -static bool encode_metadata(const std::vector>& data_in, std::vector& data_out, +static bool encode_metadata(const std::vector>& src, std::vector& dst, EMetadataEncodingType encoding_type) { - for (const auto& [key, value] : data_in) { + for (const auto& [key, value] : src) { switch (encoding_type) { case EMetadataEncodingType::INI: { - data_out.insert(data_out.end(), key.begin(), key.end()); - data_out.emplace_back('='); - data_out.insert(data_out.end(), value.begin(), value.end()); - data_out.emplace_back('\n'); + dst.insert(dst.end(), key.begin(), key.end()); + dst.emplace_back('='); + dst.insert(dst.end(), value.begin(), value.end()); + dst.emplace_back('\n'); break; } } } - return true; } -static bool decode_metadata(const std::vector& data_in, std::vector>& data_out, +static bool encode_gcode(const std::string& src, std::vector& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + { + // TODO + break; + } + } + return true; +} + +static bool decode_metadata(const std::vector& src, std::vector>& dst, EMetadataEncodingType encoding_type) { switch (encoding_type) { case EMetadataEncodingType::INI: { - auto start_it = data_in.begin(); - auto end_it = data_in.begin(); - while (end_it != data_in.end()) { - while (end_it != data_in.end() && *end_it != '\n') { + auto start_it = src.begin(); + auto end_it = src.begin(); + while (end_it != src.end()) { + while (end_it != src.end() && *end_it != '\n') { ++end_it; } const std::string item(start_it, end_it); const size_t pos = item.find_first_of('='); if (pos != std::string::npos) { - data_out.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); + dst.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); start_it = ++end_it; } } @@ -108,12 +130,30 @@ static bool decode_metadata(const std::vector& data_in, std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + { + // TODO + break; + } + } + return true; +} + +static bool compress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) { return true; } -static bool uncompress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +static bool uncompress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) { return true; } @@ -357,7 +397,6 @@ EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) return EResult::ReadError; if (encoding_type > metadata_encoding_types_count()) - // Found invalid metadata encoding type return EResult::InvalidMetadataEncodingType; std::vector data; @@ -620,11 +659,99 @@ EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const { + if (encoding_type > gcode_encoding_types_count()) + return EResult::InvalidGCodeEncodingType; + + BlockHeader block_header = { (uint16_t)EBlockType::GCode, (uint16_t)compression_type, (uint32_t)0 }; + std::vector out_data; + if (!raw_data.empty()) { + // process payload encoding + std::vector uncompressed_data; + if (!encode_gcode(raw_data, uncompressed_data, (EGCodeEncodingType)encoding_type)) + return EResult::GCodeEncodingError; + // process payload compression + block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); + std::vector compressed_data; + if (compression_type != ECompressionType::None) { + if (!compress(uncompressed_data, compressed_data, compression_type)) + return EResult::DataCompressionError; + block_header.compressed_size = (uint32_t)compressed_data.size(); + } + out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); + } + + // write block header + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) + return EResult::WriteError; + if (!out_data.empty()) { +#if ENABLE_BINARIZED_GCODE_DEBUG + const std::string out = "GCodeBlock data size:" + std::to_string(out_data.size()) + "\n"; + OutputDebugStringA(out.c_str()); +#endif // ENABLE_BINARIZED_GCODE_DEBUG + if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) + return EResult::WriteError; + } + + // write checksum + if (checksum_type != EChecksumType::None) { + Checksum cs(checksum_type); + // update checksum with block header + block_header.update_checksum(cs); + // update checksum with block payload + cs.append(encode((const void*)&encoding_type, sizeof(encoding_type))); + if (!out_data.empty()) + cs.append(out_data); + res = cs.write(file); + if (res != EResult::Success) + // propagate error + return res; + } + return EResult::Success; } EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) { + const ECompressionType compression_type = (ECompressionType)block_header.compression; + + if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) + return EResult::ReadError; + if (encoding_type > gcode_encoding_types_count()) + return EResult::InvalidGCodeEncodingType; + + std::vector data; + const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; + if (data_size > 0) { + data.resize(data_size); + if (!read_from_file(file, (void*)data.data(), data_size)) + return EResult::ReadError; + } + + std::vector uncompressed_data; + if (compression_type != ECompressionType::None) { + if (!uncompress(data, uncompressed_data, compression_type)) + return EResult::DataUncompressionError; + } + + if (!decode_gcode((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EGCodeEncodingType)encoding_type)) + return EResult::GCodeDecodingError; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + const EResult res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + return EResult::Success; } @@ -660,60 +787,116 @@ EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) } #endif // ENABLE_CHECKSUM_BLOCK -EResult Binarizer::initialize(FILE& file, EChecksumType checksum_type) +EResult Binarizer::initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type) { if (!m_enabled) return EResult::Success; - // initialize checksum + m_file = &file; + + m_gcode_encoding_type = gcode_encoding_type; m_checksum_type = checksum_type; #if ENABLE_CHECKSUM_BLOCK + // initialize checksum m_checksum = ChecksumBlock(); #endif // ENABLE_CHECKSUM_BLOCK // save header FileHeader file_header; file_header.checksum_type = (uint16_t)m_checksum_type; - EResult res = file_header.write(file); + EResult res = file_header.write(*m_file); if (res != EResult::Success) return res; // save file metadata block - res = m_binary_data.file_metadata.write(file, ECompressionType::None, m_checksum_type); + res = m_binary_data.file_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; // save printer metadata block - res = m_binary_data.printer_metadata.write(file, ECompressionType::None, m_checksum_type); + res = m_binary_data.printer_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; // save thumbnail blocks for (const ThumbnailBlock& block : m_binary_data.thumbnails) { - res = block.write(file, m_checksum_type); + res = block.write(*m_file, m_checksum_type); if (res != EResult::Success) return res; } - // save slicer metadata block - res = m_binary_data.slicer_metadata.write(file, ECompressionType::None, m_checksum_type); + // save print metadata block + res = m_binary_data.print_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; - // save gcode block + // save slicer metadata block + res = m_binary_data.slicer_metadata.write(*m_file, m_compression_type, m_checksum_type); + if (res != EResult::Success) + return res; return EResult::Success; } -EResult Binarizer::finalize(FILE& file) +static EResult write_gcode_block(FILE& file, const std::string& raw_data, EGCodeEncodingType encoding_type, ECompressionType compression_type, + EChecksumType checksum_type) +{ + GCodeBlock block; + block.encoding_type = (uint16_t)encoding_type; + block.raw_data = raw_data; + return block.write(file, compression_type, checksum_type); +} + +EResult Binarizer::append_gcode(const std::string& gcode) +{ + if (gcode.empty()) + return EResult::Success; + + assert(m_file != nullptr); + if (m_file == nullptr) + return EResult::WriteError; + + auto it_begin = gcode.begin(); + do { + const size_t begin_pos = std::distance(gcode.begin(), it_begin); + const size_t end_line_pos = gcode.find_first_of('\n', begin_pos); + if (end_line_pos == std::string::npos) + return EResult::WriteError; + + const size_t line_size = 1 + end_line_pos - begin_pos; + if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { + if (!m_gcode_cache.empty()) { + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + if (res != EResult::Success) + // propagate error + return res; + m_gcode_cache.clear(); + } + } + + if (line_size > MAX_GCODE_CACHE_SIZE) + return EResult::WriteError; + + m_gcode_cache.insert(m_gcode_cache.end(), it_begin, it_begin + line_size); + it_begin += line_size; + } + while (it_begin != gcode.end()); + + return EResult::Success; +} + +EResult Binarizer::finalize() { if (!m_enabled) return EResult::Success; - // save print metadata block - EResult res = m_binary_data.print_metadata.write(file, ECompressionType::None, m_checksum_type); - if (res != EResult::Success) - return res; + // save gcode cache, if not empty + if (!m_gcode_cache.empty()) { + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + if (res != EResult::Success) + // propagate error + return res; + } #if ENABLE_CHECKSUM_BLOCK if (m_checksum_type != EChecksumType::None) { diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 058cabd475..3dd2d14ca7 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -29,10 +29,13 @@ enum class EResult : uint16_t InvalidBlockType, InvalidCompressionType, InvalidMetadataEncodingType, + InvalidGCodeEncodingType, DataCompressionError, DataUncompressionError, MetadataEncodingError, MetadataDecodingError, + GCodeEncodingError, + GCodeDecodingError, BlockNotFound, InvalidChecksum, InvalidThumbnailFormat, @@ -195,12 +198,23 @@ struct SlicerMetadataBlock : public BaseMetadataBlock EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); }; -struct GCodeBlock : public BaseMetadataBlock +enum class EGCodeEncodingType : uint16_t { + None, + MeatPack, +}; + +struct GCodeBlock +{ + uint16_t encoding_type{ 0 }; + std::string raw_data; + // write block header and data EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; // read block data EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(encoding_type); } }; #if ENABLE_CHECKSUM_BLOCK @@ -248,16 +262,20 @@ public: BinaryData& get_binary_data() { return m_binary_data; } const BinaryData& get_binary_data() const { return m_binary_data; } - EResult initialize(FILE& file, EChecksumType checksum_type); - EResult finalize(FILE& file); + EResult initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type); + EResult append_gcode(const std::string& gcode); + EResult finalize(); private: bool m_enabled{ false }; EChecksumType m_checksum_type{ EChecksumType::None }; ECompressionType m_compression_type{ ECompressionType::None }; + EGCodeEncodingType m_gcode_encoding_type{ EGCodeEncodingType::None }; + FILE* m_file{ nullptr }; BinaryData m_binary_data; + std::string m_gcode_cache; #if ENABLE_CHECKSUM_BLOCK ChecksumBlock m_checksum; #endif // ENABLE_CHECKSUM_BLOCK diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e6d97a3f8c..1cdcba4115 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1127,10 +1127,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct FILE* m_file{ nullptr }; }; +#if ENABLE_GCODE_VIEWER_STATISTICS + m_start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); if (file == nullptr) throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); + fseek(file, 0, SEEK_END); + const long file_size = ftell(file); + rewind(file); + ScopedFile scoped_file(file); BinaryGCode::set_checksum_max_cache_size(1024); @@ -1141,7 +1149,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + BinaryGCode::translate_result(res) + "\n"); - const long first_block_header_position = ftell(file); const bool verify_checksum = true; // read file metadata block @@ -1222,7 +1229,27 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); } + // read print metadata block + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); + BinaryGCode::PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Print metadata:\n"); + for (const auto& [key, value] : print_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + // read slicer metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); BinaryGCode::SlicerMetadataBlock slicer_metadata_block; @@ -1250,25 +1277,38 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct config.load_from_ini_string(str, ForwardCompatibilitySubstitutionRule::EnableSilent); apply_config(config); - // read print metadata block + m_result.filename = filename; + m_result.id = ++s_result_id; + initialize_result_moves(); + + // read gcodes block res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) - throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - BinaryGCode::PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG - OutputDebugStringA("Print metadata:\n"); - for (const auto& [key, value] : print_metadata_block.raw_data) { - OutputDebugStringA(key.c_str()); - OutputDebugStringA("->"); - OutputDebugStringA(value.c_str()); - OutputDebugStringA("\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::GCode) + throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); + while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::GCode) { + BinaryGCode::GCodeBlock block; + res = block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + + // TODO: Update m_result.lines_ends + + m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + this->process_gcode_line(line, true); + }); + + if (ftell(file) == file_size) + break; + + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG + + // Don't post-process the G-code to update time stamps. + this->finalize(false); } #endif // ENABLE_BINARIZED_GCODE @@ -3622,9 +3662,57 @@ void GCodeProcessor::post_process() } #if ENABLE_BINARIZED_GCODE + std::vector filament_mm(m_result.extruders_count, 0.0); + std::vector filament_cm3(m_result.extruders_count, 0.0); + std::vector filament_g(m_result.extruders_count, 0.0); + std::vector filament_cost(m_result.extruders_count, 0.0); + + double filament_total_g = 0.0; + double filament_total_cost = 0.0; + + for (const auto& [id, volume] : m_result.print_statistics.volumes_per_extruder) { + filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); + filament_cm3[id] = volume * 0.001; + filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); + filament_cost[id] = filament_g[id] * double(m_result.filament_cost[id]) * 0.001; + filament_total_g += filament_g[id]; + filament_total_cost += filament_cost[id]; + } + if (m_binarizer.is_enabled()) { - BinaryGCode::set_checksum_max_cache_size(4096); - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EChecksumType::CRC32); + // update print metadata + auto update_value = [](std::string& value, const std::vector& values) { + char buf[1024]; + value.clear(); + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); + value += buf; + } + }; + + // update binary data + BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + for (auto& [key, value] : binary_data.print_metadata.raw_data) { + if (key == PrintStatistics::FilamentUsedMm) update_value(value, filament_mm); + else if (key == PrintStatistics::FilamentUsedG) update_value(value, filament_g); + else if (key == PrintStatistics::TotalFilamentUsedG) update_value(value, { filament_total_g }); + else if (key == PrintStatistics::FilamentUsedCm3) update_value(value, filament_cm3); + else if (key == PrintStatistics::FilamentCost) update_value(value, filament_cost); + else if (key == PrintStatistics::TotalFilamentCost) update_value(value, { filament_total_cost }); + } + + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); + binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + } + } + + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EGCodeEncodingType::None, BinaryGCode::EChecksumType::CRC32); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } @@ -3876,9 +3964,30 @@ void GCodeProcessor::post_process() } } - write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + } + else +#endif // ENABLE_BINARIZED_GCODE + write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + update_out_file_pos(out, out_string, result); +#endif // ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE + void update_out_file_pos(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result) { + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + result.lines_ends.emplace_back(m_out_file_pos + i + 1); + } + m_out_file_pos += out_string.size(); + } +#endif // ENABLE_BINARIZED_GCODE + // flush the current content of the cache to file void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { // collect lines to flush into a single string @@ -3892,7 +4001,18 @@ void GCodeProcessor::post_process() m_statistics.remove_all_lines(); #endif // NDEBUG - write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + } + else +#endif // ENABLE_BINARIZED_GCODE + write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + update_out_file_pos(out, out_string, result); +#endif // ENABLE_BINARIZED_GCODE } void synchronize_moves(GCodeProcessorResult& result) const { @@ -3922,12 +4042,13 @@ void GCodeProcessor::post_process() } #if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE +#else for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); } m_out_file_pos += out_string.size(); +#endif // ENABLE_BINARIZED_GCODE } } }; @@ -3999,6 +4120,7 @@ void GCodeProcessor::post_process() return processed; }; +#if !ENABLE_BINARIZED_GCODE std::vector filament_mm(m_result.extruders_count, 0.0); std::vector filament_cm3(m_result.extruders_count, 0.0); std::vector filament_g(m_result.extruders_count, 0.0); @@ -4015,6 +4137,7 @@ void GCodeProcessor::post_process() filament_total_g += filament_g[id]; filament_total_cost += filament_cost[id]; } +#endif // !ENABLE_BINARIZED_GCODE auto process_used_filament = [&](std::string& gcode_line) { // Prefilter for parsing speed. @@ -4036,12 +4159,21 @@ void GCodeProcessor::post_process() }; bool ret = false; +#if ENABLE_BINARIZED_GCODE + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedMmMask, filament_mm); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedGMask, filament_g); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentUsedGMask, { filament_total_g }); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedCm3Mask, filament_cm3); + ret |= process_tag(gcode_line, PrintStatistics::FilamentCostMask, filament_cost); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentCostMask, { filament_total_cost }); +#else ret |= process_tag(gcode_line, "; filament used [mm] =", filament_mm); ret |= process_tag(gcode_line, "; filament used [g] =", filament_g); ret |= process_tag(gcode_line, "; total filament used [g] =", { filament_total_g }); ret |= process_tag(gcode_line, "; filament used [cm3] =", filament_cm3); ret |= process_tag(gcode_line, "; filament cost =", filament_cost); ret |= process_tag(gcode_line, "; total filament cost =", { filament_total_cost }); +#endif // ENABLE_BINARIZED_GCODE return ret; }; @@ -4251,39 +4383,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - // update print metadata - auto update_value = [](std::string& value, const std::vector& values) { - char buf[1024]; - value.clear(); - for (size_t i = 0; i < values.size(); ++i) { - sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); - value += buf; - } - }; - - // update binary data - BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); - for (auto& [key, value] : binary_data.print_metadata.raw_data) { - if (key == "filament used [mm]") update_value(value, filament_mm); - else if (key == "filament used [g]") update_value(value, filament_g); - else if (key == "total filament used [g]") update_value(value, { filament_total_g }); - else if (key == "filament used [cm3]") update_value(value, filament_cm3); - else if (key == "filament cost") update_value(value, filament_cost); - else if (key == "total filament cost") update_value(value, { filament_total_cost }); - } - - for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { - const TimeMachine& machine = m_time_processor.machines[i]; - PrintEstimatedStatistics::ETimeMode mode = static_cast(i); - if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { - char buf[128]; - sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); - binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); - binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); - } - } - - const BinaryGCode::EResult res = m_binarizer.finalize(*out.f); + const BinaryGCode::EResult res = m_binarizer.finalize(); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2bd92340d8..c8de08d107 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1548,6 +1548,28 @@ std::string Print::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } +#if ENABLE_BINARIZED_GCODE +const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; +const std::string PrintStatistics::FilamentUsedGMask = "; " + PrintStatistics::FilamentUsedG + " ="; + +const std::string PrintStatistics::TotalFilamentUsedG = "total " + PrintStatistics::FilamentUsedG; +const std::string PrintStatistics::TotalFilamentUsedGMask = "; " + PrintStatistics::TotalFilamentUsedG + " ="; +const std::string PrintStatistics::TotalFilamentUsedGValueMask = TotalFilamentUsedGMask + " %.2lf\n"; + +const std::string PrintStatistics::FilamentUsedCm3 = "filament used [cm3]"; +const std::string PrintStatistics::FilamentUsedCm3Mask = "; " + PrintStatistics::FilamentUsedCm3 + " ="; + +const std::string PrintStatistics::FilamentUsedMm = "filament used [mm]"; +const std::string PrintStatistics::FilamentUsedMmMask = "; " + PrintStatistics::FilamentUsedMm + " ="; + +const std::string PrintStatistics::FilamentCost = "filament cost"; +const std::string PrintStatistics::FilamentCostMask = "; " + PrintStatistics::FilamentCost + " ="; + +const std::string PrintStatistics::TotalFilamentCost = "total " + PrintStatistics::FilamentCost; +const std::string PrintStatistics::TotalFilamentCostMask = "; " + PrintStatistics::TotalFilamentCost + " ="; +const std::string PrintStatistics::TotalFilamentCostValueMask = PrintStatistics::TotalFilamentCostMask + " %.2lf\n"; +#endif // ENABLE_BINARIZED_GCODE + DynamicConfig PrintStatistics::config() const { DynamicConfig config; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 059491951e..9490e07c5d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -528,6 +528,23 @@ struct PrintStatistics filament_stats.clear(); printing_extruders.clear(); } + +#if ENABLE_BINARIZED_GCODE + static const std::string FilamentUsedG; + static const std::string FilamentUsedGMask; + static const std::string TotalFilamentUsedG; + static const std::string TotalFilamentUsedGMask; + static const std::string TotalFilamentUsedGValueMask; + static const std::string FilamentUsedCm3; + static const std::string FilamentUsedCm3Mask; + static const std::string FilamentUsedMm; + static const std::string FilamentUsedMmMask; + static const std::string FilamentCost; + static const std::string FilamentCostMask; + static const std::string TotalFilamentCost; + static const std::string TotalFilamentCostMask; + static const std::string TotalFilamentCostValueMask; +#endif // ENABLE_BINARIZED_GCODE }; using PrintObjectPtrs = std::vector; From a11009c3e03884ac162b383fadd066b3f1e48d55 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Jul 2023 08:29:13 +0200 Subject: [PATCH 03/48] SPE-1784: New compressed (binary) gcode format integration Populated printer metadata block Small optimization for print metadata --- src/libslic3r/GCode.cpp | 46 +++++++++++--------------- src/libslic3r/GCode/GCodeProcessor.cpp | 32 ++++++++++-------- src/libslic3r/GCode/GCodeProcessor.hpp | 8 ++--- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f6b772e946..c9136af7a4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1036,14 +1036,7 @@ namespace DoExport { ++ dst.second; }; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", used_filament); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, std::string(buf) }); - sprintf(buf, "%.2lf", extruded_volume * 0.001); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, std::string(buf) }); - } - else { + if (!export_binary_data) { #endif // ENABLE_BINARIZED_GCODE append(out_filament_used_mm, "%.2lf", used_filament); append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); @@ -1053,23 +1046,13 @@ namespace DoExport { if (filament_weight > 0.) { print_statistics.total_weight = print_statistics.total_weight + filament_weight; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_weight); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, std::string(buf) }); - } - else + if (!export_binary_data) #endif // ENABLE_BINARIZED_GCODE append(out_filament_used_g, "%.2lf", filament_weight); if (filament_cost > 0.) { print_statistics.total_cost = print_statistics.total_cost + filament_cost; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_cost); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, std::string(buf) }); - } - else + if (!export_binary_data) #endif // ENABLE_BINARIZED_GCODE append(out_filament_cost, "%.2lf", filament_cost); } @@ -1165,11 +1148,27 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // file data binary_data.file_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); - // add here other key/value pairs // config data binary_data.slicer_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); + + // printer data + binary_data.printer_metadata.raw_data.emplace_back("printer model" , print.config().printer_model.value); // duplicated into config data + std::string filament_types_str; + for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { + filament_types_str += print.config().filament_type.values[i]; + if (i < print.config().filament_type.values.size() - 1) + filament_types_str += ", "; + } + binary_data.printer_metadata.raw_data.emplace_back("filament type", filament_types_str); // duplicated into config data + std::string nozzle_diameters_str; + char buf[1024]; + for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { + sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2lf, " : "%.2lf", print.config().nozzle_diameter.values[i]); + nozzle_diameters_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("nozzle diameter", nozzle_diameters_str); // duplicated into config data } // modifies m_silent_time_estimator_enabled @@ -1615,11 +1614,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (export_to_binary_gcode) { BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); - char buf[128]; - sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, std::string(buf) }); - sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, std::string(buf) }); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 1cdcba4115..b66850fd07 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1354,7 +1354,7 @@ void GCodeProcessor::finalize(bool perform_post_process) m_used_filaments.process_caches(this); - update_estimated_times_stats(); + update_estimated_statistics(); #if ENABLE_GCODE_VIEWER_DATA_CHECKING std::cout << "\n"; @@ -3681,25 +3681,27 @@ void GCodeProcessor::post_process() if (m_binarizer.is_enabled()) { // update print metadata - auto update_value = [](std::string& value, const std::vector& values) { + auto stringify = [](const std::vector& values) { + std::string ret; char buf[1024]; - value.clear(); for (size_t i = 0; i < values.size(); ++i) { - sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); - value += buf; + sprintf(buf, i < values.size() - 1 ? "%.2lf, " : "%.2lf", values[i]); + ret += buf; } + return ret; }; // update binary data BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); - for (auto& [key, value] : binary_data.print_metadata.raw_data) { - if (key == PrintStatistics::FilamentUsedMm) update_value(value, filament_mm); - else if (key == PrintStatistics::FilamentUsedG) update_value(value, filament_g); - else if (key == PrintStatistics::TotalFilamentUsedG) update_value(value, { filament_total_g }); - else if (key == PrintStatistics::FilamentUsedCm3) update_value(value, filament_cm3); - else if (key == PrintStatistics::FilamentCost) update_value(value, filament_cost); - else if (key == PrintStatistics::TotalFilamentCost) update_value(value, { filament_total_cost }); - } + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, stringify(filament_cost) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g }) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost }) }); + + binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); // duplicated into print metadata + binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); // duplicated into print metadata for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = m_time_processor.machines[i]; @@ -3709,6 +3711,8 @@ void GCodeProcessor::post_process() sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + + binary_data.printer_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); } } @@ -4596,7 +4600,7 @@ void GCodeProcessor::simulate_st_synchronize(float additional_time) } } -void GCodeProcessor::update_estimated_times_stats() +void GCodeProcessor::update_estimated_statistics() { auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) { PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast(mode)]; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fe2db07f99..9ca4455945 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -66,10 +66,10 @@ namespace Slic3r { } }; - std::vector volumes_per_color_change; - std::map volumes_per_extruder; + std::vector volumes_per_color_change; + std::map volumes_per_extruder; std::map> used_filaments_per_role; - std::map cost_per_extruder; + std::map cost_per_extruder; std::array(ETimeMode::Count)> modes; @@ -831,7 +831,7 @@ namespace Slic3r { // Simulates firmware st_synchronize() call void simulate_st_synchronize(float additional_time = 0.0f); - void update_estimated_times_stats(); + void update_estimated_statistics(); double extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section); }; From ee87536ff60d3ac7daacc7a26f3f9f0fd7c45e2a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Jul 2023 12:24:24 +0200 Subject: [PATCH 04/48] Added debug imgui dialog to allow to change binary gcode parameters at runtime --- src/libslic3r/GCode/GCodeBinarizer.cpp | 29 ++++++------ src/libslic3r/GCode/GCodeBinarizer.hpp | 16 ++++--- src/libslic3r/GCode/GCodeProcessor.cpp | 6 ++- src/libslic3r/GCode/GCodeProcessor.hpp | 5 +++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 61 ++++++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 4 ++ src/slic3r/GUI/ImGuiWrapper.cpp | 5 ++- 8 files changed, 103 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index ddb1816368..4191d0c3a6 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,7 +12,7 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; -static const size_t MAX_GCODE_CACHE_SIZE = 65536; +static constexpr size_t MAX_GCODE_CACHE_SIZE = 65536; std::string translate_result(BinaryGCode::EResult result) { @@ -110,17 +110,17 @@ static bool decode_metadata(const std::vector& src, std::vector MAX_GCODE_CACHE_SIZE) { if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); if (res != EResult::Success) // propagate error return res; @@ -892,7 +891,7 @@ EResult Binarizer::finalize() // save gcode cache, if not empty if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); if (res != EResult::Success) // propagate error return res; diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 3dd2d14ca7..ad4d1c640c 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -252,27 +252,31 @@ struct BinaryData } }; +struct BinarizerConfig +{ + ECompressionType compression{ ECompressionType::None }; + EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; + EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; + EChecksumType checksum{ EChecksumType::None }; +}; + class Binarizer { public: bool is_enabled() const { return m_enabled; } void set_enabled(bool enable) { m_enabled = enable; } - void set_compression_type(ECompressionType type) { m_compression_type = type; } BinaryData& get_binary_data() { return m_binary_data; } const BinaryData& get_binary_data() const { return m_binary_data; } - EResult initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type); + EResult initialize(FILE& file, const BinarizerConfig& config); EResult append_gcode(const std::string& gcode); EResult finalize(); private: bool m_enabled{ false }; - EChecksumType m_checksum_type{ EChecksumType::None }; - ECompressionType m_compression_type{ ECompressionType::None }; - EGCodeEncodingType m_gcode_encoding_type{ EGCodeEncodingType::None }; - + BinarizerConfig m_config; FILE* m_file{ nullptr }; BinaryData m_binary_data; std::string m_gcode_cache; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index b66850fd07..e72745fac1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -69,6 +69,10 @@ const std::vector GCodeProcessor::Reserved_Tags = { const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; +#if ENABLE_BINARIZED_GCODE +BinaryGCode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +#endif // ENABLE_BINARIZED + #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -3716,7 +3720,7 @@ void GCodeProcessor::post_process() } } - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EGCodeEncodingType::None, BinaryGCode::EChecksumType::CRC32); + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 9ca4455945..333f3295e6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -527,10 +527,15 @@ namespace Slic3r { }; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + static BinaryGCode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE BinaryGCode::Binarizer m_binarizer; + static BinaryGCode::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 92b6f8578c..6af385e71a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,6 +69,7 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) +#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index debdc2054d..9b89596001 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2048,6 +2048,11 @@ void GLCanvas3D::render() #endif // ENABLE_SLA_VIEW_DEBUG_WINDOW } +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + if (wxGetApp().plater()->is_view3D_shown() && current_printer_technology() != ptSLA && fff_print()->config().gcode_binary) + show_binary_gcode_debug_window(); +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + std::string tooltip; // Negative coordinate means out of the window, likely because the window was deactivated. @@ -7866,6 +7871,62 @@ void GLCanvas3D::GizmoHighlighter::blink() invalidate(); } +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW +void GLCanvas3D::show_binary_gcode_debug_window() +{ + BinaryGCode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Compression"); + ImGui::TableSetColumnIndex(1); + const std::vector gcode_compressions = { "None" }; + int compression = (int)binarizer_config.compression; + if (imgui.combo(std::string("##1"), gcode_compressions, compression, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.compression = (BinaryGCode::ECompressionType)compression; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GGcode encoding"); + ImGui::TableSetColumnIndex(1); + const std::vector gcode_encodings = { "None", "MeatPack" }; + int gcode_encoding = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encoding; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); + ImGui::TableSetColumnIndex(1); + const std::vector metadata_encodings = { "INI" }; + int metadata_encoding = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encoding; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); + ImGui::TableSetColumnIndex(1); + const std::vector checksums = { "None", "CRC32" }; + int checksum = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), checksums, checksum, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.checksum = (BinaryGCode::EChecksumType)checksum; + + ImGui::EndTable(); + + ImGui::Separator(); + imgui.text("!!! WARNING !!!"); + imgui.text("Changing values does NOT invalidate the current slice"); + } + + imgui.end(); +} +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) { const ModelVolume * ret = nullptr; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4d7368ed26..2ef9c8d189 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1113,6 +1113,10 @@ private: bool _deactivate_arrange_menu(); float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } + +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + void show_binary_gcode_debug_window(); +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW }; const ModelVolume *get_model_volume(const GLVolume &v, const Model &model); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 1a800eee42..d774aeb044 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -782,7 +782,8 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& bool ImGuiWrapper::combo(const std::string& label, const std::vector& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/) { // this is to force the label to the left of the widget: - if (!label.empty()) { + const bool hidden_label = boost::starts_with(label, "##"); + if (!label.empty() && !hidden_label) { text(label); ImGui::SameLine(label_width); } @@ -792,7 +793,7 @@ bool ImGuiWrapper::combo(const std::string& label, const std::vector= 0 ? options[selection].c_str() : ""; - if (ImGui::BeginCombo(("##" + label).c_str(), selection_str, flags)) { + if (ImGui::BeginCombo(hidden_label ? label.c_str() : ("##" + label).c_str(), selection_str, flags)) { for (int i = 0; i < (int)options.size(); i++) { if (ImGui::Selectable(options[i].c_str(), i == selection)) { selection_out = i; From 8bb6224ba84093a53223e36afa2a8f93101f902a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Jul 2023 12:27:09 +0200 Subject: [PATCH 05/48] Attempt to fix perl test --- t/layers.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/layers.t b/t/layers.t index 4d958808a6..fef94457c2 100644 --- a/t/layers.t +++ b/t/layers.t @@ -62,6 +62,7 @@ use Slic3r::Test qw(_eq); { my $config = Slic3r::Config->new; $config->set('fill_density', 0); # just for making the test faster + $config->set('gcode_binary', 0); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2); my @z = (); From 63310776455f7b56e6e1a72a569fe944c1f163e2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Jul 2023 12:37:03 +0200 Subject: [PATCH 06/48] SPE-1784: New compressed (binary) gcode format integration Added MeatPack encoding to GCode Block --- src/libslic3r/GCode/GCodeBinarizer.cpp | 491 +++++++++++++++++++++++-- src/libslic3r/GCode/GCodeBinarizer.hpp | 3 +- src/slic3r/GUI/GLCanvas3D.cpp | 26 +- 3 files changed, 479 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 4191d0c3a6..350905ecda 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,7 +12,429 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; -static constexpr size_t MAX_GCODE_CACHE_SIZE = 65536; +static constexpr const size_t MAX_GCODE_CACHE_SIZE = 65536; + +namespace MeatPack { +static constexpr const uint8_t Command_None{ 0 }; +//#Command_TogglePacking = 253 -- Currently unused, byte 253 can be reused later. +static constexpr const uint8_t Command_EnablePacking{ 251 }; +static constexpr const uint8_t Command_DisablePacking{ 250 }; +static constexpr const uint8_t Command_ResetAll{ 249 }; +static constexpr const uint8_t Command_QueryConfig{ 248 }; +static constexpr const uint8_t Command_EnableNoSpaces{ 247 }; +static constexpr const uint8_t Command_DisableNoSpaces{ 246 }; +static constexpr const uint8_t Command_SignalByte{ 0xFF }; + +static constexpr const uint8_t Flag_OmitWhitespaces{ 0x01 }; +static constexpr const uint8_t Flag_RemoveComments{ 0x02 }; + +static constexpr const uint8_t BothUnpackable{ 0b11111111 }; +static constexpr const char SpaceReplacedCharacter{ 'E' }; + +static const std::unordered_map ReverseLookupTbl = { + { '0', 0b00000000 }, + { '1', 0b00000001 }, + { '2', 0b00000010 }, + { '3', 0b00000011 }, + { '4', 0b00000100 }, + { '5', 0b00000101 }, + { '6', 0b00000110 }, + { '7', 0b00000111 }, + { '8', 0b00001000 }, + { '9', 0b00001001 }, + { '.', 0b00001010 }, + { ' ', 0b00001011 }, + { '\n', 0b00001100 }, + { 'G', 0b00001101 }, + { 'X', 0b00001110 }, + { '\0', 0b00001111 } // never used, 0b1111 is used to indicate the next 8-bits is a full character +}; + +class MPBinarizer +{ +public: + explicit MPBinarizer(uint8_t flags = 0) : m_flags(flags) {} + + void initialize(std::vector& dst) { + initialize_lookup_tables(); + append_command(Command_EnablePacking, dst); + m_binarizing = true; + } + + void finalize(std::vector& dst) { + if ((m_flags & Flag_RemoveComments) != 0) { + assert(m_binarizing); + append_command(Command_ResetAll, dst); + m_binarizing = false; + } + } + + void binarize_line(const std::string& line, std::vector& dst) { + auto unified_method = [this](const std::string& line) { + const std::string::size_type g_idx = line.find('G'); + if (g_idx != std::string::npos) { + if (g_idx + 1 < line.size() && line[g_idx + 1] >= '0' && line[g_idx + 1] <= '9') { + if ((m_flags & Flag_OmitWhitespaces) != 0) { + std::string result = line; + std::replace(result.begin(), result.end(), 'e', 'E'); + std::replace(result.begin(), result.end(), 'x', 'X'); + std::replace(result.begin(), result.end(), 'g', 'G'); + result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); + if (result.find('*') != std::string::npos) { + size_t checksum = 0; + result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); + for (size_t i = 0; i < result.size(); ++i) { + checksum ^= static_cast(result[i]); + } + result += "*" + std::to_string(checksum); + } + result += '\n'; + return result; + } + else { + std::string result = line; + std::replace(result.begin(), result.end(), 'x', 'X'); + std::replace(result.begin(), result.end(), 'g', 'G'); + result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); + if (result.find('*') != std::string::npos) { + size_t checksum = 0; + result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); + for (size_t i = 0; i < result.size(); ++i) { + checksum ^= static_cast(result[i]); + } + result += "*" + std::to_string(checksum); + } + result += '\n'; + return result; + } + } + } + return line; + }; + auto is_packable = [this](char c) { + return (s_lookup_tables.packable[static_cast(c)] != 0); + }; + auto pack_chars = [this](char low, char high) { + return (((s_lookup_tables.value[static_cast(high)] & 0xF) << 4) | + (s_lookup_tables.value[static_cast(low)] & 0xF)); + }; + + if (!line.empty()) { + if ((m_flags & Flag_RemoveComments) == 0) { + if (line[0] == ';') { + if (m_binarizing) { + append_command(Command_DisablePacking, dst); + m_binarizing = false; + } + + dst.insert(dst.end(), line.begin(), line.end()); + return; + } + } + + if (line[0] == ';' || + line[0] == '\n' || + line[0] == '\r' || + line.size() < 2) + return; + + std::string modifiedLine = line.substr(0, line.find(';')); + if (modifiedLine.empty()) + return; + auto trim_right = [](const std::string& str) { + if (str.back() != ' ') + return str; + auto bit = str.rbegin(); + while (bit != str.rend() && *bit == ' ') { + ++bit; + } + return str.substr(0, std::distance(str.begin(), bit.base())); + }; + modifiedLine = trim_right(modifiedLine); + modifiedLine = unified_method(modifiedLine); + if (modifiedLine.back() != '\n') + modifiedLine.push_back('\n'); + const size_t line_len = modifiedLine.size(); + std::vector temp_buffer; + temp_buffer.reserve(line_len); + + for (size_t line_idx = 0; line_idx < line_len; line_idx += 2) { + const bool skip_last = line_idx == (line_len - 1); + const char char_1 = modifiedLine[line_idx]; + const char char_2 = (skip_last ? '\n' : modifiedLine[line_idx + 1]); + const bool c1_p = is_packable(char_1); + const bool c2_p = is_packable(char_2); + + if (c1_p) { + if (c2_p) + temp_buffer.emplace_back(static_cast(pack_chars(char_1, char_2))); + else { + temp_buffer.emplace_back(static_cast(pack_chars(char_1, '\0'))); + temp_buffer.emplace_back(static_cast(char_2)); + } + } + else { + if (c2_p) { + temp_buffer.emplace_back(static_cast(pack_chars('\0', char_2))); + temp_buffer.emplace_back(static_cast(char_1)); + } + else { + temp_buffer.emplace_back(static_cast(BothUnpackable)); + temp_buffer.emplace_back(static_cast(char_1)); + temp_buffer.emplace_back(static_cast(char_2)); + } + } + } + + if (!m_binarizing && !temp_buffer.empty()) { + append_command(Command_EnablePacking, dst); + m_binarizing = true; + } + + dst.insert(dst.end(), temp_buffer.begin(), temp_buffer.end()); + } + } + +private: + unsigned char m_flags{ 0 }; + bool m_binarizing{ false }; + + struct LookupTables + { + std::array packable; + std::array value; + bool initialized; + unsigned char flags; + }; + + static LookupTables s_lookup_tables; + + void append_command(unsigned char cmd, std::vector& dst) { + dst.emplace_back(Command_SignalByte); + dst.emplace_back(Command_SignalByte); + dst.emplace_back(cmd); + } + + void initialize_lookup_tables() { + if (s_lookup_tables.initialized && m_flags == s_lookup_tables.flags) + return; + + for (const auto& [c, value] : ReverseLookupTbl) { + const int index = static_cast(c); + s_lookup_tables.packable[index] = 1; + s_lookup_tables.value[index] = value; + } + + if ((m_flags & Flag_OmitWhitespaces) != 0) { + s_lookup_tables.value[static_cast(SpaceReplacedCharacter)] = ReverseLookupTbl.at(' '); + s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 1; + s_lookup_tables.packable[static_cast(' ')] = 0; + } + else { + s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 0; + s_lookup_tables.packable[static_cast(' ')] = 1; + } + + s_lookup_tables.initialized = true; + s_lookup_tables.flags = m_flags; + } +}; + +MPBinarizer::LookupTables MPBinarizer::s_lookup_tables = { { 0 }, { 0 }, false, 0 }; + +static constexpr const unsigned char SecondNotPacked{ 0b11110000 }; +static constexpr const unsigned char FirstNotPacked{ 0b00001111 }; +static constexpr const unsigned char NextPackedFirst{ 0b00000001 }; +static constexpr const unsigned char NextPackedSecond{ 0b00000010 }; + +// See for reference: https://github.com/scottmudge/Prusa-Firmware-MeatPack/blob/MK3_sm_MeatPack/Firmware/meatpack.cpp +static void unbinarize(const std::vector& src, std::string& dst) { + bool unbinarizing = false; + bool cmd_active = false; // Is a command pending + uint8_t char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs + size_t cmd_count = 0; // Counts how many command bytes are received (need 2) + size_t full_char_queue = 0; // Counts how many full-width characters are to be received + std::array char_out_buf; // Output buffer for caching up to 2 characters + size_t char_out_count = 0; // Stores number of characters to be read out + + auto handle_command = [&](uint8_t c) { + switch (c) + { + case Command_EnablePacking: { unbinarizing = true; break; } + case Command_DisablePacking: { unbinarizing = false; break; } + case Command_ResetAll: { unbinarizing = false; break; } + default: + case Command_QueryConfig: { break; } + } + }; + + auto handle_output_char = [&](uint8_t c) { + char_out_buf[char_out_count++] = c; + }; + + auto get_char = [](uint8_t c) { + switch (c) + { + case 0b0000: { return '0'; } + case 0b0001: { return '1'; } + case 0b0010: { return '2'; } + case 0b0011: { return '3'; } + case 0b0100: { return '4'; } + case 0b0101: { return '5'; } + case 0b0110: { return '6'; } + case 0b0111: { return '7'; } + case 0b1000: { return '8'; } + case 0b1001: { return '9'; } + case 0b1010: { return '.'; } + case 0b1011: { return 'E'; } + case 0b1100: { return '\n'; } + case 0b1101: { return 'G'; } + case 0b1110: { return 'X'; } + } + return '\0'; + }; + + auto unpack_chars = [&](uint8_t pk, std::array& chars_out) { + uint8_t out = 0; + + // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. + if ((pk & FirstNotPacked) == FirstNotPacked) + out |= NextPackedFirst; + else + chars_out[0] = get_char(pk & 0xF); // Assign lower char + + // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. + if ((pk & SecondNotPacked) == SecondNotPacked) + out |= NextPackedSecond; + else + chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char + + return out; + }; + + auto handle_rx_char = [&](uint8_t c) { + if (unbinarizing) { + if (full_char_queue > 0) { + handle_output_char(c); + if (char_buf > 0) { + handle_output_char(char_buf); + char_buf = 0; + } + --full_char_queue; + } + else { + std::array buf = { 0, 0 }; + const uint8_t res = unpack_chars(c, buf); + + if ((res & NextPackedFirst) != 0) { + ++full_char_queue; + if ((res & NextPackedSecond) != 0) + ++full_char_queue; + else + char_buf = buf[1]; + } + else { + handle_output_char(buf[0]); + if (buf[0] != '\n') { + if ((res & NextPackedSecond) != 0) + ++full_char_queue; + else + handle_output_char(buf[1]); + } + } + } + } + else // Packing not enabled, just copy character to output + handle_output_char(c); + }; + + auto get_result_char = [&](std::array& chars_out) { + if (char_out_count > 0) { + const size_t res = char_out_count; + for (uint8_t i = 0; i < char_out_count; ++i) { + chars_out[i] = (char)char_out_buf[i]; + } + char_out_count = 0; + return res; + } + return (size_t)0; + }; + + std::vector unbin_buffer(2 * src.size(), 0); + auto it_unbin_end = unbin_buffer.begin(); + +#if ENABLE_BINARIZED_GCODE_DEBUG + size_t line_start = 0; +#endif // ENABLE_BINARIZED_GCODE_DEBUG + bool add_space = false; + + auto begin = src.begin(); + auto end = src.end(); + + auto it_bin = begin; + while (it_bin != end) { + uint8_t c_bin = *it_bin; + if (c_bin == Command_SignalByte) { + if (cmd_count > 0) { + cmd_active = true; + cmd_count = 0; + } + else + ++cmd_count; + } + else { + if (cmd_active) { + handle_command(c_bin); + cmd_active = false; + } + else { + if (cmd_count > 0) { + handle_rx_char(Command_SignalByte); + cmd_count = 0; + } + + handle_rx_char(c_bin); + } + } + + std::array c_unbin{ 0, 0 }; + const size_t char_count = get_result_char(c_unbin); + for (size_t i = 0; i < char_count; ++i) { + // GCodeReader::parse_line_internal() is unable to parse a G line where the data are not separated by spaces + // so we add them where needed + if (c_unbin[i] == 'G' && std::distance(unbin_buffer.begin(), it_unbin_end) > 0 && *std::prev(it_unbin_end, 1) == '\n') + add_space = true; + else if (c_unbin[i] == '\n') + add_space = false; + + if (add_space && *std::prev(it_unbin_end, 1) != ' ' && + (c_unbin[i] == 'X' || c_unbin[i] == 'Y' || c_unbin[i] == 'Z' || c_unbin[i] == 'E' || c_unbin[i] == 'F')) { + *it_unbin_end = ' '; + ++it_unbin_end; + } + + if (c_unbin[i] != '\n' || std::distance(unbin_buffer.begin(), it_unbin_end) == 0 || *std::prev(it_unbin_end, 1) != '\n') { + *it_unbin_end = c_unbin[i]; + ++it_unbin_end; + } + +#if ENABLE_BINARIZED_GCODE_DEBUG + if (c_unbin[i] == '\n') { + const std::string out(unbin_buffer.begin() + line_start, it_unbin_end); + if (!out.empty()) { + OutputDebugStringA(out.c_str()); + line_start = std::distance(unbin_buffer.begin(), it_unbin_end); + } + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + } + + ++it_bin; + } + + dst.insert(dst.end(), unbin_buffer.begin(), it_unbin_end); +} +} // namespace MeatPack std::string translate_result(BinaryGCode::EResult result) { @@ -52,7 +474,7 @@ static uint16_t block_types_count() { return 1 + (uint16_t)EBlockTyp static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } -static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPack; } +static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPackComments; } static bool write_to_file(FILE& file, const void* data, size_t data_size) { @@ -85,24 +507,6 @@ static bool encode_metadata(const std::vector& dst, EGCodeEncodingType encoding_type) -{ - switch (encoding_type) - { - case EGCodeEncodingType::None: - { - dst.insert(dst.end(), src.begin(), src.end()); - break; - } - case EGCodeEncodingType::MeatPack: - { - // TODO - break; - } - } - return true; -} - static bool decode_metadata(const std::vector& src, std::vector>& dst, EMetadataEncodingType encoding_type) { @@ -130,6 +534,39 @@ static bool decode_metadata(const std::vector& src, std::vector& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + case EGCodeEncodingType::MeatPackComments: + { + uint8_t binarizer_flags = (encoding_type == EGCodeEncodingType::MeatPack) ? MeatPack::Flag_RemoveComments : 0; + binarizer_flags |= MeatPack::Flag_OmitWhitespaces; + MeatPack::MPBinarizer binarizer(binarizer_flags); + binarizer.initialize(dst); + auto begin_it = src.begin(); + auto end_it = src.begin(); + while (end_it != src.end()) { + while (end_it != src.end() && *end_it != '\n') { + ++end_it; + } + const std::string line(begin_it, ++end_it); + binarizer.binarize_line(line, dst); + begin_it = end_it; + } + binarizer.finalize(dst); + break; + } + } + return true; +} + static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) { switch (encoding_type) @@ -140,8 +577,9 @@ static bool decode_gcode(const std::vector& src, std::string& dst, EGCo break; } case EGCodeEncodingType::MeatPack: + case EGCodeEncodingType::MeatPackComments: { - // TODO + MeatPack::unbinarize(src, dst); break; } } @@ -837,13 +1275,12 @@ EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) return EResult::Success; } -static EResult write_gcode_block(FILE& file, const std::string& raw_data, EGCodeEncodingType encoding_type, ECompressionType compression_type, - EChecksumType checksum_type) +static EResult write_gcode_block(FILE& file, const std::string& raw_data, const BinarizerConfig& config) { GCodeBlock block; - block.encoding_type = (uint16_t)encoding_type; + block.encoding_type = (uint16_t)config.gcode_encoding; block.raw_data = raw_data; - return block.write(file, compression_type, checksum_type); + return block.write(file, config.compression, config.checksum); } EResult Binarizer::append_gcode(const std::string& gcode) @@ -865,7 +1302,7 @@ EResult Binarizer::append_gcode(const std::string& gcode) const size_t line_size = 1 + end_line_pos - begin_pos; if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); if (res != EResult::Success) // propagate error return res; @@ -891,7 +1328,7 @@ EResult Binarizer::finalize() // save gcode cache, if not empty if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); if (res != EResult::Success) // propagate error return res; diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index ad4d1c640c..595f5da806 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -202,6 +202,7 @@ enum class EGCodeEncodingType : uint16_t { None, MeatPack, + MeatPackComments }; struct GCodeBlock @@ -257,7 +258,7 @@ struct BinarizerConfig ECompressionType compression{ ECompressionType::None }; EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; - EChecksumType checksum{ EChecksumType::None }; + EChecksumType checksum{ EChecksumType::CRC32 }; }; class Binarizer diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9b89596001..1ee779e421 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7885,36 +7885,36 @@ void GLCanvas3D::show_binary_gcode_debug_window() imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Compression"); ImGui::TableSetColumnIndex(1); const std::vector gcode_compressions = { "None" }; - int compression = (int)binarizer_config.compression; - if (imgui.combo(std::string("##1"), gcode_compressions, compression, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.compression = (BinaryGCode::ECompressionType)compression; + int compressions_id = (int)binarizer_config.compression; + if (imgui.combo(std::string("##1"), gcode_compressions, compressions_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.compression = (BinaryGCode::ECompressionType)compressions_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GGcode encoding"); ImGui::TableSetColumnIndex(1); - const std::vector gcode_encodings = { "None", "MeatPack" }; - int gcode_encoding = (int)binarizer_config.gcode_encoding; - if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encoding; + const std::vector gcode_encodings = { "None", "MeatPack", "MeatPack Comments"}; + int gcode_encodings_id = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encodings_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); ImGui::TableSetColumnIndex(1); const std::vector metadata_encodings = { "INI" }; - int metadata_encoding = (int)binarizer_config.metadata_encoding; - if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encoding; + int metadata_encodings_id = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encodings_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); ImGui::TableSetColumnIndex(1); const std::vector checksums = { "None", "CRC32" }; - int checksum = (int)binarizer_config.checksum; - if (imgui.combo(std::string("##4"), checksums, checksum, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.checksum = (BinaryGCode::EChecksumType)checksum; + int checksums_id = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), checksums, checksums_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.checksum = (BinaryGCode::EChecksumType)checksums_id; ImGui::EndTable(); From 7e56d807185348987026786c4f8607fcfdc84d6e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Jul 2023 13:30:18 +0200 Subject: [PATCH 07/48] SPE-1784: New compressed (binary) gcode format integration Added Heatshrink compression --- src/CMakeLists.txt | 1 + src/heatshrink/CMakeLists.txt | 11 + src/heatshrink/LICENSE | 14 + src/heatshrink/README.md | 7 + src/heatshrink/heatshrink_common.h | 20 + src/heatshrink/heatshrink_config.h | 26 ++ src/heatshrink/heatshrink_decoder.c | 367 +++++++++++++++ src/heatshrink/heatshrink_decoder.h | 100 ++++ src/heatshrink/heatshrink_encoder.c | 604 +++++++++++++++++++++++++ src/heatshrink/heatshrink_encoder.h | 109 +++++ src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/GCode/GCodeBinarizer.cpp | 158 ++++++- src/libslic3r/GCode/GCodeBinarizer.hpp | 13 +- src/slic3r/GUI/GLCanvas3D.cpp | 85 +++- 14 files changed, 1482 insertions(+), 34 deletions(-) create mode 100644 src/heatshrink/CMakeLists.txt create mode 100644 src/heatshrink/LICENSE create mode 100644 src/heatshrink/README.md create mode 100644 src/heatshrink/heatshrink_common.h create mode 100644 src/heatshrink/heatshrink_config.h create mode 100644 src/heatshrink/heatshrink_decoder.c create mode 100644 src/heatshrink/heatshrink_decoder.h create mode 100644 src/heatshrink/heatshrink_encoder.c create mode 100644 src/heatshrink/heatshrink_encoder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 50eccfc849..9a037e44fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(libigl) add_subdirectory(hints) add_subdirectory(qoi) add_subdirectory(libnest2d) +add_subdirectory(heatshrink) find_package(Qhull 7.2 REQUIRED) add_library(qhull INTERFACE) diff --git a/src/heatshrink/CMakeLists.txt b/src/heatshrink/CMakeLists.txt new file mode 100644 index 0000000000..94c93315ba --- /dev/null +++ b/src/heatshrink/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.12) +project(heatshrink) + +add_library(heatshrink STATIC + heatshrink_common.h + heatshrink_config.h + heatshrink_decoder.c + heatshrink_decoder.h + heatshrink_encoder.c + heatshrink_encoder.h +) diff --git a/src/heatshrink/LICENSE b/src/heatshrink/LICENSE new file mode 100644 index 0000000000..9132cb6a0c --- /dev/null +++ b/src/heatshrink/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2013-2015, Scott Vokes +All rights reserved. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/heatshrink/README.md b/src/heatshrink/README.md new file mode 100644 index 0000000000..4de08bee29 --- /dev/null +++ b/src/heatshrink/README.md @@ -0,0 +1,7 @@ +** heatshrink is a data compression library for embedded/real-time systems.** + +For more information go to https://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ + +THIS DIRECTORY CONTAINS THE heatshrink v0.4.1 b9ac05e SOURCE DISTRIBUTION. + + diff --git a/src/heatshrink/heatshrink_common.h b/src/heatshrink/heatshrink_common.h new file mode 100644 index 0000000000..bc89774f5e --- /dev/null +++ b/src/heatshrink/heatshrink_common.h @@ -0,0 +1,20 @@ +#ifndef HEATSHRINK_H +#define HEATSHRINK_H + +#define HEATSHRINK_AUTHOR "Scott Vokes " +#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" + +/* Version 0.4.1 */ +#define HEATSHRINK_VERSION_MAJOR 0 +#define HEATSHRINK_VERSION_MINOR 4 +#define HEATSHRINK_VERSION_PATCH 1 + +#define HEATSHRINK_MIN_WINDOW_BITS 4 +#define HEATSHRINK_MAX_WINDOW_BITS 15 + +#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 + +#define HEATSHRINK_LITERAL_MARKER 0x01 +#define HEATSHRINK_BACKREF_MARKER 0x00 + +#endif diff --git a/src/heatshrink/heatshrink_config.h b/src/heatshrink/heatshrink_config.h new file mode 100644 index 0000000000..13135b9395 --- /dev/null +++ b/src/heatshrink/heatshrink_config.h @@ -0,0 +1,26 @@ +#ifndef HEATSHRINK_CONFIG_H +#define HEATSHRINK_CONFIG_H + +/* Should functionality assuming dynamic allocation be used? */ +#ifndef HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_DYNAMIC_ALLOC 1 +#endif + +#if HEATSHRINK_DYNAMIC_ALLOC + /* Optional replacement of malloc/free */ + #define HEATSHRINK_MALLOC(SZ) malloc(SZ) + #define HEATSHRINK_FREE(P, SZ) free(P) +#else + /* Required parameters for static configuration */ + #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 + #define HEATSHRINK_STATIC_WINDOW_BITS 8 + #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 +#endif + +/* Turn on logging for debugging. */ +#define HEATSHRINK_DEBUGGING_LOGS 0 + +/* Use indexing for faster compression. (This requires additional space.) */ +#define HEATSHRINK_USE_INDEX 1 + +#endif diff --git a/src/heatshrink/heatshrink_decoder.c b/src/heatshrink/heatshrink_decoder.c new file mode 100644 index 0000000000..0f118cf98a --- /dev/null +++ b/src/heatshrink/heatshrink_decoder.c @@ -0,0 +1,367 @@ +#include +#include +#include "heatshrink_decoder.h" + +/* States for the polling state machine. */ +typedef enum { + HSDS_TAG_BIT, /* tag bit */ + HSDS_YIELD_LITERAL, /* ready to yield literal byte */ + HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ + HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ + HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ + HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ + HSDS_YIELD_BACKREF, /* ready to yield back-reference */ +} HSD_state; + +#if HEATSHRINK_DEBUGGING_LOGS +#include +#include +#include +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#define ASSERT(X) assert(X) +static const char *state_names[] = { + "tag_bit", + "yield_literal", + "backref_index_msb", + "backref_index_lsb", + "backref_count_msb", + "backref_count_lsb", + "yield_backref", +}; +#else +#define LOG(...) /* no-op */ +#define ASSERT(X) /* no-op */ +#endif + +typedef struct { + uint8_t *buf; /* output buffer */ + size_t buf_size; /* buffer size */ + size_t *output_size; /* bytes pushed to buffer, so far */ +} output_info; + +#define NO_BITS ((uint16_t)-1) + +/* Forward references. */ +static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); +static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); + +#if HEATSHRINK_DYNAMIC_ALLOC +heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, + uint8_t window_sz2, + uint8_t lookahead_sz2) { + if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || + (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || + (input_buffer_size == 0) || + (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || + (lookahead_sz2 >= window_sz2)) { + return NULL; + } + size_t buffers_sz = (1 << window_sz2) + input_buffer_size; + size_t sz = sizeof(heatshrink_decoder) + buffers_sz; + heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); + if (hsd == NULL) { return NULL; } + hsd->input_buffer_size = input_buffer_size; + hsd->window_sz2 = window_sz2; + hsd->lookahead_sz2 = lookahead_sz2; + heatshrink_decoder_reset(hsd); + LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", + sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); + return hsd; +} + +void heatshrink_decoder_free(heatshrink_decoder *hsd) { + size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; + size_t sz = sizeof(heatshrink_decoder) + buffers_sz; + HEATSHRINK_FREE(hsd, sz); + (void)sz; /* may not be used by free */ +} +#endif + +void heatshrink_decoder_reset(heatshrink_decoder *hsd) { + size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); + size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); + memset(hsd->buffers, 0, buf_sz + input_sz); + hsd->state = HSDS_TAG_BIT; + hsd->input_size = 0; + hsd->input_index = 0; + hsd->bit_index = 0x00; + hsd->current_byte = 0x00; + hsd->output_count = 0; + hsd->output_index = 0; + hsd->head_index = 0; +} + +/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ +HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, + uint8_t *in_buf, size_t size, size_t *input_size) { + if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { + return HSDR_SINK_ERROR_NULL; + } + + size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; + if (rem == 0) { + *input_size = 0; + return HSDR_SINK_FULL; + } + + size = rem < size ? rem : size; + LOG("-- sinking %zd bytes\n", size); + /* copy into input buffer (at head of buffers) */ + memcpy(&hsd->buffers[hsd->input_size], in_buf, size); + hsd->input_size += size; + *input_size = size; + return HSDR_SINK_OK; +} + + +/***************** + * Decompression * + *****************/ + +#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) +#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) + +// States +static HSD_state st_tag_bit(heatshrink_decoder *hsd); +static HSD_state st_yield_literal(heatshrink_decoder *hsd, + output_info *oi); +static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); +static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); +static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); +static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); +static HSD_state st_yield_backref(heatshrink_decoder *hsd, + output_info *oi); + +HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { + if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { + return HSDR_POLL_ERROR_NULL; + } + *output_size = 0; + + output_info oi; + oi.buf = out_buf; + oi.buf_size = out_buf_size; + oi.output_size = output_size; + + while (1) { + LOG("-- poll, state is %d (%s), input_size %d\n", + hsd->state, state_names[hsd->state], hsd->input_size); + uint8_t in_state = hsd->state; + switch (in_state) { + case HSDS_TAG_BIT: + hsd->state = st_tag_bit(hsd); + break; + case HSDS_YIELD_LITERAL: + hsd->state = st_yield_literal(hsd, &oi); + break; + case HSDS_BACKREF_INDEX_MSB: + hsd->state = st_backref_index_msb(hsd); + break; + case HSDS_BACKREF_INDEX_LSB: + hsd->state = st_backref_index_lsb(hsd); + break; + case HSDS_BACKREF_COUNT_MSB: + hsd->state = st_backref_count_msb(hsd); + break; + case HSDS_BACKREF_COUNT_LSB: + hsd->state = st_backref_count_lsb(hsd); + break; + case HSDS_YIELD_BACKREF: + hsd->state = st_yield_backref(hsd, &oi); + break; + default: + return HSDR_POLL_ERROR_UNKNOWN; + } + + /* If the current state cannot advance, check if input or output + * buffer are exhausted. */ + if (hsd->state == in_state) { + if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } + return HSDR_POLL_EMPTY; + } + } +} + +static HSD_state st_tag_bit(heatshrink_decoder *hsd) { + uint32_t bits = get_bits(hsd, 1); // get tag bit + if (bits == NO_BITS) { + return HSDS_TAG_BIT; + } else if (bits) { + return HSDS_YIELD_LITERAL; + } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { + return HSDS_BACKREF_INDEX_MSB; + } else { + hsd->output_index = 0; + return HSDS_BACKREF_INDEX_LSB; + } +} + +static HSD_state st_yield_literal(heatshrink_decoder *hsd, + output_info *oi) { + /* Emit a repeated section from the window buffer, and add it (again) + * to the window buffer. (Note that the repetition can include + * itself.)*/ + if (*oi->output_size < oi->buf_size) { + uint16_t byte = get_bits(hsd, 8); + if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ + uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; + uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; + uint8_t c = byte & 0xFF; + LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); + buf[hsd->head_index++ & mask] = c; + push_byte(hsd, oi, c); + return HSDS_TAG_BIT; + } else { + return HSDS_YIELD_LITERAL; + } +} + +static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { + uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); + ASSERT(bit_ct > 8); + uint16_t bits = get_bits(hsd, bit_ct - 8); + LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } + hsd->output_index = bits << 8; + return HSDS_BACKREF_INDEX_LSB; +} + +static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { + uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); + uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); + LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } + hsd->output_index |= bits; + hsd->output_index++; + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + hsd->output_count = 0; + return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; +} + +static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + ASSERT(br_bit_ct > 8); + uint16_t bits = get_bits(hsd, br_bit_ct - 8); + LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } + hsd->output_count = bits << 8; + return HSDS_BACKREF_COUNT_LSB; +} + +static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); + LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } + hsd->output_count |= bits; + hsd->output_count++; + return HSDS_YIELD_BACKREF; +} + +static HSD_state st_yield_backref(heatshrink_decoder *hsd, + output_info *oi) { + size_t count = oi->buf_size - *oi->output_size; + if (count > 0) { + size_t i = 0; + if (hsd->output_count < count) count = hsd->output_count; + uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; + uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; + uint16_t neg_offset = hsd->output_index; + LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); + ASSERT(neg_offset <= mask + 1); + ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); + + for (i=0; ihead_index - neg_offset) & mask]; + push_byte(hsd, oi, c); + buf[hsd->head_index & mask] = c; + hsd->head_index++; + LOG(" -- ++ 0x%02x\n", c); + } + hsd->output_count -= count; + if (hsd->output_count == 0) { return HSDS_TAG_BIT; } + } + return HSDS_YIELD_BACKREF; +} + +/* Get the next COUNT bits from the input buffer, saving incremental progress. + * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ +static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) { + uint16_t accumulator = 0; + int i = 0; + if (count > 15) { return NO_BITS; } + LOG("-- popping %u bit(s)\n", count); + + /* If we aren't able to get COUNT bits, suspend immediately, because we + * don't track how many bits of COUNT we've accumulated before suspend. */ + if (hsd->input_size == 0) { + if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } + } + + for (i = 0; i < count; i++) { + if (hsd->bit_index == 0x00) { + if (hsd->input_size == 0) { + LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", + accumulator, accumulator); + return NO_BITS; + } + hsd->current_byte = hsd->buffers[hsd->input_index++]; + LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); + if (hsd->input_index == hsd->input_size) { + hsd->input_index = 0; /* input is exhausted */ + hsd->input_size = 0; + } + hsd->bit_index = 0x80; + } + accumulator <<= 1; + if (hsd->current_byte & hsd->bit_index) { + accumulator |= 0x01; + if (0) { + LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", + accumulator, hsd->bit_index); + } + } else { + if (0) { + LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", + accumulator, hsd->bit_index); + } + } + hsd->bit_index >>= 1; + } + + if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); } + return accumulator; +} + +HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { + if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } + switch (hsd->state) { + case HSDS_TAG_BIT: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + /* If we want to finish with no input, but are in these states, it's + * because the 0-bit padding to the last byte looks like a backref + * marker bit followed by all 0s for index and count bits. */ + case HSDS_BACKREF_INDEX_LSB: + case HSDS_BACKREF_INDEX_MSB: + case HSDS_BACKREF_COUNT_LSB: + case HSDS_BACKREF_COUNT_MSB: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + /* If the output stream is padded with 0xFFs (possibly due to being in + * flash memory), also explicitly check the input size rather than + * uselessly returning MORE but yielding 0 bytes when polling. */ + case HSDS_YIELD_LITERAL: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + default: + return HSDR_FINISH_MORE; + } +} + +static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { + LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); + oi->buf[(*oi->output_size)++] = byte; + (void)hsd; +} diff --git a/src/heatshrink/heatshrink_decoder.h b/src/heatshrink/heatshrink_decoder.h new file mode 100644 index 0000000000..bda83991ef --- /dev/null +++ b/src/heatshrink/heatshrink_decoder.h @@ -0,0 +1,100 @@ +#ifndef HEATSHRINK_DECODER_H +#define HEATSHRINK_DECODER_H + +#include +#include +#include "heatshrink_common.h" +#include "heatshrink_config.h" + +typedef enum { + HSDR_SINK_OK, /* data sunk, ready to poll */ + HSDR_SINK_FULL, /* out of space in internal buffer */ + HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ +} HSD_sink_res; + +typedef enum { + HSDR_POLL_EMPTY, /* input exhausted */ + HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ + HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ + HSDR_POLL_ERROR_UNKNOWN=-2, +} HSD_poll_res; + +typedef enum { + HSDR_FINISH_DONE, /* output is done */ + HSDR_FINISH_MORE, /* more output remains */ + HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ +} HSD_finish_res; + +#if HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ + ((BUF)->input_buffer_size) +#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ + ((BUF)->window_sz2) +#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ + ((BUF)->lookahead_sz2) +#else +#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ + HEATSHRINK_STATIC_INPUT_BUFFER_SIZE +#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ + (HEATSHRINK_STATIC_WINDOW_BITS) +#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ + (HEATSHRINK_STATIC_LOOKAHEAD_BITS) +#endif + +typedef struct { + uint16_t input_size; /* bytes in input buffer */ + uint16_t input_index; /* offset to next unprocessed input byte */ + uint16_t output_count; /* how many bytes to output */ + uint16_t output_index; /* index for bytes to output */ + uint16_t head_index; /* head of window buffer */ + uint8_t state; /* current state machine node */ + uint8_t current_byte; /* current byte of input */ + uint8_t bit_index; /* current bit index */ + +#if HEATSHRINK_DYNAMIC_ALLOC + /* Fields that are only used if dynamically allocated. */ + uint8_t window_sz2; /* window buffer bits */ + uint8_t lookahead_sz2; /* lookahead bits */ + uint16_t input_buffer_size; /* input buffer size */ + + /* Input buffer, then expansion window buffer */ + uint8_t buffers[]; +#else + /* Input buffer, then expansion window buffer */ + uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) + + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; +#endif +} heatshrink_decoder; + +#if HEATSHRINK_DYNAMIC_ALLOC +/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, + * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead + * size of 2^lookahead_sz2. (The window buffer and lookahead sizes + * must match the settings used when the data was compressed.) + * Returns NULL on error. */ +heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, + uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); + +/* Free a decoder. */ +void heatshrink_decoder_free(heatshrink_decoder *hsd); +#endif + +/* Reset a decoder. */ +void heatshrink_decoder_reset(heatshrink_decoder *hsd); + +/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to + * indicate how many bytes were actually sunk (in case a buffer was filled). */ +HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, + uint8_t *in_buf, size_t size, size_t *input_size); + +/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into + * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ +HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size); + +/* Notify the dencoder that the input stream is finished. + * If the return value is HSDR_FINISH_MORE, there is still more output, so + * call heatshrink_decoder_poll and repeat. */ +HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); + +#endif diff --git a/src/heatshrink/heatshrink_encoder.c b/src/heatshrink/heatshrink_encoder.c new file mode 100644 index 0000000000..edf4abebcc --- /dev/null +++ b/src/heatshrink/heatshrink_encoder.c @@ -0,0 +1,604 @@ +#include +#include +#include +#include "heatshrink_encoder.h" + +typedef enum { + HSES_NOT_FULL, /* input buffer not full enough */ + HSES_FILLED, /* buffer is full */ + HSES_SEARCH, /* searching for patterns */ + HSES_YIELD_TAG_BIT, /* yield tag bit */ + HSES_YIELD_LITERAL, /* emit literal byte */ + HSES_YIELD_BR_INDEX, /* yielding backref index */ + HSES_YIELD_BR_LENGTH, /* yielding backref length */ + HSES_SAVE_BACKLOG, /* copying buffer to backlog */ + HSES_FLUSH_BITS, /* flush bit buffer */ + HSES_DONE, /* done */ +} HSE_state; + +#if HEATSHRINK_DEBUGGING_LOGS +#include +#include +#include +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#define ASSERT(X) assert(X) +static const char *state_names[] = { + "not_full", + "filled", + "search", + "yield_tag_bit", + "yield_literal", + "yield_br_index", + "yield_br_length", + "save_backlog", + "flush_bits", + "done", +}; +#else +#define LOG(...) /* no-op */ +#define ASSERT(X) /* no-op */ +#endif + +// Encoder flags +enum { + FLAG_IS_FINISHING = 0x01, +}; + +typedef struct { + uint8_t *buf; /* output buffer */ + size_t buf_size; /* buffer size */ + size_t *output_size; /* bytes pushed to buffer, so far */ +} output_info; + +#define MATCH_NOT_FOUND ((uint16_t)-1) + +static uint16_t get_input_offset(heatshrink_encoder *hse); +static uint16_t get_input_buffer_size(heatshrink_encoder *hse); +static uint16_t get_lookahead_size(heatshrink_encoder *hse); +static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); +static int can_take_byte(output_info *oi); +static int is_finishing(heatshrink_encoder *hse); +static void save_backlog(heatshrink_encoder *hse); + +/* Push COUNT (max 8) bits to the output buffer, which has room. */ +static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, + output_info *oi); +static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); +static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); + +#if HEATSHRINK_DYNAMIC_ALLOC +heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, + uint8_t lookahead_sz2) { + if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || + (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || + (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || + (lookahead_sz2 >= window_sz2)) { + return NULL; + } + + /* Note: 2 * the window size is used because the buffer needs to fit + * (1 << window_sz2) bytes for the current input, and an additional + * (1 << window_sz2) bytes for the previous buffer of input, which + * will be scanned for useful backreferences. */ + size_t buf_sz = (2 << window_sz2); + + heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse) + buf_sz); + if (hse == NULL) { return NULL; } + hse->window_sz2 = window_sz2; + hse->lookahead_sz2 = lookahead_sz2; + heatshrink_encoder_reset(hse); + +#if HEATSHRINK_USE_INDEX + size_t index_sz = buf_sz*sizeof(uint16_t); + hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); + if (hse->search_index == NULL) { + HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); + return NULL; + } + hse->search_index->size = index_sz; +#endif + + LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", + buf_sz, get_input_buffer_size(hse)); + return hse; +} + +void heatshrink_encoder_free(heatshrink_encoder *hse) { + size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); +#if HEATSHRINK_USE_INDEX + size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; + HEATSHRINK_FREE(hse->search_index, index_sz); + (void)index_sz; +#endif + HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder) + buf_sz); + (void)buf_sz; +} +#endif + +void heatshrink_encoder_reset(heatshrink_encoder *hse) { + size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); + memset(hse->buffer, 0, buf_sz); + hse->input_size = 0; + hse->state = HSES_NOT_FULL; + hse->match_scan_index = 0; + hse->flags = 0; + hse->bit_index = 0x80; + hse->current_byte = 0x00; + hse->match_length = 0; + + hse->outgoing_bits = 0x0000; + hse->outgoing_bits_count = 0; + + #ifdef LOOP_DETECT + hse->loop_detect = (uint32_t)-1; + #endif +} + +HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, + uint8_t *in_buf, size_t size, size_t *input_size) { + if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { + return HSER_SINK_ERROR_NULL; + } + + /* Sinking more content after saying the content is done, tsk tsk */ + if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } + + /* Sinking more content before processing is done */ + if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } + + uint16_t write_offset = get_input_offset(hse) + hse->input_size; + uint16_t ibs = get_input_buffer_size(hse); + uint16_t rem = ibs - hse->input_size; + uint16_t cp_sz = rem < size ? rem : size; + + memcpy(&hse->buffer[write_offset], in_buf, cp_sz); + *input_size = cp_sz; + hse->input_size += cp_sz; + + LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", + cp_sz, size, write_offset, hse->input_size); + if (cp_sz == rem) { + LOG("-- internal buffer is now full\n"); + hse->state = HSES_FILLED; + } + + return HSER_SINK_OK; +} + + +/*************** + * Compression * + ***************/ + +static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, + uint16_t end, const uint16_t maxlen, uint16_t *match_length); +static void do_indexing(heatshrink_encoder *hse); + +static HSE_state st_step_search(heatshrink_encoder *hse); +static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_literal(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_br_index(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_br_length(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_save_backlog(heatshrink_encoder *hse); +static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, + output_info *oi); + +HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { + if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { + return HSER_POLL_ERROR_NULL; + } + if (out_buf_size == 0) { + LOG("-- MISUSE: output buffer size is 0\n"); + return HSER_POLL_ERROR_MISUSE; + } + *output_size = 0; + + output_info oi; + oi.buf = out_buf; + oi.buf_size = out_buf_size; + oi.output_size = output_size; + + while (1) { + LOG("-- polling, state %u (%s), flags 0x%02x\n", + hse->state, state_names[hse->state], hse->flags); + + uint8_t in_state = hse->state; + switch (in_state) { + case HSES_NOT_FULL: + return HSER_POLL_EMPTY; + case HSES_FILLED: + do_indexing(hse); + hse->state = HSES_SEARCH; + break; + case HSES_SEARCH: + hse->state = st_step_search(hse); + break; + case HSES_YIELD_TAG_BIT: + hse->state = st_yield_tag_bit(hse, &oi); + break; + case HSES_YIELD_LITERAL: + hse->state = st_yield_literal(hse, &oi); + break; + case HSES_YIELD_BR_INDEX: + hse->state = st_yield_br_index(hse, &oi); + break; + case HSES_YIELD_BR_LENGTH: + hse->state = st_yield_br_length(hse, &oi); + break; + case HSES_SAVE_BACKLOG: + hse->state = st_save_backlog(hse); + break; + case HSES_FLUSH_BITS: + hse->state = st_flush_bit_buffer(hse, &oi); + case HSES_DONE: + return HSER_POLL_EMPTY; + default: + LOG("-- bad state %s\n", state_names[hse->state]); + return HSER_POLL_ERROR_MISUSE; + } + + if (hse->state == in_state) { + /* Check if output buffer is exhausted. */ + if (*output_size == out_buf_size) return HSER_POLL_MORE; + } + } +} + +HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { + if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } + LOG("-- setting is_finishing flag\n"); + hse->flags |= FLAG_IS_FINISHING; + if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } + return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; +} + +static HSE_state st_step_search(heatshrink_encoder *hse) { + uint16_t window_length = get_input_buffer_size(hse); + uint16_t lookahead_sz = get_lookahead_size(hse); + uint16_t msi = hse->match_scan_index; + LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", + msi, hse->input_size + msi, 2*window_length, hse->input_size); + + bool fin = is_finishing(hse); + if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { + /* Current search buffer is exhausted, copy it into the + * backlog and await more input. */ + LOG("-- end of search @ %d\n", msi); + return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; + } + + uint16_t input_offset = get_input_offset(hse); + uint16_t end = input_offset + msi; + uint16_t start = end - window_length; + + uint16_t max_possible = lookahead_sz; + if (hse->input_size - msi < lookahead_sz) { + max_possible = hse->input_size - msi; + } + + uint16_t match_length = 0; + uint16_t match_pos = find_longest_match(hse, + start, end, max_possible, &match_length); + + if (match_pos == MATCH_NOT_FOUND) { + LOG("ss Match not found\n"); + hse->match_scan_index++; + hse->match_length = 0; + return HSES_YIELD_TAG_BIT; + } else { + LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); + hse->match_pos = match_pos; + hse->match_length = match_length; + ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); + + return HSES_YIELD_TAG_BIT; + } +} + +static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + if (hse->match_length == 0) { + add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); + return HSES_YIELD_LITERAL; + } else { + add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); + hse->outgoing_bits = hse->match_pos - 1; + hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); + return HSES_YIELD_BR_INDEX; + } + } else { + return HSES_YIELD_TAG_BIT; /* output is full, continue */ + } +} + +static HSE_state st_yield_literal(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + push_literal_byte(hse, oi); + return HSES_SEARCH; + } else { + return HSES_YIELD_LITERAL; + } +} + +static HSE_state st_yield_br_index(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + LOG("-- yielding backref index %u\n", hse->match_pos); + if (push_outgoing_bits(hse, oi) > 0) { + return HSES_YIELD_BR_INDEX; /* continue */ + } else { + hse->outgoing_bits = hse->match_length - 1; + hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); + return HSES_YIELD_BR_LENGTH; /* done */ + } + } else { + return HSES_YIELD_BR_INDEX; /* continue */ + } +} + +static HSE_state st_yield_br_length(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + LOG("-- yielding backref length %u\n", hse->match_length); + if (push_outgoing_bits(hse, oi) > 0) { + return HSES_YIELD_BR_LENGTH; + } else { + hse->match_scan_index += hse->match_length; + hse->match_length = 0; + return HSES_SEARCH; + } + } else { + return HSES_YIELD_BR_LENGTH; + } +} + +static HSE_state st_save_backlog(heatshrink_encoder *hse) { + LOG("-- saving backlog\n"); + save_backlog(hse); + return HSES_NOT_FULL; +} + +static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, + output_info *oi) { + if (hse->bit_index == 0x80) { + LOG("-- done!\n"); + return HSES_DONE; + } else if (can_take_byte(oi)) { + LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); + oi->buf[(*oi->output_size)++] = hse->current_byte; + LOG("-- done!\n"); + return HSES_DONE; + } else { + return HSES_FLUSH_BITS; + } +} + +static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { + LOG("-- adding tag bit: %d\n", tag); + push_bits(hse, 1, tag, oi); +} + +static uint16_t get_input_offset(heatshrink_encoder *hse) { + return get_input_buffer_size(hse); +} + +static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { + return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); + (void)hse; +} + +static uint16_t get_lookahead_size(heatshrink_encoder *hse) { + return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); + (void)hse; +} + +static void do_indexing(heatshrink_encoder *hse) { +#if HEATSHRINK_USE_INDEX + /* Build an index array I that contains flattened linked lists + * for the previous instances of every byte in the buffer. + * + * For example, if buf[200] == 'x', then index[200] will either + * be an offset i such that buf[i] == 'x', or a negative offset + * to indicate end-of-list. This significantly speeds up matching, + * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. + * + * Future optimization options: + * 1. Since any negative value represents end-of-list, the other + * 15 bits could be used to improve the index dynamically. + * + * 2. Likewise, the last lookahead_sz bytes of the index will + * not be usable, so temporary data could be stored there to + * dynamically improve the index. + * */ + struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); + int16_t last[256]; + memset(last, 0xFF, sizeof(last)); + + uint8_t * const data = hse->buffer; + int16_t * const index = hsi->index; + + const uint16_t input_offset = get_input_offset(hse); + const uint16_t end = input_offset + hse->input_size; + + for (uint16_t i=0; iflags & FLAG_IS_FINISHING; +} + +static int can_take_byte(output_info *oi) { + return *oi->output_size < oi->buf_size; +} + +/* Return the longest match for the bytes at buf[end:end+maxlen] between + * buf[start] and buf[end-1]. If no match is found, return -1. */ +static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, + uint16_t end, const uint16_t maxlen, uint16_t *match_length) { + LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", + end, end + maxlen, start, end + maxlen - 1, maxlen); + uint8_t *buf = hse->buffer; + + uint16_t match_maxlen = 0; + uint16_t match_index = MATCH_NOT_FOUND; + + uint16_t len = 0; + uint8_t * const needlepoint = &buf[end]; +#if HEATSHRINK_USE_INDEX + struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); + int16_t pos = hsi->index[end]; + + while (pos - (int16_t)start >= 0) { + uint8_t * const pospoint = &buf[pos]; + len = 0; + + /* Only check matches that will potentially beat the current maxlen. + * This is redundant with the index if match_maxlen is 0, but the + * added branch overhead to check if it == 0 seems to be worse. */ + if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { + pos = hsi->index[pos]; + continue; + } + + for (len = 1; len < maxlen; len++) { + if (pospoint[len] != needlepoint[len]) break; + } + + if (len > match_maxlen) { + match_maxlen = len; + match_index = pos; + if (len == maxlen) { break; } /* won't find better */ + } + pos = hsi->index[pos]; + } +#else + for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { + uint8_t * const pospoint = &buf[pos]; + if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) + && (*pospoint == *needlepoint)) { + for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", + pos + len, pospoint[len], needlepoint[len], start); + } + if (pospoint[len] != needlepoint[len]) { break; } + } + if (len > match_maxlen) { + match_maxlen = len; + match_index = pos; + if (len == maxlen) { break; } /* don't keep searching */ + } + } + } +#endif + + const size_t break_even_point = + (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + + HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); + + /* Instead of comparing break_even_point against 8*match_maxlen, + * compare match_maxlen against break_even_point/8 to avoid + * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and + * 3, respectively, break_even_point/8 will always be at least 1. */ + if (match_maxlen > (break_even_point / 8)) { + LOG("-- best match: %u bytes at -%u\n", + match_maxlen, end - match_index); + *match_length = match_maxlen; + return end - match_index; + } + LOG("-- none found\n"); + return MATCH_NOT_FOUND; +} + +static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { + uint8_t count = 0; + uint8_t bits = 0; + if (hse->outgoing_bits_count > 8) { + count = 8; + bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); + } else { + count = hse->outgoing_bits_count; + bits = hse->outgoing_bits; + } + + if (count > 0) { + LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); + push_bits(hse, count, bits, oi); + hse->outgoing_bits_count -= count; + } + return count; +} + +/* Push COUNT (max 8) bits to the output buffer, which has room. + * Bytes are set from the lowest bits, up. */ +static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, + output_info *oi) { + ASSERT(count <= 8); + LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); + + /* If adding a whole byte and at the start of a new output byte, + * just push it through whole and skip the bit IO loop. */ + if (count == 8 && hse->bit_index == 0x80) { + oi->buf[(*oi->output_size)++] = bits; + } else { + for (int i=count - 1; i>=0; i--) { + bool bit = bits & (1 << i); + if (bit) { hse->current_byte |= hse->bit_index; } + if (0) { + LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", + bit ? 1 : 0, hse->bit_index, hse->current_byte); + } + hse->bit_index >>= 1; + if (hse->bit_index == 0x00) { + hse->bit_index = 0x80; + LOG(" > pushing byte 0x%02x\n", hse->current_byte); + oi->buf[(*oi->output_size)++] = hse->current_byte; + hse->current_byte = 0x00; + } + } + } +} + +static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { + uint16_t processed_offset = hse->match_scan_index - 1; + uint16_t input_offset = get_input_offset(hse) + processed_offset; + uint8_t c = hse->buffer[input_offset]; + LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", + c, isprint(c) ? c : '.', input_offset); + push_bits(hse, 8, c, oi); +} + +static void save_backlog(heatshrink_encoder *hse) { + size_t input_buf_sz = get_input_buffer_size(hse); + + uint16_t msi = hse->match_scan_index; + + /* Copy processed data to beginning of buffer, so it can be + * used for future matches. Don't bother checking whether the + * input is less than the maximum size, because if it isn't, + * we're done anyway. */ + uint16_t rem = input_buf_sz - msi; // unprocessed bytes + uint16_t shift_sz = input_buf_sz + rem; + + memmove(&hse->buffer[0], + &hse->buffer[input_buf_sz - rem], + shift_sz); + + hse->match_scan_index = 0; + hse->input_size -= input_buf_sz - rem; +} diff --git a/src/heatshrink/heatshrink_encoder.h b/src/heatshrink/heatshrink_encoder.h new file mode 100644 index 0000000000..18c17731b1 --- /dev/null +++ b/src/heatshrink/heatshrink_encoder.h @@ -0,0 +1,109 @@ +#ifndef HEATSHRINK_ENCODER_H +#define HEATSHRINK_ENCODER_H + +#include +#include +#include "heatshrink_common.h" +#include "heatshrink_config.h" + +typedef enum { + HSER_SINK_OK, /* data sunk into input buffer */ + HSER_SINK_ERROR_NULL=-1, /* NULL argument */ + HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ +} HSE_sink_res; + +typedef enum { + HSER_POLL_EMPTY, /* input exhausted */ + HSER_POLL_MORE, /* poll again for more output */ + HSER_POLL_ERROR_NULL=-1, /* NULL argument */ + HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ +} HSE_poll_res; + +typedef enum { + HSER_FINISH_DONE, /* encoding is complete */ + HSER_FINISH_MORE, /* more output remaining; use poll */ + HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ +} HSE_finish_res; + +#if HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ + ((HSE)->window_sz2) +#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ + ((HSE)->lookahead_sz2) +#define HEATSHRINK_ENCODER_INDEX(HSE) \ + ((HSE)->search_index) +struct hs_index { + uint16_t size; + int16_t index[]; +}; +#else +#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ + (HEATSHRINK_STATIC_WINDOW_BITS) +#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ + (HEATSHRINK_STATIC_LOOKAHEAD_BITS) +#define HEATSHRINK_ENCODER_INDEX(HSE) \ + (&(HSE)->search_index) +struct hs_index { + uint16_t size; + int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; +}; +#endif + +typedef struct { + uint16_t input_size; /* bytes in input buffer */ + uint16_t match_scan_index; + uint16_t match_length; + uint16_t match_pos; + uint16_t outgoing_bits; /* enqueued outgoing bits */ + uint8_t outgoing_bits_count; + uint8_t flags; + uint8_t state; /* current state machine node */ + uint8_t current_byte; /* current byte of output */ + uint8_t bit_index; /* current bit index */ +#if HEATSHRINK_DYNAMIC_ALLOC + uint8_t window_sz2; /* 2^n size of window */ + uint8_t lookahead_sz2; /* 2^n size of lookahead */ +#if HEATSHRINK_USE_INDEX + struct hs_index *search_index; +#endif + /* input buffer and / sliding window for expansion */ + uint8_t buffer[]; +#else + #if HEATSHRINK_USE_INDEX + struct hs_index search_index; + #endif + /* input buffer and / sliding window for expansion */ + uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; +#endif +} heatshrink_encoder; + +#if HEATSHRINK_DYNAMIC_ALLOC +/* Allocate a new encoder struct and its buffers. + * Returns NULL on error. */ +heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, + uint8_t lookahead_sz2); + +/* Free an encoder. */ +void heatshrink_encoder_free(heatshrink_encoder *hse); +#endif + +/* Reset an encoder. */ +void heatshrink_encoder_reset(heatshrink_encoder *hse); + +/* Sink up to SIZE bytes from IN_BUF into the encoder. + * INPUT_SIZE is set to the number of bytes actually sunk (in case a + * buffer was filled.). */ +HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, + uint8_t *in_buf, size_t size, size_t *input_size); + +/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into + * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ +HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size); + +/* Notify the encoder that the input stream is finished. + * If the return value is HSER_FINISH_MORE, there is still more output, so + * call heatshrink_encoder_poll and repeat. */ +HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); + +#endif diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 2c4a22fc5d..9eebd36e8d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -510,6 +510,7 @@ target_link_libraries(libslic3r ZLIB::ZLIB JPEG::JPEG qoi + heatshrink ) if (APPLE) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 350905ecda..8afda44169 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -6,6 +6,11 @@ #include #endif // ENABLE_BINARIZED_GCODE_DEBUG +extern "C" { +#include +#include +} + #include #include @@ -111,10 +116,10 @@ public: } return line; }; - auto is_packable = [this](char c) { + auto is_packable = [](char c) { return (s_lookup_tables.packable[static_cast(c)] != 0); }; - auto pack_chars = [this](char low, char high) { + auto pack_chars = [](char low, char high) { return (((s_lookup_tables.value[static_cast(high)] & 0xF) << 4) | (s_lookup_tables.value[static_cast(low)] & 0xF)); }; @@ -471,7 +476,7 @@ void set_checksum_max_cache_size(size_t size) { g_checksum_max_cache_size = size static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } -static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } +static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::Heatshrink_12_4; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPackComments; } @@ -586,13 +591,141 @@ static bool decode_gcode(const std::vector& src, std::string& dst, EGCo return true; } -static bool compress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) +static bool compress(const std::vector& src, std::vector& dst, ECompressionType compression_type) { + switch (compression_type) + { + case ECompressionType::Heatshrink_11_4: + case ECompressionType::Heatshrink_12_4: + { + const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; + const uint8_t lookahead_sz = 4; + heatshrink_encoder* encoder = heatshrink_encoder_alloc(window_sz, lookahead_sz); + if (encoder == nullptr) + return false; + + // calculate the maximum compressed size (assuming a conservative estimate) + const size_t src_size = src.size(); + const size_t max_compressed_size = src_size + (src_size >> 2); + dst.resize(max_compressed_size); + + uint8_t* buf = const_cast(src.data()); + uint8_t* outbuf = dst.data(); + + // compress data + size_t tosink = src_size; + size_t output_size = 0; + while (tosink > 0) { + size_t sunk = 0; + const HSE_sink_res sink_res = heatshrink_encoder_sink(encoder, buf, tosink, &sunk); + if (sink_res != HSER_SINK_OK) { + heatshrink_encoder_free(encoder); + return false; + } + if (sunk == 0) + // all input data processed + break; + + tosink -= sunk; + buf += sunk; + + size_t polled = 0; + const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); + if (poll_res < 0) { + heatshrink_encoder_free(encoder); + return false; + } + output_size += polled; + } + + // input data finished + const HSE_finish_res finish_res = heatshrink_encoder_finish(encoder); + if (finish_res < 0) { + heatshrink_encoder_free(encoder); + return false; + } + + // poll for final output + size_t polled = 0; + const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); + if (poll_res < 0) { + heatshrink_encoder_free(encoder); + return false; + } + dst.resize(output_size + polled); + heatshrink_encoder_free(encoder); + break; + } + case ECompressionType::None: + default: + { + break; + } + } + return true; } -static bool uncompress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) +static bool uncompress(const std::vector& src, std::vector& dst, ECompressionType compression_type, size_t uncompressed_size) { + switch (compression_type) + { + case ECompressionType::Heatshrink_11_4: + case ECompressionType::Heatshrink_12_4: + { + const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; + const uint8_t lookahead_sz = 4; + const uint16_t input_buffer_size = 2048; + heatshrink_decoder* decoder = heatshrink_decoder_alloc(input_buffer_size, window_sz, lookahead_sz); + if (decoder == nullptr) + return false; + + dst.resize(uncompressed_size); + + uint8_t* buf = const_cast(src.data()); + uint8_t* outbuf = dst.data(); + + uint32_t sunk = 0; + uint32_t polled = 0; + + const size_t compressed_size = src.size(); + while (sunk < compressed_size) { + size_t count = 0; + const HSD_sink_res sink_res = heatshrink_decoder_sink(decoder, &buf[sunk], compressed_size - sunk, &count); + if (sink_res < 0) { + heatshrink_decoder_free(decoder); + return false; + } + + sunk += (uint32_t)count; + + HSD_poll_res poll_res; + do { + poll_res = heatshrink_decoder_poll(decoder, &outbuf[polled], uncompressed_size - polled, &count); + if (poll_res < 0) { + heatshrink_decoder_free(decoder); + return false; + } + polled += (uint32_t)count; + } while (polled < uncompressed_size && poll_res == HSDR_POLL_MORE); + } + + const HSD_finish_res finish_res = heatshrink_decoder_finish(decoder); + if (finish_res < 0) { + heatshrink_decoder_free(decoder); + return false; + } + + heatshrink_decoder_free(decoder); + break; + } + case ECompressionType::None: + default: + { + break; + } + } + return true; } @@ -847,7 +980,7 @@ EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header std::vector uncompressed_data; if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type)) + if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) return EResult::DataUncompressionError; } @@ -956,6 +1089,7 @@ EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, con return EResult::InvalidThumbnailHeight; if (block_header.uncompressed_size == 0) return EResult::InvalidThumbnailDataSize; + data.resize(block_header.uncompressed_size); if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) return EResult::ReadError; @@ -1173,7 +1307,7 @@ EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const B std::vector uncompressed_data; if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type)) + if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) return EResult::DataUncompressionError; } @@ -1246,12 +1380,12 @@ EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) return res; // save file metadata block - res = m_binary_data.file_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.file_metadata.write(*m_file, m_config.compression.file_metadata, m_config.checksum); if (res != EResult::Success) return res; // save printer metadata block - res = m_binary_data.printer_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.printer_metadata.write(*m_file, m_config.compression.printer_metadata, m_config.checksum); if (res != EResult::Success) return res; @@ -1263,12 +1397,12 @@ EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) } // save print metadata block - res = m_binary_data.print_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.print_metadata.write(*m_file, m_config.compression.print_metadata, m_config.checksum); if (res != EResult::Success) return res; // save slicer metadata block - res = m_binary_data.slicer_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.slicer_metadata.write(*m_file, m_config.compression.slicer_metadata, m_config.checksum); if (res != EResult::Success) return res; @@ -1280,7 +1414,7 @@ static EResult write_gcode_block(FILE& file, const std::string& raw_data, const GCodeBlock block; block.encoding_type = (uint16_t)config.gcode_encoding; block.raw_data = raw_data; - return block.write(file, config.compression, config.checksum); + return block.write(file, config.compression.gcode, config.checksum); } EResult Binarizer::append_gcode(const std::string& gcode) diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 595f5da806..6e95d6da89 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -105,6 +105,8 @@ enum class EBlockType : uint16_t enum class ECompressionType : uint16_t { None, + Heatshrink_11_4, + Heatshrink_12_4, }; struct BlockHeader @@ -255,7 +257,16 @@ struct BinaryData struct BinarizerConfig { - ECompressionType compression{ ECompressionType::None }; + struct Compression + { + ECompressionType file_metadata{ ECompressionType::None }; + ECompressionType printer_metadata{ ECompressionType::None }; + ECompressionType thumbnail{ ECompressionType::None }; + ECompressionType print_metadata{ ECompressionType::None }; + ECompressionType slicer_metadata{ ECompressionType::None }; + ECompressionType gcode{ ECompressionType::None }; + }; + Compression compression; EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; EChecksumType checksum{ EChecksumType::CRC32 }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1ee779e421..5a2aa6eb93 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7880,41 +7880,84 @@ void GLCanvas3D::show_binary_gcode_debug_window() imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Compression"); - ImGui::TableSetColumnIndex(1); - const std::vector gcode_compressions = { "None" }; - int compressions_id = (int)binarizer_config.compression; - if (imgui.combo(std::string("##1"), gcode_compressions, compressions_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.compression = (BinaryGCode::ECompressionType)compressions_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GGcode encoding"); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "File metadata compression"); ImGui::TableSetColumnIndex(1); - const std::vector gcode_encodings = { "None", "MeatPack", "MeatPack Comments"}; - int gcode_encodings_id = (int)binarizer_config.gcode_encoding; - if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encodings_id; + std::vector options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; + int option_id = (int)binarizer_config.compression.file_metadata; + if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.file_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Printer metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.printer_metadata; + if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.printer_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Thumbnail compression"); + ImGui::TableSetColumnIndex(1); + options = { "None" }; + option_id = (int)binarizer_config.compression.thumbnail; + if (imgui.combo(std::string("##thumbnail_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.thumbnail = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Print metadata compression"); + ImGui::TableSetColumnIndex(1); + options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; + option_id = (int)binarizer_config.compression.print_metadata; + if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.print_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Slicer metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.slicer_metadata; + if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.slicer_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GCode compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.gcode; + if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.gcode = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GCode encoding"); + ImGui::TableSetColumnIndex(1); + options = { "None", "MeatPack", "MeatPack Comments" }; + option_id = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); ImGui::TableSetColumnIndex(1); - const std::vector metadata_encodings = { "INI" }; - int metadata_encodings_id = (int)binarizer_config.metadata_encoding; - if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encodings_id; + options = { "INI" }; + option_id = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); ImGui::TableSetColumnIndex(1); - const std::vector checksums = { "None", "CRC32" }; - int checksums_id = (int)binarizer_config.checksum; - if (imgui.combo(std::string("##4"), checksums, checksums_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.checksum = (BinaryGCode::EChecksumType)checksums_id; + options = { "None", "CRC32" }; + option_id = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.checksum = (BinaryGCode::EChecksumType)option_id; ImGui::EndTable(); From 004a64a651c66d0b360e15da2f68822e05f434e7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Jul 2023 14:07:46 +0200 Subject: [PATCH 08/48] Removed code for thumbnails compression --- src/libslic3r/GCode/GCodeBinarizer.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 6e95d6da89..ab03f32563 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -261,7 +261,6 @@ struct BinarizerConfig { ECompressionType file_metadata{ ECompressionType::None }; ECompressionType printer_metadata{ ECompressionType::None }; - ECompressionType thumbnail{ ECompressionType::None }; ECompressionType print_metadata{ ECompressionType::None }; ECompressionType slicer_metadata{ ECompressionType::None }; ECompressionType gcode{ ECompressionType::None }; @@ -299,6 +298,7 @@ private: //===================================================================================================================================== // +// GCODEVIEWER INTERFACE // FIRMWARE INTERFACE // //===================================================================================================================================== diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5a2aa6eb93..d075aeda3e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7898,20 +7898,10 @@ void GLCanvas3D::show_binary_gcode_debug_window() if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) binarizer_config.compression.printer_metadata = (BinaryGCode::ECompressionType)option_id; - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Thumbnail compression"); - ImGui::TableSetColumnIndex(1); - options = { "None" }; - option_id = (int)binarizer_config.compression.thumbnail; - if (imgui.combo(std::string("##thumbnail_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.thumbnail = (BinaryGCode::ECompressionType)option_id; - ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Print metadata compression"); ImGui::TableSetColumnIndex(1); - options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) binarizer_config.compression.print_metadata = (BinaryGCode::ECompressionType)option_id; From fba2406ace6b65f80ce84e34bd342a0d996bf8f7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Jul 2023 11:14:21 +0200 Subject: [PATCH 09/48] SPE-1784: New compressed (binary) gcode format integration Added conversion of gcodes from binary to ascii format --- resources/icons/convert_file.svg | 93 ++++++++++ src/libslic3r/GCode/GCodeBinarizer.cpp | 247 ++++++++++++++++++++++--- src/libslic3r/GCode/GCodeBinarizer.hpp | 19 +- src/slic3r/GUI/MainFrame.cpp | 22 ++- src/slic3r/GUI/Plater.cpp | 71 +++++++ src/slic3r/GUI/Plater.hpp | 4 + 6 files changed, 431 insertions(+), 25 deletions(-) create mode 100644 resources/icons/convert_file.svg diff --git a/resources/icons/convert_file.svg b/resources/icons/convert_file.svg new file mode 100644 index 0000000000..2de2b707f0 --- /dev/null +++ b/resources/icons/convert_file.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 8afda44169..cd46324174 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -6,6 +6,10 @@ #include #endif // ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_FILE_CONVERSION_INTERFACE +#include +#endif // ENABLE_FILE_CONVERSION_INTERFACE + extern "C" { #include #include @@ -441,32 +445,34 @@ static void unbinarize(const std::vector& src, std::string& dst) { } } // namespace MeatPack -std::string translate_result(BinaryGCode::EResult result) +std::string translate_result(EResult result) { switch (result) { - case BinaryGCode::EResult::Success: { return "Success"; } - case BinaryGCode::EResult::ReadError: { return "Read error"; } - case BinaryGCode::EResult::WriteError: { return "Write error"; } - case BinaryGCode::EResult::InvalidMagicNumber: { return "Invalid magic number"; } - case BinaryGCode::EResult::InvalidVersionNumber: { return "Invalid version number"; } - case BinaryGCode::EResult::InvalidChecksumType: { return "Invalid checksum type"; } - case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } - case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } - case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } - case BinaryGCode::EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } - case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } - case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } - case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } - case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } - case BinaryGCode::EResult::GCodeEncodingError: { return "GCode encoding error"; } - case BinaryGCode::EResult::GCodeDecodingError: { return "GCode decoding error"; } - case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } - case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } - case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } - case BinaryGCode::EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } - case BinaryGCode::EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } - case BinaryGCode::EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } + case EResult::Success: { return "Success"; } + case EResult::ReadError: { return "Read error"; } + case EResult::WriteError: { return "Write error"; } + case EResult::InvalidMagicNumber: { return "Invalid magic number"; } + case EResult::InvalidVersionNumber: { return "Invalid version number"; } + case EResult::InvalidChecksumType: { return "Invalid checksum type"; } + case EResult::InvalidBlockType: { return "Invalid block type"; } + case EResult::InvalidCompressionType: { return "Invalid compression type"; } + case EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } + case EResult::DataCompressionError: { return "Data compression error"; } + case EResult::DataUncompressionError: { return "Data uncompression error"; } + case EResult::MetadataEncodingError: { return "Data encoding error"; } + case EResult::MetadataDecodingError: { return "Data decoding error"; } + case EResult::GCodeEncodingError: { return "GCode encoding error"; } + case EResult::GCodeDecodingError: { return "GCode decoding error"; } + case EResult::BlockNotFound: { return "Block not found"; } + case EResult::InvalidChecksum: { return "Invalid checksum"; } + case EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } + case EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } + case EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } + case EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } + case EResult::InvalidBinaryGCodeFile: { return "Invalid binary GCode file"; } + case EResult::InvalidSequenceOfBlocks: { return "Invalid sequence of blocks"; } } return std::string(); } @@ -1658,5 +1664,200 @@ extern size_t block_content_size(const FileHeader& file_header, const BlockHeade #endif // ENABLE_CHECKSUM_BLOCK } +#if ENABLE_FILE_CONVERSION_INTERFACE +EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file) +{ + return EResult::WriteError; +} + +EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum) +{ + auto write_line = [&](const std::string& line) { + fwrite(line.data(), 1, line.length(), &dst_file); + return !ferror(&dst_file); + }; + + auto write_metadata = [&](const std::vector>& data) { + for (const auto& [key, value] : data) { + if (!write_line("; " + key + " = " + value + "\n")) + return false; + } + return !ferror(&dst_file); + }; + + if (!is_valid_binary_gcode(src_file)) + return EResult::InvalidBinaryGCodeFile; + + fseek(&src_file, 0, SEEK_END); + const long file_size = ftell(&src_file); + rewind(&src_file); + + // + // read file header + // + FileHeader file_header; + EResult res = read_header(src_file, file_header, nullptr); + if (res != EResult::Success) + // propagate error + return res; + + // + // convert file metadata block + // + BlockHeader block_header; + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::FileMetadata) + return EResult::InvalidSequenceOfBlocks; + FileMetadataBlock file_metadata_block; + res = file_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), + [](const std::pair& item) { return item.first == "Producer"; }); + const std::string producer_str = (producer_it != file_metadata_block.raw_data.end()) ? producer_it->second : "Unknown"; + if (!write_line("; generated by " + producer_str + "\n\n\n")) + return EResult::WriteError; + + // + // convert printer metadata block + // + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) + return EResult::InvalidSequenceOfBlocks; + PrinterMetadataBlock printer_metadata_block; + res = printer_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_metadata(printer_metadata_block.raw_data)) + return EResult::WriteError; + + // + // convert thumbnail blocks + // + long restore_position = ftell(&src_file); + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + while ((EBlockType)block_header.type == EBlockType::Thumbnail) { + ThumbnailBlock thumbnail_block; + res = thumbnail_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + static constexpr const size_t max_row_length = 78; + std::string encoded; + encoded.resize(boost::beast::detail::base64::encoded_size(thumbnail_block.data.size())); + encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)thumbnail_block.data.data(), thumbnail_block.data.size())); + std::string format; + switch ((EThumbnailFormat)thumbnail_block.format) + { + default: + case EThumbnailFormat::PNG: { format = "thumbnail"; break; } + case EThumbnailFormat::JPG: { format = "thumbnail_JPG"; break; } + case EThumbnailFormat::QOI: { format = "thumbnail_QOI"; break; } + } + if (!write_line(";\n; " + format + " begin " + std::to_string(thumbnail_block.width) + "x" + std::to_string(thumbnail_block.height) + + " " + std::to_string(encoded.length()) + "\n")) + return EResult::WriteError; + while (encoded.size() > max_row_length) { + if (!write_line("; " + encoded.substr(0, max_row_length) + "\n")) + return EResult::WriteError; + encoded = encoded.substr(max_row_length); + } + if (encoded.size() > 0) { + if (!write_line("; " + encoded + "\n")) + return EResult::WriteError; + } + if (!write_line("; " + format + " end\n;\n\n")) + return EResult::WriteError; + + restore_position = ftell(&src_file); + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + } + + // + // convert gcode blocks + // + res = skip_block_content(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + res = read_next_block_header(src_file, file_header, block_header, EBlockType::GCode, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + while ((EBlockType)block_header.type == EBlockType::GCode) { + GCodeBlock block; + res = block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_line(block.raw_data)) + return EResult::WriteError; + if (ftell(&src_file) == file_size) + break; + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + } + + // + // convert print metadata block + // + fseek(&src_file, restore_position, SEEK_SET); + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::PrintMetadata) + return EResult::InvalidSequenceOfBlocks; + PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_line("\n")) + return EResult::WriteError; + if (!write_metadata(print_metadata_block.raw_data)) + return EResult::WriteError; + + // + // convert slicer metadata block + // + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) + return EResult::InvalidSequenceOfBlocks; + SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_line("\n; prusaslicer_config = begin\n")) + return EResult::WriteError; + if (!write_metadata(slicer_metadata_block.raw_data)) + return EResult::WriteError; + if (!write_line("; prusaslicer_config = end\n\n")) + return EResult::WriteError; + + return EResult::Success; +} +#endif // ENABLE_FILE_CONVERSION_INTERFACE + } // namespace BinaryGCode diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index ab03f32563..236aee1c5f 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -6,6 +6,7 @@ #endif // _WIN32 #define ENABLE_CHECKSUM_BLOCK 0 +#define ENABLE_FILE_CONVERSION_INTERFACE 1 #include #include @@ -41,7 +42,9 @@ enum class EResult : uint16_t InvalidThumbnailFormat, InvalidThumbnailWidth, InvalidThumbnailHeight, - InvalidThumbnailDataSize + InvalidThumbnailDataSize, + InvalidBinaryGCodeFile, + InvalidSequenceOfBlocks }; // Returns a string description of the given result @@ -359,6 +362,20 @@ extern size_t checksum_size(EChecksumType type); // Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); +#if ENABLE_FILE_CONVERSION_INTERFACE +//===================================================================================================================================== +// +// FILE CONVERSION INTERFACE +// +//===================================================================================================================================== + +// Converts the gcode file contained into src_file from ascii to binary format and save the results into dst_file +extern EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file); + +// Converts the gcode file contained into src_file from binary to ascii format and save the results into dst_file +extern EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum); +#endif // ENABLE_FILE_CONVERSION_INTERFACE + } // namespace BinaryGCode #endif // slic3r_GCode_GCodeBinarizer_hpp_ diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2a4d8ae180..23ae8a1a24 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1349,6 +1349,17 @@ void MainFrame::init_menubar_as_editor() []() {return true; }, this); append_submenu(fileMenu, export_menu, wxID_ANY, _L("&Export"), ""); +#if ENABLE_BINARIZED_GCODE + wxMenu* convert_menu = new wxMenu(); + append_menu_item(convert_menu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + [this]() { return true; }, this); + append_menu_item(convert_menu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + [this]() { return true; }, this); + append_submenu(fileMenu, convert_menu, wxID_ANY, _L("&Convert"), ""); +#endif // ENABLE_BINARIZED_GCODE + append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD Card / Flash Drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."), [this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr, [this]() {return can_eject(); }, this); @@ -1621,13 +1632,22 @@ void MainFrame::init_menubar_as_gcodeviewer() _L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); }, "", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this); #endif // __APPLE__ +#if ENABLE_BINARIZED_GCODE + fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + [this]() { return true; }, this); + append_menu_item(fileMenu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + [this]() { return true; }, this); +#endif // ENABLE_BINARIZED_GCODE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); append_menu_item(fileMenu, wxID_ANY, _L("Open &PrusaSlicer") + dots, _L("Open PrusaSlicer"), [](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, - []() {return true; }, this); + []() { return true; }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fcd97cdc15..2878e28a5a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5430,6 +5430,77 @@ void Plater::reload_gcode_from_disk() load_gcode(filename); } +#if ENABLE_BINARIZED_GCODE +static bool is_valid_binary_gcode(const wxString& filename) +{ + FILE* file = boost::nowide::fopen(into_u8(filename).c_str(), "rb"); + if (file == nullptr) + return false; + + const bool ret = BinaryGCode::is_valid_binary_gcode(*file); + fclose(file); + return ret; +} + +void Plater::convert_gcode_to_ascii() +{ + // Ask user for a gcode file name. + wxString input_file; + wxGetApp().load_gcode(this, input_file); + + class ScopedFile + { + public: + explicit ScopedFile(FILE* file) : m_file(file) {} + ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } + private: + FILE* m_file{ nullptr }; + }; + + // Open source file + FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); + if (in_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_in_file(in_file); + + // Set out filename + boost::filesystem::path path(into_u8(input_file)); + const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_ascii" + path.extension().string(); + + // Open destination file + FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); + if (out_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_out_file(out_file); + + // Perform conversion + { + wxBusyCursor busy; + BinaryGCode::EResult res = BinaryGCode::from_binary_to_ascii(*in_file, *out_file, true); + if (res != BinaryGCode::EResult::Success) { + MessageDialog msg_dlg(this, _L(BinaryGCode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + msg_dlg.ShowModal(); + return; + } + } + + MessageDialog msg_dlg(this, _L("Succesfully created gcode ascii file:\n") + output_file, _L("Convert gcode file to ascii format"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); +} + +void Plater::convert_gcode_to_binary() +{ + MessageDialog msg_dlg(this, _L("Not implemented yet."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); +} +#endif // ENABLE_BINARIZED_GCODE + void Plater::refresh_print() { p->preview->refresh_print(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 8eeabae428..342555040b 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -172,6 +172,10 @@ public: void load_gcode(); void load_gcode(const wxString& filename); void reload_gcode_from_disk(); +#if ENABLE_BINARIZED_GCODE + void convert_gcode_to_ascii(); + void convert_gcode_to_binary(); +#endif // ENABLE_BINARIZED_GCODE void refresh_print(); std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false); From a8d89140717020a52bff4e16feb4373c7781d042 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Jul 2023 13:21:50 +0200 Subject: [PATCH 10/48] SPE-1784: New compressed (binary) gcode format integration Added Deflate compression using zlib --- src/libslic3r/GCode/GCodeBinarizer.cpp | 100 +++++++++++++++++++++++++ src/libslic3r/GCode/GCodeBinarizer.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 11 --- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index cd46324174..e8bd9bffed 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -14,6 +14,7 @@ extern "C" { #include #include } +#include #include #include @@ -601,6 +602,56 @@ static bool compress(const std::vector& src, std::vector& dst, { switch (compression_type) { + case ECompressionType::Deflate: + { + dst.clear(); + + const size_t BUFSIZE = 2048; + std::vector temp_buffer(BUFSIZE); + + z_stream strm{}; + strm.next_in = const_cast(src.data()); + strm.avail_in = (uInt)src.size(); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + + const int level = Z_DEFAULT_COMPRESSION; + int res = deflateInit(&strm, level); + if (res != Z_OK) + return false; + + while (strm.avail_in > 0) { + res = deflate(&strm, Z_NO_FLUSH); + if (res != Z_OK) { + deflateEnd(&strm); + return false; + } + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + } + + int deflate_res = Z_OK; + while (deflate_res == Z_OK) { + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + deflate_res = deflate(&strm, Z_FINISH); + } + + if (deflate_res != Z_STREAM_END) { + deflateEnd(&strm); + return false; + } + + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); + deflateEnd(&strm); + break; + } case ECompressionType::Heatshrink_11_4: case ECompressionType::Heatshrink_12_4: { @@ -676,6 +727,55 @@ static bool uncompress(const std::vector& src, std::vector& ds { switch (compression_type) { + case ECompressionType::Deflate: + { + dst.clear(); + dst.reserve(uncompressed_size); + + const size_t BUFSIZE = 2048; + std::vector temp_buffer(BUFSIZE); + + z_stream strm{}; + strm.next_in = const_cast(src.data()); + strm.avail_in = (uInt)src.size(); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + int res = inflateInit(&strm); + if (res != Z_OK) + return false; + + while (strm.avail_in > 0) { + res = inflate(&strm, Z_NO_FLUSH); + if (res != Z_OK && res != Z_STREAM_END) { + inflateEnd(&strm); + return false; + } + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + } + + int inflate_res = Z_OK; + while (inflate_res == Z_OK) { + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + inflate_res = inflate(&strm, Z_FINISH); + } + + if (inflate_res != Z_STREAM_END) { + inflateEnd(&strm); + return false; + } + + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); + inflateEnd(&strm); + break; + } case ECompressionType::Heatshrink_11_4: case ECompressionType::Heatshrink_12_4: { diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 236aee1c5f..05733cd51a 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -108,6 +108,7 @@ enum class EBlockType : uint16_t enum class ECompressionType : uint16_t { None, + Deflate, Heatshrink_11_4, Heatshrink_12_4, }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d075aeda3e..fccacd2c09 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7885,7 +7885,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "File metadata compression"); ImGui::TableSetColumnIndex(1); - std::vector options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; + std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) binarizer_config.compression.file_metadata = (BinaryGCode::ECompressionType)option_id; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2878e28a5a..355ff79f6a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5431,17 +5431,6 @@ void Plater::reload_gcode_from_disk() } #if ENABLE_BINARIZED_GCODE -static bool is_valid_binary_gcode(const wxString& filename) -{ - FILE* file = boost::nowide::fopen(into_u8(filename).c_str(), "rb"); - if (file == nullptr) - return false; - - const bool ret = BinaryGCode::is_valid_binary_gcode(*file); - fclose(file); - return ret; -} - void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. From f02e8e3438fe3f4b9a1b7b2d34eb21b03e5cd7dd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Jul 2023 09:04:44 +0200 Subject: [PATCH 11/48] Renamed namespace --- src/libslic3r/GCode.cpp | 10 +- src/libslic3r/GCode/GCodeBinarizer.cpp | 4 +- src/libslic3r/GCode/GCodeBinarizer.hpp | 6 +- src/libslic3r/GCode/GCodeProcessor.cpp | 140 ++++++++++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 10 +- src/libslic3r/GCode/Thumbnails.hpp | 10 +- src/slic3r/GUI/GLCanvas3D.cpp | 18 ++-- src/slic3r/GUI/Plater.cpp | 14 ++- 8 files changed, 110 insertions(+), 102 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c9136af7a4..91771220ba 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -984,7 +984,7 @@ namespace DoExport { unsigned int initial_extruder_id, PrintStatistics &print_statistics, bool export_binary_data, - BinaryGCode::BinaryData &binary_data) + bgcode::BinaryData &binary_data) #else static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, @@ -1132,7 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // 1) generate the thumbnails // 2) collect the config data if (export_to_binary_gcode) { - BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::BinaryData& binary_data = m_processor.get_binary_data(); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. @@ -1146,11 +1146,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // file data - binary_data.file_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + binary_data.file_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - binary_data.slicer_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); // printer data @@ -1613,7 +1613,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(filament_stats_string_out); if (export_to_binary_gcode) { - BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index e8bd9bffed..1a4748bad9 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -19,7 +19,7 @@ extern "C" { #include #include -namespace BinaryGCode { +namespace bgcode { static size_t g_checksum_max_cache_size = 65536; static constexpr const size_t MAX_GCODE_CACHE_SIZE = 65536; @@ -1959,5 +1959,5 @@ EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksu } #endif // ENABLE_FILE_CONVERSION_INTERFACE -} // namespace BinaryGCode +} // namespace bgcode diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 05733cd51a..2961fe548f 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -14,7 +14,7 @@ #include #include -namespace BinaryGCode { +namespace bgcode { static const std::array MAGIC{ 'G', 'C', 'D', 'E' }; static const uint32_t VERSION = 1; @@ -48,7 +48,7 @@ enum class EResult : uint16_t }; // Returns a string description of the given result -extern std::string translate_result(BinaryGCode::EResult result); +extern std::string translate_result(EResult result); enum class EChecksumType : uint16_t { @@ -377,6 +377,6 @@ extern EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file); extern EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum); #endif // ENABLE_FILE_CONVERSION_INTERFACE -} // namespace BinaryGCode +} // namespace bgcode #endif // slic3r_GCode_GCodeBinarizer_hpp_ diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e72745fac1..6c0fb8c2cf 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -70,7 +70,7 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -BinaryGCode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; #endif // ENABLE_BINARIZED #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1041,7 +1041,7 @@ void GCodeProcessor::process_file(const std::string& filename, std::function& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1174,15 +1174,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrinterMetadata) + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - BinaryGCode::PrinterMetadataBlock printer_metadata_block; + bgcode::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1194,25 +1194,25 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read thumbnail blocks - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::Thumbnail) { - BinaryGCode::ThumbnailBlock thumbnail_block; + while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::Thumbnail) { + bgcode::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const BinaryGCode::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const bgcode::ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); - switch ((BinaryGCode::EThumbnailFormat)block.format) + switch ((bgcode::EThumbnailFormat)block.format) { - case BinaryGCode::EThumbnailFormat::PNG: { ret += ".png"; break; } - case BinaryGCode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case BinaryGCode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + case bgcode::EThumbnailFormat::PNG: { ret += ".png"; break; } + case bgcode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case bgcode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } } return ret; }; @@ -1228,18 +1228,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); } // read print metadata block - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - BinaryGCode::PrintMetadataBlock print_metadata_block; + bgcode::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1251,15 +1251,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read slicer metadata block - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - BinaryGCode::SlicerMetadataBlock slicer_metadata_block; + bgcode::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1286,16 +1286,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::GCode) + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); - while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::GCode) { - BinaryGCode::GCodeBlock block; + while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::GCode) { + bgcode::GCodeBlock block; res = block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); // TODO: Update m_result.lines_ends @@ -1306,9 +1306,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file) == file_size) break; - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); } // Don't post-process the G-code to update time stamps. @@ -3696,7 +3696,7 @@ void GCodeProcessor::post_process() }; // update binary data - BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + bgcode::BinaryData& binary_data = m_binarizer.get_binary_data(); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); @@ -3720,8 +3720,8 @@ void GCodeProcessor::post_process() } } - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != BinaryGCode::EResult::Success) + const bgcode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -3843,12 +3843,12 @@ void GCodeProcessor::post_process() size_t m_out_file_pos{ 0 }; #if ENABLE_BINARIZED_GCODE - BinaryGCode::Binarizer& m_binarizer; + bgcode::Binarizer& m_binarizer; #endif // ENABLE_BINARIZED_GCODE public: #if ENABLE_BINARIZED_GCODE - ExportLines(BinaryGCode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) + ExportLines(bgcode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else @@ -3974,8 +3974,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); - if (res != BinaryGCode::EResult::Success) + bgcode::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4011,8 +4011,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); - if (res != BinaryGCode::EResult::Success) + bgcode::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4391,8 +4391,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - const BinaryGCode::EResult res = m_binarizer.finalize(); - if (res != BinaryGCode::EResult::Success) + const bgcode::EResult res = m_binarizer.finalize(); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 333f3295e6..b3390ed1c6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -528,14 +528,14 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW - static BinaryGCode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + static bgcode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } #endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE - BinaryGCode::Binarizer m_binarizer; - static BinaryGCode::BinarizerConfig s_binarizer_config; + bgcode::Binarizer m_binarizer; + static bgcode::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; @@ -635,8 +635,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } #if ENABLE_BINARIZED_GCODE - BinaryGCode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } - const BinaryGCode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } + bgcode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } #endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index b2b5d60cb1..6fd28af333 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -60,7 +60,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, #if ENABLE_BINARIZED_GCODE template -inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { out_thumbnails.clear(); @@ -70,13 +70,13 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - BinaryGCode::ThumbnailBlock& block = out_thumbnails.emplace_back(BinaryGCode::ThumbnailBlock()); + bgcode::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::ThumbnailBlock()); block.width = (uint16_t)data.width; block.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fccacd2c09..be81ea7f1f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7874,7 +7874,7 @@ void GLCanvas3D::GizmoHighlighter::blink() #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW void GLCanvas3D::show_binary_gcode_debug_window() { - BinaryGCode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -7888,7 +7888,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.file_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.file_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7896,7 +7896,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.printer_metadata; if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.printer_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.printer_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7904,7 +7904,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.print_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.print_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7912,7 +7912,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.slicer_metadata; if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.slicer_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.slicer_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7920,7 +7920,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.gcode; if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.gcode = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.gcode = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7929,7 +7929,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "MeatPack", "MeatPack Comments" }; option_id = (int)binarizer_config.gcode_encoding; if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)option_id; + binarizer_config.gcode_encoding = (bgcode::EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7938,7 +7938,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "INI" }; option_id = (int)binarizer_config.metadata_encoding; if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)option_id; + binarizer_config.metadata_encoding = (bgcode::EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7947,7 +7947,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "CRC32" }; option_id = (int)binarizer_config.checksum; if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.checksum = (BinaryGCode::EChecksumType)option_id; + binarizer_config.checksum = (bgcode::EChecksumType)option_id; ImGui::EndTable(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 355ff79f6a..22076aadff 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5442,6 +5442,9 @@ void Plater::convert_gcode_to_ascii() public: explicit ScopedFile(FILE* file) : m_file(file) {} ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void unscope() { m_file = nullptr; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: FILE* m_file{ nullptr }; }; @@ -5471,10 +5474,15 @@ void Plater::convert_gcode_to_ascii() // Perform conversion { wxBusyCursor busy; - BinaryGCode::EResult res = BinaryGCode::from_binary_to_ascii(*in_file, *out_file, true); - if (res != BinaryGCode::EResult::Success) { - MessageDialog msg_dlg(this, _L(BinaryGCode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + bgcode::EResult res = bgcode::from_binary_to_ascii(*in_file, *out_file, true); + if (res != bgcode::EResult::Success) { + MessageDialog msg_dlg(this, _L(bgcode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + scoped_out_file.unscope(); + fclose(out_file); + boost::nowide::remove(output_file.c_str()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return; } } From 2cde917f11cbd83e3a2820fd6b7b592950c1fcf3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Jul 2023 10:22:22 +0200 Subject: [PATCH 12/48] Delete output file when gcode conversion fails --- src/slic3r/GUI/Plater.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22076aadff..13330199db 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5442,9 +5442,7 @@ void Plater::convert_gcode_to_ascii() public: explicit ScopedFile(FILE* file) : m_file(file) {} ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void unscope() { m_file = nullptr; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: FILE* m_file{ nullptr }; }; @@ -5478,11 +5476,9 @@ void Plater::convert_gcode_to_ascii() if (res != bgcode::EResult::Success) { MessageDialog msg_dlg(this, _L(bgcode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ scoped_out_file.unscope(); fclose(out_file); boost::nowide::remove(output_file.c_str()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return; } } From cc7f2f3aaefcbdac5839afdb322e8194a38e01de Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 28 Jul 2023 14:48:14 +0200 Subject: [PATCH 13/48] Fixed missing whitespace in GXX lines when unbinarizing using meatpack --- src/libslic3r/GCode/GCodeBinarizer.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 1a4748bad9..d1e2368863 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -407,18 +407,31 @@ static void unbinarize(const std::vector& src, std::string& dst) { } } + auto is_gline_parameter = [](const char c) { + static const std::vector parameters = { + // G0, G1 + 'X', 'Y', 'Z', 'E', 'F', + // G2, G3 + 'I', 'J', 'R', + // G29 + 'P', 'W', 'H', 'C', 'A' + }; + return std::find(parameters.begin(), parameters.end(), c) != parameters.end(); + }; + std::array c_unbin{ 0, 0 }; const size_t char_count = get_result_char(c_unbin); for (size_t i = 0; i < char_count; ++i) { // GCodeReader::parse_line_internal() is unable to parse a G line where the data are not separated by spaces // so we add them where needed - if (c_unbin[i] == 'G' && std::distance(unbin_buffer.begin(), it_unbin_end) > 0 && *std::prev(it_unbin_end, 1) == '\n') + const size_t curr_unbin_buffer_length = std::distance(unbin_buffer.begin(), it_unbin_end); + if (c_unbin[i] == 'G' && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) == '\n')) add_space = true; else if (c_unbin[i] == '\n') add_space = false; - if (add_space && *std::prev(it_unbin_end, 1) != ' ' && - (c_unbin[i] == 'X' || c_unbin[i] == 'Y' || c_unbin[i] == 'Z' || c_unbin[i] == 'E' || c_unbin[i] == 'F')) { + if (add_space && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) != ' ') && + is_gline_parameter(c_unbin[i])) { *it_unbin_end = ' '; ++it_unbin_end; } From 866fc767bb129b042669bc5a50deb98cd0cc5218 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 28 Jul 2023 19:30:49 +0200 Subject: [PATCH 14/48] Integrating LibBGCode into deps with an optional var for source dir One can then co-develop the two projects with relative convenience --- deps/CMakeLists.txt | 9 +++++++-- deps/LibBGCode/LibBGCode.cmake | 24 ++++++++++++++++++++++++ src/libslic3r/CMakeLists.txt | 3 +++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 deps/LibBGCode/LibBGCode.cmake diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index b00f85ba70..1008707d78 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -20,8 +20,8 @@ # therefore, unfortunatelly, the installation cannot be copied/moved elsewhere without re-installing wxWidgets. # +cmake_minimum_required(VERSION 3.10) project(PrusaSlicer-deps) -cmake_minimum_required(VERSION 3.2) include(ExternalProject) include(ProcessorCount) @@ -62,6 +62,8 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE) message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.") endif () +cmake_policy(SET CMP0135 NEW) + function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) @@ -195,6 +197,8 @@ include(NanoSVG/NanoSVG.cmake) include(wxWidgets/wxWidgets.cmake) include(OCCT/OCCT.cmake) +include(LibBGCode/LibBGCode.cmake) + set(_dep_list dep_Boost dep_TBB @@ -206,10 +210,11 @@ set(_dep_list dep_OpenCSG dep_CGAL dep_Qhull - dep_OCCT + # dep_OCCT ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} + dep_LibBGCode ) # if (NOT MSVC) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake new file mode 100644 index 0000000000..6ecd5ee725 --- /dev/null +++ b/deps/LibBGCode/LibBGCode.cmake @@ -0,0 +1,24 @@ +set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") + +set(_source_dir_line "URL;https://github.com/prusa3d/libbgcode/archive/refs/heads/main.zip") + +if (LibBGCode_SOURCE_DIR) + set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") +endif () + +prusaslicer_add_cmake_project(LibBGCode_deps + ${_source_dir_line} + SOURCE_SUBDIR deps + CMAKE_ARGS + -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} + -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF + -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON + -DDESTDIR=${DESTDIR} +) + +prusaslicer_add_cmake_project(LibBGCode + ${_source_dir_line} + DEPENDS dep_LibBGCode_deps + CMAKE_ARGS + -DLibBGCode_BUILD_TESTS:BOOL=OFF +) \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..f11986d314 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -14,6 +14,8 @@ if (TARGET OpenVDB::openvdb) set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp OpenVDBUtilsLegacy.hpp) endif() +find_package(LibBGCode REQUIRED COMPONENTS Convert) + set(SLIC3R_SOURCES pchheader.cpp pchheader.hpp @@ -508,6 +510,7 @@ target_link_libraries(libslic3r ZLIB::ZLIB JPEG::JPEG qoi + LibBGCode::bgcode_convert ) if (APPLE) From d9f0c1a0526343f4b2d3076d386bb8fa52241651 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 29 Jul 2023 15:41:13 +0200 Subject: [PATCH 15/48] fix unknown policy on older cmakes --- deps/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 1008707d78..fce6a6e753 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -62,7 +62,9 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE) message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.") endif () -cmake_policy(SET CMP0135 NEW) +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + cmake_policy(SET CMP0135 NEW) +endif () function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) From 6d3d3298dafaf74a406c5dedc6f6788c0b8df5f6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 29 Jul 2023 15:45:29 +0200 Subject: [PATCH 16/48] use git to download libbgcode by default should work on the build server --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 6ecd5ee725..61b7f34504 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,6 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "URL;https://github.com/prusa3d/libbgcode/archive/refs/heads/main.zip") +set(_source_dir_line "GIT_REPOSITORY;git@github.com:prusa3d/libbgcode.git;GIT_TAG;main") if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From be468dca797a32260d4daf84a391038853f053ee Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 1 Aug 2023 13:34:39 +0200 Subject: [PATCH 17/48] Revert accidental commenting out of dep_OCCT --- deps/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index fce6a6e753..d5edcb5719 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -212,7 +212,7 @@ set(_dep_list dep_OpenCSG dep_CGAL dep_Qhull - # dep_OCCT + dep_OCCT ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} From 2c80865ea90dd9a53992c12b672067299415376a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Aug 2023 15:32:26 +0200 Subject: [PATCH 18/48] Fix missing dependent targets from libbgcode deps --- deps/LibBGCode/LibBGCode.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 61b7f34504..074adefffc 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -9,6 +9,7 @@ endif () prusaslicer_add_cmake_project(LibBGCode_deps ${_source_dir_line} SOURCE_SUBDIR deps + DEPENDS dep_Boost ${ZLIB_PKG} CMAKE_ARGS -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF From 07065c9e195c2895330fa13a5cbb42a6ae5e4482 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 08:56:21 +0200 Subject: [PATCH 19/48] Partial revert of 7e56d807185348987026786c4f8607fcfdc84d6e - Removal of integrated Heatshrink library --- src/CMakeLists.txt | 1 - src/heatshrink/CMakeLists.txt | 11 - src/heatshrink/LICENSE | 14 - src/heatshrink/README.md | 7 - src/heatshrink/heatshrink_common.h | 20 - src/heatshrink/heatshrink_config.h | 26 -- src/heatshrink/heatshrink_decoder.c | 367 ----------------- src/heatshrink/heatshrink_decoder.h | 100 ----- src/heatshrink/heatshrink_encoder.c | 604 ---------------------------- src/heatshrink/heatshrink_encoder.h | 109 ----- 10 files changed, 1259 deletions(-) delete mode 100644 src/heatshrink/CMakeLists.txt delete mode 100644 src/heatshrink/LICENSE delete mode 100644 src/heatshrink/README.md delete mode 100644 src/heatshrink/heatshrink_common.h delete mode 100644 src/heatshrink/heatshrink_config.h delete mode 100644 src/heatshrink/heatshrink_decoder.c delete mode 100644 src/heatshrink/heatshrink_decoder.h delete mode 100644 src/heatshrink/heatshrink_encoder.c delete mode 100644 src/heatshrink/heatshrink_encoder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cda4310d9f..fec0d4cf70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,6 @@ add_subdirectory(libigl) add_subdirectory(hints) add_subdirectory(qoi) add_subdirectory(libnest2d) -add_subdirectory(heatshrink) find_package(Qhull 7.2 REQUIRED) add_library(qhull INTERFACE) diff --git a/src/heatshrink/CMakeLists.txt b/src/heatshrink/CMakeLists.txt deleted file mode 100644 index 94c93315ba..0000000000 --- a/src/heatshrink/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 2.8.12) -project(heatshrink) - -add_library(heatshrink STATIC - heatshrink_common.h - heatshrink_config.h - heatshrink_decoder.c - heatshrink_decoder.h - heatshrink_encoder.c - heatshrink_encoder.h -) diff --git a/src/heatshrink/LICENSE b/src/heatshrink/LICENSE deleted file mode 100644 index 9132cb6a0c..0000000000 --- a/src/heatshrink/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2013-2015, Scott Vokes -All rights reserved. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/heatshrink/README.md b/src/heatshrink/README.md deleted file mode 100644 index 4de08bee29..0000000000 --- a/src/heatshrink/README.md +++ /dev/null @@ -1,7 +0,0 @@ -** heatshrink is a data compression library for embedded/real-time systems.** - -For more information go to https://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ - -THIS DIRECTORY CONTAINS THE heatshrink v0.4.1 b9ac05e SOURCE DISTRIBUTION. - - diff --git a/src/heatshrink/heatshrink_common.h b/src/heatshrink/heatshrink_common.h deleted file mode 100644 index bc89774f5e..0000000000 --- a/src/heatshrink/heatshrink_common.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HEATSHRINK_H -#define HEATSHRINK_H - -#define HEATSHRINK_AUTHOR "Scott Vokes " -#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" - -/* Version 0.4.1 */ -#define HEATSHRINK_VERSION_MAJOR 0 -#define HEATSHRINK_VERSION_MINOR 4 -#define HEATSHRINK_VERSION_PATCH 1 - -#define HEATSHRINK_MIN_WINDOW_BITS 4 -#define HEATSHRINK_MAX_WINDOW_BITS 15 - -#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 - -#define HEATSHRINK_LITERAL_MARKER 0x01 -#define HEATSHRINK_BACKREF_MARKER 0x00 - -#endif diff --git a/src/heatshrink/heatshrink_config.h b/src/heatshrink/heatshrink_config.h deleted file mode 100644 index 13135b9395..0000000000 --- a/src/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -/* Should functionality assuming dynamic allocation be used? */ -#ifndef HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DYNAMIC_ALLOC 1 -#endif - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/src/heatshrink/heatshrink_decoder.c b/src/heatshrink/heatshrink_decoder.c deleted file mode 100644 index 0f118cf98a..0000000000 --- a/src/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,367 +0,0 @@ -#include -#include -#include "heatshrink_decoder.h" - -/* States for the polling state machine. */ -typedef enum { - HSDS_TAG_BIT, /* tag bit */ - HSDS_YIELD_LITERAL, /* ready to yield literal byte */ - HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ - HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ - HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ - HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ - HSDS_YIELD_BACKREF, /* ready to yield back-reference */ -} HSD_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "tag_bit", - "yield_literal", - "backref_index_msb", - "backref_index_lsb", - "backref_count_msb", - "backref_count_lsb", - "yield_backref", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define NO_BITS ((uint16_t)-1) - -/* Forward references. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (input_buffer_size == 0) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - size_t buffers_sz = (1 << window_sz2) + input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); - if (hsd == NULL) { return NULL; } - hsd->input_buffer_size = input_buffer_size; - hsd->window_sz2 = window_sz2; - hsd->lookahead_sz2 = lookahead_sz2; - heatshrink_decoder_reset(hsd); - LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", - sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); - return hsd; -} - -void heatshrink_decoder_free(heatshrink_decoder *hsd) { - size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); - size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); - memset(hsd->buffers, 0, buf_sz + input_sz); - hsd->state = HSDS_TAG_BIT; - hsd->input_size = 0; - hsd->input_index = 0; - hsd->bit_index = 0x00; - hsd->current_byte = 0x00; - hsd->output_count = 0; - hsd->output_index = 0; - hsd->head_index = 0; -} - -/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSDR_SINK_ERROR_NULL; - } - - size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; - if (rem == 0) { - *input_size = 0; - return HSDR_SINK_FULL; - } - - size = rem < size ? rem : size; - LOG("-- sinking %zd bytes\n", size); - /* copy into input buffer (at head of buffers) */ - memcpy(&hsd->buffers[hsd->input_size], in_buf, size); - hsd->input_size += size; - *input_size = size; - return HSDR_SINK_OK; -} - - -/***************** - * Decompression * - *****************/ - -#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) -#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) - -// States -static HSD_state st_tag_bit(heatshrink_decoder *hsd); -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi); -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi); - -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSDR_POLL_ERROR_NULL; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- poll, state is %d (%s), input_size %d\n", - hsd->state, state_names[hsd->state], hsd->input_size); - uint8_t in_state = hsd->state; - switch (in_state) { - case HSDS_TAG_BIT: - hsd->state = st_tag_bit(hsd); - break; - case HSDS_YIELD_LITERAL: - hsd->state = st_yield_literal(hsd, &oi); - break; - case HSDS_BACKREF_INDEX_MSB: - hsd->state = st_backref_index_msb(hsd); - break; - case HSDS_BACKREF_INDEX_LSB: - hsd->state = st_backref_index_lsb(hsd); - break; - case HSDS_BACKREF_COUNT_MSB: - hsd->state = st_backref_count_msb(hsd); - break; - case HSDS_BACKREF_COUNT_LSB: - hsd->state = st_backref_count_lsb(hsd); - break; - case HSDS_YIELD_BACKREF: - hsd->state = st_yield_backref(hsd, &oi); - break; - default: - return HSDR_POLL_ERROR_UNKNOWN; - } - - /* If the current state cannot advance, check if input or output - * buffer are exhausted. */ - if (hsd->state == in_state) { - if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } - return HSDR_POLL_EMPTY; - } - } -} - -static HSD_state st_tag_bit(heatshrink_decoder *hsd) { - uint32_t bits = get_bits(hsd, 1); // get tag bit - if (bits == NO_BITS) { - return HSDS_TAG_BIT; - } else if (bits) { - return HSDS_YIELD_LITERAL; - } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { - return HSDS_BACKREF_INDEX_MSB; - } else { - hsd->output_index = 0; - return HSDS_BACKREF_INDEX_LSB; - } -} - -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi) { - /* Emit a repeated section from the window buffer, and add it (again) - * to the window buffer. (Note that the repetition can include - * itself.)*/ - if (*oi->output_size < oi->buf_size) { - uint16_t byte = get_bits(hsd, 8); - if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint8_t c = byte & 0xFF; - LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); - buf[hsd->head_index++ & mask] = c; - push_byte(hsd, oi, c); - return HSDS_TAG_BIT; - } else { - return HSDS_YIELD_LITERAL; - } -} - -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - ASSERT(bit_ct > 8); - uint16_t bits = get_bits(hsd, bit_ct - 8); - LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } - hsd->output_index = bits << 8; - return HSDS_BACKREF_INDEX_LSB; -} - -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); - LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } - hsd->output_index |= bits; - hsd->output_index++; - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - hsd->output_count = 0; - return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - ASSERT(br_bit_ct > 8); - uint16_t bits = get_bits(hsd, br_bit_ct - 8); - LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } - hsd->output_count = bits << 8; - return HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); - LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } - hsd->output_count |= bits; - hsd->output_count++; - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi) { - size_t count = oi->buf_size - *oi->output_size; - if (count > 0) { - size_t i = 0; - if (hsd->output_count < count) count = hsd->output_count; - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint16_t neg_offset = hsd->output_index; - LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); - ASSERT(neg_offset <= mask + 1); - ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); - - for (i=0; ihead_index - neg_offset) & mask]; - push_byte(hsd, oi, c); - buf[hsd->head_index & mask] = c; - hsd->head_index++; - LOG(" -- ++ 0x%02x\n", c); - } - hsd->output_count -= count; - if (hsd->output_count == 0) { return HSDS_TAG_BIT; } - } - return HSDS_YIELD_BACKREF; -} - -/* Get the next COUNT bits from the input buffer, saving incremental progress. - * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) { - uint16_t accumulator = 0; - int i = 0; - if (count > 15) { return NO_BITS; } - LOG("-- popping %u bit(s)\n", count); - - /* If we aren't able to get COUNT bits, suspend immediately, because we - * don't track how many bits of COUNT we've accumulated before suspend. */ - if (hsd->input_size == 0) { - if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } - } - - for (i = 0; i < count; i++) { - if (hsd->bit_index == 0x00) { - if (hsd->input_size == 0) { - LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", - accumulator, accumulator); - return NO_BITS; - } - hsd->current_byte = hsd->buffers[hsd->input_index++]; - LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); - if (hsd->input_index == hsd->input_size) { - hsd->input_index = 0; /* input is exhausted */ - hsd->input_size = 0; - } - hsd->bit_index = 0x80; - } - accumulator <<= 1; - if (hsd->current_byte & hsd->bit_index) { - accumulator |= 0x01; - if (0) { - LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } else { - if (0) { - LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } - hsd->bit_index >>= 1; - } - - if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); } - return accumulator; -} - -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { - if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } - switch (hsd->state) { - case HSDS_TAG_BIT: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If we want to finish with no input, but are in these states, it's - * because the 0-bit padding to the last byte looks like a backref - * marker bit followed by all 0s for index and count bits. */ - case HSDS_BACKREF_INDEX_LSB: - case HSDS_BACKREF_INDEX_MSB: - case HSDS_BACKREF_COUNT_LSB: - case HSDS_BACKREF_COUNT_MSB: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If the output stream is padded with 0xFFs (possibly due to being in - * flash memory), also explicitly check the input size rather than - * uselessly returning MORE but yielding 0 bytes when polling. */ - case HSDS_YIELD_LITERAL: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - default: - return HSDR_FINISH_MORE; - } -} - -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { - LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); - oi->buf[(*oi->output_size)++] = byte; - (void)hsd; -} diff --git a/src/heatshrink/heatshrink_decoder.h b/src/heatshrink/heatshrink_decoder.h deleted file mode 100644 index bda83991ef..0000000000 --- a/src/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef HEATSHRINK_DECODER_H -#define HEATSHRINK_DECODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSDR_SINK_OK, /* data sunk, ready to poll */ - HSDR_SINK_FULL, /* out of space in internal buffer */ - HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ -} HSD_sink_res; - -typedef enum { - HSDR_POLL_EMPTY, /* input exhausted */ - HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ - HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ - HSDR_POLL_ERROR_UNKNOWN=-2, -} HSD_poll_res; - -typedef enum { - HSDR_FINISH_DONE, /* output is done */ - HSDR_FINISH_MORE, /* more output remains */ - HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ -} HSD_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ - ((BUF)->input_buffer_size) -#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ - ((BUF)->window_sz2) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - ((BUF)->lookahead_sz2) -#else -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ - HEATSHRINK_STATIC_INPUT_BUFFER_SIZE -#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t input_index; /* offset to next unprocessed input byte */ - uint16_t output_count; /* how many bytes to output */ - uint16_t output_index; /* index for bytes to output */ - uint16_t head_index; /* head of window buffer */ - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of input */ - uint8_t bit_index; /* current bit index */ - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Fields that are only used if dynamically allocated. */ - uint8_t window_sz2; /* window buffer bits */ - uint8_t lookahead_sz2; /* lookahead bits */ - uint16_t input_buffer_size; /* input buffer size */ - - /* Input buffer, then expansion window buffer */ - uint8_t buffers[]; -#else - /* Input buffer, then expansion window buffer */ - uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) - + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; -#endif -} heatshrink_decoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, - * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead - * size of 2^lookahead_sz2. (The window buffer and lookahead sizes - * must match the settings used when the data was compressed.) - * Returns NULL on error. */ -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); - -/* Free a decoder. */ -void heatshrink_decoder_free(heatshrink_decoder *hsd); -#endif - -/* Reset a decoder. */ -void heatshrink_decoder_reset(heatshrink_decoder *hsd); - -/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to - * indicate how many bytes were actually sunk (in case a buffer was filled). */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the dencoder that the input stream is finished. - * If the return value is HSDR_FINISH_MORE, there is still more output, so - * call heatshrink_decoder_poll and repeat. */ -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); - -#endif diff --git a/src/heatshrink/heatshrink_encoder.c b/src/heatshrink/heatshrink_encoder.c deleted file mode 100644 index edf4abebcc..0000000000 --- a/src/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,604 +0,0 @@ -#include -#include -#include -#include "heatshrink_encoder.h" - -typedef enum { - HSES_NOT_FULL, /* input buffer not full enough */ - HSES_FILLED, /* buffer is full */ - HSES_SEARCH, /* searching for patterns */ - HSES_YIELD_TAG_BIT, /* yield tag bit */ - HSES_YIELD_LITERAL, /* emit literal byte */ - HSES_YIELD_BR_INDEX, /* yielding backref index */ - HSES_YIELD_BR_LENGTH, /* yielding backref length */ - HSES_SAVE_BACKLOG, /* copying buffer to backlog */ - HSES_FLUSH_BITS, /* flush bit buffer */ - HSES_DONE, /* done */ -} HSE_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "not_full", - "filled", - "search", - "yield_tag_bit", - "yield_literal", - "yield_br_index", - "yield_br_length", - "save_backlog", - "flush_bits", - "done", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -// Encoder flags -enum { - FLAG_IS_FINISHING = 0x01, -}; - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define MATCH_NOT_FOUND ((uint16_t)-1) - -static uint16_t get_input_offset(heatshrink_encoder *hse); -static uint16_t get_input_buffer_size(heatshrink_encoder *hse); -static uint16_t get_lookahead_size(heatshrink_encoder *hse); -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); -static int can_take_byte(output_info *oi); -static int is_finishing(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); - -/* Push COUNT (max 8) bits to the output buffer, which has room. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi); -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - - /* Note: 2 * the window size is used because the buffer needs to fit - * (1 << window_sz2) bytes for the current input, and an additional - * (1 << window_sz2) bytes for the previous buffer of input, which - * will be scanned for useful backreferences. */ - size_t buf_sz = (2 << window_sz2); - - heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse) + buf_sz); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - heatshrink_encoder_reset(hse); - -#if HEATSHRINK_USE_INDEX - size_t index_sz = buf_sz*sizeof(uint16_t); - hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); - if (hse->search_index == NULL) { - HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); - return NULL; - } - hse->search_index->size = index_sz; -#endif - - LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", - buf_sz, get_input_buffer_size(hse)); - return hse; -} - -void heatshrink_encoder_free(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); -#if HEATSHRINK_USE_INDEX - size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; - HEATSHRINK_FREE(hse->search_index, index_sz); - (void)index_sz; -#endif - HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder) + buf_sz); - (void)buf_sz; -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - memset(hse->buffer, 0, buf_sz); - hse->input_size = 0; - hse->state = HSES_NOT_FULL; - hse->match_scan_index = 0; - hse->flags = 0; - hse->bit_index = 0x80; - hse->current_byte = 0x00; - hse->match_length = 0; - - hse->outgoing_bits = 0x0000; - hse->outgoing_bits_count = 0; - - #ifdef LOOP_DETECT - hse->loop_detect = (uint32_t)-1; - #endif -} - -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSER_SINK_ERROR_NULL; - } - - /* Sinking more content after saying the content is done, tsk tsk */ - if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } - - /* Sinking more content before processing is done */ - if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } - - uint16_t write_offset = get_input_offset(hse) + hse->input_size; - uint16_t ibs = get_input_buffer_size(hse); - uint16_t rem = ibs - hse->input_size; - uint16_t cp_sz = rem < size ? rem : size; - - memcpy(&hse->buffer[write_offset], in_buf, cp_sz); - *input_size = cp_sz; - hse->input_size += cp_sz; - - LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", - cp_sz, size, write_offset, hse->input_size); - if (cp_sz == rem) { - LOG("-- internal buffer is now full\n"); - hse->state = HSES_FILLED; - } - - return HSER_SINK_OK; -} - - -/*************** - * Compression * - ***************/ - -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length); -static void do_indexing(heatshrink_encoder *hse); - -static HSE_state st_step_search(heatshrink_encoder *hse); -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_save_backlog(heatshrink_encoder *hse); -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi); - -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSER_POLL_ERROR_NULL; - } - if (out_buf_size == 0) { - LOG("-- MISUSE: output buffer size is 0\n"); - return HSER_POLL_ERROR_MISUSE; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- polling, state %u (%s), flags 0x%02x\n", - hse->state, state_names[hse->state], hse->flags); - - uint8_t in_state = hse->state; - switch (in_state) { - case HSES_NOT_FULL: - return HSER_POLL_EMPTY; - case HSES_FILLED: - do_indexing(hse); - hse->state = HSES_SEARCH; - break; - case HSES_SEARCH: - hse->state = st_step_search(hse); - break; - case HSES_YIELD_TAG_BIT: - hse->state = st_yield_tag_bit(hse, &oi); - break; - case HSES_YIELD_LITERAL: - hse->state = st_yield_literal(hse, &oi); - break; - case HSES_YIELD_BR_INDEX: - hse->state = st_yield_br_index(hse, &oi); - break; - case HSES_YIELD_BR_LENGTH: - hse->state = st_yield_br_length(hse, &oi); - break; - case HSES_SAVE_BACKLOG: - hse->state = st_save_backlog(hse); - break; - case HSES_FLUSH_BITS: - hse->state = st_flush_bit_buffer(hse, &oi); - case HSES_DONE: - return HSER_POLL_EMPTY; - default: - LOG("-- bad state %s\n", state_names[hse->state]); - return HSER_POLL_ERROR_MISUSE; - } - - if (hse->state == in_state) { - /* Check if output buffer is exhausted. */ - if (*output_size == out_buf_size) return HSER_POLL_MORE; - } - } -} - -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { - if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } - LOG("-- setting is_finishing flag\n"); - hse->flags |= FLAG_IS_FINISHING; - if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } - return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; -} - -static HSE_state st_step_search(heatshrink_encoder *hse) { - uint16_t window_length = get_input_buffer_size(hse); - uint16_t lookahead_sz = get_lookahead_size(hse); - uint16_t msi = hse->match_scan_index; - LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", - msi, hse->input_size + msi, 2*window_length, hse->input_size); - - bool fin = is_finishing(hse); - if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d\n", msi); - return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - uint16_t start = end - window_length; - - uint16_t max_possible = lookahead_sz; - if (hse->input_size - msi < lookahead_sz) { - max_possible = hse->input_size - msi; - } - - uint16_t match_length = 0; - uint16_t match_pos = find_longest_match(hse, - start, end, max_possible, &match_length); - - if (match_pos == MATCH_NOT_FOUND) { - LOG("ss Match not found\n"); - hse->match_scan_index++; - hse->match_length = 0; - return HSES_YIELD_TAG_BIT; - } else { - LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); - hse->match_pos = match_pos; - hse->match_length = match_length; - ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); - - return HSES_YIELD_TAG_BIT; - } -} - -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - if (hse->match_length == 0) { - add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); - return HSES_YIELD_LITERAL; - } else { - add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); - hse->outgoing_bits = hse->match_pos - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); - return HSES_YIELD_BR_INDEX; - } - } else { - return HSES_YIELD_TAG_BIT; /* output is full, continue */ - } -} - -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - push_literal_byte(hse, oi); - return HSES_SEARCH; - } else { - return HSES_YIELD_LITERAL; - } -} - -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref index %u\n", hse->match_pos); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_INDEX; /* continue */ - } else { - hse->outgoing_bits = hse->match_length - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); - return HSES_YIELD_BR_LENGTH; /* done */ - } - } else { - return HSES_YIELD_BR_INDEX; /* continue */ - } -} - -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref length %u\n", hse->match_length); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_LENGTH; - } else { - hse->match_scan_index += hse->match_length; - hse->match_length = 0; - return HSES_SEARCH; - } - } else { - return HSES_YIELD_BR_LENGTH; - } -} - -static HSE_state st_save_backlog(heatshrink_encoder *hse) { - LOG("-- saving backlog\n"); - save_backlog(hse); - return HSES_NOT_FULL; -} - -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi) { - if (hse->bit_index == 0x80) { - LOG("-- done!\n"); - return HSES_DONE; - } else if (can_take_byte(oi)) { - LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); - oi->buf[(*oi->output_size)++] = hse->current_byte; - LOG("-- done!\n"); - return HSES_DONE; - } else { - return HSES_FLUSH_BITS; - } -} - -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { - LOG("-- adding tag bit: %d\n", tag); - push_bits(hse, 1, tag, oi); -} - -static uint16_t get_input_offset(heatshrink_encoder *hse) { - return get_input_buffer_size(hse); -} - -static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - (void)hse; -} - -static uint16_t get_lookahead_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - (void)hse; -} - -static void do_indexing(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - /* Build an index array I that contains flattened linked lists - * for the previous instances of every byte in the buffer. - * - * For example, if buf[200] == 'x', then index[200] will either - * be an offset i such that buf[i] == 'x', or a negative offset - * to indicate end-of-list. This significantly speeds up matching, - * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. - * - * Future optimization options: - * 1. Since any negative value represents end-of-list, the other - * 15 bits could be used to improve the index dynamically. - * - * 2. Likewise, the last lookahead_sz bytes of the index will - * not be usable, so temporary data could be stored there to - * dynamically improve the index. - * */ - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t last[256]; - memset(last, 0xFF, sizeof(last)); - - uint8_t * const data = hse->buffer; - int16_t * const index = hsi->index; - - const uint16_t input_offset = get_input_offset(hse); - const uint16_t end = input_offset + hse->input_size; - - for (uint16_t i=0; iflags & FLAG_IS_FINISHING; -} - -static int can_take_byte(output_info *oi) { - return *oi->output_size < oi->buf_size; -} - -/* Return the longest match for the bytes at buf[end:end+maxlen] between - * buf[start] and buf[end-1]. If no match is found, return -1. */ -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length) { - LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", - end, end + maxlen, start, end + maxlen - 1, maxlen); - uint8_t *buf = hse->buffer; - - uint16_t match_maxlen = 0; - uint16_t match_index = MATCH_NOT_FOUND; - - uint16_t len = 0; - uint8_t * const needlepoint = &buf[end]; -#if HEATSHRINK_USE_INDEX - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t pos = hsi->index[end]; - - while (pos - (int16_t)start >= 0) { - uint8_t * const pospoint = &buf[pos]; - len = 0; - - /* Only check matches that will potentially beat the current maxlen. - * This is redundant with the index if match_maxlen is 0, but the - * added branch overhead to check if it == 0 seems to be worse. */ - if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { - pos = hsi->index[pos]; - continue; - } - - for (len = 1; len < maxlen; len++) { - if (pospoint[len] != needlepoint[len]) break; - } - - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* won't find better */ - } - pos = hsi->index[pos]; - } -#else - for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { - uint8_t * const pospoint = &buf[pos]; - if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) - && (*pospoint == *needlepoint)) { - for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", - pos + len, pospoint[len], needlepoint[len], start); - } - if (pospoint[len] != needlepoint[len]) { break; } - } - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* don't keep searching */ - } - } - } -#endif - - const size_t break_even_point = - (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + - HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - - /* Instead of comparing break_even_point against 8*match_maxlen, - * compare match_maxlen against break_even_point/8 to avoid - * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and - * 3, respectively, break_even_point/8 will always be at least 1. */ - if (match_maxlen > (break_even_point / 8)) { - LOG("-- best match: %u bytes at -%u\n", - match_maxlen, end - match_index); - *match_length = match_maxlen; - return end - match_index; - } - LOG("-- none found\n"); - return MATCH_NOT_FOUND; -} - -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { - uint8_t count = 0; - uint8_t bits = 0; - if (hse->outgoing_bits_count > 8) { - count = 8; - bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); - } else { - count = hse->outgoing_bits_count; - bits = hse->outgoing_bits; - } - - if (count > 0) { - LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); - push_bits(hse, count, bits, oi); - hse->outgoing_bits_count -= count; - } - return count; -} - -/* Push COUNT (max 8) bits to the output buffer, which has room. - * Bytes are set from the lowest bits, up. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi) { - ASSERT(count <= 8); - LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); - - /* If adding a whole byte and at the start of a new output byte, - * just push it through whole and skip the bit IO loop. */ - if (count == 8 && hse->bit_index == 0x80) { - oi->buf[(*oi->output_size)++] = bits; - } else { - for (int i=count - 1; i>=0; i--) { - bool bit = bits & (1 << i); - if (bit) { hse->current_byte |= hse->bit_index; } - if (0) { - LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", - bit ? 1 : 0, hse->bit_index, hse->current_byte); - } - hse->bit_index >>= 1; - if (hse->bit_index == 0x00) { - hse->bit_index = 0x80; - LOG(" > pushing byte 0x%02x\n", hse->current_byte); - oi->buf[(*oi->output_size)++] = hse->current_byte; - hse->current_byte = 0x00; - } - } - } -} - -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { - uint16_t processed_offset = hse->match_scan_index - 1; - uint16_t input_offset = get_input_offset(hse) + processed_offset; - uint8_t c = hse->buffer[input_offset]; - LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", - c, isprint(c) ? c : '.', input_offset); - push_bits(hse, 8, c, oi); -} - -static void save_backlog(heatshrink_encoder *hse) { - size_t input_buf_sz = get_input_buffer_size(hse); - - uint16_t msi = hse->match_scan_index; - - /* Copy processed data to beginning of buffer, so it can be - * used for future matches. Don't bother checking whether the - * input is less than the maximum size, because if it isn't, - * we're done anyway. */ - uint16_t rem = input_buf_sz - msi; // unprocessed bytes - uint16_t shift_sz = input_buf_sz + rem; - - memmove(&hse->buffer[0], - &hse->buffer[input_buf_sz - rem], - shift_sz); - - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/src/heatshrink/heatshrink_encoder.h b/src/heatshrink/heatshrink_encoder.h deleted file mode 100644 index 18c17731b1..0000000000 --- a/src/heatshrink/heatshrink_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef HEATSHRINK_ENCODER_H -#define HEATSHRINK_ENCODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSER_SINK_OK, /* data sunk into input buffer */ - HSER_SINK_ERROR_NULL=-1, /* NULL argument */ - HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ -} HSE_sink_res; - -typedef enum { - HSER_POLL_EMPTY, /* input exhausted */ - HSER_POLL_MORE, /* poll again for more output */ - HSER_POLL_ERROR_NULL=-1, /* NULL argument */ - HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ -} HSE_poll_res; - -typedef enum { - HSER_FINISH_DONE, /* encoding is complete */ - HSER_FINISH_MORE, /* more output remaining; use poll */ - HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ -} HSE_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ - ((HSE)->window_sz2) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ - ((HSE)->lookahead_sz2) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - ((HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[]; -}; -#else -#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - (&(HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; -}; -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t match_scan_index; - uint16_t match_length; - uint16_t match_pos; - uint16_t outgoing_bits; /* enqueued outgoing bits */ - uint8_t outgoing_bits_count; - uint8_t flags; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of output */ - uint8_t bit_index; /* current bit index */ -#if HEATSHRINK_DYNAMIC_ALLOC - uint8_t window_sz2; /* 2^n size of window */ - uint8_t lookahead_sz2; /* 2^n size of lookahead */ -#if HEATSHRINK_USE_INDEX - struct hs_index *search_index; -#endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[]; -#else - #if HEATSHRINK_USE_INDEX - struct hs_index search_index; - #endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; -#endif -} heatshrink_encoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a new encoder struct and its buffers. - * Returns NULL on error. */ -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, - uint8_t lookahead_sz2); - -/* Free an encoder. */ -void heatshrink_encoder_free(heatshrink_encoder *hse); -#endif - -/* Reset an encoder. */ -void heatshrink_encoder_reset(heatshrink_encoder *hse); - -/* Sink up to SIZE bytes from IN_BUF into the encoder. - * INPUT_SIZE is set to the number of bytes actually sunk (in case a - * buffer was filled.). */ -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the encoder that the input stream is finished. - * If the return value is HSER_FINISH_MORE, there is still more output, so - * call heatshrink_encoder_poll and repeat. */ -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); - -#endif From 793a2710dd9919027950a41a0dda25198f5583b2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 3 Aug 2023 09:45:49 +0200 Subject: [PATCH 20/48] Change libbgcode git repo link to use https instead of git@ --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 074adefffc..cfdcd009bd 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,6 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "GIT_REPOSITORY;git@github.com:prusa3d/libbgcode.git;GIT_TAG;main") +set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;main") if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From c82f9c76273cdbcb4409cfadf07fb0186d0baf3f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 12:37:41 +0200 Subject: [PATCH 21/48] Removed code moved to libbgcode library --- src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/GCode/GCodeBinarizer.cpp | 1976 ------------------------ src/libslic3r/GCode/GCodeBinarizer.hpp | 382 ----- 3 files changed, 2360 deletions(-) delete mode 100644 src/libslic3r/GCode/GCodeBinarizer.cpp delete mode 100644 src/libslic3r/GCode/GCodeBinarizer.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c1dcd961cf..f11986d314 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -164,8 +164,6 @@ set(SLIC3R_SOURCES GCode/WipeTower.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp - GCode/GCodeBinarizer.cpp - GCode/GCodeBinarizer.hpp GCode/AvoidCrossingPerimeters.cpp GCode/AvoidCrossingPerimeters.hpp GCode.cpp diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp deleted file mode 100644 index d1e2368863..0000000000 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ /dev/null @@ -1,1976 +0,0 @@ -#include "GCodeBinarizer.hpp" - -#if ENABLE_BINARIZED_GCODE_DEBUG -#define NOMINMAX -#include -#include -#endif // ENABLE_BINARIZED_GCODE_DEBUG - -#if ENABLE_FILE_CONVERSION_INTERFACE -#include -#endif // ENABLE_FILE_CONVERSION_INTERFACE - -extern "C" { -#include -#include -} -#include - -#include -#include - -namespace bgcode { - -static size_t g_checksum_max_cache_size = 65536; -static constexpr const size_t MAX_GCODE_CACHE_SIZE = 65536; - -namespace MeatPack { -static constexpr const uint8_t Command_None{ 0 }; -//#Command_TogglePacking = 253 -- Currently unused, byte 253 can be reused later. -static constexpr const uint8_t Command_EnablePacking{ 251 }; -static constexpr const uint8_t Command_DisablePacking{ 250 }; -static constexpr const uint8_t Command_ResetAll{ 249 }; -static constexpr const uint8_t Command_QueryConfig{ 248 }; -static constexpr const uint8_t Command_EnableNoSpaces{ 247 }; -static constexpr const uint8_t Command_DisableNoSpaces{ 246 }; -static constexpr const uint8_t Command_SignalByte{ 0xFF }; - -static constexpr const uint8_t Flag_OmitWhitespaces{ 0x01 }; -static constexpr const uint8_t Flag_RemoveComments{ 0x02 }; - -static constexpr const uint8_t BothUnpackable{ 0b11111111 }; -static constexpr const char SpaceReplacedCharacter{ 'E' }; - -static const std::unordered_map ReverseLookupTbl = { - { '0', 0b00000000 }, - { '1', 0b00000001 }, - { '2', 0b00000010 }, - { '3', 0b00000011 }, - { '4', 0b00000100 }, - { '5', 0b00000101 }, - { '6', 0b00000110 }, - { '7', 0b00000111 }, - { '8', 0b00001000 }, - { '9', 0b00001001 }, - { '.', 0b00001010 }, - { ' ', 0b00001011 }, - { '\n', 0b00001100 }, - { 'G', 0b00001101 }, - { 'X', 0b00001110 }, - { '\0', 0b00001111 } // never used, 0b1111 is used to indicate the next 8-bits is a full character -}; - -class MPBinarizer -{ -public: - explicit MPBinarizer(uint8_t flags = 0) : m_flags(flags) {} - - void initialize(std::vector& dst) { - initialize_lookup_tables(); - append_command(Command_EnablePacking, dst); - m_binarizing = true; - } - - void finalize(std::vector& dst) { - if ((m_flags & Flag_RemoveComments) != 0) { - assert(m_binarizing); - append_command(Command_ResetAll, dst); - m_binarizing = false; - } - } - - void binarize_line(const std::string& line, std::vector& dst) { - auto unified_method = [this](const std::string& line) { - const std::string::size_type g_idx = line.find('G'); - if (g_idx != std::string::npos) { - if (g_idx + 1 < line.size() && line[g_idx + 1] >= '0' && line[g_idx + 1] <= '9') { - if ((m_flags & Flag_OmitWhitespaces) != 0) { - std::string result = line; - std::replace(result.begin(), result.end(), 'e', 'E'); - std::replace(result.begin(), result.end(), 'x', 'X'); - std::replace(result.begin(), result.end(), 'g', 'G'); - result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); - if (result.find('*') != std::string::npos) { - size_t checksum = 0; - result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); - for (size_t i = 0; i < result.size(); ++i) { - checksum ^= static_cast(result[i]); - } - result += "*" + std::to_string(checksum); - } - result += '\n'; - return result; - } - else { - std::string result = line; - std::replace(result.begin(), result.end(), 'x', 'X'); - std::replace(result.begin(), result.end(), 'g', 'G'); - result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); - if (result.find('*') != std::string::npos) { - size_t checksum = 0; - result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); - for (size_t i = 0; i < result.size(); ++i) { - checksum ^= static_cast(result[i]); - } - result += "*" + std::to_string(checksum); - } - result += '\n'; - return result; - } - } - } - return line; - }; - auto is_packable = [](char c) { - return (s_lookup_tables.packable[static_cast(c)] != 0); - }; - auto pack_chars = [](char low, char high) { - return (((s_lookup_tables.value[static_cast(high)] & 0xF) << 4) | - (s_lookup_tables.value[static_cast(low)] & 0xF)); - }; - - if (!line.empty()) { - if ((m_flags & Flag_RemoveComments) == 0) { - if (line[0] == ';') { - if (m_binarizing) { - append_command(Command_DisablePacking, dst); - m_binarizing = false; - } - - dst.insert(dst.end(), line.begin(), line.end()); - return; - } - } - - if (line[0] == ';' || - line[0] == '\n' || - line[0] == '\r' || - line.size() < 2) - return; - - std::string modifiedLine = line.substr(0, line.find(';')); - if (modifiedLine.empty()) - return; - auto trim_right = [](const std::string& str) { - if (str.back() != ' ') - return str; - auto bit = str.rbegin(); - while (bit != str.rend() && *bit == ' ') { - ++bit; - } - return str.substr(0, std::distance(str.begin(), bit.base())); - }; - modifiedLine = trim_right(modifiedLine); - modifiedLine = unified_method(modifiedLine); - if (modifiedLine.back() != '\n') - modifiedLine.push_back('\n'); - const size_t line_len = modifiedLine.size(); - std::vector temp_buffer; - temp_buffer.reserve(line_len); - - for (size_t line_idx = 0; line_idx < line_len; line_idx += 2) { - const bool skip_last = line_idx == (line_len - 1); - const char char_1 = modifiedLine[line_idx]; - const char char_2 = (skip_last ? '\n' : modifiedLine[line_idx + 1]); - const bool c1_p = is_packable(char_1); - const bool c2_p = is_packable(char_2); - - if (c1_p) { - if (c2_p) - temp_buffer.emplace_back(static_cast(pack_chars(char_1, char_2))); - else { - temp_buffer.emplace_back(static_cast(pack_chars(char_1, '\0'))); - temp_buffer.emplace_back(static_cast(char_2)); - } - } - else { - if (c2_p) { - temp_buffer.emplace_back(static_cast(pack_chars('\0', char_2))); - temp_buffer.emplace_back(static_cast(char_1)); - } - else { - temp_buffer.emplace_back(static_cast(BothUnpackable)); - temp_buffer.emplace_back(static_cast(char_1)); - temp_buffer.emplace_back(static_cast(char_2)); - } - } - } - - if (!m_binarizing && !temp_buffer.empty()) { - append_command(Command_EnablePacking, dst); - m_binarizing = true; - } - - dst.insert(dst.end(), temp_buffer.begin(), temp_buffer.end()); - } - } - -private: - unsigned char m_flags{ 0 }; - bool m_binarizing{ false }; - - struct LookupTables - { - std::array packable; - std::array value; - bool initialized; - unsigned char flags; - }; - - static LookupTables s_lookup_tables; - - void append_command(unsigned char cmd, std::vector& dst) { - dst.emplace_back(Command_SignalByte); - dst.emplace_back(Command_SignalByte); - dst.emplace_back(cmd); - } - - void initialize_lookup_tables() { - if (s_lookup_tables.initialized && m_flags == s_lookup_tables.flags) - return; - - for (const auto& [c, value] : ReverseLookupTbl) { - const int index = static_cast(c); - s_lookup_tables.packable[index] = 1; - s_lookup_tables.value[index] = value; - } - - if ((m_flags & Flag_OmitWhitespaces) != 0) { - s_lookup_tables.value[static_cast(SpaceReplacedCharacter)] = ReverseLookupTbl.at(' '); - s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 1; - s_lookup_tables.packable[static_cast(' ')] = 0; - } - else { - s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 0; - s_lookup_tables.packable[static_cast(' ')] = 1; - } - - s_lookup_tables.initialized = true; - s_lookup_tables.flags = m_flags; - } -}; - -MPBinarizer::LookupTables MPBinarizer::s_lookup_tables = { { 0 }, { 0 }, false, 0 }; - -static constexpr const unsigned char SecondNotPacked{ 0b11110000 }; -static constexpr const unsigned char FirstNotPacked{ 0b00001111 }; -static constexpr const unsigned char NextPackedFirst{ 0b00000001 }; -static constexpr const unsigned char NextPackedSecond{ 0b00000010 }; - -// See for reference: https://github.com/scottmudge/Prusa-Firmware-MeatPack/blob/MK3_sm_MeatPack/Firmware/meatpack.cpp -static void unbinarize(const std::vector& src, std::string& dst) { - bool unbinarizing = false; - bool cmd_active = false; // Is a command pending - uint8_t char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs - size_t cmd_count = 0; // Counts how many command bytes are received (need 2) - size_t full_char_queue = 0; // Counts how many full-width characters are to be received - std::array char_out_buf; // Output buffer for caching up to 2 characters - size_t char_out_count = 0; // Stores number of characters to be read out - - auto handle_command = [&](uint8_t c) { - switch (c) - { - case Command_EnablePacking: { unbinarizing = true; break; } - case Command_DisablePacking: { unbinarizing = false; break; } - case Command_ResetAll: { unbinarizing = false; break; } - default: - case Command_QueryConfig: { break; } - } - }; - - auto handle_output_char = [&](uint8_t c) { - char_out_buf[char_out_count++] = c; - }; - - auto get_char = [](uint8_t c) { - switch (c) - { - case 0b0000: { return '0'; } - case 0b0001: { return '1'; } - case 0b0010: { return '2'; } - case 0b0011: { return '3'; } - case 0b0100: { return '4'; } - case 0b0101: { return '5'; } - case 0b0110: { return '6'; } - case 0b0111: { return '7'; } - case 0b1000: { return '8'; } - case 0b1001: { return '9'; } - case 0b1010: { return '.'; } - case 0b1011: { return 'E'; } - case 0b1100: { return '\n'; } - case 0b1101: { return 'G'; } - case 0b1110: { return 'X'; } - } - return '\0'; - }; - - auto unpack_chars = [&](uint8_t pk, std::array& chars_out) { - uint8_t out = 0; - - // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. - if ((pk & FirstNotPacked) == FirstNotPacked) - out |= NextPackedFirst; - else - chars_out[0] = get_char(pk & 0xF); // Assign lower char - - // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. - if ((pk & SecondNotPacked) == SecondNotPacked) - out |= NextPackedSecond; - else - chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char - - return out; - }; - - auto handle_rx_char = [&](uint8_t c) { - if (unbinarizing) { - if (full_char_queue > 0) { - handle_output_char(c); - if (char_buf > 0) { - handle_output_char(char_buf); - char_buf = 0; - } - --full_char_queue; - } - else { - std::array buf = { 0, 0 }; - const uint8_t res = unpack_chars(c, buf); - - if ((res & NextPackedFirst) != 0) { - ++full_char_queue; - if ((res & NextPackedSecond) != 0) - ++full_char_queue; - else - char_buf = buf[1]; - } - else { - handle_output_char(buf[0]); - if (buf[0] != '\n') { - if ((res & NextPackedSecond) != 0) - ++full_char_queue; - else - handle_output_char(buf[1]); - } - } - } - } - else // Packing not enabled, just copy character to output - handle_output_char(c); - }; - - auto get_result_char = [&](std::array& chars_out) { - if (char_out_count > 0) { - const size_t res = char_out_count; - for (uint8_t i = 0; i < char_out_count; ++i) { - chars_out[i] = (char)char_out_buf[i]; - } - char_out_count = 0; - return res; - } - return (size_t)0; - }; - - std::vector unbin_buffer(2 * src.size(), 0); - auto it_unbin_end = unbin_buffer.begin(); - -#if ENABLE_BINARIZED_GCODE_DEBUG - size_t line_start = 0; -#endif // ENABLE_BINARIZED_GCODE_DEBUG - bool add_space = false; - - auto begin = src.begin(); - auto end = src.end(); - - auto it_bin = begin; - while (it_bin != end) { - uint8_t c_bin = *it_bin; - if (c_bin == Command_SignalByte) { - if (cmd_count > 0) { - cmd_active = true; - cmd_count = 0; - } - else - ++cmd_count; - } - else { - if (cmd_active) { - handle_command(c_bin); - cmd_active = false; - } - else { - if (cmd_count > 0) { - handle_rx_char(Command_SignalByte); - cmd_count = 0; - } - - handle_rx_char(c_bin); - } - } - - auto is_gline_parameter = [](const char c) { - static const std::vector parameters = { - // G0, G1 - 'X', 'Y', 'Z', 'E', 'F', - // G2, G3 - 'I', 'J', 'R', - // G29 - 'P', 'W', 'H', 'C', 'A' - }; - return std::find(parameters.begin(), parameters.end(), c) != parameters.end(); - }; - - std::array c_unbin{ 0, 0 }; - const size_t char_count = get_result_char(c_unbin); - for (size_t i = 0; i < char_count; ++i) { - // GCodeReader::parse_line_internal() is unable to parse a G line where the data are not separated by spaces - // so we add them where needed - const size_t curr_unbin_buffer_length = std::distance(unbin_buffer.begin(), it_unbin_end); - if (c_unbin[i] == 'G' && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) == '\n')) - add_space = true; - else if (c_unbin[i] == '\n') - add_space = false; - - if (add_space && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) != ' ') && - is_gline_parameter(c_unbin[i])) { - *it_unbin_end = ' '; - ++it_unbin_end; - } - - if (c_unbin[i] != '\n' || std::distance(unbin_buffer.begin(), it_unbin_end) == 0 || *std::prev(it_unbin_end, 1) != '\n') { - *it_unbin_end = c_unbin[i]; - ++it_unbin_end; - } - -#if ENABLE_BINARIZED_GCODE_DEBUG - if (c_unbin[i] == '\n') { - const std::string out(unbin_buffer.begin() + line_start, it_unbin_end); - if (!out.empty()) { - OutputDebugStringA(out.c_str()); - line_start = std::distance(unbin_buffer.begin(), it_unbin_end); - } - } -#endif // ENABLE_BINARIZED_GCODE_DEBUG - } - - ++it_bin; - } - - dst.insert(dst.end(), unbin_buffer.begin(), it_unbin_end); -} -} // namespace MeatPack - -std::string translate_result(EResult result) -{ - switch (result) - { - case EResult::Success: { return "Success"; } - case EResult::ReadError: { return "Read error"; } - case EResult::WriteError: { return "Write error"; } - case EResult::InvalidMagicNumber: { return "Invalid magic number"; } - case EResult::InvalidVersionNumber: { return "Invalid version number"; } - case EResult::InvalidChecksumType: { return "Invalid checksum type"; } - case EResult::InvalidBlockType: { return "Invalid block type"; } - case EResult::InvalidCompressionType: { return "Invalid compression type"; } - case EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } - case EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } - case EResult::DataCompressionError: { return "Data compression error"; } - case EResult::DataUncompressionError: { return "Data uncompression error"; } - case EResult::MetadataEncodingError: { return "Data encoding error"; } - case EResult::MetadataDecodingError: { return "Data decoding error"; } - case EResult::GCodeEncodingError: { return "GCode encoding error"; } - case EResult::GCodeDecodingError: { return "GCode decoding error"; } - case EResult::BlockNotFound: { return "Block not found"; } - case EResult::InvalidChecksum: { return "Invalid checksum"; } - case EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } - case EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } - case EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } - case EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } - case EResult::InvalidBinaryGCodeFile: { return "Invalid binary GCode file"; } - case EResult::InvalidSequenceOfBlocks: { return "Invalid sequence of blocks"; } - } - return std::string(); -} - -size_t get_checksum_max_cache_size() { return g_checksum_max_cache_size; } -void set_checksum_max_cache_size(size_t size) { g_checksum_max_cache_size = size; } - -static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } -static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } -static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::Heatshrink_12_4; } -static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } -static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } -static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPackComments; } - -static bool write_to_file(FILE& file, const void* data, size_t data_size) -{ - fwrite(data, 1, data_size, &file); - return !ferror(&file); -} - -static bool read_from_file(FILE& file, void* data, size_t data_size) -{ - fread(data, 1, data_size, &file); - return !ferror(&file); -} - -static bool encode_metadata(const std::vector>& src, std::vector& dst, - EMetadataEncodingType encoding_type) -{ - for (const auto& [key, value] : src) { - switch (encoding_type) - { - case EMetadataEncodingType::INI: - { - dst.insert(dst.end(), key.begin(), key.end()); - dst.emplace_back('='); - dst.insert(dst.end(), value.begin(), value.end()); - dst.emplace_back('\n'); - break; - } - } - } - return true; -} - -static bool decode_metadata(const std::vector& src, std::vector>& dst, - EMetadataEncodingType encoding_type) -{ - switch (encoding_type) - { - case EMetadataEncodingType::INI: - { - auto begin_it = src.begin(); - auto end_it = src.begin(); - while (end_it != src.end()) { - while (end_it != src.end() && *end_it != '\n') { - ++end_it; - } - const std::string item(begin_it, end_it); - const size_t pos = item.find_first_of('='); - if (pos != std::string::npos) { - dst.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); - begin_it = ++end_it; - } - } - break; - } - } - - return true; -} - -static bool encode_gcode(const std::string& src, std::vector& dst, EGCodeEncodingType encoding_type) -{ - switch (encoding_type) - { - case EGCodeEncodingType::None: - { - dst.insert(dst.end(), src.begin(), src.end()); - break; - } - case EGCodeEncodingType::MeatPack: - case EGCodeEncodingType::MeatPackComments: - { - uint8_t binarizer_flags = (encoding_type == EGCodeEncodingType::MeatPack) ? MeatPack::Flag_RemoveComments : 0; - binarizer_flags |= MeatPack::Flag_OmitWhitespaces; - MeatPack::MPBinarizer binarizer(binarizer_flags); - binarizer.initialize(dst); - auto begin_it = src.begin(); - auto end_it = src.begin(); - while (end_it != src.end()) { - while (end_it != src.end() && *end_it != '\n') { - ++end_it; - } - const std::string line(begin_it, ++end_it); - binarizer.binarize_line(line, dst); - begin_it = end_it; - } - binarizer.finalize(dst); - break; - } - } - return true; -} - -static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) -{ - switch (encoding_type) - { - case EGCodeEncodingType::None: - { - dst.insert(dst.end(), src.begin(), src.end()); - break; - } - case EGCodeEncodingType::MeatPack: - case EGCodeEncodingType::MeatPackComments: - { - MeatPack::unbinarize(src, dst); - break; - } - } - return true; -} - -static bool compress(const std::vector& src, std::vector& dst, ECompressionType compression_type) -{ - switch (compression_type) - { - case ECompressionType::Deflate: - { - dst.clear(); - - const size_t BUFSIZE = 2048; - std::vector temp_buffer(BUFSIZE); - - z_stream strm{}; - strm.next_in = const_cast(src.data()); - strm.avail_in = (uInt)src.size(); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - - const int level = Z_DEFAULT_COMPRESSION; - int res = deflateInit(&strm, level); - if (res != Z_OK) - return false; - - while (strm.avail_in > 0) { - res = deflate(&strm, Z_NO_FLUSH); - if (res != Z_OK) { - deflateEnd(&strm); - return false; - } - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - } - - int deflate_res = Z_OK; - while (deflate_res == Z_OK) { - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - deflate_res = deflate(&strm, Z_FINISH); - } - - if (deflate_res != Z_STREAM_END) { - deflateEnd(&strm); - return false; - } - - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); - deflateEnd(&strm); - break; - } - case ECompressionType::Heatshrink_11_4: - case ECompressionType::Heatshrink_12_4: - { - const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; - const uint8_t lookahead_sz = 4; - heatshrink_encoder* encoder = heatshrink_encoder_alloc(window_sz, lookahead_sz); - if (encoder == nullptr) - return false; - - // calculate the maximum compressed size (assuming a conservative estimate) - const size_t src_size = src.size(); - const size_t max_compressed_size = src_size + (src_size >> 2); - dst.resize(max_compressed_size); - - uint8_t* buf = const_cast(src.data()); - uint8_t* outbuf = dst.data(); - - // compress data - size_t tosink = src_size; - size_t output_size = 0; - while (tosink > 0) { - size_t sunk = 0; - const HSE_sink_res sink_res = heatshrink_encoder_sink(encoder, buf, tosink, &sunk); - if (sink_res != HSER_SINK_OK) { - heatshrink_encoder_free(encoder); - return false; - } - if (sunk == 0) - // all input data processed - break; - - tosink -= sunk; - buf += sunk; - - size_t polled = 0; - const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); - if (poll_res < 0) { - heatshrink_encoder_free(encoder); - return false; - } - output_size += polled; - } - - // input data finished - const HSE_finish_res finish_res = heatshrink_encoder_finish(encoder); - if (finish_res < 0) { - heatshrink_encoder_free(encoder); - return false; - } - - // poll for final output - size_t polled = 0; - const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); - if (poll_res < 0) { - heatshrink_encoder_free(encoder); - return false; - } - dst.resize(output_size + polled); - heatshrink_encoder_free(encoder); - break; - } - case ECompressionType::None: - default: - { - break; - } - } - - return true; -} - -static bool uncompress(const std::vector& src, std::vector& dst, ECompressionType compression_type, size_t uncompressed_size) -{ - switch (compression_type) - { - case ECompressionType::Deflate: - { - dst.clear(); - dst.reserve(uncompressed_size); - - const size_t BUFSIZE = 2048; - std::vector temp_buffer(BUFSIZE); - - z_stream strm{}; - strm.next_in = const_cast(src.data()); - strm.avail_in = (uInt)src.size(); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - int res = inflateInit(&strm); - if (res != Z_OK) - return false; - - while (strm.avail_in > 0) { - res = inflate(&strm, Z_NO_FLUSH); - if (res != Z_OK && res != Z_STREAM_END) { - inflateEnd(&strm); - return false; - } - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - } - - int inflate_res = Z_OK; - while (inflate_res == Z_OK) { - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - inflate_res = inflate(&strm, Z_FINISH); - } - - if (inflate_res != Z_STREAM_END) { - inflateEnd(&strm); - return false; - } - - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); - inflateEnd(&strm); - break; - } - case ECompressionType::Heatshrink_11_4: - case ECompressionType::Heatshrink_12_4: - { - const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; - const uint8_t lookahead_sz = 4; - const uint16_t input_buffer_size = 2048; - heatshrink_decoder* decoder = heatshrink_decoder_alloc(input_buffer_size, window_sz, lookahead_sz); - if (decoder == nullptr) - return false; - - dst.resize(uncompressed_size); - - uint8_t* buf = const_cast(src.data()); - uint8_t* outbuf = dst.data(); - - uint32_t sunk = 0; - uint32_t polled = 0; - - const size_t compressed_size = src.size(); - while (sunk < compressed_size) { - size_t count = 0; - const HSD_sink_res sink_res = heatshrink_decoder_sink(decoder, &buf[sunk], compressed_size - sunk, &count); - if (sink_res < 0) { - heatshrink_decoder_free(decoder); - return false; - } - - sunk += (uint32_t)count; - - HSD_poll_res poll_res; - do { - poll_res = heatshrink_decoder_poll(decoder, &outbuf[polled], uncompressed_size - polled, &count); - if (poll_res < 0) { - heatshrink_decoder_free(decoder); - return false; - } - polled += (uint32_t)count; - } while (polled < uncompressed_size && poll_res == HSDR_POLL_MORE); - } - - const HSD_finish_res finish_res = heatshrink_decoder_finish(decoder); - if (finish_res < 0) { - heatshrink_decoder_free(decoder); - return false; - } - - heatshrink_decoder_free(decoder); - break; - } - case ECompressionType::None: - default: - { - break; - } - } - - return true; -} - -static uint32_t crc32_sw(const uint8_t* buffer, uint32_t length, uint32_t crc) -{ - uint32_t value = crc ^ 0xFFFFFFFF; - while (length--) { - value ^= (uint32_t)*buffer++; - for (int bit = 0; bit < 8; bit++) { - if (value & 1) - value = (value >> 1) ^ 0xEDB88320; - else - value >>= 1; - } - } - value ^= 0xFFFFFFFF; - return value; -} - -std::vector encode(const void* data, size_t data_size) -{ - std::vector ret(data_size); - memcpy(ret.data(), data, data_size); - return ret; -} - -Checksum::Checksum(EChecksumType type) -: m_type(type) -{ - if (m_type != EChecksumType::None) - m_checksum = std::vector(checksum_size(m_type), '\0'); -} - -EChecksumType Checksum::get_type() const -{ - return m_type; -} - -void Checksum::append(const std::vector& data) -{ - size_t remaining_data_size = std::distance(data.begin(), data.end()); - auto it_begin = data.begin(); - while (remaining_data_size + m_cache.size() > g_checksum_max_cache_size) { - update(); - if (remaining_data_size > g_checksum_max_cache_size) { - m_cache.insert(m_cache.end(), it_begin, it_begin + g_checksum_max_cache_size); - it_begin += g_checksum_max_cache_size; - remaining_data_size -= g_checksum_max_cache_size; - } - } - - m_cache.insert(m_cache.end(), it_begin, data.end()); -} - -bool Checksum::matches(Checksum& other) -{ - update(); - other.update(); - return m_checksum == other.m_checksum; -} - -EResult Checksum::write(FILE& file) -{ - if (m_type != EChecksumType::None) { - update(); - if (!write_to_file(file, (const void*)m_checksum.data(), m_checksum.size())) - return EResult::WriteError; - } - return EResult::Success; -} - -EResult Checksum::read(FILE& file) -{ - if (m_type != EChecksumType::None) { - if (!read_from_file(file, (void*)m_checksum.data(), m_checksum.size())) - return EResult::ReadError; - } - return EResult::Success; -} - -void Checksum::update() -{ - if (m_cache.empty()) - return; - - switch (m_type) - { - case EChecksumType::None: - { - break; - } - case EChecksumType::CRC32: - { - const uint32_t old_crc = *(uint32_t*)m_checksum.data(); - const uint32_t new_crc = crc32_sw(m_cache.data(), (uint32_t)m_cache.size(), old_crc); - *(uint32_t*)m_checksum.data() = new_crc; - break; - } - } - - m_cache.clear(); -} - -EResult FileHeader::write(FILE& file) const -{ - if (magic != *(uint32_t*)(MAGIC.data())) - return EResult::InvalidMagicNumber; - if (checksum_type >= checksum_types_count()) - return EResult::InvalidChecksumType; - - if (!write_to_file(file, (const void*)&magic, sizeof(magic))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&version, sizeof(version))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&checksum_type, sizeof(checksum_type))) - return EResult::WriteError; - - return EResult::Success; -} - -EResult FileHeader::read(FILE& file, const uint32_t* const max_version) -{ - if (!read_from_file(file, (void*)&magic, sizeof(magic))) - return EResult::ReadError; - if (magic != *(uint32_t*)(MAGIC.data())) - return EResult::InvalidMagicNumber; - - if (!read_from_file(file, (void*)&version, sizeof(version))) - return EResult::ReadError; - if (max_version != nullptr && version > *max_version) - return EResult::InvalidVersionNumber; - - if (!read_from_file(file, (void*)&checksum_type, sizeof(checksum_type))) - return EResult::ReadError; - if (checksum_type >= checksum_types_count()) - return EResult::InvalidChecksumType; - - return EResult::Success; -} - -void BlockHeader::update_checksum(Checksum& checksum) const -{ - checksum.append(encode((const void*)&type, sizeof(type))); - checksum.append(encode((const void*)&compression, sizeof(compression))); - checksum.append(encode((const void*)&uncompressed_size, sizeof(uncompressed_size))); - if (compression != (uint16_t)ECompressionType::None) - checksum.append(encode((const void*)&compressed_size, sizeof(compressed_size))); -} - -EResult BlockHeader::write(FILE& file) const -{ - if (!write_to_file(file, (const void*)&type, sizeof(type))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&compression, sizeof(compression))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&uncompressed_size, sizeof(uncompressed_size))) - return EResult::WriteError; - if (compression != (uint16_t)ECompressionType::None) { - if (!write_to_file(file, (const void*)&compressed_size, sizeof(compressed_size))) - return EResult::WriteError; - } - return EResult::Success; -} - -EResult BlockHeader::read(FILE& file) -{ - if (!read_from_file(file, (void*)&type, sizeof(type))) - return EResult::ReadError; - if (type >= block_types_count()) - return EResult::InvalidBlockType; - - if (!read_from_file(file, (void*)&compression, sizeof(compression))) - return EResult::ReadError; - if (compression >= compression_types_count()) - return EResult::InvalidCompressionType; - - if (!read_from_file(file, (void*)&uncompressed_size, sizeof(uncompressed_size))) - return EResult::ReadError; - if (compression != (uint16_t)ECompressionType::None) { - if (!read_from_file(file, (void*)&compressed_size, sizeof(compressed_size))) - return EResult::ReadError; - } - - return EResult::Success; -} - -EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const -{ - if (encoding_type > metadata_encoding_types_count()) - return EResult::InvalidMetadataEncodingType; - - BlockHeader block_header = { (uint16_t)block_type, (uint16_t)compression_type, (uint32_t)0 }; - std::vector out_data; - if (!raw_data.empty()) { - // process payload encoding - std::vector uncompressed_data; - if (!encode_metadata(raw_data, uncompressed_data, (EMetadataEncodingType)encoding_type)) - return EResult::MetadataEncodingError; - // process payload compression - block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); - std::vector compressed_data; - if (compression_type != ECompressionType::None) { - if (!compress(uncompressed_data, compressed_data, compression_type)) - return EResult::DataCompressionError; - block_header.compressed_size = (uint32_t)compressed_data.size(); - } - out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); - } - - // write block header - EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - - // write block payload - if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) - return EResult::WriteError; - if (!out_data.empty()) { - if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) - return EResult::WriteError; - } - - if (checksum.get_type() != EChecksumType::None) { - // update checksum with block header - block_header.update_checksum(checksum); - // update checksum with block payload - checksum.append(encode((const void*)&encoding_type, sizeof(encoding_type))); - if (!out_data.empty()) - checksum.append(out_data); - } - - return EResult::Success; -} - -EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header) -{ - const ECompressionType compression_type = (ECompressionType)block_header.compression; - - if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) - return EResult::ReadError; - if (encoding_type > metadata_encoding_types_count()) - return EResult::InvalidMetadataEncodingType; - - std::vector data; - const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; - if (data_size > 0) { - data.resize(data_size); - if (!read_from_file(file, (void*)data.data(), data_size)) - return EResult::ReadError; - } - - std::vector uncompressed_data; - if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) - return EResult::DataUncompressionError; - } - - if (!decode_metadata((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EMetadataEncodingType)encoding_type)) - return EResult::MetadataDecodingError; - - return EResult::Success; -} - -EResult FileMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::FileMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult FileMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult ThumbnailBlock::write(FILE& file, EChecksumType checksum_type) const -{ - if (format >= thumbnail_formats_count()) - return EResult::InvalidThumbnailFormat; - if (width == 0) - return EResult::InvalidThumbnailWidth; - if (height == 0) - return EResult::InvalidThumbnailHeight; - if (data.size() == 0) - return EResult::InvalidThumbnailDataSize; - - // write block header - const BlockHeader block_header = { (uint16_t)EBlockType::Thumbnail, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; - EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - - // write block payload - if (!write_to_file(file, (const void*)&format, sizeof(format))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&width, sizeof(width))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&height, sizeof(height))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)data.data(), data.size())) - return EResult::WriteError; - - if (checksum_type != EChecksumType::None) { - Checksum cs(checksum_type); - // update checksum with block header - block_header.update_checksum(cs); - // update checksum with block payload - update_checksum(cs); - // write block checksum - res = cs.write(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - if (!read_from_file(file, (void*)&format, sizeof(format))) - return EResult::ReadError; - if (format >= thumbnail_formats_count()) - return EResult::InvalidThumbnailFormat; - if (!read_from_file(file, (void*)&width, sizeof(width))) - return EResult::ReadError; - if (width == 0) - return EResult::InvalidThumbnailWidth; - if (!read_from_file(file, (void*)&height, sizeof(height))) - return EResult::ReadError; - if (height == 0) - return EResult::InvalidThumbnailHeight; - if (block_header.uncompressed_size == 0) - return EResult::InvalidThumbnailDataSize; - - data.resize(block_header.uncompressed_size); - if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) - return EResult::ReadError; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - const EResult res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -void ThumbnailBlock::update_checksum(Checksum& checksum) const -{ - checksum.append(encode((const void*)&format, sizeof(format))); - checksum.append(encode((const void*)&width, sizeof(width))); - checksum.append(encode((const void*)&height, sizeof(height))); - checksum.append(data); -} - -EResult PrinterMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::PrinterMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult PrinterMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult PrintMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::PrintMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult PrintMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult SlicerMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::SlicerMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - if (encoding_type > gcode_encoding_types_count()) - return EResult::InvalidGCodeEncodingType; - - BlockHeader block_header = { (uint16_t)EBlockType::GCode, (uint16_t)compression_type, (uint32_t)0 }; - std::vector out_data; - if (!raw_data.empty()) { - // process payload encoding - std::vector uncompressed_data; - if (!encode_gcode(raw_data, uncompressed_data, (EGCodeEncodingType)encoding_type)) - return EResult::GCodeEncodingError; - // process payload compression - block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); - std::vector compressed_data; - if (compression_type != ECompressionType::None) { - if (!compress(uncompressed_data, compressed_data, compression_type)) - return EResult::DataCompressionError; - block_header.compressed_size = (uint32_t)compressed_data.size(); - } - out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); - } - - // write block header - EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - - // write block payload - if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) - return EResult::WriteError; - if (!out_data.empty()) { -#if ENABLE_BINARIZED_GCODE_DEBUG - const std::string out = "GCodeBlock data size:" + std::to_string(out_data.size()) + "\n"; - OutputDebugStringA(out.c_str()); -#endif // ENABLE_BINARIZED_GCODE_DEBUG - if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) - return EResult::WriteError; - } - - // write checksum - if (checksum_type != EChecksumType::None) { - Checksum cs(checksum_type); - // update checksum with block header - block_header.update_checksum(cs); - // update checksum with block payload - cs.append(encode((const void*)&encoding_type, sizeof(encoding_type))); - if (!out_data.empty()) - cs.append(out_data); - res = cs.write(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - const ECompressionType compression_type = (ECompressionType)block_header.compression; - - if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) - return EResult::ReadError; - if (encoding_type > gcode_encoding_types_count()) - return EResult::InvalidGCodeEncodingType; - - std::vector data; - const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; - if (data_size > 0) { - data.resize(data_size); - if (!read_from_file(file, (void*)data.data(), data_size)) - return EResult::ReadError; - } - - std::vector uncompressed_data; - if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) - return EResult::DataUncompressionError; - } - - if (!decode_gcode((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EGCodeEncodingType)encoding_type)) - return EResult::GCodeDecodingError; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - const EResult res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -#if ENABLE_CHECKSUM_BLOCK -EResult ChecksumBlock::write(FILE& file) const -{ - if (!data.empty()) { - const BlockHeader block_header = { (uint16_t)EBlockType::Checksum, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; - // write block header - const EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - // write block payload - if (!write_to_file(file, (const void*)data.data(), data.size())) - return EResult::WriteError; - } - - return EResult::Success; -} - -EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) -{ - if (block_header.uncompressed_size > 0) { - data.resize(block_header.uncompressed_size); - if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) - return EResult::ReadError; - } - else - data.clear(); - - return EResult::Success; -} -#endif // ENABLE_CHECKSUM_BLOCK - -EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) -{ - if (!m_enabled) - return EResult::Success; - - m_file = &file; - - m_config = config; -#if ENABLE_CHECKSUM_BLOCK - // initialize checksum - m_checksum = ChecksumBlock(); -#endif // ENABLE_CHECKSUM_BLOCK - - // save header - FileHeader file_header; - file_header.checksum_type = (uint16_t)m_config.checksum; - EResult res = file_header.write(*m_file); - if (res != EResult::Success) - return res; - - // save file metadata block - res = m_binary_data.file_metadata.write(*m_file, m_config.compression.file_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - // save printer metadata block - res = m_binary_data.printer_metadata.write(*m_file, m_config.compression.printer_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - // save thumbnail blocks - for (const ThumbnailBlock& block : m_binary_data.thumbnails) { - res = block.write(*m_file, m_config.checksum); - if (res != EResult::Success) - return res; - } - - // save print metadata block - res = m_binary_data.print_metadata.write(*m_file, m_config.compression.print_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - // save slicer metadata block - res = m_binary_data.slicer_metadata.write(*m_file, m_config.compression.slicer_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - return EResult::Success; -} - -static EResult write_gcode_block(FILE& file, const std::string& raw_data, const BinarizerConfig& config) -{ - GCodeBlock block; - block.encoding_type = (uint16_t)config.gcode_encoding; - block.raw_data = raw_data; - return block.write(file, config.compression.gcode, config.checksum); -} - -EResult Binarizer::append_gcode(const std::string& gcode) -{ - if (gcode.empty()) - return EResult::Success; - - assert(m_file != nullptr); - if (m_file == nullptr) - return EResult::WriteError; - - auto it_begin = gcode.begin(); - do { - const size_t begin_pos = std::distance(gcode.begin(), it_begin); - const size_t end_line_pos = gcode.find_first_of('\n', begin_pos); - if (end_line_pos == std::string::npos) - return EResult::WriteError; - - const size_t line_size = 1 + end_line_pos - begin_pos; - if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { - if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); - if (res != EResult::Success) - // propagate error - return res; - m_gcode_cache.clear(); - } - } - - if (line_size > MAX_GCODE_CACHE_SIZE) - return EResult::WriteError; - - m_gcode_cache.insert(m_gcode_cache.end(), it_begin, it_begin + line_size); - it_begin += line_size; - } - while (it_begin != gcode.end()); - - return EResult::Success; -} - -EResult Binarizer::finalize() -{ - if (!m_enabled) - return EResult::Success; - - // save gcode cache, if not empty - if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); - if (res != EResult::Success) - // propagate error - return res; - } - -#if ENABLE_CHECKSUM_BLOCK - if (m_checksum_type != EChecksumType::None) { - // save checksum - // dummy checksum until it is not properly implemented - switch (m_checksum_type) - { - case EChecksumType::CRC32: - case EChecksumType::MD5: - { - m_checksum.data.clear(); - break; - } - } - - res = m_checksum.write(file); - if (res != EResult::Success) - return res; - } -#endif // ENABLE_CHECKSUM_BLOCK - - return EResult::Success; -} - -bool is_valid_binary_gcode(FILE& file) -{ - // cache file position - const long curr_pos = ftell(&file); - rewind(&file); - - std::array magic; - fread((void*)magic.data(), 1, magic.size(), &file); - if (ferror(&file)) - return false; - else { - // restore file position - fseek(&file, curr_pos, SEEK_SET); - return magic == MAGIC; - } -} - -EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version) -{ - rewind(&file); - return header.read(file, max_version); -} - -static EResult checksums_match(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // cache file position - const long curr_pos = ftell(&file); - - Checksum curr_cs((EChecksumType)file_header.checksum_type); - // update block checksum block header - block_header.update_checksum(curr_cs); - - // read block payload - size_t remaining_payload_size = block_payload_size(block_header); - while (remaining_payload_size > 0) { - const size_t size_to_read = std::min(remaining_payload_size, g_checksum_max_cache_size); - std::vector payload(size_to_read); - if (!read_from_file(file, payload.data(), payload.size())) - return EResult::ReadError; - curr_cs.append(payload); - remaining_payload_size -= size_to_read; - } - - // read checksum - Checksum read_cs((EChecksumType)file_header.checksum_type); - EResult res = read_cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - - // Verify checksum - if (!curr_cs.matches(read_cs)) - return EResult::InvalidChecksum; - - // restore file position - fseek(&file, curr_pos, SEEK_SET); - - return EResult::Success; -} - -EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum) -{ - if (verify_checksum && (EChecksumType)file_header.checksum_type != EChecksumType::None) { - const EResult res = block_header.read(file); - if (res != EResult::Success) - // propagate error - return res; - - return checksums_match(file, file_header, block_header); - } - else - return block_header.read(file); -} - -EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum) -{ - // cache file position - const long curr_pos = ftell(&file); - - do { - EResult res = read_next_block_header(file, file_header, block_header, false); - if (res != EResult::Success) - // propagate error - return res; - else if (feof(&file)) { - // block not found - // restore file position - fseek(&file, curr_pos, SEEK_SET); - return EResult::BlockNotFound; - } - else if ((EBlockType)block_header.type == type) { - // block found - if (verify_checksum) { - res = checksums_match(file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - else - break; - } - } - - if (!feof(&file)) { - res = skip_block_content(file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - } - } while (true); - - return EResult::Success; -} - -EResult skip_block_payload(FILE& file, const BlockHeader& block_header) -{ - fseek(&file, (long)block_payload_size(block_header), SEEK_CUR); - return ferror(&file) ? EResult::ReadError : EResult::Success; -} - -EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - fseek(&file, (long)block_content_size(file_header, block_header), SEEK_CUR); - return ferror(&file) ? EResult::ReadError : EResult::Success; -} - -size_t block_parameters_size(EBlockType type) -{ - switch (type) - { - case EBlockType::FileMetadata: { return FileMetadataBlock::get_parameters_size(); } - case EBlockType::GCode: { return GCodeBlock::get_parameters_size(); } - case EBlockType::SlicerMetadata: { return SlicerMetadataBlock::get_parameters_size(); } - case EBlockType::PrinterMetadata: { return PrinterMetadataBlock::get_parameters_size(); } - case EBlockType::PrintMetadata: { return PrintMetadataBlock::get_parameters_size(); } - case EBlockType::Thumbnail: { return ThumbnailBlock::get_parameters_size(); } - } - return 0; -} - -size_t block_payload_size(const BlockHeader& block_header) -{ - size_t ret = block_parameters_size((EBlockType)block_header.type); - ret += ((ECompressionType)block_header.compression == ECompressionType::None) ? - block_header.uncompressed_size : block_header.compressed_size; - return ret; -} - -size_t checksum_size(EChecksumType type) -{ - switch (type) - { - case EChecksumType::None: { return 0; } - case EChecksumType::CRC32: { return 4; } - } - return 0; -} - -extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header) -{ -#if ENABLE_CHECKSUM_BLOCK - return ((EBlockType)block_header.type == EBlockType::Checksum) ? - block_payload_size(block_header) : block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); -#else - return block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); -#endif // ENABLE_CHECKSUM_BLOCK -} - -#if ENABLE_FILE_CONVERSION_INTERFACE -EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file) -{ - return EResult::WriteError; -} - -EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum) -{ - auto write_line = [&](const std::string& line) { - fwrite(line.data(), 1, line.length(), &dst_file); - return !ferror(&dst_file); - }; - - auto write_metadata = [&](const std::vector>& data) { - for (const auto& [key, value] : data) { - if (!write_line("; " + key + " = " + value + "\n")) - return false; - } - return !ferror(&dst_file); - }; - - if (!is_valid_binary_gcode(src_file)) - return EResult::InvalidBinaryGCodeFile; - - fseek(&src_file, 0, SEEK_END); - const long file_size = ftell(&src_file); - rewind(&src_file); - - // - // read file header - // - FileHeader file_header; - EResult res = read_header(src_file, file_header, nullptr); - if (res != EResult::Success) - // propagate error - return res; - - // - // convert file metadata block - // - BlockHeader block_header; - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::FileMetadata) - return EResult::InvalidSequenceOfBlocks; - FileMetadataBlock file_metadata_block; - res = file_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), - [](const std::pair& item) { return item.first == "Producer"; }); - const std::string producer_str = (producer_it != file_metadata_block.raw_data.end()) ? producer_it->second : "Unknown"; - if (!write_line("; generated by " + producer_str + "\n\n\n")) - return EResult::WriteError; - - // - // convert printer metadata block - // - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) - return EResult::InvalidSequenceOfBlocks; - PrinterMetadataBlock printer_metadata_block; - res = printer_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_metadata(printer_metadata_block.raw_data)) - return EResult::WriteError; - - // - // convert thumbnail blocks - // - long restore_position = ftell(&src_file); - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - while ((EBlockType)block_header.type == EBlockType::Thumbnail) { - ThumbnailBlock thumbnail_block; - res = thumbnail_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - static constexpr const size_t max_row_length = 78; - std::string encoded; - encoded.resize(boost::beast::detail::base64::encoded_size(thumbnail_block.data.size())); - encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)thumbnail_block.data.data(), thumbnail_block.data.size())); - std::string format; - switch ((EThumbnailFormat)thumbnail_block.format) - { - default: - case EThumbnailFormat::PNG: { format = "thumbnail"; break; } - case EThumbnailFormat::JPG: { format = "thumbnail_JPG"; break; } - case EThumbnailFormat::QOI: { format = "thumbnail_QOI"; break; } - } - if (!write_line(";\n; " + format + " begin " + std::to_string(thumbnail_block.width) + "x" + std::to_string(thumbnail_block.height) + - " " + std::to_string(encoded.length()) + "\n")) - return EResult::WriteError; - while (encoded.size() > max_row_length) { - if (!write_line("; " + encoded.substr(0, max_row_length) + "\n")) - return EResult::WriteError; - encoded = encoded.substr(max_row_length); - } - if (encoded.size() > 0) { - if (!write_line("; " + encoded + "\n")) - return EResult::WriteError; - } - if (!write_line("; " + format + " end\n;\n\n")) - return EResult::WriteError; - - restore_position = ftell(&src_file); - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - } - - // - // convert gcode blocks - // - res = skip_block_content(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - res = read_next_block_header(src_file, file_header, block_header, EBlockType::GCode, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - while ((EBlockType)block_header.type == EBlockType::GCode) { - GCodeBlock block; - res = block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_line(block.raw_data)) - return EResult::WriteError; - if (ftell(&src_file) == file_size) - break; - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - } - - // - // convert print metadata block - // - fseek(&src_file, restore_position, SEEK_SET); - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::PrintMetadata) - return EResult::InvalidSequenceOfBlocks; - PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_line("\n")) - return EResult::WriteError; - if (!write_metadata(print_metadata_block.raw_data)) - return EResult::WriteError; - - // - // convert slicer metadata block - // - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) - return EResult::InvalidSequenceOfBlocks; - SlicerMetadataBlock slicer_metadata_block; - res = slicer_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_line("\n; prusaslicer_config = begin\n")) - return EResult::WriteError; - if (!write_metadata(slicer_metadata_block.raw_data)) - return EResult::WriteError; - if (!write_line("; prusaslicer_config = end\n\n")) - return EResult::WriteError; - - return EResult::Success; -} -#endif // ENABLE_FILE_CONVERSION_INTERFACE - -} // namespace bgcode - diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp deleted file mode 100644 index 2961fe548f..0000000000 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ /dev/null @@ -1,382 +0,0 @@ -#ifndef slic3r_GCode_GCodeBinarizer_hpp_ -#define slic3r_GCode_GCodeBinarizer_hpp_ - -#ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_DEBUG 1 -#endif // _WIN32 - -#define ENABLE_CHECKSUM_BLOCK 0 -#define ENABLE_FILE_CONVERSION_INTERFACE 1 - -#include -#include -#include -#include -#include - -namespace bgcode { - -static const std::array MAGIC{ 'G', 'C', 'D', 'E' }; -static const uint32_t VERSION = 1; - -enum class EResult : uint16_t -{ - Success, - ReadError, - WriteError, - InvalidMagicNumber, - InvalidVersionNumber, - InvalidChecksumType, - InvalidBlockType, - InvalidCompressionType, - InvalidMetadataEncodingType, - InvalidGCodeEncodingType, - DataCompressionError, - DataUncompressionError, - MetadataEncodingError, - MetadataDecodingError, - GCodeEncodingError, - GCodeDecodingError, - BlockNotFound, - InvalidChecksum, - InvalidThumbnailFormat, - InvalidThumbnailWidth, - InvalidThumbnailHeight, - InvalidThumbnailDataSize, - InvalidBinaryGCodeFile, - InvalidSequenceOfBlocks -}; - -// Returns a string description of the given result -extern std::string translate_result(EResult result); - -enum class EChecksumType : uint16_t -{ - None, - CRC32 -}; - -class Checksum -{ -public: - // Constructs a checksum of the given type. - // The checksum data are sized accordingly. - explicit Checksum(EChecksumType type); - - EChecksumType get_type() const; - - // Appends the given data to the cache and performs a checksum update if - // the size of the cache exceeds the max checksum cache size. - void append(const std::vector& data); - // Returns true if the given checksum is equal to this one - bool matches(Checksum& other); - - EResult write(FILE& file); - EResult read(FILE& file); - -private: - EChecksumType m_type; - std::vector m_cache; - std::vector m_checksum; - - void update(); -}; - -struct FileHeader -{ - uint32_t magic{ *(uint32_t*)(MAGIC.data()) }; - uint32_t version{ VERSION }; - uint16_t checksum_type{ (uint16_t)EChecksumType::None }; - - EResult write(FILE& file) const; - EResult read(FILE& file, const uint32_t* const max_version); -}; - -enum class EBlockType : uint16_t -{ -#if ENABLE_CHECKSUM_BLOCK - Checksum, -#endif // ENABLE_CHECKSUM_BLOCK - FileMetadata, - GCode, - SlicerMetadata, - PrinterMetadata, - PrintMetadata, - Thumbnail -}; - -enum class ECompressionType : uint16_t -{ - None, - Deflate, - Heatshrink_11_4, - Heatshrink_12_4, -}; - -struct BlockHeader -{ - uint16_t type{ 0 }; - uint16_t compression{ 0 }; - uint32_t uncompressed_size{ 0 }; - uint32_t compressed_size{ 0 }; - - // Updates the given checksum with the data of this BlockHeader - void update_checksum(Checksum& checksum) const; - - EResult write(FILE& file) const; - EResult read(FILE& file); -}; - -enum class EMetadataEncodingType : uint16_t -{ - INI, -}; - -struct BaseMetadataBlock -{ - // type of data encoding - uint16_t encoding_type{ 0 }; - // data in key/value form - std::vector> raw_data; - - // write block header and data in encoded format - EResult write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const; - // read block data in encoded format - EResult read_data(FILE& file, const BlockHeader& block_header); - - static size_t get_parameters_size() { return sizeof(encoding_type); } -}; - -struct FileMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -enum class EThumbnailFormat : uint16_t -{ - PNG, - JPG, - QOI -}; - -struct ThumbnailBlock -{ - uint16_t format{ 0 }; - uint16_t width{ 0 }; - uint16_t height{ 0 }; - std::vector data; - - // write block header and data - EResult write(FILE& file, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); - - static size_t get_parameters_size() { return sizeof(format) + sizeof(width) + sizeof(height); } - -private: - void update_checksum(Checksum& checksum) const; -}; - -struct PrinterMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -struct PrintMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -struct SlicerMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -enum class EGCodeEncodingType : uint16_t -{ - None, - MeatPack, - MeatPackComments -}; - -struct GCodeBlock -{ - uint16_t encoding_type{ 0 }; - std::string raw_data; - - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); - - static size_t get_parameters_size() { return sizeof(encoding_type); } -}; - -#if ENABLE_CHECKSUM_BLOCK -struct ChecksumBlock -{ - std::vector data; - - // write block header and data - EResult write(FILE& file) const; - // read block data - EResult read_data(FILE& file, const BlockHeader& block_header); -}; -#endif // ENABLE_CHECKSUM_BLOCK - -//===================================================================================================================================== -// -// PRUSASLICER INTERFACE -// -//===================================================================================================================================== - -struct BinaryData -{ - FileMetadataBlock file_metadata; - PrinterMetadataBlock printer_metadata; - std::vector thumbnails; - SlicerMetadataBlock slicer_metadata; - PrintMetadataBlock print_metadata; - - void reset() { - file_metadata.raw_data.clear(); - printer_metadata.raw_data.clear(); - thumbnails.clear(); - slicer_metadata.raw_data.clear(); - print_metadata.raw_data.clear(); - } -}; - -struct BinarizerConfig -{ - struct Compression - { - ECompressionType file_metadata{ ECompressionType::None }; - ECompressionType printer_metadata{ ECompressionType::None }; - ECompressionType print_metadata{ ECompressionType::None }; - ECompressionType slicer_metadata{ ECompressionType::None }; - ECompressionType gcode{ ECompressionType::None }; - }; - Compression compression; - EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; - EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; - EChecksumType checksum{ EChecksumType::CRC32 }; -}; - -class Binarizer -{ -public: - bool is_enabled() const { return m_enabled; } - void set_enabled(bool enable) { m_enabled = enable; } - - BinaryData& get_binary_data() { return m_binary_data; } - const BinaryData& get_binary_data() const { return m_binary_data; } - - EResult initialize(FILE& file, const BinarizerConfig& config); - EResult append_gcode(const std::string& gcode); - EResult finalize(); - -private: - bool m_enabled{ false }; - - BinarizerConfig m_config; - FILE* m_file{ nullptr }; - BinaryData m_binary_data; - std::string m_gcode_cache; -#if ENABLE_CHECKSUM_BLOCK - ChecksumBlock m_checksum; -#endif // ENABLE_CHECKSUM_BLOCK -}; - -//===================================================================================================================================== -// -// GCODEVIEWER INTERFACE -// FIRMWARE INTERFACE -// -//===================================================================================================================================== - -// Get the max size of the cache used to calculate checksums, in bytes -size_t get_checksum_max_cache_size(); -// Set the max size of the cache used to calculate checksums, in bytes -void set_checksum_max_cache_size(size_t size); - -// Returns true if the given file is a valid binary gcode -// Does not modify the file position -extern bool is_valid_binary_gcode(FILE& file); - -// Reads the file header. -// If max_version is not null, version is checked against the passed value -// If return == EResult::Success: -// - header will contain the file header -// - file position will be set at the start of the 1st block header -extern EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version); - -// Reads next block header from the current file position. -// File position must be at the start of a block header. -// If return == EResult::Success: -// - block_header will contain the header of the block -// - file position will be set at the start of the block parameters data -extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum); - -// Searches and reads next block header with the given type from the current file position. -// File position must be at the start of a block header. -// If return == EResult::Success: -// - block_header will contain the header of the block with the required type -// - file position will be set at the start of the block parameters data -// otherwise: -// - file position will keep the current value -extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum); - -// Skips the payload (parameters + data) of the block with the given block header. -// File position must be at the start of the block parameters. -// If return == EResult::Success: -// - file position will be set at the start of the block checksum, if present, or of next block header -extern EResult skip_block_payload(FILE& file, const BlockHeader& block_header); - -// Skips the content (parameters + data + checksum) of the block with the given block header. -// File position must be at the start of the block parameters. -// If return == EResult::Success: -// - file position will be set at the start of the next block header -extern EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); - -// Returns the size of the parameters of the given block type, in bytes. -extern size_t block_parameters_size(EBlockType type); - -// Returns the size of the payload (parameters + data) of the block with the given header, in bytes. -extern size_t block_payload_size(const BlockHeader& block_header); - -// Returns the size of the checksum of the given type, in bytes. -extern size_t checksum_size(EChecksumType type); - -// Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. -extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); - -#if ENABLE_FILE_CONVERSION_INTERFACE -//===================================================================================================================================== -// -// FILE CONVERSION INTERFACE -// -//===================================================================================================================================== - -// Converts the gcode file contained into src_file from ascii to binary format and save the results into dst_file -extern EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file); - -// Converts the gcode file contained into src_file from binary to ascii format and save the results into dst_file -extern EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum); -#endif // ENABLE_FILE_CONVERSION_INTERFACE - -} // namespace bgcode - -#endif // slic3r_GCode_GCodeBinarizer_hpp_ From ec16420f117c5792c212c4f9a4f571a7c9a7fb1c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 12:57:55 +0200 Subject: [PATCH 22/48] Integrated library libbgcode --- src/libslic3r/GCode.cpp | 12 +-- src/libslic3r/GCode.hpp | 7 +- src/libslic3r/GCode/GCodeProcessor.cpp | 141 ++++++++++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 12 +-- src/libslic3r/GCode/Thumbnails.hpp | 17 +-- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 18 ++-- src/slic3r/GUI/Plater.cpp | 10 +- 8 files changed, 112 insertions(+), 106 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 91771220ba..a037c0c483 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -841,7 +841,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); #if ENABLE_BINARIZED_GCODE - m_processor.get_binary_data().reset(); + m_processor.get_binary_data() = bgcode::base::BinaryData(); #endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) @@ -984,7 +984,7 @@ namespace DoExport { unsigned int initial_extruder_id, PrintStatistics &print_statistics, bool export_binary_data, - bgcode::BinaryData &binary_data) + bgcode::base::BinaryData &binary_data) #else static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, @@ -1132,7 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // 1) generate the thumbnails // 2) collect the config data if (export_to_binary_gcode) { - bgcode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. @@ -1146,11 +1146,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // file data - binary_data.file_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; + binary_data.file_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; + binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); // printer data @@ -1613,7 +1613,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(filament_stats_string_out); if (export_to_binary_gcode) { - bgcode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 7358c15058..1821576fd7 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -21,9 +21,6 @@ #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" #include "GCode/ThumbnailData.hpp" -#if ENABLE_BINARIZED_GCODE -#include "GCode/GCodeBinarizer.hpp" -#endif // ENABLE_BINARIZED_GCODE #include #include @@ -31,6 +28,10 @@ #include "GCode/PressureEqualizer.hpp" +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + namespace Slic3r { // Forward declarations. diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6c0fb8c2cf..6ebddec853 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -70,7 +70,7 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -bgcode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::base::BinarizerConfig GCodeProcessor::s_binarizer_config{}; #endif // ENABLE_BINARIZED #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1041,7 +1041,7 @@ void GCodeProcessor::process_file(const std::string& filename, std::function& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1174,15 +1174,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrinterMetadata) + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - bgcode::PrinterMetadataBlock printer_metadata_block; + bgcode::base::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1194,25 +1194,24 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read thumbnail blocks - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); - while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::Thumbnail) { - bgcode::ThumbnailBlock thumbnail_block; + while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { + bgcode::base::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const bgcode::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); - switch ((bgcode::EThumbnailFormat)block.format) + switch ((bgcode::core::EThumbnailFormat)block.format) { - case bgcode::EThumbnailFormat::PNG: { ret += ".png"; break; } - case bgcode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case bgcode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + case bgcode::core::EThumbnailFormat::PNG: { ret += ".png"; break; } + case bgcode::core::EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case bgcode::core::EThumbnailFormat::QOI: { ret += ".qoi"; break; } } return ret; }; @@ -1228,18 +1227,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); } // read print metadata block - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrintMetadata) + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - bgcode::PrintMetadataBlock print_metadata_block; + bgcode::base::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1251,15 +1250,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read slicer metadata block - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::SlicerMetadata) + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - bgcode::SlicerMetadataBlock slicer_metadata_block; + bgcode::base::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1286,16 +1285,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::GCode) + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); - while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::GCode) { - bgcode::GCodeBlock block; + while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { + bgcode::base::GCodeBlock block; res = block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); // TODO: Update m_result.lines_ends @@ -1306,9 +1305,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file) == file_size) break; - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); } // Don't post-process the G-code to update time stamps. @@ -3696,7 +3695,7 @@ void GCodeProcessor::post_process() }; // update binary data - bgcode::BinaryData& binary_data = m_binarizer.get_binary_data(); + bgcode::base::BinaryData& binary_data = m_binarizer.get_binary_data(); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); @@ -3720,8 +3719,8 @@ void GCodeProcessor::post_process() } } - const bgcode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != bgcode::EResult::Success) + const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -3843,12 +3842,12 @@ void GCodeProcessor::post_process() size_t m_out_file_pos{ 0 }; #if ENABLE_BINARIZED_GCODE - bgcode::Binarizer& m_binarizer; + bgcode::base::Binarizer& m_binarizer; #endif // ENABLE_BINARIZED_GCODE public: #if ENABLE_BINARIZED_GCODE - ExportLines(bgcode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) + ExportLines(bgcode::base::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else @@ -3974,8 +3973,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::EResult::Success) + bgcode::core::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4011,8 +4010,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::EResult::Success) + bgcode::core::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4391,8 +4390,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - const bgcode::EResult res = m_binarizer.finalize(); - if (res != bgcode::EResult::Success) + const bgcode::core::EResult res = m_binarizer.finalize(); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index b3390ed1c6..303a6868d1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -8,7 +8,7 @@ #include "libslic3r/CustomGCode.hpp" #if ENABLE_BINARIZED_GCODE -#include "GCodeBinarizer.hpp" +#include #endif // ENABLE_BINARIZED_GCODE #include @@ -528,14 +528,14 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW - static bgcode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + static bgcode::base::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } #endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE - bgcode::Binarizer m_binarizer; - static bgcode::BinarizerConfig s_binarizer_config; + bgcode::base::Binarizer m_binarizer; + static bgcode::base::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; @@ -635,8 +635,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } #if ENABLE_BINARIZED_GCODE - bgcode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } - const bgcode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } + bgcode::base::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::base::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } #endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 6fd28af333..7e99013962 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -4,14 +4,15 @@ #include "../Point.hpp" #include "../PrintConfig.hpp" #include "ThumbnailData.hpp" -#if ENABLE_BINARIZED_GCODE -#include "GCode/GCodeBinarizer.hpp" -#endif // ENABLE_BINARIZED_GCODE #include #include #include +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + #include namespace Slic3r::GCodeThumbnails { @@ -60,7 +61,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, #if ENABLE_BINARIZED_GCODE template -inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { out_thumbnails.clear(); @@ -70,13 +71,13 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - bgcode::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::ThumbnailBlock()); + bgcode::base::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::base::ThumbnailBlock()); block.width = (uint16_t)data.width; block.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 6af385e71a..db25b587f7 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,6 +69,7 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) +#define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index be81ea7f1f..b609795fd2 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7874,7 +7874,7 @@ void GLCanvas3D::GizmoHighlighter::blink() #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW void GLCanvas3D::show_binary_gcode_debug_window() { - bgcode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -7888,7 +7888,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.file_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.file_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7896,7 +7896,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.printer_metadata; if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.printer_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.printer_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7904,7 +7904,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.print_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.print_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7912,7 +7912,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.slicer_metadata; if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.slicer_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.slicer_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7920,7 +7920,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.gcode; if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.gcode = (bgcode::ECompressionType)option_id; + binarizer_config.compression.gcode = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7929,7 +7929,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "MeatPack", "MeatPack Comments" }; option_id = (int)binarizer_config.gcode_encoding; if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.gcode_encoding = (bgcode::EGCodeEncodingType)option_id; + binarizer_config.gcode_encoding = (bgcode::core::EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7938,7 +7938,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "INI" }; option_id = (int)binarizer_config.metadata_encoding; if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.metadata_encoding = (bgcode::EMetadataEncodingType)option_id; + binarizer_config.metadata_encoding = (bgcode::core::EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7947,7 +7947,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "CRC32" }; option_id = (int)binarizer_config.checksum; if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.checksum = (bgcode::EChecksumType)option_id; + binarizer_config.checksum = (bgcode::core::EChecksumType)option_id; ImGui::EndTable(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 20d6835b16..f23413270c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -36,6 +36,10 @@ #include #endif +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" @@ -5472,9 +5476,9 @@ void Plater::convert_gcode_to_ascii() // Perform conversion { wxBusyCursor busy; - bgcode::EResult res = bgcode::from_binary_to_ascii(*in_file, *out_file, true); - if (res != bgcode::EResult::Success) { - MessageDialog msg_dlg(this, _L(bgcode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file, *out_file, true); + if (res != bgcode::core::EResult::Success) { + MessageDialog msg_dlg(this, _L(bgcode::core::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); scoped_out_file.unscope(); fclose(out_file); From cd43a8e7448a4dd85452d80df3980d5034eb86c4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 15:31:13 +0200 Subject: [PATCH 23/48] Fixes required by changes into libbgcode library interface --- src/libslic3r/GCode/GCodeProcessor.cpp | 29 +++++++++++++------------- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6ebddec853..66f7cc297a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1151,7 +1151,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct bgcode::core::FileHeader file_header; bgcode::core::EResult res = bgcode::core::read_header(*file, file_header, nullptr); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + + std::string(bgcode::core::translate_result(res)) + "\n"); const bool verify_checksum = true; @@ -1159,13 +1160,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct bgcode::core::BlockHeader block_header; res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); bgcode::base::FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1176,13 +1177,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read printer metadata block res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); bgcode::base::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1196,13 +1197,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read thumbnail blocks res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { bgcode::base::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { @@ -1229,7 +1230,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); } // read print metadata block @@ -1238,7 +1239,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct bgcode::base::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1252,13 +1253,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read slicer metadata block res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); bgcode::base::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1287,14 +1288,14 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read gcodes block res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { bgcode::base::GCodeBlock block; res = block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); // TODO: Update m_result.lines_ends @@ -1307,7 +1308,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); } // Don't post-process the G-code to update time stamps. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f23413270c..c41b20675a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5478,7 +5478,7 @@ void Plater::convert_gcode_to_ascii() wxBusyCursor busy; bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file, *out_file, true); if (res != bgcode::core::EResult::Success) { - MessageDialog msg_dlg(this, _L(bgcode::core::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); scoped_out_file.unscope(); fclose(out_file); From 1e993a481aa64a2d0e42f6491d3bc78fcb98db0b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 15:44:04 +0200 Subject: [PATCH 24/48] Implemented command 'Convert ascii G-code to binary' --- src/slic3r/GUI/Plater.cpp | 72 +++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c41b20675a..691a597362 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5435,22 +5435,22 @@ void Plater::reload_gcode_from_disk() } #if ENABLE_BINARIZED_GCODE +class ScopedFile +{ +public: + explicit ScopedFile(FILE* file) : m_file(file) {} + ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } + void unscope() { m_file = nullptr; } +private: + FILE* m_file{ nullptr }; +}; + void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. wxString input_file; wxGetApp().load_gcode(this, input_file); - class ScopedFile - { - public: - explicit ScopedFile(FILE* file) : m_file(file) {} - ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } - void unscope() { m_file = nullptr; } - private: - FILE* m_file{ nullptr }; - }; - // Open source file FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); if (in_file == nullptr) { @@ -5493,7 +5493,57 @@ void Plater::convert_gcode_to_ascii() void Plater::convert_gcode_to_binary() { - MessageDialog msg_dlg(this, _L("Not implemented yet."), _L("Error"), wxICON_ERROR | wxOK); + // Ask user for a gcode file name. + wxString input_file; + wxGetApp().load_gcode(this, input_file); + + // Open source file + FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); + if (in_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_in_file(in_file); + + // Set out filename + boost::filesystem::path path(into_u8(input_file)); + const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_binary" + path.extension().string(); + + // Open destination file + FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); + if (out_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_out_file(out_file); + + // Perform conversion + { + wxBusyCursor busy; + // TODO: allow custommization of config + bgcode::base::BinarizerConfig config; + config.checksum = bgcode::core::EChecksumType::CRC32; + config.compression.file_metadata = bgcode::core::ECompressionType::None; + config.compression.print_metadata = bgcode::core::ECompressionType::None; + config.compression.printer_metadata = bgcode::core::ECompressionType::None; + config.compression.slicer_metadata = bgcode::core::ECompressionType::Deflate; + config.compression.gcode = bgcode::core::ECompressionType::Heatshrink_12_4; + config.gcode_encoding = bgcode::core::EGCodeEncodingType::MeatPackComments; + config.metadata_encoding = bgcode::core::EMetadataEncodingType::INI; + bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, config); + if (res != bgcode::core::EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + msg_dlg.ShowModal(); + scoped_out_file.unscope(); + fclose(out_file); + boost::nowide::remove(output_file.c_str()); + return; + } + } + + MessageDialog msg_dlg(this, _L("Succesfully created gcode binary file:\n") + output_file, _L("Convert gcode file to binary format"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); } #endif // ENABLE_BINARIZED_GCODE From 28346f78ddfae943cc1c4cd5b2695e702302e62c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 08:33:28 +0200 Subject: [PATCH 25/48] Fixed naming of metadata exported to gcode file in binary format --- src/libslic3r/GCode.cpp | 8 +++----- src/slic3r/GUI/Plater.cpp | 13 ++----------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a037c0c483..0740ffb415 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1146,29 +1146,27 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // file data - binary_data.file_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); // printer data - binary_data.printer_metadata.raw_data.emplace_back("printer model" , print.config().printer_model.value); // duplicated into config data + binary_data.printer_metadata.raw_data.emplace_back("printer_model", print.config().printer_model.value); // duplicated into config data std::string filament_types_str; for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { filament_types_str += print.config().filament_type.values[i]; if (i < print.config().filament_type.values.size() - 1) filament_types_str += ", "; } - binary_data.printer_metadata.raw_data.emplace_back("filament type", filament_types_str); // duplicated into config data + binary_data.printer_metadata.raw_data.emplace_back("filament_type", filament_types_str); // duplicated into config data std::string nozzle_diameters_str; char buf[1024]; for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2lf, " : "%.2lf", print.config().nozzle_diameter.values[i]); nozzle_diameters_str += buf; } - binary_data.printer_metadata.raw_data.emplace_back("nozzle diameter", nozzle_diameters_str); // duplicated into config data + binary_data.printer_metadata.raw_data.emplace_back("nozzle_diameter", nozzle_diameters_str); // duplicated into config data } // modifies m_silent_time_estimator_enabled diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 691a597362..e0c1527e84 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5522,17 +5522,8 @@ void Plater::convert_gcode_to_binary() // Perform conversion { wxBusyCursor busy; - // TODO: allow custommization of config - bgcode::base::BinarizerConfig config; - config.checksum = bgcode::core::EChecksumType::CRC32; - config.compression.file_metadata = bgcode::core::ECompressionType::None; - config.compression.print_metadata = bgcode::core::ECompressionType::None; - config.compression.printer_metadata = bgcode::core::ECompressionType::None; - config.compression.slicer_metadata = bgcode::core::ECompressionType::Deflate; - config.compression.gcode = bgcode::core::ECompressionType::Heatshrink_12_4; - config.gcode_encoding = bgcode::core::EGCodeEncodingType::MeatPackComments; - config.metadata_encoding = bgcode::core::EMetadataEncodingType::INI; - bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, config); + const bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, binarizer_config); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); From 7ab044fb861ca2961172472e1d6d6e3e50f6c5f1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 3 Aug 2023 13:00:41 +0200 Subject: [PATCH 26/48] Add debug build for LibBGCode in deps Probably no need for debug build of it's respective heatshrink dependency, as it's a C only lib --- deps/LibBGCode/LibBGCode.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index cfdcd009bd..d0c6ac6834 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -22,4 +22,8 @@ prusaslicer_add_cmake_project(LibBGCode DEPENDS dep_LibBGCode_deps CMAKE_ARGS -DLibBGCode_BUILD_TESTS:BOOL=OFF -) \ No newline at end of file +) + +if (MSVC) + add_debug_dep(dep_LibBGCode) +endif () \ No newline at end of file From 87f6fed2746095ca2096ce8b5d46b010956df8c1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 10:16:37 +0200 Subject: [PATCH 27/48] Fixed link of RelWithDebInfo configuration --- src/libslic3r/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index f11986d314..ce57ba00f2 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -15,6 +15,9 @@ if (TARGET OpenVDB::openvdb) endif() find_package(LibBGCode REQUIRED COMPONENTS Convert) +slic3r_remap_configs(LibBGCode::bgcode_core RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_base RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_convert RelWithDebInfo Release) set(SLIC3R_SOURCES pchheader.cpp From eb8d01888db68da9bce82ebe324f148f43ec35cb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 12:35:40 +0200 Subject: [PATCH 28/48] Added import of config from binary gcode files --- src/libslic3r/Config.cpp | 100 ++++++++++++++++++++++--- src/libslic3r/Config.hpp | 5 +- src/libslic3r/GCode/GCodeProcessor.cpp | 77 ++++++++----------- src/libslic3r/PresetBundle.cpp | 33 ++++++-- src/slic3r/GUI/Plater.cpp | 40 +++------- 5 files changed, 164 insertions(+), 91 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 51870e93d7..af28307faf 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -23,6 +23,10 @@ #include #include +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + //FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion) // This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy(). #include "PrintConfig.hpp" @@ -720,11 +724,41 @@ void ConfigBase::setenv_() const } } -ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { - return is_gcode_file(file) ? - this->load_from_gcode_file(file, compatibility_rule) : - this->load_from_ini(file, compatibility_rule); +#if ENABLE_BINARIZED_GCODE + enum class EFileType + { + Ini, + AsciiGCode, + BinaryGCode + }; + + EFileType file_type; + + if (is_gcode_file(filename)) { + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + + file_type = (bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + fclose(file); + } + else + file_type = EFileType::Ini; + + switch (file_type) + { + case EFileType::Ini: { return this->load_from_ini(filename, compatibility_rule); } + case EFileType::AsciiGCode: { return this->load_from_gcode_file(filename, compatibility_rule);} + case EFileType::BinaryGCode: { return this->load_from_binary_gcode_file(filename, compatibility_rule);} + default: { throw Slic3r::RuntimeError("Invalid file: " + filename + "\n"); } + } +#else + return is_gcode_file(filename) ? + this->load_from_gcode_file(filename, compatibility_rule) : + this->load_from_ini(filename, compatibility_rule); +#endif // ENABLE_BINARIZED_GCODE } ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -925,15 +959,15 @@ private: }; // Load the config keys from the tail of a G-code file. -ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { // Read a 64k block from the end of the G-code. - boost::nowide::ifstream ifs(file, std::ifstream::binary); + boost::nowide::ifstream ifs(filename, std::ifstream::binary); // Look for Slic3r or PrusaSlicer header. // Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user. bool has_delimiters = false; { - static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; + static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; static constexpr const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; std::string header; bool header_found = false; @@ -983,7 +1017,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo break; } if (! end_found) - throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", file)); + throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", filename)); std::string key, value; while (reader.getline(line)) { if (line == "; prusaslicer_config = begin") { @@ -1006,7 +1040,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo } } if (! begin_found) - throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", file)); + throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", filename)); } else { @@ -1014,8 +1048,8 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo // Try a heuristics reading the G-code from back. ifs.seekg(0, ifs.end); auto file_length = ifs.tellg(); - auto data_length = std::min(65535, file_length - header_end_pos); - ifs.seekg(file_length - data_length, ifs.beg); + auto data_length = std::min(65535, file_length - header_end_pos); + ifs.seekg(file_length - data_length, ifs.beg); std::vector data(size_t(data_length) + 1, 0); ifs.read(data.data(), data_length); ifs.close(); @@ -1023,10 +1057,52 @@ 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)); + throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs)); return std::move(substitutions_ctxt.substitutions); } +#if ENABLE_BINARIZED_GCODE +ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); + + FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (file.f == nullptr) + throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); + + bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", + std::string(bgcode::core::translate_result(res)))); + + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + bgcode::core::FileHeader file_header; + res = bgcode::core::read_header(*file.f, file_header, nullptr); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + + bgcode::core::BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, true); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) + throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); + bgcode::base::SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(*file.f, file_header, block_header); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + this->set_deserialize(key, value, substitutions_ctxt); + } + + return std::move(substitutions_ctxt.substitutions); +} +#endif // ENABLE_BINARIZED_GCODE + void ConfigBase::save(const std::string &file) const { boost::nowide::ofstream c; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 28410b87dc..4995384203 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2303,7 +2303,10 @@ public: // Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF. // Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment). ConfigSubstitutions load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule); - ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); + ConfigSubstitutions load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule); +#if ENABLE_BINARIZED_GCODE + ConfigSubstitutions load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule); +#endif // ENABLE_BINARIZED_GCODE ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule); void save(const std::string &file) const; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 66f7cc297a..37ac7eb37a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1122,34 +1122,23 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi #if ENABLE_BINARIZED_GCODE void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) { - class ScopedFile - { - public: - explicit ScopedFile(FILE* file) : m_file(file) {} - ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } - private: - FILE* m_file{ nullptr }; - }; - #if ENABLE_GCODE_VIEWER_STATISTICS m_start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS - FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); - if (file == nullptr) + FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (file.f == nullptr) throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); - fseek(file, 0, SEEK_END); - const long file_size = ftell(file); - rewind(file); - - ScopedFile scoped_file(file); + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); bgcode::core::set_checksum_max_cache_size(1024); // read file header bgcode::core::FileHeader file_header; - bgcode::core::EResult res = bgcode::core::read_header(*file, file_header, nullptr); + bgcode::core::EResult res = bgcode::core::read_header(*file.f, file_header, nullptr); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1158,15 +1147,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read file metadata block bgcode::core::BlockHeader block_header; - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); bgcode::base::FileMetadataBlock file_metadata_block; - res = file_metadata_block.read_data(*file, file_header, block_header); + res = file_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1175,15 +1164,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); bgcode::base::PrinterMetadataBlock printer_metadata_block; - res = printer_metadata_block.read_data(*file, file_header, block_header); + res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1195,15 +1184,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read thumbnail blocks - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { bgcode::base::ThumbnailBlock thumbnail_block; - res = thumbnail_block.read_data(*file, file_header, block_header); + res = thumbnail_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { @@ -1228,18 +1217,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } // read print metadata block if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); bgcode::base::PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(*file, file_header, block_header); + res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1251,15 +1240,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read slicer metadata block - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); bgcode::base::SlicerMetadataBlock slicer_metadata_block; - res = slicer_metadata_block.read_data(*file, file_header, block_header); + res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1286,16 +1275,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { bgcode::base::GCodeBlock block; - res = block.read_data(*file, file_header, block_header); + res = block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); // TODO: Update m_result.lines_ends @@ -1303,12 +1292,12 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct this->process_gcode_line(line, true); }); - if (ftell(file) == file_size) + if (ftell(file.f) == file_size) break; - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } // Don't post-process the G-code to update time stamps. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 730574af13..45e4d0535f 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -22,6 +22,9 @@ #include #include +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions. @@ -875,14 +878,32 @@ DynamicPrintConfig PresetBundle::full_sla_config() const // If the file is loaded successfully, its print / filament / printer profiles will be activated. ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, ForwardCompatibilitySubstitutionRule compatibility_rule) { - if (is_gcode_file(path)) { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); +#if ENABLE_BINARIZED_GCODE + if (is_gcode_file(path)) { + FILE* file = boost::nowide::fopen(path.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Error opening the file: " + path + "\n"); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success; + fclose(file); + + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + ConfigSubstitutions config_substitutions = is_binary ? config.load_from_binary_gcode_file(path, compatibility_rule) : + config.load_from_gcode_file(path, compatibility_rule); + Preset::normalize(config); + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } +#else + if (is_gcode_file(path)) { + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, compatibility_rule); Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return config_substitutions; - } + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } +#endif // ENABLE_BINARIZED_GCODE // 1) Try to load the config file into a boost property tree. boost::property_tree::ptree tree; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e0c1527e84..b9eb5a3c1c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5435,16 +5435,6 @@ void Plater::reload_gcode_from_disk() } #if ENABLE_BINARIZED_GCODE -class ScopedFile -{ -public: - explicit ScopedFile(FILE* file) : m_file(file) {} - ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } - void unscope() { m_file = nullptr; } -private: - FILE* m_file{ nullptr }; -}; - void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. @@ -5452,36 +5442,33 @@ void Plater::convert_gcode_to_ascii() wxGetApp().load_gcode(this, input_file); // Open source file - FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); - if (in_file == nullptr) { + FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; + if (in_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_in_file(in_file); // Set out filename boost::filesystem::path path(into_u8(input_file)); const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_ascii" + path.extension().string(); // Open destination file - FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); - if (out_file == nullptr) { + FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; + if (out_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_out_file(out_file); // Perform conversion { wxBusyCursor busy; - bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file, *out_file, true); + bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); - scoped_out_file.unscope(); - fclose(out_file); + out_file.close(); boost::nowide::remove(output_file.c_str()); return; } @@ -5498,37 +5485,34 @@ void Plater::convert_gcode_to_binary() wxGetApp().load_gcode(this, input_file); // Open source file - FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); - if (in_file == nullptr) { + FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; + if (in_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_in_file(in_file); // Set out filename boost::filesystem::path path(into_u8(input_file)); const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_binary" + path.extension().string(); // Open destination file - FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); - if (out_file == nullptr) { + FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; + if (out_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_out_file(out_file); // Perform conversion { wxBusyCursor busy; const bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); - bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, binarizer_config); + bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); - scoped_out_file.unscope(); - fclose(out_file); + out_file.close(); boost::nowide::remove(output_file.c_str()); return; } From 5deb4470c7ed8e0709809f98525082965ba700d8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 14:44:45 +0200 Subject: [PATCH 29/48] Changes required by updated library libbgcode --- src/libslic3r/CMakeLists.txt | 2 +- src/libslic3r/Config.cpp | 4 ++-- src/libslic3r/GCode.cpp | 8 ++++---- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/GCode/GCodeProcessor.cpp | 22 +++++++++++----------- src/libslic3r/GCode/GCodeProcessor.hpp | 12 ++++++------ src/libslic3r/GCode/Thumbnails.hpp | 6 +++--- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ce57ba00f2..de4ec3a5d7 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -16,7 +16,7 @@ endif() find_package(LibBGCode REQUIRED COMPONENTS Convert) slic3r_remap_configs(LibBGCode::bgcode_core RelWithDebInfo Release) -slic3r_remap_configs(LibBGCode::bgcode_base RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_binarize RelWithDebInfo Release) slic3r_remap_configs(LibBGCode::bgcode_convert RelWithDebInfo Release) set(SLIC3R_SOURCES diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index af28307faf..5d2de1aaa5 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -24,7 +24,7 @@ #include #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE //FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion) @@ -1090,7 +1090,7 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); - bgcode::base::SlicerMetadataBlock slicer_metadata_block; + bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0740ffb415..d10cd6bb59 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -841,7 +841,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); #if ENABLE_BINARIZED_GCODE - m_processor.get_binary_data() = bgcode::base::BinaryData(); + m_processor.get_binary_data() = bgcode::binarize::BinaryData(); #endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) @@ -984,7 +984,7 @@ namespace DoExport { unsigned int initial_extruder_id, PrintStatistics &print_statistics, bool export_binary_data, - bgcode::base::BinaryData &binary_data) + bgcode::binarize::BinaryData &binary_data) #else static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, @@ -1132,7 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // 1) generate the thumbnails // 2) collect the config data if (export_to_binary_gcode) { - bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. @@ -1611,7 +1611,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(filament_stats_string_out); if (export_to_binary_gcode) { - bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1821576fd7..4032aef39e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -29,7 +29,7 @@ #include "GCode/PressureEqualizer.hpp" #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE namespace Slic3r { diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 37ac7eb37a..5787b6e17d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -70,7 +70,7 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -bgcode::base::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{}; #endif // ENABLE_BINARIZED #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1152,7 +1152,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); - bgcode::base::FileMetadataBlock file_metadata_block; + bgcode::binarize::FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1169,7 +1169,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - bgcode::base::PrinterMetadataBlock printer_metadata_block; + bgcode::binarize::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1189,13 +1189,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { - bgcode::base::ThumbnailBlock thumbnail_block; + bgcode::binarize::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const bgcode::binarize::ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); switch ((bgcode::core::EThumbnailFormat)block.format) { @@ -1225,7 +1225,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read print metadata block if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - bgcode::base::PrintMetadataBlock print_metadata_block; + bgcode::binarize::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1245,7 +1245,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - bgcode::base::SlicerMetadataBlock slicer_metadata_block; + bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1281,7 +1281,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { - bgcode::base::GCodeBlock block; + bgcode::binarize::GCodeBlock block; res = block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -3685,7 +3685,7 @@ void GCodeProcessor::post_process() }; // update binary data - bgcode::base::BinaryData& binary_data = m_binarizer.get_binary_data(); + bgcode::binarize::BinaryData& binary_data = m_binarizer.get_binary_data(); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); @@ -3832,12 +3832,12 @@ void GCodeProcessor::post_process() size_t m_out_file_pos{ 0 }; #if ENABLE_BINARIZED_GCODE - bgcode::base::Binarizer& m_binarizer; + bgcode::binarize::Binarizer& m_binarizer; #endif // ENABLE_BINARIZED_GCODE public: #if ENABLE_BINARIZED_GCODE - ExportLines(bgcode::base::Binarizer& binarizer, EWriteType type, TimeMachine& machine) + ExportLines(bgcode::binarize::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 303a6868d1..bef1cd68f3 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -8,7 +8,7 @@ #include "libslic3r/CustomGCode.hpp" #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE #include @@ -528,14 +528,14 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW - static bgcode::base::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + static bgcode::binarize::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } #endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE - bgcode::base::Binarizer m_binarizer; - static bgcode::base::BinarizerConfig s_binarizer_config; + bgcode::binarize::Binarizer m_binarizer; + static bgcode::binarize::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; @@ -635,8 +635,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } #if ENABLE_BINARIZED_GCODE - bgcode::base::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } - const bgcode::base::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } + bgcode::binarize::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::binarize::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } #endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 7e99013962..4bd6637879 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -10,7 +10,7 @@ #include #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE #include @@ -61,7 +61,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, #if ENABLE_BINARIZED_GCODE template -inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { out_thumbnails.clear(); @@ -71,7 +71,7 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - bgcode::base::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::base::ThumbnailBlock()); + bgcode::binarize::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::binarize::ThumbnailBlock()); block.width = (uint16_t)data.width; block.height = (uint16_t)data.height; switch (format) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b609795fd2..c4a8d093df 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7874,7 +7874,7 @@ void GLCanvas3D::GizmoHighlighter::blink() #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW void GLCanvas3D::show_binary_gcode_debug_window() { - bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b9eb5a3c1c..ddab7d6c32 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5507,7 +5507,7 @@ void Plater::convert_gcode_to_binary() // Perform conversion { wxBusyCursor busy; - const bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + const bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); From 7b822184a7a124cba970a17e709d5f1825c3454d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 7 Aug 2023 09:20:50 +0200 Subject: [PATCH 30/48] Fixes in Plater::convert_gcode_to_ascii() and Plater::convert_gcode_to_binary() --- src/slic3r/GUI/Plater.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ddab7d6c32..693ba0e5fa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5440,6 +5440,8 @@ void Plater::convert_gcode_to_ascii() // Ask user for a gcode file name. wxString input_file; wxGetApp().load_gcode(this, input_file); + if (input_file.empty()) + return; // Open source file FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; @@ -5483,6 +5485,8 @@ void Plater::convert_gcode_to_binary() // Ask user for a gcode file name. wxString input_file; wxGetApp().load_gcode(this, input_file); + if (input_file.empty()) + return; // Open source file FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; From 57df182d6cdd79a6b6a98e73775b4ed52a56a58d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 7 Aug 2023 12:17:13 +0200 Subject: [PATCH 31/48] GCodeViewer: Fixed crash when selecting Layer time view after importing a binary gcode encoded using MeatPack with no comments --- src/slic3r/GUI/GCodeViewer.cpp | 50 ++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ed4713737f..fd9a44db3d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2309,20 +2309,26 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::LayerTimeLinear: case EViewType::LayerTimeLogarithmic: { - const Path::Sub_Path& sub_path = path.sub_paths.front(); - double z = static_cast(sub_path.first.position.z()); - const std::vector& zs = m_layers.get_zs(); - const std::vector& ranges = m_layers.get_ranges(); - size_t time_mode_id = static_cast(m_time_estimate_mode); - for (size_t i = 0; i < zs.size(); ++i) { - if (std::abs(zs[i] - z) < EPSILON) { - if (ranges[i].contains(sub_path.first.s_id)) { - color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], - (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); - break; +#if ENABLE_BINARIZED_GCODE + if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { +#endif // ENABLE_BINARIZED_GCODE + const Path::Sub_Path& sub_path = path.sub_paths.front(); + double z = static_cast(sub_path.first.position.z()); + const std::vector& zs = m_layers.get_zs(); + const std::vector& ranges = m_layers.get_ranges(); + size_t time_mode_id = static_cast(m_time_estimate_mode); + for (size_t i = 0; i < zs.size(); ++i) { + if (std::abs(zs[i] - z) < EPSILON) { + if (ranges[i].contains(sub_path.first.s_id)) { + color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], + (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); + break; + } } } +#if ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } @@ -3590,6 +3596,27 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f }); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f }); +#if ENABLE_BINARIZED_GCODE + std::vector view_options; + std::vector view_options_id; + if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { + view_options = { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), _u8L("Speed (mm/s)"), _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), _u8L("Volumetric flow rate (mm³/s)"), _u8L("Layer time (linear)"), _u8L("Layer time (logarithmic)"), + _u8L("Tool"), _u8L("Color Print") }; + view_options_id = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + } + else { + view_options = { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), _u8L("Speed (mm/s)"), _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), _u8L("Volumetric flow rate (mm³/s)"), _u8L("Tool"), _u8L("Color Print") }; + view_options_id = { 0, 1, 2, 3, 4, 5, 6, 9, 10 }; + if (view_type == 7 || view_type == 8) + view_type = 0; + } + auto view_type_it = std::find(view_options_id.begin(), view_options_id.end(), view_type); + int view_type_id = (view_type_it == view_options_id.end()) ? 0 : std::distance(view_options_id.begin(), view_type_it); + if (imgui.combo(std::string(), view_options, view_type_id, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f)) + view_type = view_options_id[view_type_id]; +#else imgui.combo(std::string(), { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), @@ -3601,6 +3628,7 @@ void GCodeViewer::render_legend(float& legend_height) _u8L("Layer time (logarithmic)"), _u8L("Tool"), _u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f); +#endif // ENABLE_BINARIZED_GCODE ImGui::PopStyleColor(2); if (old_view_type != view_type) { From e1724e1fa153884c5315b4005263d39eaeeab3dd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Aug 2023 12:42:26 +0200 Subject: [PATCH 32/48] Fixes required by changes in interface of libbgcode --- src/libslic3r/GCode/GCodeProcessor.cpp | 4 ++-- src/libslic3r/GCode/Thumbnails.hpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 5787b6e17d..7fa5d5382c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1196,8 +1196,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { auto format_filename = [](const std::string& stem, const bgcode::binarize::ThumbnailBlock& block) { - std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); - switch ((bgcode::core::EThumbnailFormat)block.format) + std::string ret = stem + "_" + std::to_string(block.params.width) + "x" + std::to_string(block.params.height); + switch ((bgcode::core::EThumbnailFormat)block.params.format) { case bgcode::core::EThumbnailFormat::PNG: { ret += ".png"; break; } case bgcode::core::EThumbnailFormat::JPG: { ret += ".jpg"; break; } diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 4bd6637879..c430d695c2 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -72,12 +72,12 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { bgcode::binarize::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::binarize::ThumbnailBlock()); - block.width = (uint16_t)data.width; - block.height = (uint16_t)data.height; + block.params.width = (uint16_t)data.width; + block.params.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); From 7b9982bcfebf96e87cd5fdcf4ec5f26365959e06 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 10 Aug 2023 09:15:51 +0200 Subject: [PATCH 33/48] Disable building of the cmd tool for libbgcode Needs nowide, problematic integration, we would need to get rid of boost::nowide inside PS codebase --- deps/LibBGCode/LibBGCode.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index d0c6ac6834..6a076e9f67 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -22,6 +22,7 @@ prusaslicer_add_cmake_project(LibBGCode DEPENDS dep_LibBGCode_deps CMAKE_ARGS -DLibBGCode_BUILD_TESTS:BOOL=OFF + -DLibBGCode_BUILD_CMD_TOOL:BOOL=OFF ) if (MSVC) From 6b1dc90075adc98c32955aec55bd542a1cee6a33 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Aug 2023 10:30:28 +0200 Subject: [PATCH 34/48] Added new printer metadata for binary gcode files --- src/libslic3r/GCode.cpp | 57 ++++++++++++++++++++++++-- src/libslic3r/GCode/GCodeProcessor.cpp | 24 ++++++----- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d10cd6bb59..b12b0508e7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1157,16 +1157,62 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { filament_types_str += print.config().filament_type.values[i]; if (i < print.config().filament_type.values.size() - 1) - filament_types_str += ", "; + filament_types_str += ";"; } binary_data.printer_metadata.raw_data.emplace_back("filament_type", filament_types_str); // duplicated into config data - std::string nozzle_diameters_str; char buf[1024]; + std::string nozzle_diameters_str; for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { - sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2lf, " : "%.2lf", print.config().nozzle_diameter.values[i]); + sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2g," : "%.2g", print.config().nozzle_diameter.values[i]); nozzle_diameters_str += buf; } binary_data.printer_metadata.raw_data.emplace_back("nozzle_diameter", nozzle_diameters_str); // duplicated into config data + std::string bed_temperatures_str; + for (size_t i = 0; i < print.config().bed_temperature.values.size(); ++i) { + sprintf(buf, i < print.config().bed_temperature.values.size() - 1 ? "%d," : "%d", print.config().bed_temperature.values[i]); + bed_temperatures_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("bed_temperature", bed_temperatures_str); // duplicated into config data + + const DynamicPrintConfig& cfg = print.full_print_config(); + if (auto opt = cfg.option("brim_width"); opt != nullptr) { + sprintf(buf, "%.2g", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("brim_width", buf); // duplicated into config data + } + if (auto opt = cfg.option("fill_density"); opt != nullptr) { + sprintf(buf, "%.2g%%", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("fill_density", buf); // duplicated into config data + } + if (auto opt = cfg.option("layer_height"); opt != nullptr) { + sprintf(buf, "%.2g", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("layer_height", buf); // duplicated into config data + } + if (auto opt = cfg.option("temperature"); opt != nullptr) { + auto values = dynamic_cast(opt)->values; + std::string temperatures_str; + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%d," : "%d", values[i]); + temperatures_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("temperature", temperatures_str); // duplicated into config data + } + if (auto opt = cfg.option("ironing"); opt != nullptr) + binary_data.printer_metadata.raw_data.emplace_back("ironing", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data + if (auto opt = cfg.option("support_material"); opt != nullptr) + binary_data.printer_metadata.raw_data.emplace_back("support_material", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data + if (auto opt = cfg.option("extruder_colour"); opt != nullptr) { + auto values = dynamic_cast(opt)->values; + std::string extruder_colours_str; + if (values.size() == 1 && values.front().empty()) + extruder_colours_str = "\"\""; + else { + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%s;" : "%s", values[i].c_str()); + extruder_colours_str += buf; + } + } + binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data + } } // modifies m_silent_time_estimator_enabled @@ -1613,7 +1659,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (export_to_binary_gcode) { bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) - binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); + binary_data.print_metadata.raw_data.emplace_back("total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges)); + char buf[1024]; + sprintf(buf, "%.2lf", m_max_layer_z); + binary_data.printer_metadata.raw_data.emplace_back("max_layer_z", buf); } else { #else diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7fa5d5382c..d0c92af8c7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3686,15 +3686,17 @@ void GCodeProcessor::post_process() // update binary data bgcode::binarize::BinaryData& binary_data = m_binarizer.get_binary_data(); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, stringify(filament_cost) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g }) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost }) }); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedMm, stringify(filament_mm)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedCm3, stringify(filament_cm3)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedG, stringify(filament_g)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentCost, stringify(filament_cost)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g })); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost })); - binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); // duplicated into print metadata - binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedMm, stringify(filament_mm)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedG, stringify(filament_g)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentCost, stringify(filament_cost)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedCm3, stringify(filament_cm3)); // duplicated into print metadata for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = m_time_processor.machines[i]; @@ -3702,10 +3704,10 @@ void GCodeProcessor::post_process() if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { char buf[128]; sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); - binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); - binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + binary_data.print_metadata.raw_data.emplace_back("estimated printing time " + std::string(buf), get_time_dhms(machine.time)); + binary_data.print_metadata.raw_data.emplace_back("estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front())); - binary_data.printer_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.printer_metadata.raw_data.emplace_back("estimated printing time " + std::string(buf), get_time_dhms(machine.time)); } } From 0bea17c12870de8c865d6a99521fba7fe8c2e343 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Aug 2023 09:20:27 +0200 Subject: [PATCH 35/48] Preview and GCodeViewer: removed file mapping for gcode window visualization + Fixed build on non Windows OSs --- src/libslic3r/Config.cpp | 2 - src/libslic3r/GCode/GCodeProcessor.cpp | 16 +- src/libslic3r/Technologies.hpp | 9 +- src/slic3r/GUI/GCodeViewer.cpp | 232 ++++++++++++++++++++++--- src/slic3r/GUI/GCodeViewer.hpp | 49 +++++- 5 files changed, 271 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 5d2de1aaa5..d07690b73b 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -1075,8 +1075,6 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(bgcode::core::translate_result(res)))); - fseek(file.f, 0, SEEK_END); - const long file_size = ftell(file.f); rewind(file.f); bgcode::core::FileHeader file_header; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index d0c92af8c7..4953bdbdc4 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -25,10 +25,10 @@ #endif #include -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG #include #include -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG static const float DEFAULT_TOOLPATH_WIDTH = 0.4f; static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f; @@ -1173,7 +1173,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { OutputDebugStringA(key.c_str()); @@ -1181,7 +1181,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct OutputDebugStringA(value.c_str()); OutputDebugStringA("\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); @@ -1229,7 +1229,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { OutputDebugStringA(key.c_str()); @@ -1237,7 +1237,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct OutputDebugStringA(value.c_str()); OutputDebugStringA("\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); @@ -1249,7 +1249,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { OutputDebugStringA(key.c_str()); @@ -1257,7 +1257,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct OutputDebugStringA(value.c_str()); OutputDebugStringA("\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG DynamicPrintConfig config; config.apply(FullPrintConfig::defaults()); std::string str; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index db25b587f7..d7670f5c28 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -63,13 +63,16 @@ //==================== -// 2.6.1.alpha1 techs +// 2.6.2.alpha1 techs //==================== -#define ENABLE_2_6_1_ALPHA1 1 +#define ENABLE_2_6_2_ALPHA1 1 // Enable export of binarized gcode -#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) +#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) #define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) +#ifdef _WIN32 +#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) +#endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index fd9a44db3d..bfa6c87946 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -38,6 +38,11 @@ #include #include +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG +#include +#include +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG + namespace Slic3r { namespace GUI { @@ -364,13 +369,16 @@ void GCodeViewer::SequentialView::Marker::render() void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) { +#if !ENABLE_BINARIZED_GCODE assert(! m_file.is_open()); if (m_file.is_open()) return; +#endif // !ENABLE_BINARIZED_GCODE m_filename = filename; m_lines_ends = lines_ends; +#if !ENABLE_BINARIZED_GCODE m_selected_line_id = 0; m_last_lines_size = 0; @@ -383,42 +391,217 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& fil BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; reset(); } +#endif // !ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, size_t curr_line_id) +{ + auto update_lines = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); + FILE* file = boost::nowide::fopen(m_filename.c_str(), "rb"); + if (file != nullptr) { + for (size_t id = *m_cache_range.start_id; id <= *m_cache_range.end_id; ++id) { + assert(id > 0); + // read line from file + const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; + const size_t len = m_lines_ends[id - 1] - start; + std::string gline(len, '\0'); + fseek(file, start, SEEK_SET); + const size_t rsize = fread((void*)gline.data(), 1, len, file); + if (ferror(file) || rsize != len) { + m_lines_cache.clear(); + break; + } + + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + m_lines_cache.push_back({ command, parameters, comment }); + } + fclose(file); + } + }; + + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; + static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; + static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; + static const ImVec4 ELLIPSIS_COLOR = { 0.0f, 0.7f, 0.0f, 1.0f }; + + if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) + return; + + // window height + const float wnd_height = bottom - top; + + // number of visible lines + const float text_height = ImGui::CalcTextSize("0").y; + const ImGuiStyle& style = ImGui::GetStyle(); + const size_t visible_lines_count = static_cast((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y)); + + if (visible_lines_count == 0) + return; + + auto resize_range = [&](Range& range, size_t lines_count) { + const size_t half_lines_count = lines_count / 2; + range.start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; + range.end_id = *range.start_id + lines_count - 1; + if (*range.end_id >= m_lines_ends.size()) { + range.end_id = m_lines_ends.size() - 1; + range.start_id = *range.end_id - lines_count + 1; + } + }; + + // visible range + Range visible_range; + resize_range(visible_range, visible_lines_count); + + // update cache if needed + if (m_cache_range.empty() || !m_cache_range.contains(visible_range)) { + resize_range(m_cache_range, 4 * visible_range.size()); + update_lines(); + } + + // line number's column width + const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.end_id).c_str()).x; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + auto add_item_to_line = [&imgui](const std::string& txt, const ImVec4& color, float spacing, size_t& current_length) { + static const size_t LENGTH_THRESHOLD = 60; + + if (txt.empty()) + return false; + + std::string out_text = txt; + bool reduced = false; + if (current_length + out_text.length() > LENGTH_THRESHOLD) { + out_text = out_text.substr(0, LENGTH_THRESHOLD - current_length); + reduced = true; + } + + current_length += out_text.length(); + + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(color, out_text); + if (reduced) { + ImGui::SameLine(0.0f, 0.0f); + imgui.text_colored(ELLIPSIS_COLOR, "..."); + } + + return reduced; + }; + + imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f); + imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + + // center the text in the window by pushing down the first line + const float f_lines_count = static_cast(visible_lines_count); + ImGui::SetCursorPosY(0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y)); + + // render text lines + size_t max_line_length = 0; + for (size_t id = *visible_range.start_id; id <= *visible_range.end_id; ++id) { + const Line& line = m_lines_cache[id - *m_cache_range.start_id]; + + // rect around the current selected line + if (id == curr_line_id) { + const float pos_y = ImGui::GetCursorScreenPos().y; + const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y; + const float half_padding_x = 0.5f * style.WindowPadding.x; + ImGui::GetWindowDrawList()->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y }, + { ImGui::GetCurrentWindow()->Size.x - half_padding_x, pos_y + text_height + half_ItemSpacing_y }, + ImGui::GetColorU32(SELECTION_RECT_COLOR)); + } + + const std::string id_str = std::to_string(id); + // spacer to right align text + ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height }); + + size_t line_length = 0; + // render line number + bool stop_adding = add_item_to_line(id_str, LINE_NUMBER_COLOR, 0.0f, line_length); + if (!stop_adding && !line.command.empty()) + // render command + stop_adding = add_item_to_line(line.command, COMMAND_COLOR, -1.0f, line_length); + if (!stop_adding && !line.parameters.empty()) + // render parameters + stop_adding = add_item_to_line(line.parameters, PARAMETERS_COLOR, 0.0f, line_length); + if (!stop_adding && !line.comment.empty()) + // render comment + stop_adding = add_item_to_line(line.comment, COMMENT_COLOR, line.command.empty() ? -1.0f : 0.0f, line_length); + + max_line_length = std::max(max_line_length, line_length); + } + + imgui.end(); + ImGui::PopStyleVar(); + + // request an extra frame if window's width changed + if (m_max_line_length != max_line_length) { + m_max_line_length = max_line_length; + imgui.set_requires_extra_frame(); + } +} +#else void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const { auto update_lines = [this](uint64_t start_id, uint64_t end_id) { std::vector ret; ret.reserve(end_id - start_id + 1); - for (uint64_t id = start_id; id <= end_id; ++id) { - // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; - std::string gline(m_file.data() + start, len); + for (uint64_t id = start_id; id <= end_id; ++id) { + // read line from file + const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; + const size_t len = m_lines_ends[id - 1] - start; + std::string gline(m_file.data() + start, len); - std::string command; - std::string parameters; - std::string comment; + std::string command; + std::string parameters; + std::string comment; - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); - - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + // extract comment + std::vector tokens; + boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } } } + ret.push_back({ command, parameters, comment }); } - ret.push_back({ command, parameters, comment }); - } return ret; }; @@ -546,12 +729,15 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.end(); ImGui::PopStyleVar(); } +#endif // ENABLE_BINARIZED_GCODE +#if !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { if (m_file.is_open()) m_file.close(); } +#endif // !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::render(float legend_height) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 32d0a91e34..583619e623 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -6,7 +6,9 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" +#if !ENABLE_BINARIZED_GCODE #include +#endif // !ENABLE_BINARIZED_GCODE #include #include @@ -672,6 +674,50 @@ public: void render(); }; +#if ENABLE_BINARIZED_GCODE + class GCodeWindow + { + struct Line + { + std::string command; + std::string parameters; + std::string comment; + }; + + struct Range + { + std::optional start_id; + std::optional end_id; + bool empty() const { + return !start_id.has_value() || !end_id.has_value(); + } + bool contains(const Range& other) const { + return !this->empty() && !other.empty() && *this->start_id <= *other.start_id && *this->end_id >= other.end_id; + } + size_t size() const { + return empty() ? 0 : *this->end_id - *this->start_id + 1; + } + }; + + bool m_visible{ true }; + std::string m_filename; + // map for accessing data in file by line number + std::vector m_lines_ends; + std::vector m_lines_cache; + Range m_cache_range; + size_t m_max_line_length{ 0 }; + + public: + void load_gcode(const std::string& filename, const std::vector& lines_ends); + void reset() { + m_lines_ends.clear(); + m_lines_cache.clear(); + m_filename.clear(); + } + void toggle_visibility() { m_visible = !m_visible; } + void render(float top, float bottom, size_t curr_line_id); + }; +#else class GCodeWindow { struct Line @@ -691,7 +737,7 @@ public: std::vector m_lines; public: - GCodeWindow() = default; + GCodeWindow() = default; ~GCodeWindow() { stop_mapping_file(); } void load_gcode(const std::string& filename, const std::vector& lines_ends); void reset() { @@ -707,6 +753,7 @@ public: void stop_mapping_file(); }; +#endif // ENABLE_BINARIZED_GCODE struct Endpoints { From 8e391d00da90d4943b891111e92b48f69d265622 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 09:38:22 +0200 Subject: [PATCH 36/48] Changes required by update of libbgcode library interface --- src/libslic3r/Config.cpp | 8 +++++--- src/libslic3r/GCode/GCodeProcessor.cpp | 22 ++++++++++------------ src/libslic3r/PresetBundle.cpp | 3 ++- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index d07690b73b..31bd02ad10 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -741,7 +741,8 @@ ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompati if (file == nullptr) throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); - file_type = (bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + std::vector cs_buffer(65536); + file_type = (bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; fclose(file); } else @@ -1070,7 +1071,8 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f if (file.f == nullptr) throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); - bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f); + std::vector cs_buffer(65536); + bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(bgcode::core::translate_result(res)))); @@ -1083,7 +1085,7 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); bgcode::core::BlockHeader block_header; - res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, true); + res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 4953bdbdc4..f16777d384 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1041,7 +1041,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::function cs_buffer(65536); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; fclose(file); if (is_binary) @@ -1134,8 +1135,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct const long file_size = ftell(file.f); rewind(file.f); - bgcode::core::set_checksum_max_cache_size(1024); - // read file header bgcode::core::FileHeader file_header; bgcode::core::EResult res = bgcode::core::read_header(*file.f, file_header, nullptr); @@ -1143,11 +1142,10 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + std::string(bgcode::core::translate_result(res)) + "\n"); - const bool verify_checksum = true; - // read file metadata block bgcode::core::BlockHeader block_header; - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + std::vector cs_buffer(65536); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) @@ -1164,7 +1162,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) @@ -1184,7 +1182,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1217,7 +1215,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } @@ -1240,7 +1238,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) @@ -1275,7 +1273,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) @@ -1295,7 +1293,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file.f) == file_size) break; - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 45e4d0535f..34cae91a7e 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -883,7 +883,8 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw FILE* file = boost::nowide::fopen(path.c_str(), "rb"); if (file == nullptr) throw Slic3r::RuntimeError("Error opening the file: " + path + "\n"); - const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success; + std::vector cs_buffer(65536); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; fclose(file); DynamicPrintConfig config; From 98be146fa9947067e63e7f272faf096b6dc57eb3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 09:55:00 +0200 Subject: [PATCH 37/48] Some code semplification --- src/libslic3r/Config.cpp | 35 +++---- src/libslic3r/GCode/GCodeProcessor.cpp | 128 +++++++++++++------------ src/libslic3r/GCode/Thumbnails.hpp | 10 +- src/slic3r/GUI/GLCanvas3D.cpp | 17 ++-- src/slic3r/GUI/Plater.cpp | 14 +-- 5 files changed, 107 insertions(+), 97 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 31bd02ad10..629f39ef43 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -741,8 +741,9 @@ ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompati if (file == nullptr) throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + using namespace bgcode::core; std::vector cs_buffer(65536); - file_type = (bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + file_type = (is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; fclose(file); } else @@ -1071,29 +1072,31 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f if (file.f == nullptr) throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); + using namespace bgcode::core; + using namespace bgcode::binarize; std::vector cs_buffer(65536); - bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) + EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", - std::string(bgcode::core::translate_result(res)))); + std::string(translate_result(res)))); rewind(file.f); - bgcode::core::FileHeader file_header; - res = bgcode::core::read_header(*file.f, file_header, nullptr); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + FileHeader file_header; + res = read_header(*file.f, file_header, nullptr); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); - bgcode::core::BlockHeader block_header; - res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); - bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; + SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); for (const auto& [key, value] : slicer_metadata_block.raw_data) { this->set_deserialize(key, value, substitutions_ctxt); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f16777d384..1e2596e15a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1041,8 +1041,9 @@ void GCodeProcessor::process_file(const std::string& filename, std::function cs_buffer(65536); - const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; + const bool is_binary = is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success; fclose(file); if (is_binary) @@ -1136,24 +1137,26 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct rewind(file.f); // read file header - bgcode::core::FileHeader file_header; - bgcode::core::EResult res = bgcode::core::read_header(*file.f, file_header, nullptr); - if (res != bgcode::core::EResult::Success) + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res != EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + - std::string(bgcode::core::translate_result(res)) + "\n"); + std::string(translate_result(res)) + "\n"); // read file metadata block - bgcode::core::BlockHeader block_header; + BlockHeader block_header; std::vector cs_buffer(65536); - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); - bgcode::binarize::FileMetadataBlock file_metadata_block; + FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1162,15 +1165,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - bgcode::binarize::PrinterMetadataBlock printer_metadata_block; + PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1182,24 +1185,24 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); - while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { - bgcode::binarize::ThumbnailBlock thumbnail_block; + while ((EBlockType)block_header.type == EBlockType::Thumbnail) { + ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const bgcode::binarize::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.params.width) + "x" + std::to_string(block.params.height); - switch ((bgcode::core::EThumbnailFormat)block.params.format) + switch ((EThumbnailFormat)block.params.format) { - case bgcode::core::EThumbnailFormat::PNG: { ret += ".png"; break; } - case bgcode::core::EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case bgcode::core::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + case EThumbnailFormat::PNG: { ret += ".png"; break; } + case EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case EThumbnailFormat::QOI: { ret += ".qoi"; break; } } return ret; }; @@ -1215,18 +1218,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); } // read print metadata block - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) + if ((EBlockType)block_header.type != EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - bgcode::binarize::PrintMetadataBlock print_metadata_block; + PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1238,15 +1241,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; + SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1273,16 +1276,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); - while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { - bgcode::binarize::GCodeBlock block; + while ((EBlockType)block_header.type == EBlockType::GCode) { + GCodeBlock block; res = block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); // TODO: Update m_result.lines_ends @@ -1293,9 +1296,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file.f) == file_size) break; - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); } // Don't post-process the G-code to update time stamps. @@ -3709,8 +3712,9 @@ void GCodeProcessor::post_process() } } - const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != bgcode::core::EResult::Success) + using namespace bgcode::core; + const EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -4000,8 +4004,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::core::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::core::EResult::Success) + if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4380,8 +4383,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - const bgcode::core::EResult res = m_binarizer.finalize(); - if (res != bgcode::core::EResult::Success) + if (m_binarizer.finalize() != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index c430d695c2..820ee4a19c 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -64,6 +64,8 @@ template inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { + using namespace bgcode::core; + using namespace bgcode::binarize; out_thumbnails.clear(); if (thumbnail_cb != nullptr) { ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true }); @@ -71,13 +73,13 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - bgcode::binarize::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::binarize::ThumbnailBlock()); + ThumbnailBlock& block = out_thumbnails.emplace_back(ThumbnailBlock()); block.params.width = (uint16_t)data.width; block.params.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c4a8d093df..2687ddcf4c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7879,6 +7879,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + using namespace bgcode::core; if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { ImGui::TableNextRow(); @@ -7888,7 +7889,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.file_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.file_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7896,7 +7897,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.printer_metadata; if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.printer_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.printer_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7904,7 +7905,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.print_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.print_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7912,7 +7913,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.slicer_metadata; if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.slicer_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.slicer_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7920,7 +7921,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.gcode; if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.gcode = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.gcode = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7929,7 +7930,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "MeatPack", "MeatPack Comments" }; option_id = (int)binarizer_config.gcode_encoding; if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.gcode_encoding = (bgcode::core::EGCodeEncodingType)option_id; + binarizer_config.gcode_encoding = (EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7938,7 +7939,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "INI" }; option_id = (int)binarizer_config.metadata_encoding; if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.metadata_encoding = (bgcode::core::EMetadataEncodingType)option_id; + binarizer_config.metadata_encoding = (EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7947,7 +7948,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "CRC32" }; option_id = (int)binarizer_config.checksum; if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.checksum = (bgcode::core::EChecksumType)option_id; + binarizer_config.checksum = (EChecksumType)option_id; ImGui::EndTable(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 693ba0e5fa..018e338335 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5466,9 +5466,10 @@ void Plater::convert_gcode_to_ascii() // Perform conversion { wxBusyCursor busy; - bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); - if (res != bgcode::core::EResult::Success) { - MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + using namespace bgcode::core; + EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); + if (res != EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); out_file.close(); boost::nowide::remove(output_file.c_str()); @@ -5511,10 +5512,11 @@ void Plater::convert_gcode_to_binary() // Perform conversion { wxBusyCursor busy; + using namespace bgcode::core; const bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); - bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); - if (res != bgcode::core::EResult::Success) { - MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); + if (res != EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); out_file.close(); boost::nowide::remove(output_file.c_str()); From cfb09222dcd5948d1841ff05736ff3068acd65c6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 13:15:48 +0200 Subject: [PATCH 38/48] LibBGCode.cmake modified to download library from latest commit --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 6a076e9f67..bf3225b28e 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,6 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;main") +set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;4482d545328ebc1051d3e118ee65952bbcabb751") if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From 0b6ab8359aabd5fc7c9e5141cca4eccfa9448766 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 13:18:25 +0200 Subject: [PATCH 39/48] Removed unused parameter --- src/libslic3r/GCode/GCodeProcessor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 1e2596e15a..253da95b13 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3975,12 +3975,12 @@ void GCodeProcessor::post_process() #endif // ENABLE_BINARIZED_GCODE write_to_file(out, out_string, result, out_path); #if ENABLE_BINARIZED_GCODE - update_out_file_pos(out, out_string, result); + update_out_file_pos(out_string, result); #endif // ENABLE_BINARIZED_GCODE } #if ENABLE_BINARIZED_GCODE - void update_out_file_pos(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result) { + void update_out_file_pos(const std::string& out_string, GCodeProcessorResult& result) { for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); @@ -4011,7 +4011,7 @@ void GCodeProcessor::post_process() #endif // ENABLE_BINARIZED_GCODE write_to_file(out, out_string, result, out_path); #if ENABLE_BINARIZED_GCODE - update_out_file_pos(out, out_string, result); + update_out_file_pos(out_string, result); #endif // ENABLE_BINARIZED_GCODE } From 184fad3d8eb2722d3134cae06e8f8d9c2db33f73 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 17:37:08 +0200 Subject: [PATCH 40/48] Follow-up of cfb09222dcd5948d1841ff05736ff3068acd65c6 - Change git url to simple zip url for LibBGCode --- deps/LibBGCode/LibBGCode.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index bf3225b28e..10fe70914d 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,9 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;4482d545328ebc1051d3e118ee65952bbcabb751") +set(_source_dir_line + URL https://github.com/prusa3d/libbgcode/archive/4482d545328ebc1051d3e118ee65952bbcabb751.zip + URL_HASH SHA256=a9baea92b0defcd460cb620774a3d4aac200f40bce4b2a44af8c69ef090d8fc2 +) if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From 271d297bb8768da740dfde8c327ccb211c2bf068 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 16 Aug 2023 09:14:08 +0200 Subject: [PATCH 41/48] Updated LibBGCode.cmake to download newer version of libbgcode --- deps/LibBGCode/LibBGCode.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 10fe70914d..4e8bd2cdfc 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/4482d545328ebc1051d3e118ee65952bbcabb751.zip - URL_HASH SHA256=a9baea92b0defcd460cb620774a3d4aac200f40bce4b2a44af8c69ef090d8fc2 + URL https://github.com/prusa3d/libbgcode/archive/767a0970a0df277c366a08f32b72e39b6e628b54.zip + URL_HASH SHA256=60025397e9ce301ac925b59a12cfced26034b6124866b618c4771842d109847f ) if (LibBGCode_SOURCE_DIR) From e2472fc78cc04fdbd4f4377fd32ce1b6f1c24875 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 16 Aug 2023 12:23:37 +0200 Subject: [PATCH 42/48] Updated LibBGCode.cmake to download newer version of libbgcode --- deps/LibBGCode/LibBGCode.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 4e8bd2cdfc..cf90266678 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/767a0970a0df277c366a08f32b72e39b6e628b54.zip - URL_HASH SHA256=60025397e9ce301ac925b59a12cfced26034b6124866b618c4771842d109847f + URL https://github.com/prusa3d/libbgcode/archive/49e7b47b7a9a7e9365613d3f90ad49d71a0e93d6.zip + URL_HASH SHA256=b9513ab2bbaf06a79c35c4d69226d211f1226ca625ce07c04f54b89b93e9bc75 ) if (LibBGCode_SOURCE_DIR) From cad97bc9a2843055bf31ba8cb78da4d940f7311d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 12:26:56 +0200 Subject: [PATCH 43/48] GCodeViewer - Update gcode window for binary gcode files --- src/libslic3r/GCode/GCodeProcessor.cpp | 52 ++++--- src/libslic3r/GCode/GCodeProcessor.hpp | 7 + src/libslic3r/GCodeReader.cpp | 9 ++ src/libslic3r/GCodeReader.hpp | 4 + src/slic3r/GUI/GCodeViewer.cpp | 199 +++++++++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 18 ++- 6 files changed, 221 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 253da95b13..ffb35c8e59 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -464,6 +464,9 @@ void GCodeProcessorResult::reset() { #else void GCodeProcessorResult::reset() { +#if ENABLE_BINARIZED_GCODE + is_binary_file = false; +#endif // ENABLE_BINARIZED_GCODE moves.clear(); lines_ends.clear(); bed_shape = Pointfs(); @@ -564,6 +567,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) #if ENABLE_BINARIZED_GCODE m_binarizer.set_enabled(config.gcode_binary); + m_result.is_binary_file = config.gcode_binary; #endif // ENABLE_BINARIZED_GCODE m_producer = EProducer::PrusaSlicer; @@ -1104,6 +1108,9 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi // process gcode m_result.filename = filename; +#if ENABLE_BINARIZED_GCODE + m_result.is_binary_file = false; +#endif // ENABLE_BINARIZED_GCODE m_result.id = ++s_result_id; initialize_result_moves(); size_t parse_line_callback_cntr = 10000; @@ -1122,6 +1129,16 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi } #if ENABLE_BINARIZED_GCODE +static void update_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +{ + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1); + } + if (out_file_pos != nullptr) + *out_file_pos += out_string.size(); +} + void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) { #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1272,6 +1289,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct apply_config(config); m_result.filename = filename; + m_result.is_binary_file = true; m_result.id = ++s_result_id; initialize_result_moves(); @@ -1287,7 +1305,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); - // TODO: Update m_result.lines_ends + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + update_out_file_pos(block.raw_data, lines_ends, nullptr); m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { this->process_gcode_line(line, true); @@ -3971,23 +3990,14 @@ void GCodeProcessor::post_process() if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } - else -#endif // ENABLE_BINARIZED_GCODE + else { write_to_file(out, out_string, result, out_path); -#if ENABLE_BINARIZED_GCODE - update_out_file_pos(out_string, result); -#endif // ENABLE_BINARIZED_GCODE - } - -#if ENABLE_BINARIZED_GCODE - void update_out_file_pos(const std::string& out_string, GCodeProcessorResult& result) { - for (size_t i = 0; i < out_string.size(); ++i) { - if (out_string[i] == '\n') - result.lines_ends.emplace_back(m_out_file_pos + i + 1); + update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } - m_out_file_pos += out_string.size(); - } +#else + write_to_file(out, out_string, result, out_path); #endif // ENABLE_BINARIZED_GCODE + } // flush the current content of the cache to file void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { @@ -4007,11 +4017,12 @@ void GCodeProcessor::post_process() if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } - else -#endif // ENABLE_BINARIZED_GCODE + else { write_to_file(out, out_string, result, out_path); -#if ENABLE_BINARIZED_GCODE - update_out_file_pos(out_string, result); + update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + } +#else + write_to_file(out, out_string, result, out_path); #endif // ENABLE_BINARIZED_GCODE } @@ -4312,6 +4323,9 @@ void GCodeProcessor::post_process() }; m_result.lines_ends.clear(); +#if ENABLE_BINARIZED_GCODE + m_result.lines_ends.emplace_back(std::vector()); +#endif // ENABLE_BINARIZED_GCODE unsigned int line_id = 0; // Backtrace data for Tx gcode lines diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index bef1cd68f3..1126432518 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -139,10 +139,17 @@ namespace Slic3r { }; std::string filename; +#if ENABLE_BINARIZED_GCODE + bool is_binary_file; +#endif // ENABLE_BINARIZED_GCODE unsigned int id; std::vector moves; // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. +#if ENABLE_BINARIZED_GCODE + std::vector> lines_ends; +#else std::vector lines_ends; +#endif // ENABLE_BINARIZED_GCODE Pointfs bed_shape; float max_print_height; SettingsIds settings_ids; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index cb0366757a..2b94963e93 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -195,11 +195,20 @@ bool GCodeReader::parse_file(const std::string &file, callback_t callback) return this->parse_file_internal(file, callback, [](size_t){}); } +#if ENABLE_BINARIZED_GCODE +bool GCodeReader::parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends) +{ + lines_ends.clear(); + lines_ends.push_back(std::vector()); + return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos) { lines_ends.front().emplace_back(file_pos); }); +} +#else bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector &lines_ends) { lines_ends.clear(); return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); }); } +#endif // ENABLE_BINARIZED_GCODE bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback) { diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 58f55fdcf7..7d1dad92a0 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -131,7 +131,11 @@ public: bool parse_file(const std::string &file, callback_t callback); // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code // as an overlay in the 3D scene. +#if ENABLE_BINARIZED_GCODE + bool parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends); +#else bool parse_file(const std::string &file, callback_t callback, std::vector &lines_ends); +#endif // ENABLE_BINARIZED_GCODE // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed. bool parse_file_raw(const std::string &file, raw_line_callback_t callback); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bfa6c87946..6425c359b3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -367,18 +367,22 @@ void GCodeViewer::SequentialView::Marker::render() ImGui::PopStyleVar(); } +#if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const GCodeProcessorResult& gcode_result) +{ + m_filename = gcode_result.filename; + m_is_binary_file = gcode_result.is_binary_file; + m_lines_ends = gcode_result.lines_ends; +} +#else void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) { -#if !ENABLE_BINARIZED_GCODE - assert(! m_file.is_open()); + assert(!m_file.is_open()); if (m_file.is_open()) return; -#endif // !ENABLE_BINARIZED_GCODE - m_filename = filename; + m_filename = filename; m_lines_ends = lines_ends; - -#if !ENABLE_BINARIZED_GCODE m_selected_line_id = 0; m_last_lines_size = 0; @@ -391,54 +395,150 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& fil BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; reset(); } -#endif // !ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE #if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::add_gcode_line_to_lines_cache(const std::string& src) +{ + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, src, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + + m_lines_cache.push_back({ command, parameters, comment }); +} + void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, size_t curr_line_id) { - auto update_lines = [this]() { + auto update_lines_ascii = [this]() { m_lines_cache.clear(); m_lines_cache.reserve(m_cache_range.size()); + const std::vector& lines_ends = m_lines_ends.front(); FILE* file = boost::nowide::fopen(m_filename.c_str(), "rb"); if (file != nullptr) { - for (size_t id = *m_cache_range.start_id; id <= *m_cache_range.end_id; ++id) { + for (size_t id = *m_cache_range.min; id <= *m_cache_range.max; ++id) { assert(id > 0); // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; + const size_t begin = id == 1 ? 0 : lines_ends[id - 2]; + const size_t len = lines_ends[id - 1] - begin; std::string gline(len, '\0'); - fseek(file, start, SEEK_SET); + fseek(file, begin, SEEK_SET); const size_t rsize = fread((void*)gline.data(), 1, len, file); if (ferror(file) || rsize != len) { m_lines_cache.clear(); break; } - std::string command; - std::string parameters; - std::string comment; + add_gcode_line_to_lines_cache(gline); + } + fclose(file); + } + }; - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); + auto update_lines_binary = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; + size_t cumulative_lines_count = 0; + std::vector cumulative_lines_counts; + cumulative_lines_counts.reserve(m_lines_ends.size()); + for (size_t i = 0; i < m_lines_ends.size(); ++i) { + cumulative_lines_count += m_lines_ends[i].size(); + cumulative_lines_counts.emplace_back(cumulative_lines_count); + } + + size_t first_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.min <= cumulative_lines_counts[i]) { + first_block_id = i; + break; + } + } + size_t last_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.max <= cumulative_lines_counts[i]) { + last_block_id = i; + break; + } + } + assert(last_block_id >= first_block_id); + + FilePtr file(boost::nowide::fopen(m_filename.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + if (res == EResult::Success) { + for (size_t i = 0; i < first_block_id; ++i) { + skip_block(*file.f, file_header, block_header); + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; + } + } + + for (size_t i = first_block_id; i <= last_block_id; ++i) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) { + m_lines_cache.clear(); + return; + } + + const size_t ref_id = (i == 0) ? 0 : i - 1; + const size_t first_line_id = (i == 0) ? *m_cache_range.min : + (*m_cache_range.min - 1 >= cumulative_lines_counts[ref_id]) ? *m_cache_range.min - cumulative_lines_counts[ref_id] : 1; + const size_t last_line_id = (*m_cache_range.max - 1 <= cumulative_lines_counts[i]) ? + (i == 0) ? *m_cache_range.max : *m_cache_range.max - cumulative_lines_counts[ref_id] : m_lines_ends[i].size() - 1; + + for (size_t j = first_line_id; j <= last_line_id; ++j) { + const size_t begin = (j == 1) ? 0 : m_lines_ends[i][j - 2]; + const size_t end = m_lines_ends[i][j - 1]; + std::string gline; + gline.insert(gline.end(), block.raw_data.begin() + begin, block.raw_data.begin() + end); + add_gcode_line_to_lines_cache(gline); + } + + if (ftell(file.f) == file_size) + break; + + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; } } } - m_lines_cache.push_back({ command, parameters, comment }); } - fclose(file); } }; @@ -463,13 +563,20 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s if (visible_lines_count == 0) return; + if (m_lines_ends.empty() || m_lines_ends.front().empty()) + return; + auto resize_range = [&](Range& range, size_t lines_count) { const size_t half_lines_count = lines_count / 2; - range.start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; - range.end_id = *range.start_id + lines_count - 1; - if (*range.end_id >= m_lines_ends.size()) { - range.end_id = m_lines_ends.size() - 1; - range.start_id = *range.end_id - lines_count + 1; + range.min = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; + range.max = *range.min + lines_count - 1; + size_t lines_ends_count = 0; + for (const auto& le : m_lines_ends) { + lines_ends_count += le.size(); + } + if (*range.max >= lines_ends_count) { + range.max = lines_ends_count - 1; + range.min = *range.max - lines_count + 1; } }; @@ -480,11 +587,17 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s // update cache if needed if (m_cache_range.empty() || !m_cache_range.contains(visible_range)) { resize_range(m_cache_range, 4 * visible_range.size()); - update_lines(); + if (m_is_binary_file) + update_lines_binary(); + else + update_lines_ascii(); } + if (m_lines_cache.empty()) + return; + // line number's column width - const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.end_id).c_str()).x; + const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.max).c_str()).x; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -525,8 +638,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s // render text lines size_t max_line_length = 0; - for (size_t id = *visible_range.start_id; id <= *visible_range.end_id; ++id) { - const Line& line = m_lines_cache[id - *m_cache_range.start_id]; + for (size_t id = *visible_range.min; id <= *visible_range.max; ++id) { + const Line& line = m_lines_cache[id - *m_cache_range.min]; // rect around the current selected line if (id == curr_line_id) { @@ -729,15 +842,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.end(); ImGui::PopStyleVar(); } -#endif // ENABLE_BINARIZED_GCODE -#if !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { if (m_file.is_open()) m_file.close(); } -#endif // !ENABLE_BINARIZED_GCODE +#endif // ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::render(float legend_height) { @@ -917,7 +1028,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr // release gpu memory, if used reset(); +#if ENABLE_BINARIZED_GCODE + m_sequential_view.gcode_window.load_gcode(gcode_result); +#else m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); +#endif // ENABLE_BINARIZED_GCODE if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 583619e623..60c350afe2 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -686,29 +686,30 @@ public: struct Range { - std::optional start_id; - std::optional end_id; + std::optional min; + std::optional max; bool empty() const { - return !start_id.has_value() || !end_id.has_value(); + return !min.has_value() || !max.has_value(); } bool contains(const Range& other) const { - return !this->empty() && !other.empty() && *this->start_id <= *other.start_id && *this->end_id >= other.end_id; + return !this->empty() && !other.empty() && *this->min <= *other.min && *this->max >= other.max; } size_t size() const { - return empty() ? 0 : *this->end_id - *this->start_id + 1; + return empty() ? 0 : *this->max - *this->min + 1; } }; bool m_visible{ true }; std::string m_filename; + bool m_is_binary_file{ false }; // map for accessing data in file by line number - std::vector m_lines_ends; + std::vector> m_lines_ends; std::vector m_lines_cache; Range m_cache_range; size_t m_max_line_length{ 0 }; public: - void load_gcode(const std::string& filename, const std::vector& lines_ends); + void load_gcode(const GCodeProcessorResult& gcode_result); void reset() { m_lines_ends.clear(); m_lines_cache.clear(); @@ -716,6 +717,9 @@ public: } void toggle_visibility() { m_visible = !m_visible; } void render(float top, float bottom, size_t curr_line_id); + + private: + void add_gcode_line_to_lines_cache(const std::string& src); }; #else class GCodeWindow From aec962c2950721482af957fbac1bd12108ec2801 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 13:22:38 +0200 Subject: [PATCH 44/48] Preview - Update gcode window when exporting to binary gcode --- src/libslic3r/GCode/GCodeProcessor.cpp | 57 +++++++++++++++++++++++++- src/libslic3r/Technologies.hpp | 2 +- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ffb35c8e59..7065f5db14 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3670,9 +3670,8 @@ void GCodeProcessor::post_process() // temporary file to contain modified gcode std::string out_path = m_result.filename + ".postprocess"; FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") }; - if (out.f == nullptr) { + if (out.f == nullptr) throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); - } #if ENABLE_BINARIZED_GCODE std::vector filament_mm(m_result.extruders_count, 0.0); @@ -4405,6 +4404,60 @@ void GCodeProcessor::post_process() out.close(); in.close(); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + // updates m_result.lines_ends from binarized gcode file + m_result.lines_ends.clear(); + + FilePtr file(boost::nowide::fopen(out_path.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + while (res == EResult::Success) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + break; + + // extract lines ends from block + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + for (size_t i = 0; i < block.raw_data.size(); ++i) { + if (block.raw_data[i] == '\n') + lines_ends.emplace_back(i + 1); + } + + if (ftell(file.f) == file_size) + break; + + // read next block header + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success) + break; + if (block_header.type != (uint16_t)EBlockType::GCode) { + res = EResult::InvalidBlockType; + break; + } + } + } + + if (res != EResult::Success && !m_result.lines_ends.empty() && !m_result.lines_ends.front().empty()) + // some error occourred, clear lines ends + m_result.lines_ends = { std::vector() }; + } + } +#endif // ENABLE_BINARIZED_GCODE + export_lines.synchronize_moves(m_result); if (rename_file(out_path, m_result.filename)) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d7670f5c28..45db76451b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -71,7 +71,7 @@ #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) #define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) #ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) +#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (0 && ENABLE_BINARIZED_GCODE_DEBUG) #endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) From bfbe4dacaea58e99cc1361507f7e5a016c709dd9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 14:26:03 +0200 Subject: [PATCH 45/48] Few small refactorings --- src/libslic3r/Config.cpp | 7 +++---- src/libslic3r/GCode.cpp | 9 +++------ src/libslic3r/GCode/GCodeProcessor.cpp | 18 ++++++++---------- src/libslic3r/Technologies.hpp | 4 ++-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 629f39ef43..5ed0553a11 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -1077,16 +1077,14 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f std::vector cs_buffer(65536); EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", - std::string(translate_result(res)))); - - rewind(file.f); + throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(translate_result(res)))); FileHeader file_header; res = read_header(*file.f, file_header, nullptr); if (res != EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + // searches for config block BlockHeader block_header; res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) @@ -1098,6 +1096,7 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f if (res != EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + // extracts data from block for (const auto& [key, value] : slicer_metadata_block.raw_data) { this->set_deserialize(key, value, substitutions_ctxt); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index b12b0508e7..ff49610764 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1214,13 +1214,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data } } - - // modifies m_silent_time_estimator_enabled - DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); -#else - // modifies m_silent_time_estimator_enabled - DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); #endif // ENABLE_BINARIZED_GCODE + + // modifies m_silent_time_estimator_enabled + DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); if (! print.config().gcode_substitutions.values.empty()) { m_find_replace = make_unique(print.config()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7065f5db14..0e58a318dd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -71,7 +71,7 @@ const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{}; -#endif // ENABLE_BINARIZED +#endif // ENABLE_BINARIZED_GCODE #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; @@ -1129,7 +1129,7 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi } #if ENABLE_BINARIZED_GCODE -static void update_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) { for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') @@ -1306,7 +1306,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); - update_out_file_pos(block.raw_data, lines_ends, nullptr); + update_lines_ends_and_out_file_pos(block.raw_data, lines_ends, nullptr); m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { this->process_gcode_line(line, true); @@ -3730,9 +3730,8 @@ void GCodeProcessor::post_process() } } - using namespace bgcode::core; - const EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != EResult::Success) + const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -3985,13 +3984,12 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::core::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::core::EResult::Success) + if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else { write_to_file(out, out_string, result, out_path); - update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } #else write_to_file(out, out_string, result, out_path); @@ -4018,7 +4016,7 @@ void GCodeProcessor::post_process() } else { write_to_file(out, out_string, result, out_path); - update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } #else write_to_file(out, out_string, result, out_path); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 45db76451b..6a1743285e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,9 +69,9 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) -#define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) +#define ENABLE_BINARIZED_GCODE_DEBUG (0 && ENABLE_BINARIZED_GCODE) #ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (0 && ENABLE_BINARIZED_GCODE_DEBUG) +#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) #endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) From 259fc0ff4b24f6e2f66091d11d3183f2d60bf4c4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 28 Aug 2023 19:21:08 +0200 Subject: [PATCH 46/48] Setting the binary G-code default compression. --- src/libslic3r/GCode/GCodeProcessor.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 232c59b7db..aeefa72e83 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -69,7 +69,18 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{ + { + bgcode::core::ECompressionType::None, // file metadata + bgcode::core::ECompressionType::None, // printer metadata + bgcode::core::ECompressionType::Deflate, // print metadata + bgcode::core::ECompressionType::Deflate, // slicer metadata + bgcode::core::ECompressionType::Heatshrink_12_4, // gcode + }, + bgcode::core::EGCodeEncodingType::MeatPackComments, + bgcode::core::EMetadataEncodingType::INI, + bgcode::core::EChecksumType::CRC32 +}; #endif // ENABLE_BINARIZED_GCODE #if ENABLE_GCODE_VIEWER_DATA_CHECKING From 2df6f290e769a57afa1cdab3d559a77ee5328b1d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Aug 2023 10:55:25 +0200 Subject: [PATCH 47/48] Try to fix build of perl bindings on Linux --- deps/LibBGCode/LibBGCode.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index cf90266678..e51bae4f49 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -15,6 +15,7 @@ prusaslicer_add_cmake_project(LibBGCode_deps DEPENDS dep_Boost ${ZLIB_PKG} CMAKE_ARGS -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} + -DDEP_CMAKE_OPTS:STRING="-DCMAKE_POSITION_INDEPENDENT_CODE=ON" -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON -DDESTDIR=${DESTDIR} From 7bf1189c697fadd3cfdf082c17489a6899fd1ddf Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Aug 2023 11:15:55 +0200 Subject: [PATCH 48/48] Follow up fix, second try --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index e51bae4f49..076fe290a2 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -15,7 +15,7 @@ prusaslicer_add_cmake_project(LibBGCode_deps DEPENDS dep_Boost ${ZLIB_PKG} CMAKE_ARGS -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} - -DDEP_CMAKE_OPTS:STRING="-DCMAKE_POSITION_INDEPENDENT_CODE=ON" + -DDEP_CMAKE_OPTS:STRING=-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON -DDESTDIR=${DESTDIR}