Fix garbled Chinese filenames when importing ZIP files. (#14633)

* Fix garbled Chinese filenames when importing ZIP files.

* fix: avoid repeated ZIP path CRC calculation
This commit is contained in:
anjis
2026-07-08 20:24:55 +08:00
committed by GitHub
parent 12b63ebe36
commit cab62070b5
4 changed files with 42 additions and 19 deletions

View File

@@ -1,6 +1,8 @@
#include <exception>
#include <cstdint>
#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<const unsigned char*>(path.data()), path.size());
while (offset + 4 <= extra.size()) {
const unsigned char* field = reinterpret_cast<const unsigned char*>(extra.data() + offset);
const std::uint16_t len = field[2] | (static_cast<std::uint16_t>(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<mz_uint32>(field[5]) |
(static_cast<mz_uint32>(field[6]) << 8) |
(static_cast<mz_uint32>(field[7]) << 16) |
(static_cast<mz_uint32>(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);

View File

@@ -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:

View File

@@ -221,16 +221,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
std::vector<std::pair<boost::filesystem::path, size_t>> 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;

View File

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