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);