diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
index 0afd438c6..f448e0fad 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
@@ -205,7 +205,12 @@ public bool CanPatchEntries
protected byte[] AESAuthCode;
///
- public Encoding ZipCryptoEncoding { get; set; } = StringCodec.DefaultZipCryptoEncoding;
+ public Encoding ZipCryptoEncoding {
+ get => _stringCodec.ZipCryptoEncoding;
+ set {
+ _stringCodec = _stringCodec.WithZipCryptoEncoding(value);
+ }
+ }
///
/// Encrypt a block of data
@@ -508,6 +513,9 @@ public override void Write(byte[] buffer, int offset, int count)
private bool isClosed_;
+ ///
+ protected StringCodec _stringCodec = ZipStrings.GetStringCodec();
+
#endregion Instance Fields
}
}
diff --git a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs
index 29185cbec..baa1771cb 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs
@@ -358,7 +358,7 @@ public bool UseUnicode
public int LegacyCodePage
{
get => _stringCodec.CodePage;
- set => _stringCodec.CodePage = value;
+ set => _stringCodec = StringCodec.FromCodePage(value);
}
///
@@ -579,7 +579,7 @@ public void ExtractZip(Stream inputStream, string targetDirectory,
directoryFilter_ = new NameFilter(directoryFilter);
restoreDateTimeOnExtract_ = restoreDateTime;
- using (zipFile_ = new ZipFile(inputStream, !isStreamOwner))
+ using (zipFile_ = new ZipFile(inputStream, !isStreamOwner, _stringCodec))
{
if (password_ != null)
{
@@ -994,8 +994,7 @@ private static bool NameIsValid(string name)
private INameTransform extractNameTransform_;
private UseZip64 useZip64_ = UseZip64.Dynamic;
private CompressionLevel compressionLevel_ = CompressionLevel.DEFAULT_COMPRESSION;
- private StringCodec _stringCodec = new StringCodec();
-
+ private StringCodec _stringCodec = ZipStrings.GetStringCodec();
private string password_;
#endregion Instance Fields
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
index 3abe9516b..69bb9f6a9 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
@@ -7,6 +7,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Security.Cryptography;
using System.Text;
@@ -504,6 +505,7 @@ public ZipFile(Stream stream) :
///
/// The to read archive data from.
/// true to leave the stream open when the ZipFile is disposed, false to dispose of it
+ ///
///
/// An i/o error occurs
///
@@ -516,7 +518,7 @@ public ZipFile(Stream stream) :
///
/// The stream argument is null.
///
- public ZipFile(Stream stream, bool leaveOpen)
+ public ZipFile(Stream stream, bool leaveOpen, StringCodec stringCodec = null)
{
if (stream == null)
{
@@ -531,6 +533,11 @@ public ZipFile(Stream stream, bool leaveOpen)
baseStream_ = stream;
isStreamOwner = !leaveOpen;
+ if (stringCodec != null)
+ {
+ _stringCodec = stringCodec;
+ }
+
if (baseStream_.Length > 0)
{
try
@@ -736,14 +743,20 @@ public ZipEntry this[int index]
public Encoding ZipCryptoEncoding
{
get => _stringCodec.ZipCryptoEncoding;
- set => _stringCodec.ZipCryptoEncoding = value;
+ set => _stringCodec = _stringCodec.WithZipCryptoEncoding(value);
}
///
public StringCodec StringCodec
{
- get => _stringCodec;
- set => _stringCodec = value;
+ set {
+ _stringCodec = value;
+ if (!isNewArchive_)
+ {
+ // Since the string codec was changed
+ ReadEntries();
+ }
+ }
}
#endregion Properties
@@ -1592,7 +1605,7 @@ public void CommitUpdate()
{
RunUpdates();
}
- else if (commentEdited_)
+ else if (commentEdited_ && !isNewArchive_)
{
UpdateCommentOnly();
}
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs
index 905d856c8..ec63d7943 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs
@@ -95,7 +95,7 @@ internal static int WriteLocalHeader(Stream stream, ZipEntry entry, out EntryPat
}
}
- byte[] name = stringCodec.ZipOutputEncoding.GetBytes(entry.Name);
+ byte[] name = stringCodec.ZipEncoding(entry.IsUnicodeText).GetBytes(entry.Name);
if (name.Length > 0xFFFF)
{
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs
index e49ebddfb..ddfd9086b 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs
@@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq;
namespace ICSharpCode.SharpZipLib.Zip
{
@@ -104,6 +105,21 @@ public ZipInputStream(Stream baseInputStream, int bufferSize)
internalReader = new ReadDataHandler(ReadingNotAvailable);
}
+ ///
+ /// Creates a new Zip input stream, for reading a zip archive.
+ ///
+ /// The underlying providing data.
+ ///
+ public ZipInputStream(Stream baseInputStream, StringCodec stringCodec)
+ : base(baseInputStream, new Inflater(true))
+ {
+ internalReader = new ReadDataHandler(ReadingNotAvailable);
+ if (stringCodec != null)
+ {
+ _stringCodec = stringCodec;
+ }
+ }
+
#endregion Constructors
///
@@ -558,7 +574,9 @@ private int InitialRead(byte[] destination, int offset, int count)
// Generate and set crypto transform...
var managed = new PkzipClassicManaged();
+ Console.WriteLine($"Input Encoding: {_stringCodec.ZipCryptoEncoding.EncodingName}");
byte[] key = PkzipClassic.GenerateKeys(_stringCodec.ZipCryptoEncoding.GetBytes(password));
+ Console.WriteLine($"Input Bytes: {string.Join(", ", key.Select(b => $"{b:x2}").ToArray())}");
inputBuffer.CryptoTransform = managed.CreateDecryptor(key, null);
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
index e21d7fb54..3f4d0240b 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
@@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@@ -80,7 +81,12 @@ public ZipOutputStream(Stream baseOutputStream, int bufferSize)
{
}
- internal ZipOutputStream(Stream baseOutputStream, StringCodec stringCodec) : this(baseOutputStream)
+ ///
+ /// Creates a new Zip output stream, writing a zip archive.
+ ///
+ /// The output stream to which the archive contents are written.
+ ///
+ public ZipOutputStream(Stream baseOutputStream, StringCodec stringCodec) : this(baseOutputStream)
{
_stringCodec = stringCodec;
}
@@ -160,7 +166,7 @@ public UseZip64 UseZip64
/// Defaults to , set to null to disable transforms and use names as supplied.
///
public INameTransform NameTransform { get; set; } = new PathTransformer();
-
+
///
/// Get/set the password used for encryption.
///
@@ -742,7 +748,9 @@ private byte[] CreateZipCryptoHeader(long crcValue)
private void InitializeZipCryptoPassword(string password)
{
var pkManaged = new PkzipClassicManaged();
+ Console.WriteLine($"Output Encoding: {ZipCryptoEncoding.EncodingName}");
byte[] key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(password));
+ Console.WriteLine($"Output Bytes: {string.Join(", ", key.Select(b => $"{b:x2}").ToArray())}");
cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
}
@@ -970,8 +978,6 @@ public override void Flush()
///
private string password;
- private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec();
-
#endregion Instance Fields
#region Static Fields
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs
index 29fa98014..3eab416ef 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs
@@ -20,7 +20,7 @@ public static bool IsZipUnicode(this Encoding e)
///
public static class ZipStrings
{
- static readonly StringCodec CompatCodec = new StringCodec();
+ static StringCodec CompatCodec = StringCodec.Default;
private static bool compatibilityMode;
@@ -29,7 +29,7 @@ public static class ZipStrings
///
///
public static StringCodec GetStringCodec()
- => compatibilityMode ? CompatCodec : new StringCodec();
+ => compatibilityMode ? CompatCodec : StringCodec.Default;
///
[Obsolete("Use ZipFile/Zip*Stream StringCodec instead")]
@@ -38,7 +38,11 @@ public static int CodePage
get => CompatCodec.CodePage;
set
{
- CompatCodec.CodePage = value;
+ CompatCodec = new StringCodec(CompatCodec.ForceZipLegacyEncoding, Encoding.GetEncoding(value))
+ {
+ ZipArchiveCommentEncoding = CompatCodec.ZipArchiveCommentEncoding,
+ ZipCryptoEncoding = CompatCodec.ZipCryptoEncoding,
+ };
compatibilityMode = true;
}
}
@@ -54,7 +58,11 @@ public static bool UseUnicode
get => !CompatCodec.ForceZipLegacyEncoding;
set
{
- CompatCodec.ForceZipLegacyEncoding = !value;
+ CompatCodec = new StringCodec(!value, CompatCodec.LegacyEncoding)
+ {
+ ZipArchiveCommentEncoding = CompatCodec.ZipArchiveCommentEncoding,
+ ZipCryptoEncoding = CompatCodec.ZipCryptoEncoding,
+ };
compatibilityMode = true;
}
}
@@ -102,25 +110,42 @@ public static byte[] ConvertToArray(int flags, string str)
///
public class StringCodec
{
- static StringCodec()
+ internal StringCodec(bool forceLegacyEncoding, Encoding legacyEncoding)
{
- try
- {
- var platformCodepage = Encoding.Default.CodePage;
- SystemDefaultCodePage = (platformCodepage == 1 || platformCodepage == 2 || platformCodepage == 3 || platformCodepage == 42) ? FallbackCodePage : platformCodepage;
- }
- catch
- {
- SystemDefaultCodePage = FallbackCodePage;
- }
-
- SystemDefaultEncoding = Encoding.GetEncoding(SystemDefaultCodePage);
+ LegacyEncoding = legacyEncoding;
+ ForceZipLegacyEncoding = forceLegacyEncoding;
+ ZipArchiveCommentEncoding = legacyEncoding;
+ ZipCryptoEncoding = legacyEncoding;
}
+ ///
+ /// Creates a StringCodec that uses the system default encoder or UTF-8 depending on whether the zip entry Unicode flag is set
+ ///
+ public static StringCodec Default
+ => new StringCodec(false, SystemDefaultEncoding);
+
+ ///
+ /// Creates a StringCodec that uses an encoding from the specified code page except for zip entries with the Unicode flag
+ ///
+ public static StringCodec FromCodePage(int codePage)
+ => new StringCodec(false, Encoding.GetEncoding(codePage));
+
+ ///
+ /// Creates a StringCodec that uses an the specified encoding, except for zip entries with the Unicode flag
+ ///
+ public static StringCodec FromEncoding(Encoding encoding)
+ => new StringCodec(false, encoding);
+
+ ///
+ /// Creates a StringCodec that uses the zip specification encoder or UTF-8 depending on whether the zip entry Unicode flag is set
+ ///
+ public static StringCodec WithStrictSpecEncoding()
+ => new StringCodec(false, Encoding.GetEncoding(ZipSpecCodePage));
+
///
/// If set, use the encoding set by for zip entries instead of the defaults
///
- public bool ForceZipLegacyEncoding { get; set; }
+ public bool ForceZipLegacyEncoding { get; internal set; }
///
/// The default encoding used for ZipCrypto passwords in zip files, set to
@@ -137,7 +162,8 @@ static StringCodec()
///
/// Returns if is set, otherwise it returns the encoding indicated by
///
- public Encoding ZipEncoding(bool unicode) => unicode ? UnicodeZipEncoding : _legacyEncoding;
+ public Encoding ZipEncoding(bool unicode)
+ => unicode ? UnicodeZipEncoding : LegacyEncoding;
///
/// Returns the appropriate encoding for an input according to .
@@ -145,22 +171,19 @@ static StringCodec()
///
///
///
- public Encoding ZipInputEncoding(GeneralBitFlags flags) => ZipInputEncoding((int)flags);
+ public Encoding ZipInputEncoding(GeneralBitFlags flags)
+ => ZipEncoding(!ForceZipLegacyEncoding && flags.HasAny(GeneralBitFlags.UnicodeText));
///
- public Encoding ZipInputEncoding(int flags) => ZipEncoding(!ForceZipLegacyEncoding && (flags & (int)GeneralBitFlags.UnicodeText) != 0);
+ public Encoding ZipInputEncoding(int flags) => ZipInputEncoding((GeneralBitFlags)flags);
/// Code page encoding, used for non-unicode strings
///
/// The original Zip specification (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) states
/// that file names should only be encoded with IBM Code Page 437 or UTF-8.
/// In practice, most zip apps use OEM or system encoding (typically cp437 on Windows).
- /// Let's be good citizens and default to UTF-8 http://utf8everywhere.org/
///
- private Encoding _legacyEncoding = SystemDefaultEncoding;
-
- private Encoding _zipArchiveCommentEncoding;
- private Encoding _zipCryptoEncoding;
+ public Encoding LegacyEncoding { get; internal set; }
///
/// Returns the UTF-8 code page (65001) used for zip entries with unicode flag set
@@ -171,43 +194,67 @@ static StringCodec()
/// Code page used for non-unicode strings and legacy zip encoding (if is set).
/// Default value is
///
- public int CodePage
- {
- get => _legacyEncoding.CodePage;
- set => _legacyEncoding = (value < 4 || value > 65535 || value == 42)
- ? throw new ArgumentOutOfRangeException(nameof(value))
- : Encoding.GetEncoding(value);
- }
+ public int CodePage => LegacyEncoding.CodePage;
- private const int FallbackCodePage = 437;
+ ///
+ /// The non-unicode code page that should be used according to the zip specification
+ ///
+ public const int ZipSpecCodePage = 437;
///
- /// Operating system default codepage, or if it could not be retrieved, the fallback code page IBM 437.
+ /// Operating system default codepage.
///
- public static int SystemDefaultCodePage { get; }
+ public static int SystemDefaultCodePage => SystemDefaultEncoding.CodePage;
///
- /// The system default encoding, based on
+ /// The system default encoding.
///
- public static Encoding SystemDefaultEncoding { get; }
+ public static Encoding SystemDefaultEncoding => Encoding.GetEncoding(0);
///
/// The encoding used for the zip archive comment. Defaults to the encoding for , since
/// no unicode flag can be set for it in the files.
///
- public Encoding ZipArchiveCommentEncoding
- {
- get => _zipArchiveCommentEncoding ?? _legacyEncoding;
- set => _zipArchiveCommentEncoding = value;
- }
+ public Encoding ZipArchiveCommentEncoding { get; internal set; }
///
/// The encoding used for the ZipCrypto passwords. Defaults to .
///
- public Encoding ZipCryptoEncoding
- {
- get => _zipCryptoEncoding ?? DefaultZipCryptoEncoding;
- set => _zipCryptoEncoding = value;
- }
+ public Encoding ZipCryptoEncoding { get; internal set; }
+
+ ///
+ /// Create a copy of this StringCodec with the specified zip archive comment encoding
+ ///
+ ///
+ ///
+ public StringCodec WithZipArchiveCommentEncoding(Encoding commentEncoding)
+ => new StringCodec(ForceZipLegacyEncoding, LegacyEncoding)
+ {
+ ZipArchiveCommentEncoding = commentEncoding,
+ ZipCryptoEncoding = ZipCryptoEncoding
+ };
+
+ ///
+ /// Create a copy of this StringCodec with the specified zip crypto password encoding
+ ///
+ ///
+ ///
+ public StringCodec WithZipCryptoEncoding(Encoding cryptoEncoding)
+ => new StringCodec(ForceZipLegacyEncoding, LegacyEncoding)
+ {
+ ZipArchiveCommentEncoding = ZipArchiveCommentEncoding,
+ ZipCryptoEncoding = cryptoEncoding
+ };
+
+ ///
+ /// Create a copy of this StringCodec that ignores the Unicode flag when reading entries
+ ///
+ ///
+ public StringCodec WithForcedLegacyEncoding()
+ => new StringCodec(true, LegacyEncoding)
+ {
+ ZipArchiveCommentEncoding = ZipArchiveCommentEncoding,
+ ZipCryptoEncoding = ZipCryptoEncoding
+ };
}
}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs
index 8026668ff..f3bf9a995 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs
@@ -73,57 +73,6 @@ private void ExerciseZip(CompressionMethod method, int compressionLevel,
}
}
- private string DescribeAttributes(FieldAttributes attributes)
- {
- string att = string.Empty;
- if ((FieldAttributes.Public & attributes) != 0)
- {
- att = att + "Public,";
- }
-
- if ((FieldAttributes.Static & attributes) != 0)
- {
- att = att + "Static,";
- }
-
- if ((FieldAttributes.Literal & attributes) != 0)
- {
- att = att + "Literal,";
- }
-
- if ((FieldAttributes.HasDefault & attributes) != 0)
- {
- att = att + "HasDefault,";
- }
-
- if ((FieldAttributes.InitOnly & attributes) != 0)
- {
- att = att + "InitOnly,";
- }
-
- if ((FieldAttributes.Assembly & attributes) != 0)
- {
- att = att + "Assembly,";
- }
-
- if ((FieldAttributes.FamANDAssem & attributes) != 0)
- {
- att = att + "FamANDAssembly,";
- }
-
- if ((FieldAttributes.FamORAssem & attributes) != 0)
- {
- att = att + "FamORAssembly,";
- }
-
- if ((FieldAttributes.HasFieldMarshal & attributes) != 0)
- {
- att = att + "HasFieldMarshal,";
- }
-
- return att;
- }
-
///
/// Invalid passwords should be detected early if possible, seekable stream
/// Note: Have a 1/255 chance of failing due to CRC collision (hence retried once)
@@ -803,7 +752,7 @@ private object UnZipZeroLength(byte[] zipped)
[TestCase("a/b/c/d/e/f/g/h/SomethingLikeAnArchiveName.txt")]
public void LegacyNameConversion(string name)
{
- var encoding = new StringCodec().ZipEncoding(false);
+ var encoding = StringCodec.Default.ZipEncoding(false);
byte[] intermediate = encoding.GetBytes(name);
string final = encoding.GetString(intermediate);
@@ -816,22 +765,22 @@ public void UnicodeNameConversion()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
- var codec = new StringCodec() {CodePage = 850};
+ var codec = StringCodec.FromCodePage(850);
string sample = "Hello world";
byte[] rawData = Encoding.ASCII.GetBytes(sample);
- var converted = codec.ZipInputEncoding(0).GetString(rawData);
+ var converted = codec.LegacyEncoding.GetString(rawData);
Assert.AreEqual(sample, converted);
- converted = codec.ZipInputEncoding((int)GeneralBitFlags.UnicodeText).GetString(rawData);
+ converted = codec.ZipInputEncoding(GeneralBitFlags.UnicodeText).GetString(rawData);
Assert.AreEqual(sample, converted);
// This time use some greek characters
sample = "\u03A5\u03d5\u03a3";
rawData = Encoding.UTF8.GetBytes(sample);
- converted = codec.ZipInputEncoding((int)GeneralBitFlags.UnicodeText).GetString(rawData);
+ converted = codec.ZipInputEncoding(GeneralBitFlags.UnicodeText).GetString(rawData);
Assert.AreEqual(sample, converted);
}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStringsTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStringsTests.cs
new file mode 100644
index 000000000..cd213df6e
--- /dev/null
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStringsTests.cs
@@ -0,0 +1,239 @@
+using ICSharpCode.SharpZipLib.Tests.TestSupport;
+using ICSharpCode.SharpZipLib.Tests.Zip;
+using ICSharpCode.SharpZipLib.Zip;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Does = ICSharpCode.SharpZipLib.Tests.TestSupport.Does;
+
+// As there is no way to order the test namespace execution order we use a name that should be alphabetically sorted before any other namespace
+// This is because we have one test that only works when no encoding provider has been loaded which is not reversable once done.
+namespace ICSharpCode.SharpZipLib.Tests._Zip
+{
+ [TestFixture]
+ [Order(1)]
+ public class ZipStringsTests
+ {
+ [Test]
+ [Order(1)]
+ // NOTE: This test needs to be run before any test registering CodePagesEncodingProvider.Instance
+ public void TestSystemDefaultEncoding()
+ {
+ Console.WriteLine($"Default encoding before registering provider: {Encoding.GetEncoding(0).EncodingName}");
+ Encoding.RegisterProvider(new TestEncodingProvider());
+ Console.WriteLine($"Default encoding after registering provider: {Encoding.GetEncoding(0).EncodingName}");
+
+ // Initialize a default StringCodec
+ var sc = StringCodec.Default;
+
+ var legacyEncoding = sc.ZipEncoding(false);
+ Assert.That(legacyEncoding.EncodingName, Is.EqualTo(TestEncodingProvider.DefaultEncodingName));
+ Assert.That(legacyEncoding.CodePage, Is.EqualTo(TestEncodingProvider.DefaultEncodingCodePage));
+ }
+
+ [Test]
+ [Order(2)]
+ public void TestFastZipRoundTripWithCodePage()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ using var ms = new MemoryStream();
+ using var zipFile = new TempFile();
+ using var srcDir = new TempDir();
+ using var dstDir = new TempDir();
+
+ srcDir.CreateDummyFile("file1");
+ srcDir.CreateDummyFile("слово");
+
+ foreach(var f in Directory.EnumerateFiles(srcDir.FullName))
+ {
+ Console.WriteLine(f);
+ }
+
+ var fzCreate = new FastZip() { StringCodec = StringCodec.FromCodePage(866), UseUnicode = false };
+ fzCreate.CreateZip(zipFile, srcDir.FullName, true, null);
+
+ var fzExtract = new FastZip() { StringCodec = StringCodec.FromCodePage(866) };
+ fzExtract.ExtractZip(zipFile, dstDir.FullName, null);
+
+ foreach (var f in Directory.EnumerateFiles(dstDir.FullName))
+ {
+ Console.WriteLine(f);
+ }
+
+ Assert.That(dstDir.GetFile("file1").FullName, Does.Exist);
+ Assert.That(dstDir.GetFile("слово").FullName, Does.Exist);
+ }
+
+
+ [Test]
+ [Order(2)]
+ public void TestZipFileRoundTripWithCodePage()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ using var ms = new MemoryStream();
+ using (var zf = ZipFile.Create(ms))
+ {
+ zf.StringCodec = StringCodec.FromCodePage(866);
+ zf.BeginUpdate();
+ zf.Add(MemoryDataSource.Empty, "file1", CompressionMethod.Stored, useUnicodeText: false);
+ zf.Add(MemoryDataSource.Empty, "слово", CompressionMethod.Stored, useUnicodeText: false);
+ zf.CommitUpdate();
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using (var zf = new ZipFile(ms, false, StringCodec.FromCodePage(866)) { IsStreamOwner = false })
+ {
+ Assert.That(zf.GetEntry("file1"), Is.Not.Null);
+ Assert.That(zf.GetEntry("слово"), Is.Not.Null);
+ }
+
+ }
+
+ [Test]
+ [Order(2)]
+ public void TestZipStreamRoundTripWithCodePage()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ using var ms = new MemoryStream();
+ using (var zos = new ZipOutputStream(ms, StringCodec.FromCodePage(866)) { IsStreamOwner = false })
+ {
+ zos.PutNextEntry(new ZipEntry("file1") { IsUnicodeText = false });
+ zos.PutNextEntry(new ZipEntry("слово") { IsUnicodeText = false });
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using (var zis = new ZipInputStream(ms, StringCodec.FromCodePage(866)) { IsStreamOwner = false })
+ {
+ Assert.That(zis.GetNextEntry().Name, Is.EqualTo("file1"));
+ Assert.That(zis.GetNextEntry().Name, Is.EqualTo("слово"));
+ }
+
+ }
+
+ [Test]
+ [Order(2)]
+ public void TestZipCryptoPasswordEncodingRoundtrip()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ var content = Utils.GetDummyBytes(32);
+
+ using var ms = new MemoryStream();
+ using (var zos = new ZipOutputStream(ms, StringCodec.FromCodePage(866)) { IsStreamOwner = false })
+ {
+ zos.Password = "слово";
+ zos.PutNextEntry(new ZipEntry("file1"));
+ zos.Write(content, 0, content.Length);
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using (var zis = new ZipInputStream(ms, StringCodec.FromCodePage(866)) { IsStreamOwner = false })
+ {
+ zis.Password = "слово";
+ var entry = zis.GetNextEntry();
+ var output = new byte[32];
+ Assert.That(zis.Read(output, 0, 32), Is.EqualTo(32));
+ Assert.That(output, Is.EqualTo(content));
+ }
+
+ }
+
+ [Test]
+ [Order(2)]
+ public void TestZipStreamCommentEncodingRoundtrip()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ var content = Utils.GetDummyBytes(32);
+
+ using var ms = new MemoryStream();
+ using (var zos = new ZipOutputStream(ms, StringCodec.FromCodePage(866)) { IsStreamOwner = false })
+ {
+ zos.SetComment("слово");
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using var zf = new ZipFile(ms, false, StringCodec.FromCodePage(866));
+ Assert.That(zf.ZipFileComment, Is.EqualTo("слово"));
+ }
+
+
+ [Test]
+ [Order(2)]
+ public void TestZipFileCommentEncodingRoundtrip()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+
+ var content = Utils.GetDummyBytes(32);
+
+ using var ms = new MemoryStream();
+ using (var zf = ZipFile.Create(ms))
+ {
+ zf.StringCodec = StringCodec.FromCodePage(866);
+ zf.BeginUpdate();
+ zf.SetComment("слово");
+ zf.CommitUpdate();
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ using (var zf = new ZipFile(ms, false, StringCodec.FromCodePage(866)))
+ {
+ Assert.That(zf.ZipFileComment, Is.EqualTo("слово"));
+ }
+ }
+ }
+
+
+ internal class TestEncodingProvider : EncodingProvider
+ {
+ internal static string DefaultEncodingName = "TestDefaultEncoding";
+ internal static int DefaultEncodingCodePage = -37;
+
+ class TestDefaultEncoding : Encoding
+ {
+ public override string EncodingName => DefaultEncodingName;
+ public override int CodePage => DefaultEncodingCodePage;
+
+ public override int GetByteCount(char[] chars, int index, int count)
+ => UTF8.GetByteCount(chars, index, count);
+
+ public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
+ => UTF8.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
+
+ public override int GetCharCount(byte[] bytes, int index, int count)
+ => UTF8.GetCharCount(bytes, index, count);
+
+ public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
+ => UTF8.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
+
+ public override int GetMaxByteCount(int charCount) => UTF8.GetMaxByteCount(charCount);
+
+ public override int GetMaxCharCount(int byteCount) => UTF8.GetMaxCharCount(byteCount);
+ }
+
+ TestDefaultEncoding testDefaultEncoding = new TestDefaultEncoding();
+
+ public override Encoding GetEncoding(int codepage)
+ => (codepage == 0 || codepage == DefaultEncodingCodePage) ? testDefaultEncoding : null;
+
+ public override Encoding GetEncoding(string name)
+ => DefaultEncodingName == name ? testDefaultEncoding : null;
+
+#if NET6_0_OR_GREATER
+ public override IEnumerable GetEncodings()
+ {
+ yield return new EncodingInfo(this, DefaultEncodingCodePage, DefaultEncodingName, DefaultEncodingName);
+ }
+#endif
+ }
+}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipTests.cs
index dc398000e..885c976f3 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipTests.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipTests.cs
@@ -121,6 +121,8 @@ public MemoryDataSource(byte[] data)
data_ = data;
}
+ public static MemoryDataSource Empty => new MemoryDataSource(Array.Empty());
+
#endregion Constructors
#region IDataSource Members