Get file size from Archive dialog, so correct file is opened.

This commit is contained in:
David Kocik 2023-03-09 16:16:11 +01:00
parent c06816212d
commit b8a479ea9a
3 changed files with 98 additions and 106 deletions

View File

@ -166,11 +166,11 @@ ArchiveViewCtrl::~ArchiveViewCtrl()
} }
} }
FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector<boost::filesystem::path>& selected_paths) FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector<std::pair<boost::filesystem::path, size_t>>& selected_paths_w_size)
: DPIDialog(parent_window, wxID_ANY, _(L("Archive preview")), wxDefaultPosition, : DPIDialog(parent_window, wxID_ANY, _(L("Archive preview")), wxDefaultPosition,
wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()), wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()),
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
, m_selected_paths (selected_paths) , m_selected_paths_w_size (selected_paths_w_size)
{ {
#ifdef _WIN32 #ifdef _WIN32
wxGetApp().UpdateDarkUI(this); 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); 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_uint num_entries = mz_zip_reader_get_num_files(archive);
mz_zip_archive_file_stat stat; mz_zip_archive_file_stat stat;
std::vector<boost::filesystem::path> filtered_entries; std::vector<std::pair<boost::filesystem::path, size_t>> filtered_entries; // second is unzipped size
for (mz_uint i = 0; i < num_entries; ++i) { for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(archive, i, &stat)) { if (mz_zip_reader_file_stat(archive, i, &stat)) {
std::string extra(1024, 0); std::string extra(1024, 0);
@ -232,22 +232,25 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
// filter out MACOS specific hidden files // filter out MACOS specific hidden files
if (boost::algorithm::starts_with(path.string(), "__MACOSX")) if (boost::algorithm::starts_with(path.string(), "__MACOSX"))
continue; 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 // 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<boost::filesystem::path, size_t>& p1, const std::pair<boost::filesystem::path, size_t>& p2){ return p1.first.string() < p2.first.string(); });
size_t entry_count = 0; size_t entry_count = 0;
size_t depth = 1; 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<ArchiveViewNode> parent(nullptr); std::shared_ptr<ArchiveViewNode> parent(nullptr);
depth = std::max(depth, adjust_stack(path, stack)); depth = std::max(depth, adjust_stack(path, stack));
if (!stack.empty()) if (!stack.empty())
parent = stack.back(); parent = stack.back();
if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files 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<ArchiveViewNode> 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++; entry_count++;
} }
} }
@ -305,12 +308,12 @@ void FileArchiveDialog::on_open_button()
wxDataViewItemArray top_items; wxDataViewItemArray top_items;
m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items); m_avc->get_model()->GetChildren(wxDataViewItem(nullptr), top_items);
std::function<void(ArchiveViewNode*)> deep_fill = [&paths = m_selected_paths, &deep_fill](ArchiveViewNode* node){ std::function<void(ArchiveViewNode*)> deep_fill = [&paths = m_selected_paths_w_size, &deep_fill](ArchiveViewNode* node){
if (node == nullptr) if (node == nullptr)
return; return;
if (node->get_children().empty()) { if (node->get_children().empty()) {
if (node->get_toggle()) if (node->get_toggle())
paths.emplace_back(node->get_fullpath()); paths.emplace_back(node->get_fullpath(), node->get_size());
} else { } else {
for (std::shared_ptr<ArchiveViewNode> child : node->get_children()) for (std::shared_ptr<ArchiveViewNode> child : node->get_children())
deep_fill(child.get()); deep_fill(child.get());

View File

@ -33,6 +33,8 @@ public:
void set_is_folder(bool is_folder) { m_folder = is_folder; } void set_is_folder(bool is_folder) { m_folder = is_folder; }
void set_fullpath(boost::filesystem::path path) { m_fullpath = path; } void set_fullpath(boost::filesystem::path path) { m_fullpath = path; }
boost::filesystem::path get_fullpath() const { return m_fullpath; } 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: private:
wxString m_name; wxString m_name;
@ -43,6 +45,7 @@ private:
bool m_folder { false }; bool m_folder { false };
boost::filesystem::path m_fullpath; boost::filesystem::path m_fullpath;
bool m_container { false }; bool m_container { false };
size_t m_size { 0 };
}; };
class ArchiveViewModel : public wxDataViewModel class ArchiveViewModel : public wxDataViewModel
@ -100,7 +103,7 @@ protected:
class FileArchiveDialog : public DPIDialog class FileArchiveDialog : public DPIDialog
{ {
public: public:
FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector<boost::filesystem::path>& selected_paths); FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* archive, std::vector<std::pair<boost::filesystem::path, size_t>>& selected_paths_w_size);
protected: protected:
void on_dpi_changed(const wxRect& suggested_rect) override; void on_dpi_changed(const wxRect& suggested_rect) override;
@ -109,7 +112,9 @@ protected:
void on_all_button(); void on_all_button();
void on_none_button(); void on_none_button();
std::vector<boost::filesystem::path>& 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<std::pair<boost::filesystem::path,size_t>>& m_selected_paths_w_size;
ArchiveViewCtrl* m_avc; ArchiveViewCtrl* m_avc;
}; };

