From b8a479ea9a2c69486b0133cc2017471c6a9f1a0b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 9 Mar 2023 16:16:11 +0100 Subject: [PATCH] Get file size from Archive dialog, so correct file is opened. --- src/slic3r/GUI/FileArchiveDialog.cpp | 21 ++-- src/slic3r/GUI/FileArchiveDialog.hpp | 9 +- src/slic3r/GUI/Plater.cpp | 174 ++++++++++++--------------- 3 files changed, 98 insertions(+), 106 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 8d180c4fa2..233a3e1900 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -166,11 +166,11 @@ ArchiveViewCtrl::~ArchiveViewCtrl() } } -FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector& selected_paths) +FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector>& selected_paths_w_size) : DPIDialog(parent_window, wxID_ANY, _(L("Archive preview")), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) - , m_selected_paths (selected_paths) + , m_selected_paths_w_size (selected_paths_w_size) { #ifdef _WIN32 wxGetApp().UpdateDarkUI(this); @@ -213,7 +213,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase); mz_uint num_entries = mz_zip_reader_get_num_files(archive); mz_zip_archive_file_stat stat; - std::vector filtered_entries; + std::vector> filtered_entries; // second is unzipped size for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(archive, i, &stat)) { std::string extra(1024, 0); @@ -232,22 +232,25 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar // filter out MACOS specific hidden files if (boost::algorithm::starts_with(path.string(), "__MACOSX")) continue; - filtered_entries.emplace_back(std::move(path)); + filtered_entries.emplace_back(std::move(path), stat.m_uncomp_size); } } // sorting files will help adjust_stack function to not create multiple same folders - std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() < p2.string(); }); + std::sort(filtered_entries.begin(), filtered_entries.end(), [](const std::pair& p1, const std::pair& p2){ return p1.first.string() < p2.first.string(); }); size_t entry_count = 0; size_t depth = 1; - for (const boost::filesystem::path& path : filtered_entries) + for (const auto& entry : filtered_entries) { + const boost::filesystem::path& path = entry.first; std::shared_ptr parent(nullptr); depth = std::max(depth, adjust_stack(path, stack)); if (!stack.empty()) parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files - m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + std::shared_ptr new_node = m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()), false); + new_node->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + new_node->set_size(entry.second); entry_count++; } } @@ -305,12 +308,12 @@ void FileArchiveDialog::on_open_button() wxDataViewItemArray top_items; m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items); - std::function deep_fill = [&paths = m_selected_paths, &deep_fill](ArchiveViewNode* node){ + std::function deep_fill = [&paths = m_selected_paths_w_size, &deep_fill](ArchiveViewNode* node){ if (node == nullptr) return; if (node->get_children().empty()) { if (node->get_toggle()) - paths.emplace_back(node->get_fullpath()); + paths.emplace_back(node->get_fullpath(), node->get_size()); } else { for (std::shared_ptr child : node->get_children()) deep_fill(child.get()); diff --git a/src/slic3r/GUI/FileArchiveDialog.hpp b/src/slic3r/GUI/FileArchiveDialog.hpp index 22a9ecedfa..4a335c75a7 100644 --- a/src/slic3r/GUI/FileArchiveDialog.hpp +++ b/src/slic3r/GUI/FileArchiveDialog.hpp @@ -33,6 +33,8 @@ public: void set_is_folder(bool is_folder) { m_folder = is_folder; } void set_fullpath(boost::filesystem::path path) { m_fullpath = path; } boost::filesystem::path get_fullpath() const { return m_fullpath; } + void set_size(size_t size) { m_size = size; } + size_t get_size() const { return m_size; } private: wxString m_name; @@ -43,6 +45,7 @@ private: bool m_folder { false }; boost::filesystem::path m_fullpath; bool m_container { false }; + size_t m_size { 0 }; }; class ArchiveViewModel : public wxDataViewModel @@ -100,7 +103,7 @@ protected: class FileArchiveDialog : public DPIDialog { public: - FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector& selected_paths); + FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector>& selected_paths_w_size); protected: void on_dpi_changed(const wxRect& suggested_rect) override; @@ -109,7 +112,9 @@ protected: void on_all_button(); void on_none_button(); - std::vector& m_selected_paths; + // chosen files are written into this vector and returned to caller via reference. + // path in archive and decompressed size. The size can be used to distinguish between files with same path. + std::vector>& m_selected_paths_w_size; ArchiveViewCtrl* m_avc; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 02c8959b92..e89dd26240 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5558,115 +5558,99 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) std::string err_msg = GUI::format(_utf8("Loading of a zip archive on path %1% has failed."), archive_path.string()); throw Slic3r::FileIOError(err_msg); } - mz_uint num_entries = mz_zip_reader_get_num_files(&archive); - mz_zip_archive_file_stat stat; - - std::vector selected_paths; - + // selected_paths contains paths and its uncompressed size. The size is used to distinguish between files with same path. + std::vector> selected_paths; FileArchiveDialog dlg(static_cast(wxGetApp().mainframe), &archive, selected_paths); if (dlg.ShowModal() == wxID_OK) - { - // selected_paths is already sorted - if (std::unique(selected_paths.begin(), selected_paths.end()) != selected_paths.end()) { - // notify about duplicities - wxString log = _L("Chosen paths to unzip contain duplicities. This will probably lead to fails during decompression."); - BOOST_LOG_TRIVIAL(warning) << log; - show_info(nullptr,log, _L("Warning")); - } + { std::string archive_path_string = archive_path.string(); archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4); fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data()); - std::vector> selected_paths_with_flag; // flag true if already loaded - size_t used_paths = 0; - selected_paths_with_flag.reserve(selected_paths.size()); - for (auto& path : selected_paths) { - selected_paths_with_flag.emplace_back(std::move(path), false); - } - for (mz_uint i = 0; i < num_entries; ++i) { - if (mz_zip_reader_file_stat(&archive, i, &stat)) { - wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = boost::nowide::narrow(wname); - fs::path archive_path(name); - - std::string extra(1024, 0); - size_t extra_size = mz_zip_reader_get_filename_from_extra(&archive, i, extra.data(), extra.size()); - if (extra_size > 0) { - archive_path = fs::path(extra.substr(0, extra_size)); - name = archive_path.string(); - } - - if (archive_path.empty()) - continue; - for (auto& path_w_flag : selected_paths_with_flag) { - if (path_w_flag.second) + + for (auto& path_w_size : selected_paths) { + const fs::path& path = path_w_size.first; + size_t size = path_w_size.second; + // find path in zip archive + for (mz_uint i = 0; i < num_entries; ++i) { + if (mz_zip_reader_file_stat(&archive, i, &stat)) { + if (size != stat.m_uncomp_size) // size must fit continue; - const fs::path& path = path_w_flag.first; - if (path == archive_path) { - try + wxString wname = boost::nowide::widen(stat.m_filename); + std::string name = boost::nowide::narrow(wname); + fs::path archive_path(name); + + std::string extra(1024, 0); + size_t extra_size = mz_zip_reader_get_filename_from_extra(&archive, i, extra.data(), extra.size()); + if (extra_size > 0) { + archive_path = fs::path(extra.substr(0, extra_size)); + name = archive_path.string(); + } + + if (archive_path.empty()) + continue; + if (path != archive_path) + continue; + // decompressing + try + { + std::replace(name.begin(), name.end(), '\\', '/'); + // rename if file exists + std::string filename = path.filename().string(); + std::string extension = boost::filesystem::extension(path); + std::string just_filename = filename.substr(0, filename.size() - extension.size()); + std::string final_filename = just_filename; + + size_t version = 0; + while (fs::exists(archive_dir / (final_filename + extension))) { - path_w_flag.second = true; - used_paths++; - std::replace(name.begin(), name.end(), '\\', '/'); - // rename if file exists - std::string filename = path.filename().string(); - std::string extension = boost::filesystem::extension(path); - std::string just_filename = filename.substr(0, filename.size() - extension.size()); - std::string final_filename = just_filename; - - size_t version = 0; - while (fs::exists(archive_dir / (final_filename + extension))) - { - ++version; - final_filename = just_filename + "(" + std::to_string(version) + ")"; - } - filename = final_filename + extension; - fs::path final_path = archive_dir / filename; - - std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); - if (res == 0) { - wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive))); - BOOST_LOG_TRIVIAL(error) << error_log; - show_error(nullptr, error_log); - break; - } - fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc); - file.write(buffer.c_str(), buffer.size()); - file.close(); - if (!fs::exists(final_path)) { - wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string()); - BOOST_LOG_TRIVIAL(error) << error_log; - show_error(nullptr, error_log); - break; - } - BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path; - if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) { - non_project_paths.emplace_back(final_path); - break; - } - // if 3mf - read archive headers to find project file - if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) || - (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) { - non_project_paths.emplace_back(final_path); - break; - } - - project_paths.emplace_back(final_path); + ++version; + final_filename = just_filename + "(" + std::to_string(version) + ")"; + } + filename = final_filename + extension; + fs::path final_path = archive_dir / filename; + std::string buffer((size_t)stat.m_uncomp_size, 0); + // Decompress action. We already has correct file index in stat structure. + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) { + wxString error_log = GUI::format_wxstr(_L("Failed to unzip file to %1%: %2% "), final_path.string(), mz_zip_get_error_string(mz_zip_get_last_error(&archive))); + BOOST_LOG_TRIVIAL(error) << error_log; + show_error(nullptr, error_log); break; } - catch (const std::exception& e) - { - // ensure the zip archive is closed and rethrow the exception - close_zip_reader(&archive); - throw Slic3r::FileIOError(e.what()); + // write buffer to file + fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc); + file.write(buffer.c_str(), buffer.size()); + file.close(); + if (!fs::exists(final_path)) { + wxString error_log = GUI::format_wxstr(_L("Failed to find unzipped file at %1%. Unzipping of file has failed."), final_path.string()); + BOOST_LOG_TRIVIAL(error) << error_log; + show_error(nullptr, error_log); + break; } - + BOOST_LOG_TRIVIAL(info) << "Unzipped " << final_path; + if (!boost::algorithm::iends_with(filename, ".3mf") && !boost::algorithm::iends_with(filename, ".amf")) { + non_project_paths.emplace_back(final_path); + break; + } + // if 3mf - read archive headers to find project file + if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(final_path.string())) || + (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) { + non_project_paths.emplace_back(final_path); + break; + } + + project_paths.emplace_back(final_path); + break; + } + catch (const std::exception& e) + { + // ensure the zip archive is closed and rethrow the exception + close_zip_reader(&archive); + throw Slic3r::FileIOError(e.what()); } } - if (used_paths == selected_paths_with_flag.size()) - break; } } close_zip_reader(&archive);