From e54a1201a957d67ed4bd66b763e146b7b54fcef4 Mon Sep 17 00:00:00 2001 From: zeromus Date: Thu, 8 Nov 2012 21:44:46 +0000 Subject: [PATCH] fix regressions in handling of weird characters in filenames/archives, dating from introduction of fex. also improve fex to support utf-8 extended filenames in zipfiles. --- desmume/src/windows/DeSmuME_2005.vcproj | 4 ++ desmume/src/windows/FEX_Interface.cpp | 42 +++++++++++++- desmume/src/windows/FEX_Interface.h | 2 + .../File_Extractor/fex/Zip_Extractor.cpp | 56 ++++++++++++++++++- .../File_Extractor/fex/Zip_Extractor.h | 3 + desmume/src/windows/OpenArchive.cpp | 15 ++++- 6 files changed, 116 insertions(+), 6 deletions(-) diff --git a/desmume/src/windows/DeSmuME_2005.vcproj b/desmume/src/windows/DeSmuME_2005.vcproj index 7a8d26a17..9d56651c6 100644 --- a/desmume/src/windows/DeSmuME_2005.vcproj +++ b/desmume/src/windows/DeSmuME_2005.vcproj @@ -988,6 +988,10 @@ RelativePath=".\File_Extractor\fex\Zip_Extractor.cpp" > + + diff --git a/desmume/src/windows/FEX_Interface.cpp b/desmume/src/windows/FEX_Interface.cpp index 9c80e0680..1b4022e85 100644 --- a/desmume/src/windows/FEX_Interface.cpp +++ b/desmume/src/windows/FEX_Interface.cpp @@ -129,8 +129,18 @@ ArchiveFile::ArchiveFile(const char* filename) if(!file) return; - m_filename = new char[strlen(filename)+1]; - strcpy(m_filename, filename); + //TODO - maybe windows only check here if we ever drop fex into portable code. unfortunately this is probably too unwieldy to ever happen + + //convert the filename to unicode + wchar_t temp_wchar[MAX_PATH*2]; + char filename_utf8[MAX_PATH*4]; + MultiByteToWideChar(CP_THREAD_ACP,0,filename,-1,temp_wchar,ARRAY_SIZE(temp_wchar)); + //now convert it back to utf-8. is there a way to do this in one step? + WideCharToMultiByte(CP_UTF8,0,temp_wchar,-1,filename_utf8,ARRAY_SIZE(filename_utf8), NULL, NULL); + + + m_filename = new char[strlen(filename_utf8)+1]; + strcpy(m_filename, filename_utf8); // detect archive type using format signature in file for(size_t i = 0; i < s_formatInfos.size() && m_typeIndex < 0; i++) @@ -187,11 +197,13 @@ ArchiveFile::ArchiveFile(const char* filename) m_items[0].name = new char[strlen(filename)+1]; strcpy(m_items[0].name, filename); + + m_items[0].wname = _wcsdup(temp_wchar); } else { fex_t * object; - fex_err_t err = fex_open_type( &object, filename, s_formatInfos[m_typeIndex].type ); + fex_err_t err = fex_open_type( &object, m_filename, s_formatInfos[m_typeIndex].type ); if ( !err ) { int numItems = 0; @@ -219,6 +231,21 @@ ArchiveFile::ArchiveFile(const char* filename) item.name = new char[strlen(name)+1]; strcpy(item.name, name); + const wchar_t* wname = fex_wname(object); + if(wname) + { + item.wname = _wcsdup(fex_wname(object)); + } + else + { + const char* name = fex_name(object); + wchar_t temp_wchar[MAX_PATH]; + //what code page to use??? who knows. + MultiByteToWideChar(CP_ACP,0,name,-1,temp_wchar,ARRAY_SIZE(temp_wchar)); + item.wname = _wcsdup(temp_wchar); + } + + err = fex_stat(object); if (err) break; @@ -240,6 +267,7 @@ ArchiveFile::~ArchiveFile() for(int i = 0; i < m_numItems; i++) { delete[] m_items[i].name; + free(m_items[i].wname); } delete[] m_items; delete[] m_filename; @@ -274,6 +302,14 @@ const char* ArchiveFile::GetItemName(int item) return m_items[item].name; } +const wchar_t* ArchiveFile::GetItemNameW(int item) +{ + //assert(item >= 0 && item < m_numItems); + if(!(item >= 0 && item < m_numItems)) return L""; + return m_items[item].wname; +} + + bool ArchiveFile::IsCompressed() { return (m_typeIndex >= 0); diff --git a/desmume/src/windows/FEX_Interface.h b/desmume/src/windows/FEX_Interface.h index 8bf504b3d..f03c215fa 100644 --- a/desmume/src/windows/FEX_Interface.h +++ b/desmume/src/windows/FEX_Interface.h @@ -39,6 +39,7 @@ struct ArchiveFile int GetNumItems(); int GetItemSize(int item); const char* GetItemName(int item); + const wchar_t* GetItemNameW(int item); int ExtractItem(int item, unsigned char* outBuffer, int bufSize) const; // returns size, or 0 if failed int ExtractItem(int item, const char* outFilename) const; @@ -50,6 +51,7 @@ protected: { int size; char* name; + wchar_t* wname; s64 offset; }; ArchiveItem* m_items; diff --git a/desmume/src/windows/File_Extractor/fex/Zip_Extractor.cpp b/desmume/src/windows/File_Extractor/fex/Zip_Extractor.cpp index 866c588a4..1c3826727 100644 --- a/desmume/src/windows/File_Extractor/fex/Zip_Extractor.cpp +++ b/desmume/src/windows/File_Extractor/fex/Zip_Extractor.cpp @@ -34,6 +34,8 @@ int const disk_block_size = 4 * 1024; // Read buffer used for extracting file data int const read_buf_size = 16 * 1024; +#include + struct header_t { char type [4]; @@ -250,12 +252,64 @@ blargg_err_t Zip_Extractor::update_info( bool advance_first ) if ( !advance_first ) { + char unterminate = e.filename[len]; e.filename [len] = 0; // terminate name + std::string fname = e.filename; if ( is_normal_file( e, len ) ) { - set_name( e.filename ); + e.filename[len] = unterminate; + name.resize(fname.size()+1); + if(len != 0) + { + memcpy(name.begin(),fname.c_str(),len); + name[name.size()-1] = 0; + } + set_name( name.begin() ); set_info( get_le32( e.size ), get_le32( e.date ), get_le32( e.crc ) ); + + unsigned extra_len = get_le32(e.extra_len); + + //walk over extra fields + unsigned i = len; + while(i < extra_len + len) + { + unsigned id = get_le16(e.filename + i); + i += 2; + unsigned exlen = get_le16(e.filename + i); + i += 2; + int exfield = i; + i += exlen; + if(id == 0x7075) //INFO-ZIP unicode path extra field (contains version, checksum, and utf-8 filename) + { + unsigned version = (unsigned char)*(e.filename + exfield); + if(version == 1) + { + exfield += 1; //skip version + exfield += 4; //skip crc + //the remainder is a utf-8 filename + int fnamelen = exlen-5; + char* tempbuf = (char*)malloc(fnamelen + 1); + memcpy(tempbuf,e.filename + exfield, fnamelen); + tempbuf[fnamelen] = 0; + wchar_t* wfname_buf = blargg_to_wide(tempbuf); + std::wstring wfname = wfname_buf; + free(tempbuf); + free(wfname_buf); + + size_t wfname_len = wfname.size(); + + this->wname.resize(wfname_len+1); + if(wfname_len != 0) + { + memcpy(this->wname.begin(),wfname.c_str(),wfname_len*sizeof(wchar_t)); + wname[wname.size()-1] = 0; + } + set_name( name.begin(), wname.begin() ); + + } + } + } break; } } diff --git a/desmume/src/windows/File_Extractor/fex/Zip_Extractor.h b/desmume/src/windows/File_Extractor/fex/Zip_Extractor.h index 46ced04b7..71e67aa9f 100644 --- a/desmume/src/windows/File_Extractor/fex/Zip_Extractor.h +++ b/desmume/src/windows/File_Extractor/fex/Zip_Extractor.h @@ -40,6 +40,9 @@ private: blargg_err_t first_read( int count ); void reorder_entry_header( int offset ); static blargg_err_t inflater_read( void* data, void* out, int* count ); + + blargg_vector name; + blargg_vector wname; }; #endif diff --git a/desmume/src/windows/OpenArchive.cpp b/desmume/src/windows/OpenArchive.cpp index eaee059bd..1619bb089 100644 --- a/desmume/src/windows/OpenArchive.cpp +++ b/desmume/src/windows/OpenArchive.cpp @@ -51,7 +51,10 @@ tryagain: { if(archive.GetItemSize(i)) { - const char* name = archive.GetItemName(i); + //bullshit time. convert to system locale + char name[MAX_PATH]; + WideCharToMultiByte(CP_ACP,0,archive.GetItemNameW(i),-1,name,ARRAY_SIZE(name), NULL, NULL); + const char* ext = strrchr(name, '.'); bool ok = true; if(ext++) @@ -454,7 +457,15 @@ bool ObtainFile(const char* Name, char *const & LogicalName, char *const & Physi s_tempFiles.ReleaseFile(TempFileName); s_tempFiles.ReleaseFile(PhysicalName); strcpy(PhysicalName, TempFileName); - _snprintf(LogicalName + strlen(LogicalName), 1024 - (strlen(LogicalName)+1), "|%s", archive.GetItemName(item)); + + const wchar_t* itemNameW = archive.GetItemNameW(item); + + //convert the itemname to local encoding + char itemname_utf8[MAX_PATH*4]; + WideCharToMultiByte(CP_THREAD_ACP,0,itemNameW,-1,itemname_utf8,ARRAY_SIZE(itemname_utf8),NULL,NULL); + + //strcat(LogicalName,itemname_utf8); + _snprintf(LogicalName + strlen(LogicalName), 1024 - (strlen(LogicalName)+1), "|%s", itemname_utf8); } } }