View File

@ -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()); 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); throw Slic3r::FileIOError(err_msg);
} }
mz_uint num_entries = mz_zip_reader_get_num_files(&archive); mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat; mz_zip_archive_file_stat stat;
// selected_paths contains paths and its uncompressed size. The size is used to distinguish between files with same path.
std::vector<fs::path> selected_paths; std::vector<std::pair<fs::path, size_t>> selected_paths;
FileArchiveDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe), &archive, selected_paths); FileArchiveDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe), &archive, selected_paths);
if (dlg.ShowModal() == wxID_OK) 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(); std::string archive_path_string = archive_path.string();
archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4); archive_path_string = archive_path_string.substr(0, archive_path_string.size() - 4);
fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data()); fs::path archive_dir(wxStandardPaths::Get().GetTempDir().utf8_str().data());
std::vector<std::pair<fs::path, bool>> selected_paths_with_flag; // flag true if already loaded
size_t used_paths = 0; for (auto& path_w_size : selected_paths) {
selected_paths_with_flag.reserve(selected_paths.size()); const fs::path& path = path_w_size.first;
for (auto& path : selected_paths) { size_t size = path_w_size.second;
selected_paths_with_flag.emplace_back(std::move(path), false); // find path in zip archive
} for (mz_uint i = 0; i < num_entries; ++i) {
for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(&archive, i, &stat)) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) { if (size != stat.m_uncomp_size) // size must fit
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)
continue; continue;
const fs::path& path = path_w_flag.first; wxString wname = boost::nowide::widen(stat.m_filename);
if (path == archive_path) { std::string name = boost::nowide::narrow(wname);
try 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; ++version;
used_paths++; final_filename = just_filename + "(" + std::to_string(version) + ")";
std::replace(name.begin(), name.end(), '\\', '/'); }
// rename if file exists filename = final_filename + extension;
std::string filename = path.filename().string(); fs::path final_path = archive_dir / filename;
std::string extension = boost::filesystem::extension(path); std::string buffer((size_t)stat.m_uncomp_size, 0);
std::string just_filename = filename.substr(0, filename.size() - extension.size()); // Decompress action. We already has correct file index in stat structure.
std::string final_filename = just_filename; 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) {
size_t version = 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)));
while (fs::exists(archive_dir / (final_filename + extension))) BOOST_LOG_TRIVIAL(error) << error_log;
{ show_error(nullptr, error_log);
++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);
break; break;
} }
catch (const std::exception& e) // write buffer to file
{ fs::fstream file(final_path, std::ios::out | std::ios::binary | std::ios::trunc);
// ensure the zip archive is closed and rethrow the exception file.write(buffer.c_str(), buffer.size());
close_zip_reader(&archive); file.close();
throw Slic3r::FileIOError(e.what()); 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); close_zip_reader(&archive);