From d087a7db4237a36496586831ec22d5f9222715fb Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Wed, 16 Oct 2013 17:21:07 +0300 Subject: [PATCH] SkinInstaller/SkinPackager: Fix Unicode filename handling Existing .rmskin files need to be repackaged for proper Unicode filename support. Non-ASCII filenames can be used only if 'Minimum Rainmeter' is set to at least '3.0.1'. An error will be displayed otherwise. --- SkinInstaller/DialogInstall.cpp | 18 +++++++---- SkinInstaller/DialogPackage.cpp | 33 ++++++++++++++++----- SkinInstaller/DialogPackage.h | 1 + SkinInstaller/SkinInstaller.vcxproj | 1 + SkinInstaller/SkinInstaller.vcxproj.filters | 6 ++++ 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/SkinInstaller/DialogInstall.cpp b/SkinInstaller/DialogInstall.cpp index 5af1e997..dfae8582 100644 --- a/SkinInstaller/DialogInstall.cpp +++ b/SkinInstaller/DialogInstall.cpp @@ -405,11 +405,14 @@ bool DialogInstall::ReadPackage() // Helper to sets buffer with current file name auto getFileInfo = [&]()->bool { - char cBuffer[MAX_PATH]; + char cBuffer[MAX_PATH * 3]; unz_file_info ufi; - if (unzGetCurrentFileInfo(m_PackageUnzFile, &ufi, cBuffer, MAX_PATH, nullptr, 0, nullptr, 0) == UNZ_OK) + if (unzGetCurrentFileInfo( + m_PackageUnzFile, &ufi, cBuffer, _countof(cBuffer), nullptr, 0, nullptr, 0) == UNZ_OK) { - MultiByteToWideChar(CP_ACP, 0, cBuffer, strlen(cBuffer) + 1, buffer, MAX_PATH); + const uLong ZIP_UTF8_FLAG = 1 << 11; + const DWORD codePage = (ufi.flag & ZIP_UTF8_FLAG) ? CP_UTF8 : CP_ACP; + MultiByteToWideChar(codePage, 0, cBuffer, strlen(cBuffer) + 1, buffer, MAX_PATH); while (WCHAR* pos = wcschr(buffer, L'/')) *pos = L'\\'; return true; } @@ -713,11 +716,14 @@ bool DialogInstall::InstallPackage() // Helper to sets buffer with current file name auto getFileInfo = [&]()->bool { - char cBuffer[MAX_PATH]; + char cBuffer[MAX_PATH * 3]; unz_file_info ufi; - if (unzGetCurrentFileInfo(m_PackageUnzFile, &ufi, cBuffer, MAX_PATH, nullptr, 0, nullptr, 0) == UNZ_OK) + if (unzGetCurrentFileInfo( + m_PackageUnzFile, &ufi, cBuffer, _countof(cBuffer), nullptr, 0, nullptr, 0) == UNZ_OK) { - MultiByteToWideChar(CP_ACP, 0, cBuffer, strlen(cBuffer) + 1, buffer, MAX_PATH); + const uLong ZIP_UTF8_FLAG = 1 << 11; + const DWORD codePage = (ufi.flag & ZIP_UTF8_FLAG) ? CP_UTF8 : CP_ACP; + MultiByteToWideChar(codePage, 0, cBuffer, strlen(cBuffer) + 1, buffer, MAX_PATH); while (WCHAR* pos = wcschr(buffer, L'/')) *pos = L'\\'; return true; } diff --git a/SkinInstaller/DialogPackage.cpp b/SkinInstaller/DialogPackage.cpp index 4cbf7465..ffa53dbb 100644 --- a/SkinInstaller/DialogPackage.cpp +++ b/SkinInstaller/DialogPackage.cpp @@ -17,6 +17,7 @@ */ #include "StdAfx.h" +#include "../Common/StringUtil.h" #include "Application.h" #include "DialogPackage.h" #include "DialogInstall.h" @@ -37,7 +38,8 @@ DialogPackage::DialogPackage(HWND wnd) : Dialog(wnd), m_LoadLayout(false), m_MergeSkins(false), m_PackagerThread(), - m_ZipFile() + m_ZipFile(), + m_AllowNonAsciiFilenames(false) { } @@ -263,12 +265,16 @@ bool DialogPackage::CreatePackage() WritePrivateProfileString(L"rmskin", L"MinimumRainmeter", m_MinimumRainmeter.c_str(), tempFile); WritePrivateProfileString(L"rmskin", L"MinimumWindows", m_MinimumWindows.c_str(), tempFile); + // Only Skin Installer in Rainmeter 3.0.1 support UTF-8 filenames. + m_AllowNonAsciiFilenames = DialogInstall::CompareVersions(m_MinimumRainmeter, L"3.0.1") != -1; + // Create archive and add options file and header bitmap m_ZipFile = zipOpen(ConvertToAscii(m_TargetFile.c_str()).c_str(), APPEND_STATUS_CREATE); auto cleanup = [&]()->bool { zipClose(m_ZipFile, nullptr); + DeleteFile(m_TargetFile.c_str()); return false; }; @@ -379,17 +385,30 @@ unsigned __stdcall DialogPackage::PackagerThreadProc(void* pParam) bool DialogPackage::AddFileToPackage(const WCHAR* filePath, const WCHAR* zipPath) { - std::string zipPathAscii = ConvertToAscii(zipPath); - for (int i = 0, isize = zipPathAscii.length(); i < isize; ++i) + std::string zipPathUTF8 = StringUtil::NarrowUTF8(zipPath); + for (int i = 0, isize = zipPathUTF8.length(); i < isize; ++i) { - if (zipPathAscii[i] == '\\') + if ((zipPathUTF8[i] & 0x80) != 0) { - zipPathAscii[i] = '/'; + // UTF-8 lead bit is not zero so the string is non-ASCII. + if (!m_AllowNonAsciiFilenames) + { + return false; + } + } + + if (zipPathUTF8[i] == '\\') + { + zipPathUTF8[i] = '/'; } } - int open = zipOpenNewFileInZip(m_ZipFile, zipPathAscii.c_str(), nullptr, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); - if (open != ZIP_OK) + const uLong ZIP_UTF8_FLAG = 1 << 11; + zip_fileinfo fi = {0}; + if (zipOpenNewFileInZip4( + m_ZipFile, zipPathUTF8.c_str(), &fi, + nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION, + 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0, 0, ZIP_UTF8_FLAG) != ZIP_OK) { return false; } diff --git a/SkinInstaller/DialogPackage.h b/SkinInstaller/DialogPackage.h index 40d5d059..41f891b3 100644 --- a/SkinInstaller/DialogPackage.h +++ b/SkinInstaller/DialogPackage.h @@ -121,6 +121,7 @@ private: HANDLE m_PackagerThread; zipFile m_ZipFile; + bool m_AllowNonAsciiFilenames; }; #endif diff --git a/SkinInstaller/SkinInstaller.vcxproj b/SkinInstaller/SkinInstaller.vcxproj index 674c966a..3b120bc4 100644 --- a/SkinInstaller/SkinInstaller.vcxproj +++ b/SkinInstaller/SkinInstaller.vcxproj @@ -122,6 +122,7 @@ + diff --git a/SkinInstaller/SkinInstaller.vcxproj.filters b/SkinInstaller/SkinInstaller.vcxproj.filters index e82811cf..ee92e48f 100644 --- a/SkinInstaller/SkinInstaller.vcxproj.filters +++ b/SkinInstaller/SkinInstaller.vcxproj.filters @@ -19,6 +19,9 @@ {b726e25d-2977-424a-b45b-8ee60ce6bd49} + + {b69ece81-b900-41d8-9ccd-61476261c411} + @@ -81,6 +84,9 @@ Source Files + + Common +