From cab62070b5239e52bb51e5abdafea76b5a67bda3 Mon Sep 17 00:00:00 2001 From: anjis Date: Wed, 8 Jul 2026 20:24:55 +0800 Subject: [PATCH] Fix garbled Chinese filenames when importing ZIP files. (#14633) * Fix garbled Chinese filenames when importing ZIP files. * fix: avoid repeated ZIP path CRC calculation --- src/libslic3r/miniz_extension.cpp | 39 ++++++++++++++++++++++++++++ src/libslic3r/miniz_extension.hpp | 1 + src/slic3r/GUI/FileArchiveDialog.cpp | 11 +------- src/slic3r/GUI/Plater.cpp | 10 +------ 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/miniz_extension.cpp b/src/libslic3r/miniz_extension.cpp index 424e76462c..53a4a8e9de 100644 --- a/src/libslic3r/miniz_extension.cpp +++ b/src/libslic3r/miniz_extension.cpp @@ -1,6 +1,8 @@ #include +#include #include "miniz_extension.hpp" +#include "Utils.hpp" #if defined(_MSC_VER) || defined(__MINGW64__) #include "boost/nowide/cstdio.hpp" @@ -15,6 +17,33 @@ namespace Slic3r { namespace { +std::string decode_zip_unicode_path_extra_field(const std::string& extra, const std::string& path) +{ + size_t offset = 0; + const mz_uint32 path_crc = mz_crc32(0, reinterpret_cast(path.data()), path.size()); + + while (offset + 4 <= extra.size()) { + const unsigned char* field = reinterpret_cast(extra.data() + offset); + const std::uint16_t len = field[2] | (static_cast(field[3]) << 8); + if (offset + 4 + len > extra.size()) + break; + + if (field[0] == 0x75 && field[1] == 0x70 && len >= 5 && field[4] == 0x01) { + const mz_uint32 stored_crc = + static_cast(field[5]) | + (static_cast(field[6]) << 8) | + (static_cast(field[7]) << 16) | + (static_cast(field[8]) << 24); + if (stored_crc == path_crc) + return std::string(extra.data() + offset + 9, extra.data() + offset + 4 + len); + } + + offset += 4 + len; + } + + return Slic3r::decode_path(path.c_str()); +} + bool open_zip(mz_zip_archive *zip, const char *fname, bool isread) { if (!zip) return false; @@ -76,6 +105,16 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname) bool close_zip_reader(mz_zip_archive *zip) { return close_zip(zip, true); } bool close_zip_writer(mz_zip_archive *zip) { return close_zip(zip, false); } +std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat) +{ + if (stat.m_is_utf8) + return stat.m_filename; + + std::string extra(1024, 0); + const size_t extra_size = mz_zip_reader_get_extra(zip, stat.m_file_index, extra.data(), extra.size()); + return decode_zip_unicode_path_extra_field(extra.substr(0, extra_size > 0 ? extra_size - 1 : 0), stat.m_filename); +} + MZ_Archive::MZ_Archive() { mz_zip_zero_struct(&arch); diff --git a/src/libslic3r/miniz_extension.hpp b/src/libslic3r/miniz_extension.hpp index 006226bf24..1a1c96689f 100644 --- a/src/libslic3r/miniz_extension.hpp +++ b/src/libslic3r/miniz_extension.hpp @@ -10,6 +10,7 @@ bool open_zip_reader(mz_zip_archive *zip, const std::string &fname_utf8); bool open_zip_writer(mz_zip_archive *zip, const std::string &fname_utf8); bool close_zip_reader(mz_zip_archive *zip); bool close_zip_writer(mz_zip_archive *zip); +std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat); class MZ_Archive { public: diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 18c804196f..982187211c 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -221,16 +221,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar 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); - boost::filesystem::path path; - size_t extra_size = mz_zip_reader_get_filename_from_extra(archive, i, extra.data(), extra.size()); - if (extra_size > 0) { - path = boost::filesystem::path(extra.substr(0, extra_size)); - } else { - wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = into_u8(wname); - path = boost::filesystem::path(name); - } + boost::filesystem::path path = Slic3r::decode_archive_entry_path(archive, stat); assert(!path.empty()); if (!path.has_extension()) continue; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 26b9217b3f..584386d984 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -13923,17 +13923,9 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) if (mz_zip_reader_file_stat(&archive, i, &stat)) { if (size != stat.m_uncomp_size) // size must fit continue; - wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = into_u8(wname); + std::string name = Slic3r::decode_archive_entry_path(&archive, stat); 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)