1
- using Files . Helpers ;
1
+ using Files . Extensions ;
2
+ using Files . Helpers ;
2
3
using ICSharpCode . SharpZipLib . Zip ;
3
4
using Microsoft . Toolkit . Uwp ;
4
5
using System ;
5
6
using System . Collections . Generic ;
6
7
using System . IO ;
7
8
using System . Linq ;
8
9
using System . Runtime . InteropServices . WindowsRuntime ;
10
+ using System . Text ;
9
11
using System . Threading . Tasks ;
10
12
using Windows . Foundation ;
11
13
using Windows . Storage ;
@@ -16,6 +18,60 @@ namespace Files.Filesystem.StorageItems
16
18
{
17
19
public sealed class ZipStorageFolder : BaseStorageFolder
18
20
{
21
+ public Encoding ZipEncoding { get ; set ; } = null ;
22
+
23
+ static ZipStorageFolder ( )
24
+ {
25
+ // Register all supported codepages (default is UTF-X only)
26
+ Encoding . RegisterProvider ( CodePagesEncodingProvider . Instance ) ;
27
+ // Use extended ascii so you can convert the string back to bytes
28
+ ZipStrings . CodePage = Constants . Filesystem . ExtendedAsciiCodePage ;
29
+ }
30
+
31
+ public static string DecodeEntryName ( ZipEntry entry , Encoding zipEncoding )
32
+ {
33
+ if ( zipEncoding == null || entry . IsUnicodeText )
34
+ {
35
+ return entry . Name ;
36
+ }
37
+ var decoded = Common . Extensions . IgnoreExceptions ( ( ) =>
38
+ {
39
+ var rawBytes = Encoding . GetEncoding ( Constants . Filesystem . ExtendedAsciiCodePage ) . GetBytes ( entry . Name ) ;
40
+ return zipEncoding . GetString ( rawBytes ) ;
41
+ } ) ;
42
+ return decoded ?? entry . Name ;
43
+ }
44
+
45
+ public static Encoding DetectFileEncoding ( ZipFile zipFile )
46
+ {
47
+ long readEntries = 0 ;
48
+ Ude . CharsetDetector cdet = new Ude . CharsetDetector ( ) ;
49
+ foreach ( var entry in zipFile . OfType < ZipEntry > ( ) )
50
+ {
51
+ readEntries ++ ;
52
+ if ( entry . IsUnicodeText )
53
+ {
54
+ return Encoding . UTF8 ;
55
+ }
56
+ var guessedEncoding = Common . Extensions . IgnoreExceptions ( ( ) =>
57
+ {
58
+ var rawBytes = Encoding . GetEncoding ( Constants . Filesystem . ExtendedAsciiCodePage ) . GetBytes ( entry . Name ) ;
59
+ cdet . Feed ( rawBytes , 0 , rawBytes . Length ) ;
60
+ cdet . DataEnd ( ) ;
61
+ if ( cdet . Charset != null && cdet . Confidence >= 0.9 && ( readEntries >= Math . Min ( zipFile . Count , 50 ) ) )
62
+ {
63
+ return Encoding . GetEncoding ( cdet . Charset ) ;
64
+ }
65
+ return null ;
66
+ } ) ;
67
+ if ( guessedEncoding != null )
68
+ {
69
+ return guessedEncoding ;
70
+ }
71
+ }
72
+ return Encoding . UTF8 ;
73
+ }
74
+
19
75
public ZipStorageFolder ( string path , string containerPath )
20
76
{
21
77
Name = System . IO . Path . GetFileName ( path . TrimEnd ( '\\ ' , '/' ) ) ;
@@ -108,7 +164,7 @@ public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desi
108
164
zipFile . CommitUpdate ( ) ;
109
165
110
166
var wnt = new WindowsNameTransform ( ContainerPath ) ;
111
- return new ZipStorageFolder ( wnt . TransformFile ( zipDesiredName ) , ContainerPath ) ;
167
+ return new ZipStorageFolder ( wnt . TransformFile ( zipDesiredName ) , ContainerPath ) { ZipEncoding = ZipEncoding } ;
112
168
}
113
169
} ) ;
114
170
}
@@ -141,17 +197,18 @@ public override IAsyncOperation<IStorageItem> GetItemAsync(string name)
141
197
using ( ZipFile zipFile = new ZipFile ( new FileStream ( hFile , FileAccess . Read ) ) )
142
198
{
143
199
zipFile . IsStreamOwner = true ;
200
+ ZipEncoding ??= DetectFileEncoding ( zipFile ) ;
144
201
var entry = zipFile . GetEntry ( name ) ;
145
202
if ( entry != null )
146
203
{
147
204
var wnt = new WindowsNameTransform ( ContainerPath ) ;
148
205
if ( entry . IsDirectory )
149
206
{
150
- return new ZipStorageFolder ( wnt . TransformDirectory ( entry . Name ) , ContainerPath , entry ) ;
207
+ return new ZipStorageFolder ( wnt . TransformDirectory ( DecodeEntryName ( entry , ZipEncoding ) ) , ContainerPath , entry ) { ZipEncoding = ZipEncoding } ;
151
208
}
152
209
else
153
210
{
154
- return new ZipStorageFile ( wnt . TransformFile ( entry . Name ) , ContainerPath , entry ) ;
211
+ return new ZipStorageFile ( wnt . TransformFile ( DecodeEntryName ( entry , ZipEncoding ) ) , ContainerPath , entry ) ;
155
212
}
156
213
}
157
214
return null ;
@@ -187,12 +244,13 @@ public override IAsyncOperation<IReadOnlyList<IStorageItem>> GetItemsAsync()
187
244
using ( ZipFile zipFile = new ZipFile ( new FileStream ( hFile , FileAccess . Read ) ) )
188
245
{
189
246
zipFile . IsStreamOwner = true ;
247
+ ZipEncoding ??= DetectFileEncoding ( zipFile ) ;
190
248
var wnt = new WindowsNameTransform ( ContainerPath , true ) ; // Check with Path.GetFullPath
191
249
var items = new List < IStorageItem > ( ) ;
192
250
foreach ( var entry in zipFile . OfType < ZipEntry > ( ) ) // Returns all items recursively
193
251
{
194
- string winPath = System . IO . Path . GetFullPath ( entry . IsDirectory ? wnt . TransformDirectory ( entry . Name ) : wnt . TransformFile ( entry . Name ) ) ;
195
- if ( winPath . StartsWith ( Path ) ) // Child of self
252
+ string winPath = System . IO . Path . GetFullPath ( entry . IsDirectory ? wnt . TransformDirectory ( DecodeEntryName ( entry , ZipEncoding ) ) : wnt . TransformFile ( DecodeEntryName ( entry , ZipEncoding ) ) ) ;
253
+ if ( winPath . StartsWith ( Path . WithEnding ( " \\ " ) ) ) // Child of self
196
254
{
197
255
var split = winPath . Substring ( Path . Length ) . Split ( new [ ] { '\\ ' } , StringSplitOptions . RemoveEmptyEntries ) ;
198
256
if ( split . Length > 0 )
@@ -202,7 +260,7 @@ public override IAsyncOperation<IReadOnlyList<IStorageItem>> GetItemsAsync()
202
260
var itemPath = System . IO . Path . Combine ( Path , split [ 0 ] ) ;
203
261
if ( ! items . Any ( x => x . Path == itemPath ) )
204
262
{
205
- items . Add ( new ZipStorageFolder ( itemPath , ContainerPath , entry ) ) ;
263
+ items . Add ( new ZipStorageFolder ( itemPath , ContainerPath , entry ) { ZipEncoding = ZipEncoding } ) ;
206
264
}
207
265
}
208
266
else
0 commit comments