mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Add support for creating replay mod capture files (#1246)
* Add test replay handler * Fix incorrect built raw packet * Fix incorrect built raw packet * Add filter * Add not working zip lib * Add dotNetZip lib and complete basic function * Update ReplayHandler.cs * Complete Replay handler Without client player handling * Complete replay mod - New ChatBot OnNetworkPacket event * Add auto-backup and command for Replay Mod * Add ReplayMod description to readme * Small naming changes, fix compile error on .NET4.0 * ReplayHandler slight optimizations Use Path.Combine to automatically use Windows '\' or Linux '/' Move re-usable common parts outside the Replay handler Small optimizations in building JSON strings Co-authored-by: ORelio <oreliogitantispam.l0gin@spamgourmet.com>
This commit is contained in:
parent
cd1badb9d6
commit
7e20e409a8
47 changed files with 32732 additions and 21 deletions
|
|
@ -354,6 +354,18 @@ namespace MinecraftClient
|
|||
/// <param name="protocolversion">Ptotocol version</param>
|
||||
public virtual void OnEntityMetadata(Entity entity, Dictionary<int, object> metadata) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when a network packet received or sent
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You need to enable this event by calling <see cref="SetNetworkPacketEventEnabled(bool)"/> with True before you can use this event
|
||||
/// </remarks>
|
||||
/// <param name="packetID">Packet ID</param>
|
||||
/// <param name="packetData">A copy of Packet Data</param>
|
||||
/// <param name="isLogin">The packet is login phase or playing phase</param>
|
||||
/// <param name="isInbound">The packet is received from server or sent by client</param>
|
||||
public virtual void OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound) { }
|
||||
|
||||
/* =================================================================== */
|
||||
/* ToolBox - Methods below might be useful while creating your bot. */
|
||||
/* You should not need to interact with other classes of the program. */
|
||||
|
|
@ -1286,6 +1298,27 @@ namespace MinecraftClient
|
|||
else return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable network packet event calling. If you want to capture every packet including login phase, please enable this in <see cref="Initialize()"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Enable this may increase memory usage.
|
||||
/// </remarks>
|
||||
/// <param name="enabled"></param>
|
||||
protected void SetNetworkPacketEventEnabled(bool enabled)
|
||||
{
|
||||
Handler.SetNetworkPacketCaptureEnabled(enabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the minecraft protcol number currently in use
|
||||
/// </summary>
|
||||
/// <returns>Protcol number</returns>
|
||||
protected int GetProtocolVersion()
|
||||
{
|
||||
return Handler.GetProtocolVersion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command runner definition.
|
||||
/// Returned string will be the output of the command
|
||||
|
|
|
|||
91
MinecraftClient/ChatBots/ReplayCapture.cs
Normal file
91
MinecraftClient/ChatBots/ReplayCapture.cs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
/// <summary>
|
||||
/// Record and save replay file that can be used by the Replay mod (https://www.replaymod.com/)
|
||||
/// </summary>
|
||||
public class ReplayCapture : ChatBot
|
||||
{
|
||||
private ReplayHandler replay;
|
||||
private int backupInterval = 3000; // Unit: second * 10
|
||||
private int backupCounter = -1;
|
||||
|
||||
public ReplayCapture(int backupInterval)
|
||||
{
|
||||
if (backupInterval != -1)
|
||||
this.backupInterval = backupInterval * 10;
|
||||
else this.backupInterval = -1;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetNetworkPacketEventEnabled(true);
|
||||
replay = new ReplayHandler(GetProtocolVersion());
|
||||
replay.MetaData.serverName = GetServerHost() + GetServerPort();
|
||||
backupCounter = backupInterval;
|
||||
|
||||
RegisterChatBotCommand("replay", "replay command", Command);
|
||||
}
|
||||
|
||||
public override void OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound)
|
||||
{
|
||||
replay.AddPacket(packetID, packetData, isLogin, isInbound);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (backupInterval > 0 && replay.RecordRunning)
|
||||
{
|
||||
if (backupCounter <= 0)
|
||||
{
|
||||
replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr");
|
||||
backupCounter = backupInterval;
|
||||
}
|
||||
else backupCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||
{
|
||||
replay.OnShutDown();
|
||||
return base.OnDisconnect(reason, message);
|
||||
}
|
||||
|
||||
public string Command(string cmd, string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (replay.RecordRunning)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
switch (args[0].ToLower())
|
||||
{
|
||||
case "save":
|
||||
{
|
||||
replay.CreateBackupReplay(@"replay_recordings\" + replay.GetReplayDefaultName());
|
||||
return "Replay file created.";
|
||||
}
|
||||
case "stop":
|
||||
{
|
||||
replay.OnShutDown();
|
||||
return "Record stopped.";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Available commands: save, stop";
|
||||
}
|
||||
else return "Record was stopped. Restart the program to start another record.";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return e.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,6 +84,9 @@ namespace MinecraftClient
|
|||
// players latency
|
||||
private Dictionary<string, int> playersLatency = new Dictionary<string, int>();
|
||||
|
||||
// ChatBot OnNetworkPacket event
|
||||
private bool networkPacketCaptureEnabled = false;
|
||||
|
||||
public int GetServerPort() { return port; }
|
||||
public string GetServerHost() { return host; }
|
||||
public string GetUsername() { return username; }
|
||||
|
|
@ -98,6 +101,8 @@ namespace MinecraftClient
|
|||
public int GetTotalExperience() { return playerTotalExperience; }
|
||||
public byte GetCurrentSlot() { return CurrentSlot; }
|
||||
public int GetGamemode() { return gamemode; }
|
||||
public bool GetNetworkPacketCaptureEnabled() { return networkPacketCaptureEnabled; }
|
||||
public int GetProtocolVersion() { return handler.GetProtocolVersion(); }
|
||||
|
||||
// get bots list for unloading them by commands
|
||||
public List<ChatBot> GetLoadedChatBots()
|
||||
|
|
@ -185,6 +190,7 @@ namespace MinecraftClient
|
|||
if (Settings.Mailer_Enabled) { BotLoad(new ChatBots.Mailer()); }
|
||||
if (Settings.AutoCraft_Enabled) { BotLoad(new AutoCraft(Settings.AutoCraft_configFile)); }
|
||||
if (Settings.AutoDrop_Enabled) { BotLoad(new AutoDrop(Settings.AutoDrop_Mode, Settings.AutoDrop_items)); }
|
||||
if (Settings.ReplayMod_Enabled) { BotLoad(new ReplayCapture(Settings.ReplayMod_BackupInterval)); }
|
||||
|
||||
//Add your ChatBot here by uncommenting and adapting
|
||||
//BotLoad(new ChatBots.YourBot());
|
||||
|
|
@ -757,6 +763,18 @@ namespace MinecraftClient
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable network packet event calling.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Enable this may increase memory usage.
|
||||
/// </remarks>
|
||||
/// <param name="enabled"></param>
|
||||
public void SetNetworkPacketCaptureEnabled(bool enabled)
|
||||
{
|
||||
networkPacketCaptureEnabled = enabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Getters: Retrieve data for use in other methods or ChatBots
|
||||
|
|
@ -1492,6 +1510,21 @@ namespace MinecraftClient
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a network packet received or sent
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only called if <see cref="networkPacketEventEnabled"/> is set to True
|
||||
/// </remarks>
|
||||
/// <param name="packetID">Packet ID</param>
|
||||
/// <param name="packetData">A copy of Packet Data</param>
|
||||
/// <param name="isLogin">The packet is login phase or playing phase</param>
|
||||
/// <param name="isInbound">The packet is received from server or sent by client</param>
|
||||
public void OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound)
|
||||
{
|
||||
DispatchBotEvent(bot => bot.OnNetworkPacket(packetID, packetData, isLogin, isInbound));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a server was successfully joined
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
<Compile Include="AutoTimeout.cs" />
|
||||
<Compile Include="ChatBots\AutoDrop.cs" />
|
||||
<Compile Include="ChatBots\Mailer.cs" />
|
||||
<Compile Include="ChatBots\ReplayCapture.cs" />
|
||||
<Compile Include="Commands\Entitycmd.cs" />
|
||||
<Compile Include="ChatBots\Alerts.cs" />
|
||||
<Compile Include="ChatBots\AntiAFK.cs" />
|
||||
|
|
@ -151,6 +152,40 @@
|
|||
<Compile Include="Mapping\EntityTypeExtensions.cs" />
|
||||
<Compile Include="Mapping\MaterialExtensions.cs" />
|
||||
<Compile Include="Protocol\EntityActionType.cs" />
|
||||
<Compile Include="Protocol\GuidExtensions.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\DeflateStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\ParallelDeflateOutputStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ComHelper.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\EncryptionAlgorithm.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\Events.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\Exceptions.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ExtractExistingFileAction.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\FileSelector.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\OffsetStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\Shared.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\WinZipAes.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipConstants.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipCrypto.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipDirEntry.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipEntry.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipEntry.Extract.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipEntry.Read.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipEntry.Write.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipEntrySource.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipErrorAction.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.AddUpdate.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.Check.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.Events.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.Extract.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.Read.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.Save.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.SaveSelfExtractor.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.Selector.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipFile.x-IEnumerable.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipInputStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipOutputStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Zip\ZipSegmentedStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\DataTypes.cs" />
|
||||
<Compile Include="Protocol\Handlers\Forge\FMLVersion.cs" />
|
||||
<Compile Include="Protocol\Handlers\PacketPalettes\PacketPalette110.cs" />
|
||||
|
|
@ -172,6 +207,7 @@
|
|||
<Compile Include="Protocol\Handlers\SocketWrapper.cs" />
|
||||
<Compile Include="Protocol\DataTypeGenerator.cs" />
|
||||
<Compile Include="FileMonitor.cs" />
|
||||
<Compile Include="Protocol\ReplayHandler.cs" />
|
||||
<Compile Include="WinAPI\ConsoleIcon.cs" />
|
||||
<Compile Include="ConsoleIO.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\AesFastEngine.cs" />
|
||||
|
|
@ -339,6 +375,7 @@
|
|||
<Content Include="Protocol\Dns\Records\totla.txt" />
|
||||
<Content Include="Resources\AppIcon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
|
|||
79
MinecraftClient/Protocol/GuidExtensions.cs
Normal file
79
MinecraftClient/Protocol/GuidExtensions.cs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
public static class GuidExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A CLSCompliant method to convert a Java big-endian Guid to a .NET
|
||||
/// little-endian Guid.
|
||||
/// The Guid Constructor (UInt32, UInt16, UInt16, Byte, Byte, Byte, Byte,
|
||||
/// Byte, Byte, Byte, Byte) is not CLSCompliant.
|
||||
/// </summary>
|
||||
public static Guid ToLittleEndian(this Guid javaGuid)
|
||||
{
|
||||
byte[] net = new byte[16];
|
||||
byte[] java = javaGuid.ToByteArray();
|
||||
for (int i = 8; i < 16; i++)
|
||||
{
|
||||
net[i] = java[i];
|
||||
}
|
||||
net[3] = java[0];
|
||||
net[2] = java[1];
|
||||
net[1] = java[2];
|
||||
net[0] = java[3];
|
||||
net[5] = java[4];
|
||||
net[4] = java[5];
|
||||
net[6] = java[7];
|
||||
net[7] = java[6];
|
||||
return new Guid(net);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts little-endian .NET guids to big-endian Java guids:
|
||||
/// </summary>
|
||||
public static Guid ToBigEndian(this Guid netGuid)
|
||||
{
|
||||
byte[] java = new byte[16];
|
||||
byte[] net = netGuid.ToByteArray();
|
||||
for (int i = 8; i < 16; i++)
|
||||
{
|
||||
java[i] = net[i];
|
||||
}
|
||||
java[0] = net[3];
|
||||
java[1] = net[2];
|
||||
java[2] = net[1];
|
||||
java[3] = net[0];
|
||||
java[4] = net[5];
|
||||
java[5] = net[4];
|
||||
java[6] = net[7];
|
||||
java[7] = net[6];
|
||||
return new Guid(java);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts little-endian .NET guids to big-endian Java guids:
|
||||
/// </summary>
|
||||
public static byte[] ToBigEndianBytes(this Guid netGuid)
|
||||
{
|
||||
byte[] java = new byte[16];
|
||||
byte[] net = netGuid.ToByteArray();
|
||||
for (int i = 8; i < 16; i++)
|
||||
{
|
||||
java[i] = net[i];
|
||||
}
|
||||
java[0] = net[3];
|
||||
java[1] = net[2];
|
||||
java[2] = net[1];
|
||||
java[3] = net[0];
|
||||
java[4] = net[5];
|
||||
java[5] = net[4];
|
||||
java[6] = net[7];
|
||||
java[7] = net[6];
|
||||
return java;
|
||||
}
|
||||
}
|
||||
}
|
||||
740
MinecraftClient/Protocol/Handlers/Compression/DeflateStream.cs
Normal file
740
MinecraftClient/Protocol/Handlers/Compression/DeflateStream.cs
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
// DeflateStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2010 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:48:11>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the DeflateStream class, which can be used as a replacement for
|
||||
// the System.IO.Compression.DeflateStream class in the .NET BCL.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zlib
|
||||
{
|
||||
/// <summary>
|
||||
/// A class for compressing and decompressing streams using the Deflate algorithm.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// The DeflateStream is a <see
|
||||
/// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a <see
|
||||
/// cref="System.IO.Stream"/>. It adds DEFLATE compression or decompression to any
|
||||
/// stream.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Using this stream, applications can compress or decompress data via stream
|
||||
/// <c>Read</c> and <c>Write</c> operations. Either compresssion or decompression
|
||||
/// can occur through either reading or writing. The compression format used is
|
||||
/// DEFLATE, which is documented in <see
|
||||
/// href="http://www.ietf.org/rfc/rfc1951.txt">IETF RFC 1951</see>, "DEFLATE
|
||||
/// Compressed Data Format Specification version 1.3.".
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This class is similar to <see cref="ZlibStream"/>, except that
|
||||
/// <c>ZlibStream</c> adds the <see href="http://www.ietf.org/rfc/rfc1950.txt">RFC
|
||||
/// 1950 - ZLIB</see> framing bytes to a compressed stream when compressing, or
|
||||
/// expects the RFC1950 framing bytes when decompressing. The <c>DeflateStream</c>
|
||||
/// does not.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="ZlibStream" />
|
||||
/// <seealso cref="GZipStream" />
|
||||
public class DeflateStream : System.IO.Stream
|
||||
{
|
||||
internal ZlibBaseStream _baseStream;
|
||||
internal System.IO.Stream _innerStream;
|
||||
bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Create a DeflateStream using the specified CompressionMode.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When mode is <c>CompressionMode.Compress</c>, the DeflateStream will use
|
||||
/// the default compression level. The "captive" stream will be closed when
|
||||
/// the DeflateStream is closed.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example uses a DeflateStream to compress data from a file, and writes
|
||||
/// the compressed data to another file.
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(raw, CompressionMode.Compress))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n;
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
|
||||
/// {
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode)
|
||||
: this(stream, mode, CompressionLevel.Default, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is
|
||||
/// ignored. The "captive" stream will be closed when the DeflateStream is
|
||||
/// closed.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example uses a DeflateStream to compress data from a file, and writes
|
||||
/// the compressed data to another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (var raw = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(raw,
|
||||
/// CompressionMode.Compress,
|
||||
/// CompressionLevel.BestCompression))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n= -1;
|
||||
/// while (n != 0)
|
||||
/// {
|
||||
/// if (n > 0)
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// n= input.Read(buffer, 0, buffer.Length);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using raw As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream to be read or written while deflating or inflating.</param>
|
||||
/// <param name="mode">Indicates whether the <c>DeflateStream</c> will compress or decompress.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level)
|
||||
: this(stream, mode, level, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>DeflateStream</c> using the specified
|
||||
/// <c>CompressionMode</c>, and explicitly specify whether the
|
||||
/// stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// memory stream that will be re-read after compression. Specify true for
|
||||
/// the <paramref name="leaveOpen"/> parameter to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <c>DeflateStream</c> will use the default compression level.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// See the other overloads of this constructor for example code.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream which will be read or written. This is called the
|
||||
/// "captive" stream in other places in this documentation.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="mode">
|
||||
/// Indicates whether the <c>DeflateStream</c> will compress or decompress.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="leaveOpen">true if the application would like the stream to
|
||||
/// remain open after inflation/deflation.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
: this(stream, mode, CompressionLevel.Default, leaveOpen)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>DeflateStream</c> using the specified <c>CompressionMode</c>
|
||||
/// and the specified <c>CompressionLevel</c>, and explicitly specify whether
|
||||
/// the stream should be left open after Deflation or Inflation.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When mode is <c>CompressionMode.Decompress</c>, the level parameter is ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor allows the application to request that the captive stream
|
||||
/// remain open after the deflation or inflation occurs. By default, after
|
||||
/// <c>Close()</c> is called on the stream, the captive stream is also
|
||||
/// closed. In some cases this is not desired, for example if the stream is a
|
||||
/// <see cref="System.IO.MemoryStream"/> that will be re-read after
|
||||
/// compression. Specify true for the <paramref name="leaveOpen"/> parameter
|
||||
/// to leave the stream open.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to use a <c>DeflateStream</c> to compress data from
|
||||
/// a file, and store the compressed data into another file.
|
||||
///
|
||||
/// <code>
|
||||
/// using (var output = System.IO.File.Create(fileToCompress + ".deflated"))
|
||||
/// {
|
||||
/// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
|
||||
/// {
|
||||
/// using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true))
|
||||
/// {
|
||||
/// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
|
||||
/// int n= -1;
|
||||
/// while (n != 0)
|
||||
/// {
|
||||
/// if (n > 0)
|
||||
/// compressor.Write(buffer, 0, n);
|
||||
/// n= input.Read(buffer, 0, buffer.Length);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// // can write additional data to the output stream here
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using output As FileStream = File.Create(fileToCompress & ".deflated")
|
||||
/// Using input As Stream = File.OpenRead(fileToCompress)
|
||||
/// Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True)
|
||||
/// Dim buffer As Byte() = New Byte(4096) {}
|
||||
/// Dim n As Integer = -1
|
||||
/// Do While (n <> 0)
|
||||
/// If (n > 0) Then
|
||||
/// compressor.Write(buffer, 0, n)
|
||||
/// End If
|
||||
/// n = input.Read(buffer, 0, buffer.Length)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// ' can write additional data to the output stream here.
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="stream">The stream which will be read or written.</param>
|
||||
/// <param name="mode">Indicates whether the DeflateStream will compress or decompress.</param>
|
||||
/// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
|
||||
/// <param name="level">A tuning knob to trade speed for effectiveness.</param>
|
||||
public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
|
||||
{
|
||||
_innerStream = stream;
|
||||
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
/// <summary>
|
||||
/// This property sets the flush behavior on the stream.
|
||||
/// </summary>
|
||||
/// <remarks> See the ZLIB documentation for the meaning of the flush behavior.
|
||||
/// </remarks>
|
||||
virtual public FlushType FlushMode
|
||||
{
|
||||
get { return (this._baseStream._flushMode); }
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
this._baseStream._flushMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the working buffer for the compression codec.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The working buffer is used for all stream operations. The default size is
|
||||
/// 1024 bytes. The minimum size is 128 bytes. You may get better performance
|
||||
/// with a larger buffer. Then again, you might not. You would have to test
|
||||
/// it.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
|
||||
/// stream. If you try to set it afterwards, it will throw.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public int BufferSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._bufferSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
if (this._baseStream._workingBuffer != null)
|
||||
throw new ZlibException("The working buffer is already set.");
|
||||
if (value < ZlibConstants.WorkingBufferSizeMin)
|
||||
throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
|
||||
this._baseStream._bufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ZLIB strategy to be used during compression.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// By tweaking this parameter, you may be able to optimize the compression for
|
||||
/// data with particular characteristics.
|
||||
/// </remarks>
|
||||
public CompressionStrategy Strategy
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream.Strategy;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
this._baseStream.Strategy = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes input so far.</summary>
|
||||
virtual public long TotalIn
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the total number of bytes output so far.</summary>
|
||||
virtual public long TotalOut
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Stream methods
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This may or may not result in a <c>Close()</c> call on the captive
|
||||
/// stream. See the constructors that have a <c>leaveOpen</c> parameter
|
||||
/// for more information.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Application code won't call this code directly. This method may be
|
||||
/// invoked in two distinct scenarios. If disposing == true, the method
|
||||
/// has been called directly or indirectly by a user's code, for example
|
||||
/// via the public Dispose() method. In this case, both managed and
|
||||
/// unmanaged resources can be referenced and disposed. If disposing ==
|
||||
/// false, the method has been called by the runtime from inside the
|
||||
/// object finalizer and this method should not reference other objects;
|
||||
/// in that case only unmanaged resources must be referenced or
|
||||
/// disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="disposing">
|
||||
/// true if the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && (this._baseStream != null))
|
||||
this._baseStream.Close();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be read.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports reading.
|
||||
/// </remarks>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream._stream.CanRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream supports Seek operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Always returns false.
|
||||
/// </remarks>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the stream can be written.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The return value depends on whether the captive stream supports writing.
|
||||
/// </remarks>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream._stream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
_baseStream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading this property always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the stream pointer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this property always throws a <see
|
||||
/// cref="NotImplementedException"/>. Reading will return the total bytes
|
||||
/// written out, if used in writing, or the total bytes read in, if used in
|
||||
/// reading. The count may refer to compressed bytes or uncompressed bytes,
|
||||
/// depending on how you've used the stream.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer)
|
||||
return this._baseStream._z.TotalBytesOut;
|
||||
if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader)
|
||||
return this._baseStream._z.TotalBytesIn;
|
||||
return 0;
|
||||
}
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read data from the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>DeflateStream</c> to compress data while
|
||||
/// reading, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Compress</c>, providing an uncompressed data stream.
|
||||
/// Then call Read() on that <c>DeflateStream</c>, and the data read will be
|
||||
/// compressed as you read. If you wish to use the <c>DeflateStream</c> to
|
||||
/// decompress data while reading, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Decompress</c>, providing a readable compressed data
|
||||
/// stream. Then call Read() on that <c>DeflateStream</c>, and the data read
|
||||
/// will be decompressed as you read.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="buffer">The buffer into which the read data should be placed.</param>
|
||||
/// <param name="offset">the offset within that data array to put the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="offset">this is irrelevant, since it will always throw!</param>
|
||||
/// <param name="origin">this is irrelevant, since it will always throw!</param>
|
||||
/// <returns>irrelevant!</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calling this method always throws a <see cref="NotImplementedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">this is irrelevant, since it will always throw!</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data to the stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// If you wish to use the <c>DeflateStream</c> to compress data while
|
||||
/// writing, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Compress</c>, and a writable output stream. Then call
|
||||
/// <c>Write()</c> on that <c>DeflateStream</c>, providing uncompressed data
|
||||
/// as input. The data sent to the output stream will be the compressed form
|
||||
/// of the data written. If you wish to use the <c>DeflateStream</c> to
|
||||
/// decompress data while writing, you can create a <c>DeflateStream</c> with
|
||||
/// <c>CompressionMode.Decompress</c>, and a writable output stream. Then
|
||||
/// call <c>Write()</c> on that stream, providing previously compressed
|
||||
/// data. The data sent to the output stream will be the decompressed form of
|
||||
/// the data written.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A <c>DeflateStream</c> can be used for <c>Read()</c> or <c>Write()</c>,
|
||||
/// but not both.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException("DeflateStream");
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a string into a byte array using DEFLATE (RFC 1951).
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="DeflateStream.UncompressString(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.CompressString(string)">GZipStream.CompressString(string)</seealso>
|
||||
/// <seealso cref="ZlibStream.CompressString(string)">ZlibStream.CompressString(string)</seealso>
|
||||
///
|
||||
/// <param name="s">
|
||||
/// A string to compress. The string will first be encoded
|
||||
/// using UTF8, then compressed.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The string in compressed form</returns>
|
||||
public static byte[] CompressString(String s)
|
||||
{
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
System.IO.Stream compressor =
|
||||
new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
|
||||
ZlibBaseStream.CompressString(s, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compress a byte array into a new byte array using DEFLATE.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Uncompress it with <see cref="DeflateStream.UncompressBuffer(byte[])"/>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressString(string)">DeflateStream.CompressString(string)</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.CompressBuffer(byte[])">GZipStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.CompressBuffer(byte[])">ZlibStream.CompressBuffer(byte[])</seealso>
|
||||
///
|
||||
/// <param name="b">
|
||||
/// A buffer to compress.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in compressed form</returns>
|
||||
public static byte[] CompressBuffer(byte[] b)
|
||||
{
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
System.IO.Stream compressor =
|
||||
new DeflateStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
|
||||
|
||||
ZlibBaseStream.CompressBuffer(b, compressor);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a DEFLATE'd byte array into a single string.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressString(String)">DeflateStream.CompressString(String)</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressBuffer(byte[])">DeflateStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.UncompressString(byte[])">GZipStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.UncompressString(byte[])">ZlibStream.UncompressString(byte[])</seealso>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing DEFLATE-compressed data.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The uncompressed string</returns>
|
||||
public static String UncompressString(byte[] compressed)
|
||||
{
|
||||
using (var input = new System.IO.MemoryStream(compressed))
|
||||
{
|
||||
System.IO.Stream decompressor =
|
||||
new DeflateStream(input, CompressionMode.Decompress);
|
||||
|
||||
return ZlibBaseStream.UncompressString(compressed, decompressor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Uncompress a DEFLATE'd byte array into a byte array.
|
||||
/// </summary>
|
||||
///
|
||||
/// <seealso cref="DeflateStream.CompressBuffer(byte[])">DeflateStream.CompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="DeflateStream.UncompressString(byte[])">DeflateStream.UncompressString(byte[])</seealso>
|
||||
/// <seealso cref="GZipStream.UncompressBuffer(byte[])">GZipStream.UncompressBuffer(byte[])</seealso>
|
||||
/// <seealso cref="ZlibStream.UncompressBuffer(byte[])">ZlibStream.UncompressBuffer(byte[])</seealso>
|
||||
///
|
||||
/// <param name="compressed">
|
||||
/// A buffer containing data that has been compressed with DEFLATE.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The data in uncompressed form</returns>
|
||||
public static byte[] UncompressBuffer(byte[] compressed)
|
||||
{
|
||||
using (var input = new System.IO.MemoryStream(compressed))
|
||||
{
|
||||
System.IO.Stream decompressor =
|
||||
new DeflateStream( input, CompressionMode.Decompress );
|
||||
|
||||
return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load diff
116
MinecraftClient/Protocol/Handlers/Compression/Zip/ComHelper.cs
Normal file
116
MinecraftClient/Protocol/Handlers/Compression/Zip/ComHelper.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// ComHelper.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-June-13 17:04:06>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a COM Helper class.
|
||||
//
|
||||
// Created: Tue, 08 Sep 2009 22:03
|
||||
//
|
||||
|
||||
using Interop=System.Runtime.InteropServices;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// This class exposes a set of COM-accessible wrappers for static
|
||||
/// methods available on the ZipFile class. You don't need this
|
||||
/// class unless you are using DotNetZip from a COM environment.
|
||||
/// </summary>
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000F")]
|
||||
[System.Runtime.InteropServices.ComVisible(true)]
|
||||
#if !NETCF
|
||||
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDispatch)]
|
||||
#endif
|
||||
|
||||
public class ComHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.IsZipFile(string)">ZipFile.IsZipFile(string)</see>
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
/// <returns>true if the file contains a valid zip file.</returns>
|
||||
public bool IsZipFile(string filename)
|
||||
{
|
||||
return ZipFile.IsZipFile(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.IsZipFile(string, bool)">ZipFile.IsZipFile(string, bool)</see>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We cannot use "overloaded" Method names in COM interop.
|
||||
/// So, here, we use a unique name.
|
||||
/// </remarks>
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
/// <returns>true if the file contains a valid zip file.</returns>
|
||||
public bool IsZipFileWithExtract(string filename)
|
||||
{
|
||||
return ZipFile.IsZipFile(filename, true);
|
||||
}
|
||||
|
||||
#if !NETCF
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.CheckZip(string)">ZipFile.CheckZip(string)</see>
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
|
||||
public bool CheckZip(string filename)
|
||||
{
|
||||
return ZipFile.CheckZip(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A COM-friendly wrapper for the static method <see cref="ZipFile.CheckZipPassword(string,string)"/>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="filename">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <param name="password">The password to check.</param>
|
||||
///
|
||||
/// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
|
||||
public bool CheckZipPassword(string filename, string password)
|
||||
{
|
||||
return ZipFile.CheckZipPassword(filename, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.FixZipDirectory(string)">ZipFile.FixZipDirectory(string)</see>
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename to of the zip file to fix.</param>
|
||||
public void FixZipDirectory(string filename)
|
||||
{
|
||||
ZipFile.FixZipDirectory(filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for <see cref="ZipFile.LibraryVersion">ZipFile.LibraryVersion</see>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// the version number on the DotNetZip assembly, formatted as a string.
|
||||
/// </returns>
|
||||
public string GetZipLibraryVersion()
|
||||
{
|
||||
return ZipFile.LibraryVersion.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// EncryptionAlgorithm.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-October-21 17:24:45>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the EncryptionAgorithm enum
|
||||
//
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum that provides the various encryption algorithms supported by this
|
||||
/// library.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// <c>PkzipWeak</c> implies the use of Zip 2.0 encryption, which is known to be
|
||||
/// weak and subvertible.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A note on interoperability: Values of <c>PkzipWeak</c> and <c>None</c> are
|
||||
/// specified in <see
|
||||
/// href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">PKWARE's zip
|
||||
/// specification</see>, and are considered to be "standard". Zip archives
|
||||
/// produced using these options will be interoperable with many other zip tools
|
||||
/// and libraries, including Windows Explorer.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Values of <c>WinZipAes128</c> and <c>WinZipAes256</c> are not part of the Zip
|
||||
/// specification, but rather imply the use of a vendor-specific extension from
|
||||
/// WinZip. If you want to produce interoperable Zip archives, do not use these
|
||||
/// values. For example, if you produce a zip archive using WinZipAes256, you
|
||||
/// will be able to open it in Windows Explorer on Windows XP and Vista, but you
|
||||
/// will not be able to extract entries; trying this will lead to an "unspecified
|
||||
/// error". For this reason, some people have said that a zip archive that uses
|
||||
/// WinZip's AES encryption is not actually a zip archive at all. A zip archive
|
||||
/// produced this way will be readable with the WinZip tool (Version 11 and
|
||||
/// beyond).
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// There are other third-party tools and libraries, both commercial and
|
||||
/// otherwise, that support WinZip's AES encryption. These will be able to read
|
||||
/// AES-encrypted zip archives produced by DotNetZip, and conversely applications
|
||||
/// that use DotNetZip to read zip archives will be able to read AES-encrypted
|
||||
/// archives produced by those tools or libraries. Consult the documentation for
|
||||
/// those other tools and libraries to find out if WinZip's AES encryption is
|
||||
/// supported.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// In case you care: According to <see
|
||||
/// href="http://www.winzip.com/aes_info.htm">the WinZip specification</see>, the
|
||||
/// actual AES key used is derived from the <see cref="ZipEntry.Password"/> via an
|
||||
/// algorithm that complies with <see
|
||||
/// href="http://www.ietf.org/rfc/rfc2898.txt">RFC 2898</see>, using an iteration
|
||||
/// count of 1000. The algorithm is sometimes referred to as PBKDF2, which stands
|
||||
/// for "Password Based Key Derivation Function #2".
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A word about password strength and length: The AES encryption technology is
|
||||
/// very good, but any system is only as secure as the weakest link. If you want
|
||||
/// to secure your data, be sure to use a password that is hard to guess. To make
|
||||
/// it harder to guess (increase its "entropy"), you should make it longer. If
|
||||
/// you use normal characters from an ASCII keyboard, a password of length 20 will
|
||||
/// be strong enough that it will be impossible to guess. For more information on
|
||||
/// that, I'd encourage you to read <see
|
||||
/// href="http://www.redkestrel.co.uk/Articles/RandomPasswordStrength.html">this
|
||||
/// article.</see>
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The WinZip AES algorithms are not supported with the version of DotNetZip that
|
||||
/// runs on the .NET Compact Framework. This is because .NET CF lacks the
|
||||
/// HMACSHA1 class that is required for producing the archive.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum EncryptionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// No encryption at all.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Traditional or Classic pkzip encryption.
|
||||
/// </summary>
|
||||
PkzipWeak,
|
||||
|
||||
#if AESCRYPTO
|
||||
/// <summary>
|
||||
/// WinZip AES encryption (128 key bits).
|
||||
/// </summary>
|
||||
WinZipAes128,
|
||||
|
||||
/// <summary>
|
||||
/// WinZip AES encryption (256 key bits).
|
||||
/// </summary>
|
||||
WinZipAes256,
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// An encryption algorithm that is not supported by DotNetZip.
|
||||
/// </summary>
|
||||
Unsupported = 4,
|
||||
|
||||
|
||||
// others... not implemented (yet?)
|
||||
}
|
||||
|
||||
}
|
||||
684
MinecraftClient/Protocol/Handlers/Compression/Zip/Events.cs
Normal file
684
MinecraftClient/Protocol/Handlers/Compression/Zip/Events.cs
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
// Events.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-06 12:26:24>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines events used by the ZipFile class.
|
||||
//
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate in which the application writes the <c>ZipEntry</c> content for the named entry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="entryName">The name of the entry that must be written.</param>
|
||||
/// <param name="stream">The stream to which the entry data should be written.</param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When you add an entry and specify a <c>WriteDelegate</c>, via <see
|
||||
/// cref="Ionic.Zip.ZipFile.AddEntry(string, WriteDelegate)"/>, the application
|
||||
/// code provides the logic that writes the entry data directly into the zip file.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to define a WriteDelegate that obtains a DataSet, and then
|
||||
/// writes the XML for the DataSet into the zip archive. There's no need to
|
||||
/// save the XML to a disk file first.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// private void WriteEntry (String filename, Stream output)
|
||||
/// {
|
||||
/// DataSet ds1 = ObtainDataSet();
|
||||
/// ds1.WriteXml(output);
|
||||
/// }
|
||||
///
|
||||
/// private void Run()
|
||||
/// {
|
||||
/// using (var zip = new ZipFile())
|
||||
/// {
|
||||
/// zip.AddEntry(zipEntryName, WriteEntry);
|
||||
/// zip.Save(zipFileName);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="vb">
|
||||
/// Private Sub WriteEntry (ByVal filename As String, ByVal output As Stream)
|
||||
/// DataSet ds1 = ObtainDataSet()
|
||||
/// ds1.WriteXml(stream)
|
||||
/// End Sub
|
||||
///
|
||||
/// Public Sub Run()
|
||||
/// Using zip = New ZipFile
|
||||
/// zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
|
||||
/// zip.Save(zipFileName)
|
||||
/// End Using
|
||||
/// End Sub
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, WriteDelegate)"/>
|
||||
public delegate void WriteDelegate(string entryName, System.IO.Stream stream);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delegate in which the application opens the stream, just-in-time, for the named entry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="entryName">
|
||||
/// The name of the ZipEntry that the application should open the stream for.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When you add an entry via <see cref="Ionic.Zip.ZipFile.AddEntry(string,
|
||||
/// OpenDelegate, CloseDelegate)"/>, the application code provides the logic that
|
||||
/// opens and closes the stream for the given ZipEntry.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, OpenDelegate, CloseDelegate)"/>
|
||||
public delegate System.IO.Stream OpenDelegate(string entryName);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate in which the application closes the stream, just-in-time, for the named entry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="entryName">
|
||||
/// The name of the ZipEntry that the application should close the stream for.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="stream">The stream to be closed.</param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// When you add an entry via <see cref="Ionic.Zip.ZipFile.AddEntry(string,
|
||||
/// OpenDelegate, CloseDelegate)"/>, the application code provides the logic that
|
||||
/// opens and closes the stream for the given ZipEntry.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(string, OpenDelegate, CloseDelegate)"/>
|
||||
public delegate void CloseDelegate(string entryName, System.IO.Stream stream);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for the callback by which the application tells the
|
||||
/// library the CompressionLevel to use for a file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Using this callback, the application can, for example, specify that
|
||||
/// previously-compressed files (.mp3, .png, .docx, etc) should use a
|
||||
/// <c>CompressionLevel</c> of <c>None</c>, or can set the compression level based
|
||||
/// on any other factor.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.SetCompression"/>
|
||||
public delegate Ionic.Zlib.CompressionLevel SetCompressionCallback(string localFileName, string fileNameInArchive);
|
||||
|
||||
/// <summary>
|
||||
/// In an EventArgs type, indicates which sort of progress event is being
|
||||
/// reported.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are events for reading, events for saving, and events for
|
||||
/// extracting. This enumeration allows a single EventArgs type to be sued to
|
||||
/// describe one of multiple subevents. For example, a SaveProgress event is
|
||||
/// invoked before, after, and during the saving of a single entry. The value
|
||||
/// of an enum with this type, specifies which event is being triggered. The
|
||||
/// same applies to Extraction, Reading and Adding events.
|
||||
/// </remarks>
|
||||
public enum ZipProgressEventType
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that a Add() operation has started.
|
||||
/// </summary>
|
||||
Adding_Started,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive has been added.
|
||||
/// </summary>
|
||||
Adding_AfterAddEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Add() operation has completed.
|
||||
/// </summary>
|
||||
Adding_Completed,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Read() operation has started.
|
||||
/// </summary>
|
||||
Reading_Started,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive is about to be read.
|
||||
/// </summary>
|
||||
Reading_BeforeReadEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive has just been read.
|
||||
/// </summary>
|
||||
Reading_AfterReadEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Read() operation has completed.
|
||||
/// </summary>
|
||||
Reading_Completed,
|
||||
|
||||
/// <summary>
|
||||
/// The given event reports the number of bytes read so far
|
||||
/// during a Read() operation.
|
||||
/// </summary>
|
||||
Reading_ArchiveBytesRead,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Save() operation has started.
|
||||
/// </summary>
|
||||
Saving_Started,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive is about to be written.
|
||||
/// </summary>
|
||||
Saving_BeforeWriteEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an individual entry in the archive has just been saved.
|
||||
/// </summary>
|
||||
Saving_AfterWriteEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a Save() operation has completed.
|
||||
/// </summary>
|
||||
Saving_Completed,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the zip archive has been created in a
|
||||
/// temporary location during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_AfterSaveTempArchive,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the temporary file is about to be renamed to the final archive
|
||||
/// name during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_BeforeRenameTempArchive,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the temporary file is has just been renamed to the final archive
|
||||
/// name during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_AfterRenameTempArchive,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the self-extracting archive has been compiled
|
||||
/// during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_AfterCompileSelfExtractor,
|
||||
|
||||
/// <summary>
|
||||
/// The given event is reporting the number of source bytes that have run through the compressor so far
|
||||
/// during a Save() operation.
|
||||
/// </summary>
|
||||
Saving_EntryBytesRead,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an entry is about to be extracted.
|
||||
/// </summary>
|
||||
Extracting_BeforeExtractEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an entry has just been extracted.
|
||||
/// </summary>
|
||||
Extracting_AfterExtractEntry,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that extraction of an entry would overwrite an existing
|
||||
/// filesystem file. You must use
|
||||
/// <see cref="ExtractExistingFileAction.InvokeExtractProgressEvent">
|
||||
/// ExtractExistingFileAction.InvokeExtractProgressEvent</see> in the call
|
||||
/// to <c>ZipEntry.Extract()</c> in order to receive this event.
|
||||
/// </summary>
|
||||
Extracting_ExtractEntryWouldOverwrite,
|
||||
|
||||
/// <summary>
|
||||
/// The given event is reporting the number of bytes written so far for
|
||||
/// the current entry during an Extract() operation.
|
||||
/// </summary>
|
||||
Extracting_EntryBytesWritten,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an ExtractAll operation is about to begin.
|
||||
/// </summary>
|
||||
Extracting_BeforeExtractAll,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an ExtractAll operation has completed.
|
||||
/// </summary>
|
||||
Extracting_AfterExtractAll,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an error has occurred while saving a zip file.
|
||||
/// This generally means the file cannot be opened, because it has been
|
||||
/// removed, or because it is locked by another process. It can also
|
||||
/// mean that the file cannot be Read, because of a range lock conflict.
|
||||
/// </summary>
|
||||
Error_Saving,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a save, read, or extract operation.
|
||||
/// This is a base class; you will probably use one of the classes derived from this one.
|
||||
/// </summary>
|
||||
public class ZipProgressEventArgs : EventArgs
|
||||
{
|
||||
private int _entriesTotal;
|
||||
private bool _cancel;
|
||||
private ZipEntry _latestEntry;
|
||||
private ZipProgressEventType _flavor;
|
||||
private String _archiveName;
|
||||
private Int64 _bytesTransferred;
|
||||
private Int64 _totalBytesToTransfer;
|
||||
|
||||
|
||||
internal ZipProgressEventArgs() { }
|
||||
|
||||
internal ZipProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
{
|
||||
this._archiveName = archiveName;
|
||||
this._flavor = flavor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total number of entries to be saved or extracted.
|
||||
/// </summary>
|
||||
public int EntriesTotal
|
||||
{
|
||||
get { return _entriesTotal; }
|
||||
set { _entriesTotal = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the last entry saved or extracted.
|
||||
/// </summary>
|
||||
public ZipEntry CurrentEntry
|
||||
{
|
||||
get { return _latestEntry; }
|
||||
set { _latestEntry = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In an event handler, set this to cancel the save or extract
|
||||
/// operation that is in progress.
|
||||
/// </summary>
|
||||
public bool Cancel
|
||||
{
|
||||
get { return _cancel; }
|
||||
set { _cancel = _cancel || value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of event being reported.
|
||||
/// </summary>
|
||||
public ZipProgressEventType EventType
|
||||
{
|
||||
get { return _flavor; }
|
||||
set { _flavor = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the archive name associated to this event.
|
||||
/// </summary>
|
||||
public String ArchiveName
|
||||
{
|
||||
get { return _archiveName; }
|
||||
set { _archiveName = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes read or written so far for this entry.
|
||||
/// </summary>
|
||||
public Int64 BytesTransferred
|
||||
{
|
||||
get { return _bytesTransferred; }
|
||||
set { _bytesTransferred = value; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Total number of bytes that will be read or written for this entry.
|
||||
/// This number will be -1 if the value cannot be determined.
|
||||
/// </summary>
|
||||
public Int64 TotalBytesToTransfer
|
||||
{
|
||||
get { return _totalBytesToTransfer; }
|
||||
set { _totalBytesToTransfer = value; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a Read operation.
|
||||
/// </summary>
|
||||
public class ReadProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
|
||||
internal ReadProgressEventArgs() { }
|
||||
|
||||
private ReadProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
internal static ReadProgressEventArgs Before(string archiveName, int entriesTotal)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_BeforeReadEntry);
|
||||
x.EntriesTotal = entriesTotal;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs After(string archiveName, ZipEntry entry, int entriesTotal)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_AfterReadEntry);
|
||||
x.EntriesTotal = entriesTotal;
|
||||
x.CurrentEntry = entry;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs Started(string archiveName)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_Started);
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesXferred, Int64 totalBytes)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_ArchiveBytesRead);
|
||||
x.CurrentEntry = entry;
|
||||
x.BytesTransferred = bytesXferred;
|
||||
x.TotalBytesToTransfer = totalBytes;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ReadProgressEventArgs Completed(string archiveName)
|
||||
{
|
||||
var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_Completed);
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a Add operation.
|
||||
/// </summary>
|
||||
public class AddProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
internal AddProgressEventArgs() { }
|
||||
|
||||
private AddProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
internal static AddProgressEventArgs AfterEntry(string archiveName, ZipEntry entry, int entriesTotal)
|
||||
{
|
||||
var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_AfterAddEntry);
|
||||
x.EntriesTotal = entriesTotal;
|
||||
x.CurrentEntry = entry;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static AddProgressEventArgs Started(string archiveName)
|
||||
{
|
||||
var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_Started);
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static AddProgressEventArgs Completed(string archiveName)
|
||||
{
|
||||
var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_Completed);
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of a save operation.
|
||||
/// </summary>
|
||||
public class SaveProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
private int _entriesSaved;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the SaveProgressEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="archiveName">the name of the zip archive.</param>
|
||||
/// <param name="before">whether this is before saving the entry, or after</param>
|
||||
/// <param name="entriesTotal">The total number of entries in the zip archive.</param>
|
||||
/// <param name="entriesSaved">Number of entries that have been saved.</param>
|
||||
/// <param name="entry">The entry involved in the event.</param>
|
||||
internal SaveProgressEventArgs(string archiveName, bool before, int entriesTotal, int entriesSaved, ZipEntry entry)
|
||||
: base(archiveName, (before) ? ZipProgressEventType.Saving_BeforeWriteEntry : ZipProgressEventType.Saving_AfterWriteEntry)
|
||||
{
|
||||
this.EntriesTotal = entriesTotal;
|
||||
this.CurrentEntry = entry;
|
||||
this._entriesSaved = entriesSaved;
|
||||
}
|
||||
|
||||
internal SaveProgressEventArgs() { }
|
||||
|
||||
internal SaveProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
|
||||
internal static SaveProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesXferred, Int64 totalBytes)
|
||||
{
|
||||
var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_EntryBytesRead);
|
||||
x.ArchiveName = archiveName;
|
||||
x.CurrentEntry = entry;
|
||||
x.BytesTransferred = bytesXferred;
|
||||
x.TotalBytesToTransfer = totalBytes;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static SaveProgressEventArgs Started(string archiveName)
|
||||
{
|
||||
var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_Started);
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static SaveProgressEventArgs Completed(string archiveName)
|
||||
{
|
||||
var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_Completed);
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries saved so far.
|
||||
/// </summary>
|
||||
public int EntriesSaved
|
||||
{
|
||||
get { return _entriesSaved; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the progress of the extract operation.
|
||||
/// </summary>
|
||||
public class ExtractProgressEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
private int _entriesExtracted;
|
||||
private string _target;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the ExtractProgressEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="archiveName">the name of the zip archive.</param>
|
||||
/// <param name="before">whether this is before saving the entry, or after</param>
|
||||
/// <param name="entriesTotal">The total number of entries in the zip archive.</param>
|
||||
/// <param name="entriesExtracted">Number of entries that have been extracted.</param>
|
||||
/// <param name="entry">The entry involved in the event.</param>
|
||||
/// <param name="extractLocation">The location to which entries are extracted.</param>
|
||||
internal ExtractProgressEventArgs(string archiveName, bool before, int entriesTotal, int entriesExtracted, ZipEntry entry, string extractLocation)
|
||||
: base(archiveName, (before) ? ZipProgressEventType.Extracting_BeforeExtractEntry : ZipProgressEventType.Extracting_AfterExtractEntry)
|
||||
{
|
||||
this.EntriesTotal = entriesTotal;
|
||||
this.CurrentEntry = entry;
|
||||
this._entriesExtracted = entriesExtracted;
|
||||
this._target = extractLocation;
|
||||
}
|
||||
|
||||
internal ExtractProgressEventArgs(string archiveName, ZipProgressEventType flavor)
|
||||
: base(archiveName, flavor)
|
||||
{ }
|
||||
|
||||
internal ExtractProgressEventArgs()
|
||||
{ }
|
||||
|
||||
|
||||
internal static ExtractProgressEventArgs BeforeExtractEntry(string archiveName, ZipEntry entry, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs
|
||||
{
|
||||
ArchiveName = archiveName,
|
||||
EventType = ZipProgressEventType.Extracting_BeforeExtractEntry,
|
||||
CurrentEntry = entry,
|
||||
_target = extractLocation,
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs ExtractExisting(string archiveName, ZipEntry entry, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs
|
||||
{
|
||||
ArchiveName = archiveName,
|
||||
EventType = ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite,
|
||||
CurrentEntry = entry,
|
||||
_target = extractLocation,
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs AfterExtractEntry(string archiveName, ZipEntry entry, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs
|
||||
{
|
||||
ArchiveName = archiveName,
|
||||
EventType = ZipProgressEventType.Extracting_AfterExtractEntry,
|
||||
CurrentEntry = entry,
|
||||
_target = extractLocation,
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs ExtractAllStarted(string archiveName, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_BeforeExtractAll);
|
||||
x._target = extractLocation;
|
||||
return x;
|
||||
}
|
||||
|
||||
internal static ExtractProgressEventArgs ExtractAllCompleted(string archiveName, string extractLocation)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_AfterExtractAll);
|
||||
x._target = extractLocation;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static ExtractProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesWritten, Int64 totalBytes)
|
||||
{
|
||||
var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_EntryBytesWritten);
|
||||
x.ArchiveName = archiveName;
|
||||
x.CurrentEntry = entry;
|
||||
x.BytesTransferred = bytesWritten;
|
||||
x.TotalBytesToTransfer = totalBytes;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries extracted so far. This is set only if the
|
||||
/// EventType is Extracting_BeforeExtractEntry or Extracting_AfterExtractEntry, and
|
||||
/// the Extract() is occurring witin the scope of a call to ExtractAll().
|
||||
/// </summary>
|
||||
public int EntriesExtracted
|
||||
{
|
||||
get { return _entriesExtracted; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the extraction target location, a filesystem path.
|
||||
/// </summary>
|
||||
public String ExtractLocation
|
||||
{
|
||||
get { return _target; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the an error that occurred while zipping.
|
||||
/// </summary>
|
||||
public class ZipErrorEventArgs : ZipProgressEventArgs
|
||||
{
|
||||
private Exception _exc;
|
||||
private ZipErrorEventArgs() { }
|
||||
internal static ZipErrorEventArgs Saving(string archiveName, ZipEntry entry, Exception exception)
|
||||
{
|
||||
var x = new ZipErrorEventArgs
|
||||
{
|
||||
EventType = ZipProgressEventType.Error_Saving,
|
||||
ArchiveName = archiveName,
|
||||
CurrentEntry = entry,
|
||||
_exc = exception
|
||||
};
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the exception that occurred, if any.
|
||||
/// </summary>
|
||||
public Exception @Exception
|
||||
{
|
||||
get { return _exc; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the file that caused the exception, if any.
|
||||
/// </summary>
|
||||
public String FileName
|
||||
{
|
||||
get { return CurrentEntry.LocalFileName; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
300
MinecraftClient/Protocol/Handlers/Compression/Zip/Exceptions.cs
Normal file
300
MinecraftClient/Protocol/Handlers/Compression/Zip/Exceptions.cs
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// Exceptions.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-12 12:19:10>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines exceptions used in the class library.
|
||||
//
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
#if !NETCF
|
||||
using System.Runtime.Serialization;
|
||||
#endif
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
///// <summary>
|
||||
///// Base exception type for all custom exceptions in the Zip library. It acts as a marker class.
|
||||
///// </summary>
|
||||
//[AttributeUsage(AttributeTargets.Class)]
|
||||
//public class ZipExceptionAttribute : Attribute { }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Issued when an <c>ZipEntry.ExtractWithPassword()</c> method is invoked
|
||||
/// with an incorrect password.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000B")]
|
||||
public class BadPasswordException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadPasswordException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadPasswordException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public BadPasswordException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadPasswordException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a read was attempted on a stream, and bad or incomplete data was
|
||||
/// received.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000A")]
|
||||
public class BadReadException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadReadException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadReadException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public BadReadException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadReadException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Issued when an CRC check fails upon extracting an entry from a zip archive.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00009")]
|
||||
public class BadCrcException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadCrcException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadCrcException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadCrcException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Issued when errors occur saving a self-extracting archive.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00008")]
|
||||
public class SfxGenerationException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public SfxGenerationException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public SfxGenerationException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected SfxGenerationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an operation was attempted on a ZipFile which was not possible
|
||||
/// given the state of the instance. For example, if you call <c>Save()</c> on a ZipFile
|
||||
/// which has no filename set, you can get this exception.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00007")]
|
||||
public class BadStateException : ZipException
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public BadStateException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public BadStateException(String message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public BadStateException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{}
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected BadStateException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all exceptions defined by and throw by the Zip library.
|
||||
/// </summary>
|
||||
#if !SILVERLIGHT
|
||||
[Serializable]
|
||||
#endif
|
||||
[System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00006")]
|
||||
public class ZipException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Default ctor.
|
||||
/// </summary>
|
||||
public ZipException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
public ZipException(String message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="message">The message in the exception.</param>
|
||||
/// <param name="innerException">The innerException for this exception.</param>
|
||||
public ZipException(String message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
#if ! (NETCF || SILVERLIGHT)
|
||||
/// <summary>
|
||||
/// Come on, you know how exceptions work. Why are you looking at this documentation?
|
||||
/// </summary>
|
||||
/// <param name="info">The serialization info for the exception.</param>
|
||||
/// <param name="context">The streaming context from which to deserialize.</param>
|
||||
protected ZipException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// ExtractExistingFileAction.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-August-25 08:44:37>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ExtractExistingFileAction enum
|
||||
//
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// An enum for the options when extracting an entry would overwrite an existing file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This enum describes the actions that the library can take when an
|
||||
/// <c>Extract()</c> or <c>ExtractWithPassword()</c> method is called to extract an
|
||||
/// entry to a filesystem, and the extraction would overwrite an existing filesystem
|
||||
/// file.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
public enum ExtractExistingFileAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Throw an exception when extraction would overwrite an existing file. (For
|
||||
/// COM clients, this is a 0 (zero).)
|
||||
/// </summary>
|
||||
Throw,
|
||||
|
||||
/// <summary>
|
||||
/// When extraction would overwrite an existing file, overwrite the file silently.
|
||||
/// The overwrite will happen even if the target file is marked as read-only.
|
||||
/// (For COM clients, this is a 1.)
|
||||
/// </summary>
|
||||
OverwriteSilently,
|
||||
|
||||
/// <summary>
|
||||
/// When extraction would overwrite an existing file, don't overwrite the file, silently.
|
||||
/// (For COM clients, this is a 2.)
|
||||
/// </summary>
|
||||
DoNotOverwrite,
|
||||
|
||||
/// <summary>
|
||||
/// When extraction would overwrite an existing file, invoke the ExtractProgress
|
||||
/// event, using an event type of <see
|
||||
/// cref="ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite"/>. In
|
||||
/// this way, the application can decide, just-in-time, whether to overwrite the
|
||||
/// file. For example, a GUI application may wish to pop up a dialog to allow
|
||||
/// the user to choose. You may want to examine the <see
|
||||
/// cref="ExtractProgressEventArgs.ExtractLocation"/> property before making
|
||||
/// the decision. If, after your processing in the Extract progress event, you
|
||||
/// want to NOT extract the file, set <see cref="ZipEntry.ExtractExistingFile"/>
|
||||
/// on the <c>ZipProgressEventArgs.CurrentEntry</c> to <c>DoNotOverwrite</c>.
|
||||
/// If you do want to extract the file, set <c>ZipEntry.ExtractExistingFile</c>
|
||||
/// to <c>OverwriteSilently</c>. If you want to cancel the Extraction, set
|
||||
/// <c>ZipProgressEventArgs.Cancel</c> to true. Cancelling differs from using
|
||||
/// DoNotOverwrite in that a cancel will not extract any further entries, if
|
||||
/// there are any. (For COM clients, the value of this enum is a 3.)
|
||||
/// </summary>
|
||||
InvokeExtractProgressEvent,
|
||||
}
|
||||
|
||||
}
|
||||
1608
MinecraftClient/Protocol/Handlers/Compression/Zip/FileSelector.cs
Normal file
1608
MinecraftClient/Protocol/Handlers/Compression/Zip/FileSelector.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,114 @@
|
|||
// OffsetStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-August-27 12:50:35>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for handling reading of zip archives embedded
|
||||
// into larger streams. The initial position of the stream serves as
|
||||
// the base offset for all future Seek() operations.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
internal class OffsetStream : System.IO.Stream, System.IDisposable
|
||||
{
|
||||
private Int64 _originalPosition;
|
||||
private Stream _innerStream;
|
||||
|
||||
public OffsetStream(Stream s)
|
||||
: base()
|
||||
{
|
||||
_originalPosition = s.Position;
|
||||
_innerStream = s;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return _innerStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _innerStream.CanRead; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return _innerStream.CanSeek; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _innerStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _innerStream.Position - _originalPosition; }
|
||||
set { _innerStream.Position = _originalPosition + value; }
|
||||
}
|
||||
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
return _innerStream.Seek(_originalPosition + offset, origin) - _originalPosition;
|
||||
}
|
||||
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
901
MinecraftClient/Protocol/Handlers/Compression/Zip/Shared.cs
Normal file
901
MinecraftClient/Protocol/Handlers/Compression/Zip/Shared.cs
Normal file
|
|
@ -0,0 +1,901 @@
|
|||
// Shared.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 19:41:01>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines some shared utility classes and methods.
|
||||
//
|
||||
// Created: Tue, 27 Mar 2007 15:30
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Collects general purpose utility methods.
|
||||
/// </summary>
|
||||
internal static class SharedUtilities
|
||||
{
|
||||
/// private null constructor
|
||||
//private SharedUtilities() { }
|
||||
|
||||
// workitem 8423
|
||||
public static Int64 GetFileLength(string fileName)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
throw new System.IO.FileNotFoundException(fileName);
|
||||
|
||||
long fileLength = 0L;
|
||||
FileShare fs = FileShare.ReadWrite;
|
||||
#if !NETCF
|
||||
// FileShare.Delete is not defined for the Compact Framework
|
||||
fs |= FileShare.Delete;
|
||||
#endif
|
||||
using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, fs))
|
||||
{
|
||||
fileLength = s.Length;
|
||||
}
|
||||
return fileLength;
|
||||
}
|
||||
|
||||
|
||||
[System.Diagnostics.Conditional("NETCF")]
|
||||
public static void Workaround_Ladybug318918(Stream s)
|
||||
{
|
||||
// This is a workaround for this issue:
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/318918
|
||||
// It's required only on NETCF.
|
||||
s.Flush();
|
||||
}
|
||||
|
||||
|
||||
#if LEGACY
|
||||
/// <summary>
|
||||
/// Round the given DateTime value to an even second value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Round up in the case of an odd second value. The rounding does not consider
|
||||
/// fractional seconds.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is useful because the Zip spec allows storage of time only to the nearest
|
||||
/// even second. So if you want to compare the time of an entry in the archive with
|
||||
/// it's actual time in the filesystem, you need to round the actual filesystem
|
||||
/// time, or use a 2-second threshold for the comparison.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is most nautrally an extension method for the DateTime class but this
|
||||
/// library is built for .NET 2.0, not for .NET 3.5; This means extension methods
|
||||
/// are a no-no.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="source">The DateTime value to round</param>
|
||||
/// <returns>The ruonded DateTime value</returns>
|
||||
public static DateTime RoundToEvenSecond(DateTime source)
|
||||
{
|
||||
// round to nearest second:
|
||||
if ((source.Second % 2) == 1)
|
||||
source += new TimeSpan(0, 0, 1);
|
||||
|
||||
DateTime dtRounded = new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second);
|
||||
//if (source.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1);
|
||||
return dtRounded;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if YOU_LIKE_REDUNDANT_CODE
|
||||
internal static string NormalizePath(string path)
|
||||
{
|
||||
// remove leading single dot slash
|
||||
if (path.StartsWith(".\\")) path = path.Substring(2);
|
||||
|
||||
// remove intervening dot-slash
|
||||
path = path.Replace("\\.\\", "\\");
|
||||
|
||||
// remove double dot when preceded by a directory name
|
||||
var re = new System.Text.RegularExpressions.Regex(@"^(.*\\)?([^\\\.]+\\\.\.\\)(.+)$");
|
||||
path = re.Replace(path, "$1$3");
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static System.Text.RegularExpressions.Regex doubleDotRegex1 =
|
||||
new System.Text.RegularExpressions.Regex(@"^(.*/)?([^/\\.]+/\\.\\./)(.+)$");
|
||||
|
||||
private static string SimplifyFwdSlashPath(string path)
|
||||
{
|
||||
if (path.StartsWith("./")) path = path.Substring(2);
|
||||
path = path.Replace("/./", "/");
|
||||
|
||||
// Replace foo/anything/../bar with foo/bar
|
||||
path = doubleDotRegex1.Replace(path, "$1$3");
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine for transforming path names from filesystem format (on Windows that means backslashes) to
|
||||
/// a format suitable for use within zipfiles. This means trimming the volume letter and colon (if any) And
|
||||
/// swapping backslashes for forward slashes.
|
||||
/// </summary>
|
||||
/// <param name="pathName">source path.</param>
|
||||
/// <returns>transformed path</returns>
|
||||
public static string NormalizePathForUseInZipFile(string pathName)
|
||||
{
|
||||
// boundary case
|
||||
if (String.IsNullOrEmpty(pathName)) return pathName;
|
||||
|
||||
// trim volume if necessary
|
||||
if ((pathName.Length >= 2) && ((pathName[1] == ':') && (pathName[2] == '\\')))
|
||||
pathName = pathName.Substring(3);
|
||||
|
||||
// swap slashes
|
||||
pathName = pathName.Replace('\\', '/');
|
||||
|
||||
// trim all leading slashes
|
||||
while (pathName.StartsWith("/")) pathName = pathName.Substring(1);
|
||||
|
||||
return SimplifyFwdSlashPath(pathName);
|
||||
}
|
||||
|
||||
|
||||
static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437");
|
||||
static System.Text.Encoding utf8 = System.Text.Encoding.GetEncoding("UTF-8");
|
||||
|
||||
internal static byte[] StringToByteArray(string value, System.Text.Encoding encoding)
|
||||
{
|
||||
byte[] a = encoding.GetBytes(value);
|
||||
return a;
|
||||
}
|
||||
internal static byte[] StringToByteArray(string value)
|
||||
{
|
||||
return StringToByteArray(value, ibm437);
|
||||
}
|
||||
|
||||
//internal static byte[] Utf8StringToByteArray(string value)
|
||||
//{
|
||||
// return StringToByteArray(value, utf8);
|
||||
//}
|
||||
|
||||
//internal static string StringFromBuffer(byte[] buf, int maxlength)
|
||||
//{
|
||||
// return StringFromBuffer(buf, maxlength, ibm437);
|
||||
//}
|
||||
|
||||
internal static string Utf8StringFromBuffer(byte[] buf)
|
||||
{
|
||||
return StringFromBuffer(buf, utf8);
|
||||
}
|
||||
|
||||
internal static string StringFromBuffer(byte[] buf, System.Text.Encoding encoding)
|
||||
{
|
||||
// this form of the GetString() method is required for .NET CF compatibility
|
||||
string s = encoding.GetString(buf, 0, buf.Length);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadSignature(System.IO.Stream s)
|
||||
{
|
||||
int x = 0;
|
||||
try { x = _ReadFourBytes(s, "n/a"); }
|
||||
catch (BadReadException) { }
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadEntrySignature(System.IO.Stream s)
|
||||
{
|
||||
// handle the case of ill-formatted zip archives - includes a data descriptor
|
||||
// when none is expected.
|
||||
int x = 0;
|
||||
try
|
||||
{
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x == ZipConstants.ZipEntryDataDescriptorSignature)
|
||||
{
|
||||
// advance past data descriptor - 12 bytes if not zip64
|
||||
s.Seek(12, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x != ZipConstants.ZipEntrySignature)
|
||||
{
|
||||
// Maybe zip64 was in use for the prior entry.
|
||||
// Therefore, skip another 8 bytes.
|
||||
s.Seek(8, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x != ZipConstants.ZipEntrySignature)
|
||||
{
|
||||
// seek back to the first spot
|
||||
s.Seek(-24, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (BadReadException) { }
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadInt(System.IO.Stream s)
|
||||
{
|
||||
return _ReadFourBytes(s, "Could not read block - no data! (position 0x{0:X8})");
|
||||
}
|
||||
|
||||
private static int _ReadFourBytes(System.IO.Stream s, string message)
|
||||
{
|
||||
int n = 0;
|
||||
byte[] block = new byte[4];
|
||||
#if NETCF
|
||||
// workitem 9181
|
||||
// Reading here in NETCF sometimes reads "backwards". Seems to happen for
|
||||
// larger files. Not sure why. Maybe an error in caching. If the data is:
|
||||
//
|
||||
// 00100210: 9efa 0f00 7072 6f6a 6563 742e 6963 7750 ....project.icwP
|
||||
// 00100220: 4b05 0600 0000 0006 0006 0091 0100 008e K...............
|
||||
// 00100230: 0010 0000 00 .....
|
||||
//
|
||||
// ...and the stream Position is 10021F, then a Read of 4 bytes is returning
|
||||
// 50776369, instead of 06054b50. This seems to happen the 2nd time Read()
|
||||
// is called from that Position..
|
||||
//
|
||||
// submitted to connect.microsoft.com
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=318918#tabs
|
||||
//
|
||||
for (int i = 0; i < block.Length; i++)
|
||||
{
|
||||
n+= s.Read(block, i, 1);
|
||||
}
|
||||
#else
|
||||
n = s.Read(block, 0, block.Length);
|
||||
#endif
|
||||
if (n != block.Length) throw new BadReadException(String.Format(message, s.Position));
|
||||
int data = unchecked((((block[3] * 256 + block[2]) * 256) + block[1]) * 256 + block[0]);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds a signature in the zip stream. This is useful for finding
|
||||
/// the end of a zip entry, for example, or the beginning of the next ZipEntry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Scans through 64k at a time.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// If the method fails to find the requested signature, the stream Position
|
||||
/// after completion of this method is unchanged. If the method succeeds in
|
||||
/// finding the requested signature, the stream position after completion is
|
||||
/// direct AFTER the signature found in the stream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">The stream to search</param>
|
||||
/// <param name="SignatureToFind">The 4-byte signature to find</param>
|
||||
/// <returns>The number of bytes read</returns>
|
||||
internal static long FindSignature(System.IO.Stream stream, int SignatureToFind)
|
||||
{
|
||||
long startingPosition = stream.Position;
|
||||
|
||||
int BATCH_SIZE = 65536; // 8192;
|
||||
byte[] targetBytes = new byte[4];
|
||||
targetBytes[0] = (byte)(SignatureToFind >> 24);
|
||||
targetBytes[1] = (byte)((SignatureToFind & 0x00FF0000) >> 16);
|
||||
targetBytes[2] = (byte)((SignatureToFind & 0x0000FF00) >> 8);
|
||||
targetBytes[3] = (byte)(SignatureToFind & 0x000000FF);
|
||||
byte[] batch = new byte[BATCH_SIZE];
|
||||
int n = 0;
|
||||
bool success = false;
|
||||
do
|
||||
{
|
||||
n = stream.Read(batch, 0, batch.Length);
|
||||
if (n != 0)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (batch[i] == targetBytes[3])
|
||||
{
|
||||
long curPosition = stream.Position;
|
||||
stream.Seek(i - n, System.IO.SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
|
||||
// workitem 7711
|
||||
int sig = ReadSignature(stream);
|
||||
|
||||
success = (sig == SignatureToFind);
|
||||
if (!success)
|
||||
{
|
||||
stream.Seek(curPosition, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
}
|
||||
else
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
else break;
|
||||
if (success) break;
|
||||
|
||||
} while (true);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
stream.Seek(startingPosition, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
return -1; // or throw?
|
||||
}
|
||||
|
||||
// subtract 4 for the signature.
|
||||
long bytesRead = (stream.Position - startingPosition) - 4;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// If I have a time in the .NET environment, and I want to use it for
|
||||
// SetWastWriteTime() etc, then I need to adjust it for Win32.
|
||||
internal static DateTime AdjustTime_Reverse(DateTime time)
|
||||
{
|
||||
if (time.Kind == DateTimeKind.Utc) return time;
|
||||
DateTime adjusted = time;
|
||||
if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
||||
adjusted = time - new System.TimeSpan(1, 0, 0);
|
||||
|
||||
else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
||||
adjusted = time + new System.TimeSpan(1, 0, 0);
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
#if NECESSARY
|
||||
// If I read a time from a file with GetLastWriteTime() (etc), I need
|
||||
// to adjust it for display in the .NET environment.
|
||||
internal static DateTime AdjustTime_Forward(DateTime time)
|
||||
{
|
||||
if (time.Kind == DateTimeKind.Utc) return time;
|
||||
DateTime adjusted = time;
|
||||
if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
||||
adjusted = time + new System.TimeSpan(1, 0, 0);
|
||||
|
||||
else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
||||
adjusted = time - new System.TimeSpan(1, 0, 0);
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
internal static DateTime PackedToDateTime(Int32 packedDateTime)
|
||||
{
|
||||
// workitem 7074 & workitem 7170
|
||||
if (packedDateTime == 0xFFFF || packedDateTime == 0)
|
||||
return new System.DateTime(1995, 1, 1, 0, 0, 0, 0); // return a fixed date when none is supplied.
|
||||
|
||||
Int16 packedTime = unchecked((Int16)(packedDateTime & 0x0000ffff));
|
||||
Int16 packedDate = unchecked((Int16)((packedDateTime & 0xffff0000) >> 16));
|
||||
|
||||
int year = 1980 + ((packedDate & 0xFE00) >> 9);
|
||||
int month = (packedDate & 0x01E0) >> 5;
|
||||
int day = packedDate & 0x001F;
|
||||
|
||||
int hour = (packedTime & 0xF800) >> 11;
|
||||
int minute = (packedTime & 0x07E0) >> 5;
|
||||
//int second = packedTime & 0x001F;
|
||||
int second = (packedTime & 0x001F) * 2;
|
||||
|
||||
// validation and error checking.
|
||||
// this is not foolproof but will catch most errors.
|
||||
if (second >= 60) { minute++; second = 0; }
|
||||
if (minute >= 60) { hour++; minute = 0; }
|
||||
if (hour >= 24) { day++; hour = 0; }
|
||||
|
||||
DateTime d = System.DateTime.Now;
|
||||
bool success= false;
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException)
|
||||
{
|
||||
if (year == 1980 && (month == 0 || day == 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(1980, 1, 1, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException)
|
||||
{
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(1980, 1, 1, 0, 0, 0, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException) { }
|
||||
|
||||
}
|
||||
}
|
||||
// workitem 8814
|
||||
// my god, I can't believe how many different ways applications
|
||||
// can mess up a simple date format.
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
while (year < 1980) year++;
|
||||
while (year > 2030) year--;
|
||||
while (month < 1) month++;
|
||||
while (month > 12) month--;
|
||||
while (day < 1) day++;
|
||||
while (day > 28) day--;
|
||||
while (minute < 0) minute++;
|
||||
while (minute > 59) minute--;
|
||||
while (second < 0) second++;
|
||||
while (second > 59) second--;
|
||||
d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException) { }
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
string msg = String.Format("y({0}) m({1}) d({2}) h({3}) m({4}) s({5})", year, month, day, hour, minute, second);
|
||||
throw new ZipException(String.Format("Bad date/time format in the zip file. ({0})", msg));
|
||||
|
||||
}
|
||||
// workitem 6191
|
||||
//d = AdjustTime_Reverse(d);
|
||||
d = DateTime.SpecifyKind(d, DateTimeKind.Local);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
internal
|
||||
static Int32 DateTimeToPacked(DateTime time)
|
||||
{
|
||||
// The time is passed in here only for purposes of writing LastModified to the
|
||||
// zip archive. It should always be LocalTime, but we convert anyway. And,
|
||||
// since the time is being written out, it needs to be adjusted.
|
||||
|
||||
time = time.ToLocalTime();
|
||||
// workitem 7966
|
||||
//time = AdjustTime_Forward(time);
|
||||
|
||||
// see http://www.vsft.com/hal/dostime.htm for the format
|
||||
UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00));
|
||||
UInt16 packedTime = (UInt16)((time.Second / 2 & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800));
|
||||
|
||||
Int32 result = (Int32)(((UInt32)(packedDate << 16)) | packedTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a pseudo-random filename, suitable for use as a temporary
|
||||
/// file, and open it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The System.IO.Path.GetRandomFileName() method is not available on
|
||||
/// the Compact Framework, so this library provides its own substitute
|
||||
/// on NETCF.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method produces a filename of the form
|
||||
/// DotNetZip-xxxxxxxx.tmp, where xxxxxxxx is replaced by randomly
|
||||
/// chosen characters, and creates that file.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static void CreateAndOpenUniqueTempFile(string dir,
|
||||
out Stream fs,
|
||||
out string filename)
|
||||
{
|
||||
// workitem 9763
|
||||
// http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx
|
||||
// try 3 times:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = Path.Combine(dir, InternalGetTempFileName());
|
||||
fs = new FileStream(filename, FileMode.CreateNew);
|
||||
return;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (i == 2) throw;
|
||||
}
|
||||
}
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
#if NETCF || SILVERLIGHT
|
||||
public static string InternalGetTempFileName()
|
||||
{
|
||||
return "DotNetZip-" + GenerateRandomStringImpl(8,0) + ".tmp";
|
||||
}
|
||||
|
||||
internal static string GenerateRandomStringImpl(int length, int delta)
|
||||
{
|
||||
bool WantMixedCase = (delta == 0);
|
||||
System.Random rnd = new System.Random();
|
||||
|
||||
string result = "";
|
||||
char[] a = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
// delta == 65 means uppercase
|
||||
// delta == 97 means lowercase
|
||||
if (WantMixedCase)
|
||||
delta = (rnd.Next(2) == 0) ? 65 : 97;
|
||||
a[i] = (char)(rnd.Next(26) + delta);
|
||||
}
|
||||
|
||||
result = new System.String(a);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
public static string InternalGetTempFileName()
|
||||
{
|
||||
return "DotNetZip-" + Path.GetRandomFileName().Substring(0, 8) + ".tmp";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Workitem 7889: handle ERROR_LOCK_VIOLATION during read
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This could be gracefully handled with an extension attribute, but
|
||||
/// This assembly is built for .NET 2.0, so I cannot use them.
|
||||
/// </remarks>
|
||||
internal static int ReadWithRetry(System.IO.Stream s, byte[] buffer, int offset, int count, string FileName)
|
||||
{
|
||||
int n = 0;
|
||||
bool done = false;
|
||||
#if !NETCF && !SILVERLIGHT
|
||||
int retries = 0;
|
||||
#endif
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
n = s.Read(buffer, offset, count);
|
||||
done = true;
|
||||
}
|
||||
#if NETCF || SILVERLIGHT
|
||||
catch (System.IO.IOException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
#else
|
||||
catch (System.IO.IOException ioexc1)
|
||||
{
|
||||
// Check if we can call GetHRForException,
|
||||
// which makes unmanaged code calls.
|
||||
var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
||||
if (p.IsUnrestricted())
|
||||
{
|
||||
uint hresult = _HRForException(ioexc1);
|
||||
if (hresult != 0x80070021) // ERROR_LOCK_VIOLATION
|
||||
throw new System.IO.IOException(String.Format("Cannot read file {0}", FileName), ioexc1);
|
||||
retries++;
|
||||
if (retries > 10)
|
||||
throw new System.IO.IOException(String.Format("Cannot read file {0}, at offset 0x{1:X8} after 10 retries", FileName, offset), ioexc1);
|
||||
|
||||
// max time waited on last retry = 250 + 10*550 = 5.75s
|
||||
// aggregate time waited after 10 retries: 250 + 55*550 = 30.5s
|
||||
System.Threading.Thread.Sleep(250 + retries * 550);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The permission.Demand() failed. Therefore, we cannot call
|
||||
// GetHRForException, and cannot do the subtle handling of
|
||||
// ERROR_LOCK_VIOLATION. Just bail.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
while (!done);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
#if !NETCF
|
||||
// workitem 8009
|
||||
//
|
||||
// This method must remain separate.
|
||||
//
|
||||
// Marshal.GetHRForException() is needed to do special exception handling for
|
||||
// the read. But, that method requires UnmanagedCode permissions, and is marked
|
||||
// with LinkDemand for UnmanagedCode. In an ASP.NET medium trust environment,
|
||||
// where UnmanagedCode is restricted, will generate a SecurityException at the
|
||||
// time of JIT of the method that calls a method that is marked with LinkDemand
|
||||
// for UnmanagedCode. The SecurityException, if it is restricted, will occur
|
||||
// when this method is JITed.
|
||||
//
|
||||
// The Marshal.GetHRForException() is factored out of ReadWithRetry in order to
|
||||
// avoid the SecurityException at JIT compile time. Because _HRForException is
|
||||
// called only when the UnmanagedCode is allowed. This means .NET never
|
||||
// JIT-compiles this method when UnmanagedCode is disallowed, and thus never
|
||||
// generates the JIT-compile time exception.
|
||||
//
|
||||
#endif
|
||||
private static uint _HRForException(System.Exception ex1)
|
||||
{
|
||||
return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A decorator stream. It wraps another stream, and performs bookkeeping
|
||||
/// to keep track of the stream Position.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In some cases, it is not possible to get the Position of a stream, let's
|
||||
/// say, on a write-only output stream like ASP.NET's
|
||||
/// <c>Response.OutputStream</c>, or on a different write-only stream
|
||||
/// provided as the destination for the zip by the application. In this
|
||||
/// case, programmers can use this counting stream to count the bytes read
|
||||
/// or written.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Consider the scenario of an application that saves a self-extracting
|
||||
/// archive (SFX), that uses a custom SFX stub.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Saving to a filesystem file, the application would open the
|
||||
/// filesystem file (getting a <c>FileStream</c>), save the custom sfx stub
|
||||
/// into it, and then call <c>ZipFile.Save()</c>, specifying the same
|
||||
/// FileStream. <c>ZipFile.Save()</c> does the right thing for the zipentry
|
||||
/// offsets, by inquiring the Position of the <c>FileStream</c> before writing
|
||||
/// any data, and then adding that initial offset into any ZipEntry
|
||||
/// offsets in the zip directory. Everything works fine.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Now suppose the application is an ASPNET application and it saves
|
||||
/// directly to <c>Response.OutputStream</c>. It's not possible for DotNetZip to
|
||||
/// inquire the <c>Position</c>, so the offsets for the SFX will be wrong.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The workaround is for the application to use this class to wrap
|
||||
/// <c>HttpResponse.OutputStream</c>, then write the SFX stub and the ZipFile
|
||||
/// into that wrapper stream. Because <c>ZipFile.Save()</c> can inquire the
|
||||
/// <c>Position</c>, it will then do the right thing with the offsets.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CountingStream : System.IO.Stream
|
||||
{
|
||||
// workitem 12374: this class is now public
|
||||
private System.IO.Stream _s;
|
||||
private Int64 _bytesWritten;
|
||||
private Int64 _bytesRead;
|
||||
private Int64 _initialOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor.
|
||||
/// </summary>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
public CountingStream(System.IO.Stream stream)
|
||||
: base()
|
||||
{
|
||||
_s = stream;
|
||||
try
|
||||
{
|
||||
_initialOffset = _s.Position;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_initialOffset = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped stream.
|
||||
/// </summary>
|
||||
public Stream WrappedStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return _s;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The count of bytes written out to the stream.
|
||||
/// </summary>
|
||||
public Int64 BytesWritten
|
||||
{
|
||||
get { return _bytesWritten; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the count of bytes that have been read from the stream.
|
||||
/// </summary>
|
||||
public Int64 BytesRead
|
||||
{
|
||||
get { return _bytesRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the byte count on the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='delta'>
|
||||
/// the number of bytes to subtract from the count.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Subtract delta from the count of bytes written to the stream.
|
||||
/// This is necessary when seeking back, and writing additional data,
|
||||
/// as happens in some cases when saving Zip files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Adjust(Int64 delta)
|
||||
{
|
||||
_bytesWritten -= delta;
|
||||
if (_bytesWritten < 0)
|
||||
throw new InvalidOperationException();
|
||||
if (_s as CountingStream != null)
|
||||
((CountingStream)_s).Adjust(delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The read method.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to hold the data read from the stream.</param>
|
||||
/// <param name="offset">the offset within the buffer to copy the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes read, after decryption and decompression.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int n = _s.Read(buffer, offset, count);
|
||||
_bytesRead += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data into the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count == 0) return;
|
||||
_s.Write(buffer, offset, count);
|
||||
_bytesWritten += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _s.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is possible to call Seek() on the stream.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return _s.CanSeek; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is possible to call Write() on the stream.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _s.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the underlying stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_s.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return _s.Length; } // bytesWritten??
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of number of bytes written, plus the initial
|
||||
/// offset before writing.
|
||||
/// </summary>
|
||||
public long ComputedPosition
|
||||
{
|
||||
get { return _initialOffset + _bytesWritten; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Position of the stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _s.Position; }
|
||||
set
|
||||
{
|
||||
_s.Seek(value, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_s);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek in the stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">the offset point to seek to</param>
|
||||
/// <param name="origin">the reference point from which to seek</param>
|
||||
/// <returns>The new position</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
return _s.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the length of the underlying stream. Be careful with this!
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='value'>the length to set on the underlying stream.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
_s.SetLength(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
941
MinecraftClient/Protocol/Handlers/Compression/Zip/WinZipAes.cs
Normal file
941
MinecraftClient/Protocol/Handlers/Compression/Zip/WinZipAes.cs
Normal file
|
|
@ -0,0 +1,941 @@
|
|||
//#define Trace
|
||||
|
||||
// WinZipAes.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-12 13:42:06>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the classes for dealing with WinZip's AES encryption,
|
||||
// according to the specifications for the format available on WinZip's website.
|
||||
//
|
||||
// Created: January 2009
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
#if AESCRYPTO
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a helper class supporting WinZip AES encryption.
|
||||
/// This class is intended for use only by the DotNetZip library.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Most uses of the DotNetZip library will not involve direct calls into
|
||||
/// the WinZipAesCrypto class. Instead, the WinZipAesCrypto class is
|
||||
/// instantiated and used by the ZipEntry() class when WinZip AES
|
||||
/// encryption or decryption on an entry is employed.
|
||||
/// </remarks>
|
||||
internal class WinZipAesCrypto
|
||||
{
|
||||
internal byte[] _Salt;
|
||||
internal byte[] _providedPv;
|
||||
internal byte[] _generatedPv;
|
||||
internal int _KeyStrengthInBits;
|
||||
private byte[] _MacInitializationVector;
|
||||
private byte[] _StoredMac;
|
||||
private byte[] _keyBytes;
|
||||
private Int16 PasswordVerificationStored;
|
||||
private Int16 PasswordVerificationGenerated;
|
||||
private int Rfc2898KeygenIterations = 1000;
|
||||
private string _Password;
|
||||
private bool _cryptoGenerated ;
|
||||
|
||||
private WinZipAesCrypto(string password, int KeyStrengthInBits)
|
||||
{
|
||||
_Password = password;
|
||||
_KeyStrengthInBits = KeyStrengthInBits;
|
||||
}
|
||||
|
||||
public static WinZipAesCrypto Generate(string password, int KeyStrengthInBits)
|
||||
{
|
||||
WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits);
|
||||
|
||||
int saltSizeInBytes = c._KeyStrengthInBytes / 2;
|
||||
c._Salt = new byte[saltSizeInBytes];
|
||||
Random rnd = new Random();
|
||||
rnd.NextBytes(c._Salt);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static WinZipAesCrypto ReadFromStream(string password, int KeyStrengthInBits, Stream s)
|
||||
{
|
||||
// from http://www.winzip.com/aes_info.htm
|
||||
//
|
||||
// Size(bytes) Content
|
||||
// -----------------------------------
|
||||
// Variable Salt value
|
||||
// 2 Password verification value
|
||||
// Variable Encrypted file data
|
||||
// 10 Authentication code
|
||||
//
|
||||
// ZipEntry.CompressedSize represents the size of all of those elements.
|
||||
|
||||
// salt size varies with key length:
|
||||
// 128 bit key => 8 bytes salt
|
||||
// 192 bits => 12 bytes salt
|
||||
// 256 bits => 16 bytes salt
|
||||
|
||||
WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits);
|
||||
|
||||
int saltSizeInBytes = c._KeyStrengthInBytes / 2;
|
||||
c._Salt = new byte[saltSizeInBytes];
|
||||
c._providedPv = new byte[2];
|
||||
|
||||
s.Read(c._Salt, 0, c._Salt.Length);
|
||||
s.Read(c._providedPv, 0, c._providedPv.Length);
|
||||
|
||||
c.PasswordVerificationStored = (Int16)(c._providedPv[0] + c._providedPv[1] * 256);
|
||||
if (password != null)
|
||||
{
|
||||
c.PasswordVerificationGenerated = (Int16)(c.GeneratedPV[0] + c.GeneratedPV[1] * 256);
|
||||
if (c.PasswordVerificationGenerated != c.PasswordVerificationStored)
|
||||
throw new BadPasswordException("bad password");
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public byte[] GeneratedPV
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cryptoGenerated) _GenerateCryptoBytes();
|
||||
return _generatedPv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] Salt
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Salt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int _KeyStrengthInBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return _KeyStrengthInBits / 8;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int SizeOfEncryptionMetadata
|
||||
{
|
||||
get
|
||||
{
|
||||
// 10 bytes after, (n-10) before the compressed data
|
||||
return _KeyStrengthInBytes / 2 + 10 + 2;
|
||||
}
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
set
|
||||
{
|
||||
_Password = value;
|
||||
if (_Password != null)
|
||||
{
|
||||
PasswordVerificationGenerated = (Int16)(GeneratedPV[0] + GeneratedPV[1] * 256);
|
||||
if (PasswordVerificationGenerated != PasswordVerificationStored)
|
||||
throw new Ionic.Zip.BadPasswordException();
|
||||
}
|
||||
}
|
||||
private get
|
||||
{
|
||||
return _Password;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void _GenerateCryptoBytes()
|
||||
{
|
||||
//Console.WriteLine(" provided password: '{0}'", _Password);
|
||||
|
||||
System.Security.Cryptography.Rfc2898DeriveBytes rfc2898 =
|
||||
new System.Security.Cryptography.Rfc2898DeriveBytes(_Password, Salt, Rfc2898KeygenIterations);
|
||||
|
||||
_keyBytes = rfc2898.GetBytes(_KeyStrengthInBytes); // 16 or 24 or 32 ???
|
||||
_MacInitializationVector = rfc2898.GetBytes(_KeyStrengthInBytes);
|
||||
_generatedPv = rfc2898.GetBytes(2);
|
||||
|
||||
_cryptoGenerated = true;
|
||||
}
|
||||
|
||||
|
||||
public byte[] KeyBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cryptoGenerated) _GenerateCryptoBytes();
|
||||
return _keyBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] MacIv
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_cryptoGenerated) _GenerateCryptoBytes();
|
||||
return _MacInitializationVector;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CalculatedMac;
|
||||
|
||||
|
||||
public void ReadAndVerifyMac(System.IO.Stream s)
|
||||
{
|
||||
bool invalid = false;
|
||||
|
||||
// read integrityCheckVector.
|
||||
// caller must ensure that the file pointer is in the right spot!
|
||||
_StoredMac = new byte[10]; // aka "authentication code"
|
||||
s.Read(_StoredMac, 0, _StoredMac.Length);
|
||||
|
||||
if (_StoredMac.Length != CalculatedMac.Length)
|
||||
invalid = true;
|
||||
|
||||
if (!invalid)
|
||||
{
|
||||
for (int i = 0; i < _StoredMac.Length; i++)
|
||||
{
|
||||
if (_StoredMac[i] != CalculatedMac[i])
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid)
|
||||
throw new Ionic.Zip.BadStateException("The MAC does not match.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region DONT_COMPILE_BUT_KEEP_FOR_POTENTIAL_FUTURE_USE
|
||||
#if NO
|
||||
internal class Util
|
||||
{
|
||||
private static void _Format(System.Text.StringBuilder sb1,
|
||||
byte[] b,
|
||||
int offset,
|
||||
int length)
|
||||
{
|
||||
|
||||
System.Text.StringBuilder sb2 = new System.Text.StringBuilder();
|
||||
sb1.Append("0000 ");
|
||||
int i;
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
int x = offset+i;
|
||||
if (i != 0 && i % 16 == 0)
|
||||
{
|
||||
sb1.Append(" ")
|
||||
.Append(sb2)
|
||||
.Append("\n")
|
||||
.Append(String.Format("{0:X4} ", i));
|
||||
sb2.Remove(0,sb2.Length);
|
||||
}
|
||||
sb1.Append(System.String.Format("{0:X2} ", b[x]));
|
||||
if (b[x] >=32 && b[x] <= 126)
|
||||
sb2.Append((char)b[x]);
|
||||
else
|
||||
sb2.Append(".");
|
||||
}
|
||||
if (sb2.Length > 0)
|
||||
{
|
||||
sb1.Append(new String(' ', ((16 - i%16) * 3) + 4))
|
||||
.Append(sb2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static string FormatByteArray(byte[] b, int limit)
|
||||
{
|
||||
System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
|
||||
|
||||
if ((limit * 2 > b.Length) || limit == 0)
|
||||
{
|
||||
_Format(sb1, b, 0, b.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// first N bytes of the buffer
|
||||
_Format(sb1, b, 0, limit);
|
||||
|
||||
if (b.Length > limit * 2)
|
||||
sb1.Append(String.Format("\n ...({0} other bytes here)....\n", b.Length - limit * 2));
|
||||
|
||||
// last N bytes of the buffer
|
||||
_Format(sb1, b, b.Length - limit, limit);
|
||||
}
|
||||
|
||||
return sb1.ToString();
|
||||
}
|
||||
|
||||
|
||||
internal static string FormatByteArray(byte[] b)
|
||||
{
|
||||
return FormatByteArray(b, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream that encrypts as it writes, or decrypts as it reads. The
|
||||
/// Crypto is AES in CTR (counter) mode, which is compatible with the AES
|
||||
/// encryption employed by WinZip 12.0.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The AES/CTR encryption protocol used by WinZip works like this:
|
||||
///
|
||||
/// - start with a counter, initialized to zero.
|
||||
///
|
||||
/// - to encrypt, take the data by 16-byte blocks. For each block:
|
||||
/// - apply the transform to the counter
|
||||
/// - increement the counter
|
||||
/// - XOR the result of the transform with the plaintext to
|
||||
/// get the ciphertext.
|
||||
/// - compute the mac on the encrypted bytes
|
||||
/// - when finished with all blocks, store the computed MAC.
|
||||
///
|
||||
/// - to decrypt, take the data by 16-byte blocks. For each block:
|
||||
/// - compute the mac on the encrypted bytes,
|
||||
/// - apply the transform to the counter
|
||||
/// - increement the counter
|
||||
/// - XOR the result of the transform with the ciphertext to
|
||||
/// get the plaintext.
|
||||
/// - when finished with all blocks, compare the computed MAC against
|
||||
/// the stored MAC
|
||||
///
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
//
|
||||
internal class WinZipAesCipherStream : Stream
|
||||
{
|
||||
private WinZipAesCrypto _params;
|
||||
private System.IO.Stream _s;
|
||||
private CryptoMode _mode;
|
||||
private int _nonce;
|
||||
private bool _finalBlock;
|
||||
|
||||
internal HMACSHA1 _mac;
|
||||
|
||||
// Use RijndaelManaged from .NET 2.0.
|
||||
// AesManaged came in .NET 3.5, but we want to limit
|
||||
// dependency to .NET 2.0. AES is just a restricted form
|
||||
// of Rijndael (fixed block size of 128, some crypto modes not supported).
|
||||
|
||||
internal RijndaelManaged _aesCipher;
|
||||
internal ICryptoTransform _xform;
|
||||
|
||||
private const int BLOCK_SIZE_IN_BYTES = 16;
|
||||
|
||||
private byte[] counter = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
|
||||
// I've had a problem when wrapping a WinZipAesCipherStream inside
|
||||
// a DeflateStream. Calling Read() on the DeflateStream results in
|
||||
// a Read() on the WinZipAesCipherStream, but the buffer is larger
|
||||
// than the total size of the encrypted data, and larger than the
|
||||
// initial Read() on the DeflateStream! When the encrypted
|
||||
// bytestream is embedded within a larger stream (As in a zip
|
||||
// archive), the Read() doesn't fail with EOF. This causes bad
|
||||
// data to be returned, and it messes up the MAC.
|
||||
|
||||
// This field is used to provide a hard-stop to the size of
|
||||
// data that can be read from the stream. In Read(), if the buffer or
|
||||
// read request goes beyond the stop, we truncate it.
|
||||
|
||||
private long _length;
|
||||
private long _totalBytesXferred;
|
||||
private byte[] _PendingWriteBlock;
|
||||
private int _pendingCount;
|
||||
private byte[] _iobuf;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor.
|
||||
/// </summary>
|
||||
/// <param name="s">The underlying stream</param>
|
||||
/// <param name="mode">To either encrypt or decrypt.</param>
|
||||
/// <param name="cryptoParams">The pre-initialized WinZipAesCrypto object.</param>
|
||||
/// <param name="length">The maximum number of bytes to read from the stream.</param>
|
||||
internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, long length, CryptoMode mode)
|
||||
: this(s, cryptoParams, mode)
|
||||
{
|
||||
// don't read beyond this limit!
|
||||
_length = length;
|
||||
//Console.WriteLine("max length of AES stream: {0}", _length);
|
||||
}
|
||||
|
||||
|
||||
#if WANT_TRACE
|
||||
Stream untransformed;
|
||||
String traceFileUntransformed;
|
||||
Stream transformed;
|
||||
String traceFileTransformed;
|
||||
#endif
|
||||
|
||||
|
||||
internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, CryptoMode mode)
|
||||
: base()
|
||||
{
|
||||
TraceOutput("-------------------------------------------------------");
|
||||
TraceOutput("Create {0:X8}", this.GetHashCode());
|
||||
|
||||
_params = cryptoParams;
|
||||
_s = s;
|
||||
_mode = mode;
|
||||
_nonce = 1;
|
||||
|
||||
if (_params == null)
|
||||
throw new BadPasswordException("Supply a password to use AES encryption.");
|
||||
|
||||
int keySizeInBits = _params.KeyBytes.Length * 8;
|
||||
if (keySizeInBits != 256 && keySizeInBits != 128 && keySizeInBits != 192)
|
||||
throw new ArgumentOutOfRangeException("keysize",
|
||||
"size of key must be 128, 192, or 256");
|
||||
|
||||
_mac = new HMACSHA1(_params.MacIv);
|
||||
|
||||
_aesCipher = new System.Security.Cryptography.RijndaelManaged();
|
||||
_aesCipher.BlockSize = 128;
|
||||
_aesCipher.KeySize = keySizeInBits; // 128, 192, 256
|
||||
_aesCipher.Mode = CipherMode.ECB;
|
||||
_aesCipher.Padding = PaddingMode.None;
|
||||
|
||||
byte[] iv = new byte[BLOCK_SIZE_IN_BYTES]; // all zeroes
|
||||
|
||||
// Create an ENCRYPTOR, regardless whether doing decryption or encryption.
|
||||
// It is reflexive.
|
||||
_xform = _aesCipher.CreateEncryptor(_params.KeyBytes, iv);
|
||||
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
{
|
||||
_iobuf = new byte[2048];
|
||||
_PendingWriteBlock = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
}
|
||||
|
||||
|
||||
#if WANT_TRACE
|
||||
traceFileUntransformed = "unpack\\WinZipAesCipherStream.trace.untransformed.out";
|
||||
traceFileTransformed = "unpack\\WinZipAesCipherStream.trace.transformed.out";
|
||||
|
||||
untransformed = System.IO.File.Create(traceFileUntransformed);
|
||||
transformed = System.IO.File.Create(traceFileTransformed);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void XorInPlace(byte[] buffer, int offset, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteTransformOneBlock(byte[] buffer, int offset)
|
||||
{
|
||||
System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
|
||||
_xform.TransformBlock(counter,
|
||||
0,
|
||||
BLOCK_SIZE_IN_BYTES,
|
||||
counterOut,
|
||||
0);
|
||||
XorInPlace(buffer, offset, BLOCK_SIZE_IN_BYTES);
|
||||
_mac.TransformBlock(buffer, offset, BLOCK_SIZE_IN_BYTES, null, 0);
|
||||
}
|
||||
|
||||
|
||||
private void WriteTransformBlocks(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int posn = offset;
|
||||
int last = count + offset;
|
||||
|
||||
while (posn < buffer.Length && posn < last)
|
||||
{
|
||||
WriteTransformOneBlock (buffer, posn);
|
||||
posn += BLOCK_SIZE_IN_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void WriteTransformFinalBlock()
|
||||
{
|
||||
if (_pendingCount == 0)
|
||||
throw new InvalidOperationException("No bytes available.");
|
||||
|
||||
if (_finalBlock)
|
||||
throw new InvalidOperationException("The final block has already been transformed.");
|
||||
|
||||
System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
|
||||
counterOut = _xform.TransformFinalBlock(counter,
|
||||
0,
|
||||
BLOCK_SIZE_IN_BYTES);
|
||||
XorInPlace(_PendingWriteBlock, 0, _pendingCount);
|
||||
_mac.TransformFinalBlock(_PendingWriteBlock, 0, _pendingCount);
|
||||
_finalBlock = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private int ReadTransformOneBlock(byte[] buffer, int offset, int last)
|
||||
{
|
||||
if (_finalBlock)
|
||||
throw new NotSupportedException();
|
||||
|
||||
int bytesRemaining = last - offset;
|
||||
int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES)
|
||||
? BLOCK_SIZE_IN_BYTES
|
||||
: bytesRemaining;
|
||||
|
||||
// update the counter
|
||||
System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
|
||||
|
||||
// Determine if this is the final block
|
||||
if ((bytesToRead == bytesRemaining) &&
|
||||
(_length > 0) &&
|
||||
(_totalBytesXferred + last == _length))
|
||||
{
|
||||
_mac.TransformFinalBlock(buffer, offset, bytesToRead);
|
||||
counterOut = _xform.TransformFinalBlock(counter,
|
||||
0,
|
||||
BLOCK_SIZE_IN_BYTES);
|
||||
_finalBlock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mac.TransformBlock(buffer, offset, bytesToRead, null, 0);
|
||||
_xform.TransformBlock(counter,
|
||||
0, // offset
|
||||
BLOCK_SIZE_IN_BYTES,
|
||||
counterOut,
|
||||
0); // offset
|
||||
}
|
||||
|
||||
XorInPlace(buffer, offset, bytesToRead);
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ReadTransformBlocks(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int posn = offset;
|
||||
int last = count + offset;
|
||||
|
||||
while (posn < buffer.Length && posn < last )
|
||||
{
|
||||
int n = ReadTransformOneBlock (buffer, posn, last);
|
||||
posn += n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
throw new NotSupportedException();
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException("offset",
|
||||
"Must not be less than zero.");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count",
|
||||
"Must not be less than zero.");
|
||||
|
||||
if (buffer.Length < offset + count)
|
||||
throw new ArgumentException("The buffer is too small");
|
||||
|
||||
// When I wrap a WinZipAesStream in a DeflateStream, the
|
||||
// DeflateStream asks its captive to read 4k blocks, even if the
|
||||
// encrypted bytestream is smaller than that. This is a way to
|
||||
// limit the number of bytes read.
|
||||
|
||||
int bytesToRead = count;
|
||||
|
||||
if (_totalBytesXferred >= _length)
|
||||
{
|
||||
return 0; // EOF
|
||||
}
|
||||
|
||||
long bytesRemaining = _length - _totalBytesXferred;
|
||||
if (bytesRemaining < count) bytesToRead = (int)bytesRemaining;
|
||||
|
||||
int n = _s.Read(buffer, offset, bytesToRead);
|
||||
|
||||
|
||||
#if WANT_TRACE
|
||||
untransformed.Write(buffer, offset, bytesToRead);
|
||||
#endif
|
||||
|
||||
ReadTransformBlocks(buffer, offset, bytesToRead);
|
||||
|
||||
#if WANT_TRACE
|
||||
transformed.Write(buffer, offset, bytesToRead);
|
||||
#endif
|
||||
_totalBytesXferred += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the final HMAC-SHA1-80 for the data that was encrypted.
|
||||
/// </summary>
|
||||
public byte[] FinalAuthentication
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_finalBlock)
|
||||
{
|
||||
// special-case zero-byte files
|
||||
if ( _totalBytesXferred != 0)
|
||||
throw new BadStateException("The final hash has not been computed.");
|
||||
|
||||
// Must call ComputeHash on an empty byte array when no data
|
||||
// has run through the MAC.
|
||||
|
||||
byte[] b = { };
|
||||
_mac.ComputeHash(b);
|
||||
// fall through
|
||||
}
|
||||
byte[] macBytes10 = new byte[10];
|
||||
System.Array.Copy(_mac.Hash, 0, macBytes10, 0, 10);
|
||||
return macBytes10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_finalBlock)
|
||||
throw new InvalidOperationException("The final block has already been transformed.");
|
||||
|
||||
if (_mode == CryptoMode.Decrypt)
|
||||
throw new NotSupportedException();
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException("offset",
|
||||
"Must not be less than zero.");
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count",
|
||||
"Must not be less than zero.");
|
||||
if (buffer.Length < offset + count)
|
||||
throw new ArgumentException("The offset and count are too large");
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
TraceOutput("Write off({0}) count({1})", offset, count);
|
||||
|
||||
#if WANT_TRACE
|
||||
untransformed.Write(buffer, offset, count);
|
||||
#endif
|
||||
|
||||
// For proper AES encryption, an AES encryptor application calls
|
||||
// TransformBlock repeatedly for all 16-byte blocks except the
|
||||
// last. For the last block, it then calls TransformFinalBlock().
|
||||
//
|
||||
// This class is a stream that encrypts via Write(). But, it's not
|
||||
// possible to recognize which are the "last" bytes from within the call
|
||||
// to Write(). The caller can call Write() several times in succession,
|
||||
// with varying buffers. This class only "knows" that the last bytes
|
||||
// have been written when the app calls Close().
|
||||
//
|
||||
// Therefore, this class buffers writes: After completion every Write(),
|
||||
// a 16-byte "pending" block (_PendingWriteBlock) must hold between 1
|
||||
// and 16 bytes, which will be used in TransformFinalBlock if the app
|
||||
// calls Close() immediately thereafter. Also, every write must
|
||||
// transform any pending bytes, before transforming the data passed in
|
||||
// to the current call.
|
||||
//
|
||||
// In operation, after the first call to Write() and before the call to
|
||||
// Close(), one full or partial block of bytes is always available,
|
||||
// pending. At time of Close(), this class calls
|
||||
// WriteTransformFinalBlock() to flush the pending bytes.
|
||||
//
|
||||
// This approach works whether the caller writes in odd-sized batches,
|
||||
// for example 5000 bytes, or in batches that are neat multiples of the
|
||||
// blocksize (16).
|
||||
//
|
||||
// Logicaly, what we do is this:
|
||||
//
|
||||
// 1. if there are fewer than 16 bytes (pending + current), then
|
||||
// just copy them into th pending buffer and return.
|
||||
//
|
||||
// 2. there are more than 16 bytes to write. So, take the leading slice
|
||||
// of bytes from the current buffer, enough to fill the pending
|
||||
// buffer. Transform the pending block, and write it out.
|
||||
//
|
||||
// 3. Take the trailing slice of bytes (a full block or a partial block),
|
||||
// and copy it to the pending block for next time.
|
||||
//
|
||||
// 4. transform and write all the other blocks, the middle slice.
|
||||
//
|
||||
|
||||
// There are 16 or fewer bytes, so just buffer the bytes.
|
||||
if (count + _pendingCount <= BLOCK_SIZE_IN_BYTES)
|
||||
{
|
||||
Buffer.BlockCopy(buffer,
|
||||
offset,
|
||||
_PendingWriteBlock,
|
||||
_pendingCount,
|
||||
count);
|
||||
_pendingCount += count;
|
||||
|
||||
// At this point, _PendingWriteBlock contains up to
|
||||
// BLOCK_SIZE_IN_BYTES bytes, and _pendingCount ranges from 0 to
|
||||
// BLOCK_SIZE_IN_BYTES. We don't want to xform+write them yet,
|
||||
// because this may have been the last block. The last block gets
|
||||
// written at Close().
|
||||
return;
|
||||
}
|
||||
|
||||
// We know there are at least 17 bytes, counting those in the current
|
||||
// buffer, along with the (possibly empty) pending block.
|
||||
|
||||
int bytesRemaining = count;
|
||||
int curOffset = offset;
|
||||
|
||||
// workitem 12815
|
||||
//
|
||||
// xform chunkwise ... Cannot transform in place using the original
|
||||
// buffer because that is user-maintained.
|
||||
|
||||
if (_pendingCount != 0)
|
||||
{
|
||||
// We have more than one block of data to write, therefore it is safe
|
||||
// to xform+write.
|
||||
int fillCount = BLOCK_SIZE_IN_BYTES - _pendingCount;
|
||||
|
||||
// fillCount is possibly zero here. That happens when the pending
|
||||
// buffer held 16 bytes (one complete block) before this call to
|
||||
// Write.
|
||||
if (fillCount > 0)
|
||||
{
|
||||
Buffer.BlockCopy(buffer,
|
||||
offset,
|
||||
_PendingWriteBlock,
|
||||
_pendingCount,
|
||||
fillCount);
|
||||
|
||||
// adjust counts:
|
||||
bytesRemaining -= fillCount;
|
||||
curOffset += fillCount;
|
||||
}
|
||||
|
||||
// xform and write:
|
||||
WriteTransformOneBlock(_PendingWriteBlock, 0);
|
||||
_s.Write(_PendingWriteBlock, 0, BLOCK_SIZE_IN_BYTES);
|
||||
_totalBytesXferred += BLOCK_SIZE_IN_BYTES;
|
||||
_pendingCount = 0;
|
||||
}
|
||||
|
||||
// At this point _PendingWriteBlock is empty, and bytesRemaining is
|
||||
// always greater than 0.
|
||||
|
||||
// Now, xform N blocks, where N = floor((bytesRemaining-1)/16). If
|
||||
// writing 32 bytes, then xform 1 block, and stage the remaining 16. If
|
||||
// writing 10037 bytes, xform 627 blocks of 16 bytes, then stage the
|
||||
// remaining 5 bytes.
|
||||
|
||||
int blocksToXform = (bytesRemaining-1)/BLOCK_SIZE_IN_BYTES;
|
||||
_pendingCount = bytesRemaining - (blocksToXform * BLOCK_SIZE_IN_BYTES);
|
||||
|
||||
// _pendingCount is ALWAYS between 1 and 16.
|
||||
// Put the last _pendingCount bytes into the pending block.
|
||||
Buffer.BlockCopy(buffer,
|
||||
curOffset + bytesRemaining - _pendingCount,
|
||||
_PendingWriteBlock,
|
||||
0,
|
||||
_pendingCount);
|
||||
bytesRemaining -= _pendingCount;
|
||||
_totalBytesXferred += bytesRemaining; // will be true after the loop
|
||||
|
||||
// now, transform all the full blocks preceding that.
|
||||
// bytesRemaining is always a multiple of 16 .
|
||||
if (blocksToXform > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
int c = _iobuf.Length;
|
||||
if (c > bytesRemaining) c = bytesRemaining;
|
||||
Buffer.BlockCopy(buffer,
|
||||
curOffset,
|
||||
_iobuf,
|
||||
0,
|
||||
c);
|
||||
|
||||
WriteTransformBlocks(_iobuf, 0, c);
|
||||
_s.Write(_iobuf, 0, c);
|
||||
bytesRemaining -= c;
|
||||
curOffset += c;
|
||||
} while(bytesRemaining > 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Close the stream.
|
||||
/// </summary>
|
||||
public override void Close()
|
||||
{
|
||||
TraceOutput("Close {0:X8}", this.GetHashCode());
|
||||
|
||||
// In the degenerate case, no bytes have been written to the
|
||||
// stream at all. Need to check here, and NOT emit the
|
||||
// final block if Write has not been called.
|
||||
if (_pendingCount > 0)
|
||||
{
|
||||
WriteTransformFinalBlock();
|
||||
_s.Write(_PendingWriteBlock, 0, _pendingCount);
|
||||
_totalBytesXferred += _pendingCount;
|
||||
_pendingCount = 0;
|
||||
}
|
||||
_s.Close();
|
||||
|
||||
#if WANT_TRACE
|
||||
untransformed.Close();
|
||||
transformed.Close();
|
||||
Console.WriteLine("\nuntransformed bytestream is in {0}", traceFileUntransformed);
|
||||
Console.WriteLine("\ntransformed bytestream is in {0}", traceFileTransformed);
|
||||
#endif
|
||||
TraceOutput("-------------------------------------------------------");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_mode != CryptoMode.Decrypt) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the CryptoMode is Encrypt.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return (_mode == CryptoMode.Encrypt); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the content in the stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_s.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting this property throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting or Setting this property throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method throws a NotImplementedException.
|
||||
/// </summary>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[System.Diagnostics.ConditionalAttribute("Trace")]
|
||||
private void TraceOutput(string format, params object[] varParams)
|
||||
{
|
||||
lock(_outputLock)
|
||||
{
|
||||
int tid = System.Threading.Thread.CurrentThread.GetHashCode();
|
||||
Console.ForegroundColor = (ConsoleColor) (tid % 8 + 8);
|
||||
Console.Write("{0:000} WZACS ", tid);
|
||||
Console.WriteLine(format, varParams);
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
private object _outputLock = new Object();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// ZipConstants.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-August-27 23:22:32>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines a few constants that are used in the project.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
static class ZipConstants
|
||||
{
|
||||
public const UInt32 PackedToRemovableMedia = 0x30304b50;
|
||||
public const UInt32 Zip64EndOfCentralDirectoryRecordSignature = 0x06064b50;
|
||||
public const UInt32 Zip64EndOfCentralDirectoryLocatorSignature = 0x07064b50;
|
||||
public const UInt32 EndOfCentralDirectorySignature = 0x06054b50;
|
||||
public const int ZipEntrySignature = 0x04034b50;
|
||||
public const int ZipEntryDataDescriptorSignature = 0x08074b50;
|
||||
public const int SplitArchiveSignature = 0x08074b50;
|
||||
public const int ZipDirEntrySignature = 0x02014b50;
|
||||
|
||||
|
||||
// These are dictated by the Zip Spec.See APPNOTE.txt
|
||||
public const int AesKeySize = 192; // 128, 192, 256
|
||||
public const int AesBlockSize = 128; // ???
|
||||
|
||||
public const UInt16 AesAlgId128 = 0x660E;
|
||||
public const UInt16 AesAlgId192 = 0x660F;
|
||||
public const UInt16 AesAlgId256 = 0x6610;
|
||||
|
||||
}
|
||||
}
|
||||
455
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipCrypto.cs
Normal file
455
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipCrypto.cs
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
// ZipCrypto.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2008, 2009, 2011 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-28 06:30:59>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module provides the implementation for "traditional" Zip encryption.
|
||||
//
|
||||
// Created Tue Apr 15 17:39:56 2008
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// This class implements the "traditional" or "classic" PKZip encryption,
|
||||
/// which today is considered to be weak. On the other hand it is
|
||||
/// ubiquitous. This class is intended for use only by the DotNetZip
|
||||
/// library.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Most uses of the DotNetZip library will not involve direct calls into
|
||||
/// the ZipCrypto class. Instead, the ZipCrypto class is instantiated and
|
||||
/// used by the ZipEntry() class when encryption or decryption on an entry
|
||||
/// is employed. If for some reason you really wanted to use a weak
|
||||
/// encryption algorithm in some other application, you might use this
|
||||
/// library. But you would be much better off using one of the built-in
|
||||
/// strong encryption libraries in the .NET Framework, like the AES
|
||||
/// algorithm or SHA.
|
||||
/// </remarks>
|
||||
internal class ZipCrypto
|
||||
{
|
||||
/// <summary>
|
||||
/// The default constructor for ZipCrypto.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This class is intended for internal use by the library only. It's
|
||||
/// probably not useful to you. Seriously. Stop reading this
|
||||
/// documentation. It's a waste of your time. Go do something else.
|
||||
/// Check the football scores. Go get an ice cream with a friend.
|
||||
/// Seriously.
|
||||
/// </remarks>
|
||||
///
|
||||
private ZipCrypto() { }
|
||||
|
||||
public static ZipCrypto ForWrite(string password)
|
||||
{
|
||||
ZipCrypto z = new ZipCrypto();
|
||||
if (password == null)
|
||||
throw new BadPasswordException("This entry requires a password.");
|
||||
z.InitCipher(password);
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
public static ZipCrypto ForRead(string password, ZipEntry e)
|
||||
{
|
||||
System.IO.Stream s = e._archiveStream;
|
||||
e._WeakEncryptionHeader = new byte[12];
|
||||
byte[] eh = e._WeakEncryptionHeader;
|
||||
ZipCrypto z = new ZipCrypto();
|
||||
|
||||
if (password == null)
|
||||
throw new BadPasswordException("This entry requires a password.");
|
||||
|
||||
z.InitCipher(password);
|
||||
|
||||
ZipEntry.ReadWeakEncryptionHeader(s, eh);
|
||||
|
||||
// Decrypt the header. This has a side effect of "further initializing the
|
||||
// encryption keys" in the traditional zip encryption.
|
||||
byte[] DecryptedHeader = z.DecryptMessage(eh, eh.Length);
|
||||
|
||||
// CRC check
|
||||
// According to the pkzip spec, the final byte in the decrypted header
|
||||
// is the highest-order byte in the CRC. We check it here.
|
||||
if (DecryptedHeader[11] != (byte)((e._Crc32 >> 24) & 0xff))
|
||||
{
|
||||
// In the case that bit 3 of the general purpose bit flag is set to
|
||||
// indicate the presence of an 'Extended File Header' or a 'data
|
||||
// descriptor' (signature 0x08074b50), the last byte of the decrypted
|
||||
// header is sometimes compared with the high-order byte of the
|
||||
// lastmodified time, rather than the high-order byte of the CRC, to
|
||||
// verify the password.
|
||||
//
|
||||
// This is not documented in the PKWare Appnote.txt. It was
|
||||
// discovered this by analysis of the Crypt.c source file in the
|
||||
// InfoZip library http://www.info-zip.org/pub/infozip/
|
||||
//
|
||||
// The reason for this is that the CRC for a file cannot be known
|
||||
// until the entire contents of the file have been streamed. This
|
||||
// means a tool would have to read the file content TWICE in its
|
||||
// entirety in order to perform PKZIP encryption - once to compute
|
||||
// the CRC, and again to actually encrypt.
|
||||
//
|
||||
// This is so important for performance that using the timeblob as
|
||||
// the verification should be the standard practice for DotNetZip
|
||||
// when using PKZIP encryption. This implies that bit 3 must be
|
||||
// set. The downside is that some tools still cannot cope with ZIP
|
||||
// files that use bit 3. Therefore, DotNetZip DOES NOT force bit 3
|
||||
// when PKZIP encryption is in use, and instead, reads the stream
|
||||
// twice.
|
||||
//
|
||||
|
||||
if ((e._BitField & 0x0008) != 0x0008)
|
||||
{
|
||||
throw new BadPasswordException("The password did not match.");
|
||||
}
|
||||
else if (DecryptedHeader[11] != (byte)((e._TimeBlob >> 8) & 0xff))
|
||||
{
|
||||
throw new BadPasswordException("The password did not match.");
|
||||
}
|
||||
|
||||
// We have a good password.
|
||||
}
|
||||
else
|
||||
{
|
||||
// A-OK
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// From AppNote.txt:
|
||||
/// unsigned char decrypt_byte()
|
||||
/// local unsigned short temp
|
||||
/// temp :=- Key(2) | 2
|
||||
/// decrypt_byte := (temp * (temp ^ 1)) bitshift-right 8
|
||||
/// end decrypt_byte
|
||||
/// </summary>
|
||||
private byte MagicByte
|
||||
{
|
||||
get
|
||||
{
|
||||
UInt16 t = (UInt16)((UInt16)(_Keys[2] & 0xFFFF) | 2);
|
||||
return (byte)((t * (t ^ 1)) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypting:
|
||||
// From AppNote.txt:
|
||||
// loop for i from 0 to 11
|
||||
// C := buffer(i) ^ decrypt_byte()
|
||||
// update_keys(C)
|
||||
// buffer(i) := C
|
||||
// end loop
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Call this method on a cipher text to render the plaintext. You must
|
||||
/// first initialize the cipher with a call to InitCipher.
|
||||
/// </summary>
|
||||
///
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var cipher = new ZipCrypto();
|
||||
/// cipher.InitCipher(Password);
|
||||
/// // Decrypt the header. This has a side effect of "further initializing the
|
||||
/// // encryption keys" in the traditional zip encryption.
|
||||
/// byte[] DecryptedMessage = cipher.DecryptMessage(EncryptedMessage);
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="cipherText">The encrypted buffer.</param>
|
||||
/// <param name="length">
|
||||
/// The number of bytes to encrypt.
|
||||
/// Should be less than or equal to CipherText.Length.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The plaintext.</returns>
|
||||
public byte[] DecryptMessage(byte[] cipherText, int length)
|
||||
{
|
||||
if (cipherText == null)
|
||||
throw new ArgumentNullException("cipherText");
|
||||
|
||||
if (length > cipherText.Length)
|
||||
throw new ArgumentOutOfRangeException("length",
|
||||
"Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
|
||||
|
||||
byte[] plainText = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte C = (byte)(cipherText[i] ^ MagicByte);
|
||||
UpdateKeys(C);
|
||||
plainText[i] = C;
|
||||
}
|
||||
return plainText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the converse of DecryptMessage. It encrypts the plaintext
|
||||
/// and produces a ciphertext.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="plainText">The plain text buffer.</param>
|
||||
///
|
||||
/// <param name="length">
|
||||
/// The number of bytes to encrypt.
|
||||
/// Should be less than or equal to plainText.Length.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>The ciphertext.</returns>
|
||||
public byte[] EncryptMessage(byte[] plainText, int length)
|
||||
{
|
||||
if (plainText == null)
|
||||
throw new ArgumentNullException("plaintext");
|
||||
|
||||
if (length > plainText.Length)
|
||||
throw new ArgumentOutOfRangeException("length",
|
||||
"Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
|
||||
|
||||
byte[] cipherText = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte C = plainText[i];
|
||||
cipherText[i] = (byte)(plainText[i] ^ MagicByte);
|
||||
UpdateKeys(C);
|
||||
}
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This initializes the cipher with the given password.
|
||||
/// See AppNote.txt for details.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="passphrase">
|
||||
/// The passphrase for encrypting or decrypting with this cipher.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <code>
|
||||
/// Step 1 - Initializing the encryption keys
|
||||
/// -----------------------------------------
|
||||
/// Start with these keys:
|
||||
/// Key(0) := 305419896 (0x12345678)
|
||||
/// Key(1) := 591751049 (0x23456789)
|
||||
/// Key(2) := 878082192 (0x34567890)
|
||||
///
|
||||
/// Then, initialize the keys with a password:
|
||||
///
|
||||
/// loop for i from 0 to length(password)-1
|
||||
/// update_keys(password(i))
|
||||
/// end loop
|
||||
///
|
||||
/// Where update_keys() is defined as:
|
||||
///
|
||||
/// update_keys(char):
|
||||
/// Key(0) := crc32(key(0),char)
|
||||
/// Key(1) := Key(1) + (Key(0) bitwiseAND 000000ffH)
|
||||
/// Key(1) := Key(1) * 134775813 + 1
|
||||
/// Key(2) := crc32(key(2),key(1) rightshift 24)
|
||||
/// end update_keys
|
||||
///
|
||||
/// Where crc32(old_crc,char) is a routine that given a CRC value and a
|
||||
/// character, returns an updated CRC value after applying the CRC-32
|
||||
/// algorithm described elsewhere in this document.
|
||||
///
|
||||
/// </code>
|
||||
///
|
||||
/// <para>
|
||||
/// After the keys are initialized, then you can use the cipher to
|
||||
/// encrypt the plaintext.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Essentially we encrypt the password with the keys, then discard the
|
||||
/// ciphertext for the password. This initializes the keys for later use.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
public void InitCipher(string passphrase)
|
||||
{
|
||||
byte[] p = SharedUtilities.StringToByteArray(passphrase);
|
||||
for (int i = 0; i < passphrase.Length; i++)
|
||||
UpdateKeys(p[i]);
|
||||
}
|
||||
|
||||
|
||||
private void UpdateKeys(byte byteValue)
|
||||
{
|
||||
_Keys[0] = (UInt32)crc32.ComputeCrc32((int)_Keys[0], byteValue);
|
||||
_Keys[1] = _Keys[1] + (byte)_Keys[0];
|
||||
_Keys[1] = _Keys[1] * 0x08088405 + 1;
|
||||
_Keys[2] = (UInt32)crc32.ComputeCrc32((int)_Keys[2], (byte)(_Keys[1] >> 24));
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// The byte array representing the seed keys used.
|
||||
///// Get this after calling InitCipher. The 12 bytes represents
|
||||
///// what the zip spec calls the "EncryptionHeader".
|
||||
///// </summary>
|
||||
//public byte[] KeyHeader
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// byte[] result = new byte[12];
|
||||
// result[0] = (byte)(_Keys[0] & 0xff);
|
||||
// result[1] = (byte)((_Keys[0] >> 8) & 0xff);
|
||||
// result[2] = (byte)((_Keys[0] >> 16) & 0xff);
|
||||
// result[3] = (byte)((_Keys[0] >> 24) & 0xff);
|
||||
// result[4] = (byte)(_Keys[1] & 0xff);
|
||||
// result[5] = (byte)((_Keys[1] >> 8) & 0xff);
|
||||
// result[6] = (byte)((_Keys[1] >> 16) & 0xff);
|
||||
// result[7] = (byte)((_Keys[1] >> 24) & 0xff);
|
||||
// result[8] = (byte)(_Keys[2] & 0xff);
|
||||
// result[9] = (byte)((_Keys[2] >> 8) & 0xff);
|
||||
// result[10] = (byte)((_Keys[2] >> 16) & 0xff);
|
||||
// result[11] = (byte)((_Keys[2] >> 24) & 0xff);
|
||||
// return result;
|
||||
// }
|
||||
//}
|
||||
|
||||
// private fields for the crypto stuff:
|
||||
private UInt32[] _Keys = { 0x12345678, 0x23456789, 0x34567890 };
|
||||
private Ionic.Crc.CRC32 crc32 = new Ionic.Crc.CRC32();
|
||||
|
||||
}
|
||||
|
||||
internal enum CryptoMode
|
||||
{
|
||||
Encrypt,
|
||||
Decrypt
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Stream for reading and concurrently decrypting data from a zip file,
|
||||
/// or for writing and concurrently encrypting data to a zip file.
|
||||
/// </summary>
|
||||
internal class ZipCipherStream : System.IO.Stream
|
||||
{
|
||||
private ZipCrypto _cipher;
|
||||
private System.IO.Stream _s;
|
||||
private CryptoMode _mode;
|
||||
|
||||
/// <summary> The constructor. </summary>
|
||||
/// <param name="s">The underlying stream</param>
|
||||
/// <param name="mode">To either encrypt or decrypt.</param>
|
||||
/// <param name="cipher">The pre-initialized ZipCrypto object.</param>
|
||||
public ZipCipherStream(System.IO.Stream s, ZipCrypto cipher, CryptoMode mode)
|
||||
: base()
|
||||
{
|
||||
_cipher = cipher;
|
||||
_s = s;
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_mode == CryptoMode.Encrypt)
|
||||
throw new NotSupportedException("This stream does not encrypt via Read()");
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
byte[] db = new byte[count];
|
||||
int n = _s.Read(db, 0, count);
|
||||
byte[] decrypted = _cipher.DecryptMessage(db, n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
buffer[offset + i] = decrypted[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_mode == CryptoMode.Decrypt)
|
||||
throw new NotSupportedException("This stream does not Decrypt via Write()");
|
||||
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException("buffer");
|
||||
|
||||
// workitem 7696
|
||||
if (count == 0) return;
|
||||
|
||||
byte[] plaintext = null;
|
||||
if (offset != 0)
|
||||
{
|
||||
plaintext = new byte[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
plaintext[i] = buffer[offset + i];
|
||||
}
|
||||
}
|
||||
else plaintext = buffer;
|
||||
|
||||
byte[] encrypted = _cipher.EncryptMessage(plaintext, count);
|
||||
_s.Write(encrypted, 0, encrypted.Length);
|
||||
}
|
||||
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return (_mode == CryptoMode.Decrypt); }
|
||||
}
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return (_mode == CryptoMode.Encrypt); }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
//throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
381
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipDirEntry.cs
Normal file
381
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipDirEntry.cs
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
// ZipDirEntry.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006-2011 Dino Chiesa .
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-11 12:03:03>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines members of the ZipEntry class for reading the
|
||||
// Zip file central directory.
|
||||
//
|
||||
// Created: Tue, 27 Mar 2007 15:30
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
partial class ZipEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// True if the referenced entry is a directory.
|
||||
/// </summary>
|
||||
internal bool AttributesIndicateDirectory
|
||||
{
|
||||
get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); }
|
||||
}
|
||||
|
||||
|
||||
internal void ResetDirEntry()
|
||||
{
|
||||
// __FileDataPosition is the position of the file data for an entry.
|
||||
// It is _RelativeOffsetOfLocalHeader + size of local header.
|
||||
|
||||
// We cannot know the __FileDataPosition until we read the local
|
||||
// header.
|
||||
|
||||
// The local header is not necessarily the same length as the record
|
||||
// in the central directory.
|
||||
|
||||
// Set to -1, to indicate we need to read this later.
|
||||
this.__FileDataPosition = -1;
|
||||
|
||||
// set _LengthOfHeader to 0, to indicate we need to read later.
|
||||
this._LengthOfHeader = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a human-readable string with information about the ZipEntry.
|
||||
/// </summary>
|
||||
public string Info
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new System.Text.StringBuilder();
|
||||
builder
|
||||
.Append(string.Format(" ZipEntry: {0}\n", this.FileName))
|
||||
.Append(string.Format(" Version Made By: {0}\n", this._VersionMadeBy))
|
||||
.Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded));
|
||||
|
||||
if (this._IsDirectory)
|
||||
builder.Append(" Entry type: directory\n");
|
||||
else
|
||||
{
|
||||
builder.Append(string.Format(" File type: {0}\n", this._IsText? "text":"binary"))
|
||||
.Append(string.Format(" Compression: {0}\n", this.CompressionMethod))
|
||||
.Append(string.Format(" Compressed: 0x{0:X}\n", this.CompressedSize))
|
||||
.Append(string.Format(" Uncompressed: 0x{0:X}\n", this.UncompressedSize))
|
||||
.Append(string.Format(" CRC32: 0x{0:X8}\n", this._Crc32));
|
||||
}
|
||||
builder.Append(string.Format(" Disk Number: {0}\n", this._diskNumber));
|
||||
if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF)
|
||||
builder
|
||||
.Append(string.Format(" Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader));
|
||||
else
|
||||
builder
|
||||
.Append(string.Format(" Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader));
|
||||
|
||||
builder
|
||||
.Append(string.Format(" Bit Field: 0x{0:X4}\n", this._BitField))
|
||||
.Append(string.Format(" Encrypted?: {0}\n", this._sourceIsEncrypted))
|
||||
.Append(string.Format(" Timeblob: 0x{0:X8}\n", this._TimeBlob))
|
||||
.Append(string.Format(" Time: {0}\n", Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob)));
|
||||
|
||||
builder.Append(string.Format(" Is Zip64?: {0}\n", this._InputUsesZip64));
|
||||
if (!string.IsNullOrEmpty(this._Comment))
|
||||
{
|
||||
builder.Append(string.Format(" Comment: {0}\n", this._Comment));
|
||||
}
|
||||
builder.Append("\n");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// workitem 10330
|
||||
private class CopyHelper
|
||||
{
|
||||
private static System.Text.RegularExpressions.Regex re =
|
||||
new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$");
|
||||
|
||||
private static int callCount = 0;
|
||||
|
||||
internal static string AppendCopyToFileName(string f)
|
||||
{
|
||||
callCount++;
|
||||
if (callCount > 25)
|
||||
throw new OverflowException("overflow while creating filename");
|
||||
|
||||
int n = 1;
|
||||
int r = f.LastIndexOf(".");
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
// there is no extension
|
||||
System.Text.RegularExpressions.Match m = re.Match(f);
|
||||
if (m.Success)
|
||||
{
|
||||
n = Int32.Parse(m.Groups[1].Value) + 1;
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f.Substring(0, m.Index) + copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f + copy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//System.Console.WriteLine("HasExtension");
|
||||
System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r));
|
||||
if (m.Success)
|
||||
{
|
||||
n = Int32.Parse(m.Groups[1].Value) + 1;
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f.Substring(0, m.Index) + copy + f.Substring(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
string copy = String.Format(" (copy {0})", n);
|
||||
f = f.Substring(0, r) + copy + f.Substring(r);
|
||||
}
|
||||
|
||||
//System.Console.WriteLine("returning f({0})", f);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads one entry from the zip directory structure in the zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="zf">
|
||||
/// The zipfile for which a directory entry will be read. From this param, the
|
||||
/// method gets the ReadStream and the expected text encoding
|
||||
/// (ProvisionalAlternateEncoding) which is used if the entry is not marked
|
||||
/// UTF-8.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="previouslySeen">
|
||||
/// a list of previously seen entry names; used to prevent duplicates.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>the entry read from the archive.</returns>
|
||||
internal static ZipEntry ReadDirEntry(ZipFile zf,
|
||||
Dictionary<String,Object> previouslySeen)
|
||||
{
|
||||
System.IO.Stream s = zf.ReadStream;
|
||||
System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always)
|
||||
? zf.AlternateEncoding
|
||||
: ZipFile.DefaultEncoding;
|
||||
|
||||
int signature = Ionic.Zip.SharedUtilities.ReadSignature(s);
|
||||
// return null if this is not a local file header signature
|
||||
if (IsNotValidZipDirEntrySig(signature))
|
||||
{
|
||||
s.Seek(-4, System.IO.SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
|
||||
// Getting "not a ZipDirEntry signature" here is not always wrong or an
|
||||
// error. This can happen when walking through a zipfile. After the
|
||||
// last ZipDirEntry, we expect to read an
|
||||
// EndOfCentralDirectorySignature. When we get this is how we know
|
||||
// we've reached the end of the central directory.
|
||||
if (signature != ZipConstants.EndOfCentralDirectorySignature &&
|
||||
signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature &&
|
||||
signature != ZipConstants.ZipEntrySignature // workitem 8299
|
||||
)
|
||||
{
|
||||
throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int bytesRead = 42 + 4;
|
||||
byte[] block = new byte[42];
|
||||
int n = s.Read(block, 0, block.Length);
|
||||
if (n != block.Length) return null;
|
||||
|
||||
int i = 0;
|
||||
ZipEntry zde = new ZipEntry();
|
||||
zde.AlternateEncoding = expectedEncoding;
|
||||
zde._Source = ZipEntrySource.ZipFile;
|
||||
zde._container = new ZipContainer(zf);
|
||||
|
||||
unchecked
|
||||
{
|
||||
zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256);
|
||||
zde._VersionNeeded = (short)(block[i++] + block[i++] * 256);
|
||||
zde._BitField = (short)(block[i++] + block[i++] * 256);
|
||||
zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
|
||||
zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob);
|
||||
zde._timestamp |= ZipEntryTimestamp.DOS;
|
||||
|
||||
zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
}
|
||||
|
||||
// preserve
|
||||
zde._CompressionMethod_FromZipFile = zde._CompressionMethod;
|
||||
|
||||
zde._filenameLength = (short)(block[i++] + block[i++] * 256);
|
||||
zde._extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
||||
zde._commentLength = (short)(block[i++] + block[i++] * 256);
|
||||
zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256);
|
||||
|
||||
zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256);
|
||||
zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
|
||||
zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
|
||||
// workitem 7801
|
||||
zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01);
|
||||
|
||||
block = new byte[zde._filenameLength];
|
||||
n = s.Read(block, 0, block.Length);
|
||||
bytesRead += n;
|
||||
if ((zde._BitField & 0x0800) == 0x0800)
|
||||
{
|
||||
// UTF-8 is in use
|
||||
zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
|
||||
}
|
||||
|
||||
// workitem 10330
|
||||
// insure unique entry names
|
||||
while (previouslySeen.ContainsKey(zde._FileNameInArchive))
|
||||
{
|
||||
zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive);
|
||||
zde._metadataChanged = true;
|
||||
}
|
||||
|
||||
if (zde.AttributesIndicateDirectory)
|
||||
zde.MarkAsDirectory(); // may append a slash to filename if nec.
|
||||
// workitem 6898
|
||||
else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory();
|
||||
|
||||
zde._CompressedFileDataSize = zde._CompressedSize;
|
||||
if ((zde._BitField & 0x01) == 0x01)
|
||||
{
|
||||
// this may change after processing the Extra field
|
||||
zde._Encryption_FromZipFile = zde._Encryption =
|
||||
EncryptionAlgorithm.PkzipWeak;
|
||||
zde._sourceIsEncrypted = true;
|
||||
}
|
||||
|
||||
if (zde._extraFieldLength > 0)
|
||||
{
|
||||
zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF ||
|
||||
zde._UncompressedSize == 0xFFFFFFFF ||
|
||||
zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF);
|
||||
|
||||
// Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64);
|
||||
|
||||
bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength);
|
||||
zde._CompressedFileDataSize = zde._CompressedSize;
|
||||
}
|
||||
|
||||
// we've processed the extra field, so we know the encryption method is set now.
|
||||
if (zde._Encryption == EncryptionAlgorithm.PkzipWeak)
|
||||
{
|
||||
// the "encryption header" of 12 bytes precedes the file data
|
||||
zde._CompressedFileDataSize -= 12;
|
||||
}
|
||||
#if AESCRYPTO
|
||||
else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 ||
|
||||
zde.Encryption == EncryptionAlgorithm.WinZipAes256)
|
||||
{
|
||||
zde._CompressedFileDataSize = zde.CompressedSize -
|
||||
(ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10);
|
||||
zde._LengthOfTrailer = 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
// tally the trailing descriptor
|
||||
if ((zde._BitField & 0x0008) == 0x0008)
|
||||
{
|
||||
// sig, CRC, Comp and Uncomp sizes
|
||||
if (zde._InputUsesZip64)
|
||||
zde._LengthOfTrailer += 24;
|
||||
else
|
||||
zde._LengthOfTrailer += 16;
|
||||
}
|
||||
|
||||
// workitem 12744
|
||||
zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800)
|
||||
? System.Text.Encoding.UTF8
|
||||
:expectedEncoding;
|
||||
|
||||
zde.AlternateEncodingUsage = ZipOption.Always;
|
||||
|
||||
if (zde._commentLength > 0)
|
||||
{
|
||||
block = new byte[zde._commentLength];
|
||||
n = s.Read(block, 0, block.Length);
|
||||
bytesRead += n;
|
||||
if ((zde._BitField & 0x0800) == 0x0800)
|
||||
{
|
||||
// UTF-8 is in use
|
||||
zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
|
||||
}
|
||||
}
|
||||
//zde._LengthOfDirEntry = bytesRead;
|
||||
return zde;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the passed-in value is a valid signature for a ZipDirEntry.
|
||||
/// </summary>
|
||||
/// <param name="signature">the candidate 4-byte signature value.</param>
|
||||
/// <returns>true, if the signature is valid according to the PKWare spec.</returns>
|
||||
internal static bool IsNotValidZipDirEntrySig(int signature)
|
||||
{
|
||||
return (signature != ZipConstants.ZipDirEntrySignature);
|
||||
}
|
||||
|
||||
|
||||
private Int16 _VersionMadeBy;
|
||||
private Int16 _InternalFileAttrs;
|
||||
private Int32 _ExternalFileAttrs;
|
||||
|
||||
//private Int32 _LengthOfDirEntry;
|
||||
private Int16 _filenameLength;
|
||||
private Int16 _extraFieldLength;
|
||||
private Int16 _commentLength;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,798 @@
|
|||
// ZipEntry.Read.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-09 21:31:28>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for Reading the ZipEntry from a
|
||||
// zip file.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
public partial class ZipEntry
|
||||
{
|
||||
private int _readExtraDepth;
|
||||
private void ReadExtraField()
|
||||
{
|
||||
_readExtraDepth++;
|
||||
// workitem 8098: ok (restore)
|
||||
long posn = this.ArchiveStream.Position;
|
||||
this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
||||
|
||||
byte[] block = new byte[30];
|
||||
this.ArchiveStream.Read(block, 0, block.Length);
|
||||
int i = 26;
|
||||
Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
|
||||
Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
||||
|
||||
// workitem 8098: ok (relative)
|
||||
this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
||||
|
||||
ProcessExtraField(this.ArchiveStream, extraFieldLength);
|
||||
|
||||
// workitem 8098: ok (restore)
|
||||
this.ArchiveStream.Seek(posn, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
||||
_readExtraDepth--;
|
||||
}
|
||||
|
||||
|
||||
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
|
||||
{
|
||||
int bytesRead = 0;
|
||||
|
||||
// change for workitem 8098
|
||||
ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position;
|
||||
|
||||
int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream);
|
||||
bytesRead += 4;
|
||||
|
||||
// Return false if this is not a local file header signature.
|
||||
if (ZipEntry.IsNotValidSig(signature))
|
||||
{
|
||||
// Getting "not a ZipEntry signature" is not always wrong or an error.
|
||||
// This will happen after the last entry in a zipfile. In that case, we
|
||||
// expect to read :
|
||||
// a ZipDirEntry signature (if a non-empty zip file) or
|
||||
// a ZipConstants.EndOfCentralDirectorySignature.
|
||||
//
|
||||
// Anything else is a surprise.
|
||||
|
||||
ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
||||
if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
|
||||
{
|
||||
throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] block = new byte[26];
|
||||
int n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
if (n != block.Length) return false;
|
||||
bytesRead += n;
|
||||
|
||||
int i = 0;
|
||||
ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256);
|
||||
ze._BitField = (Int16)(block[i++] + block[i++] * 256);
|
||||
ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
|
||||
ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
||||
// transform the time data into something usable (a DateTime)
|
||||
ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);
|
||||
ze._timestamp |= ZipEntryTimestamp.DOS;
|
||||
|
||||
if ((ze._BitField & 0x01) == 0x01)
|
||||
{
|
||||
ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field
|
||||
ze._sourceIsEncrypted = true;
|
||||
}
|
||||
|
||||
// NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and
|
||||
// CRC values are not true values; the true values will follow the entry data.
|
||||
// But, regardless of the status of bit 3 in the bitfield, the slots for
|
||||
// the three amigos may contain marker values for ZIP64. So we must read them.
|
||||
{
|
||||
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
|
||||
if ((uint)ze._CompressedSize == 0xFFFFFFFF ||
|
||||
(uint)ze._UncompressedSize == 0xFFFFFFFF)
|
||||
|
||||
ze._InputUsesZip64 = true;
|
||||
}
|
||||
|
||||
Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
|
||||
Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
||||
|
||||
block = new byte[filenameLength];
|
||||
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
bytesRead += n;
|
||||
|
||||
// if the UTF8 bit is set for this entry, override the
|
||||
// encoding the application requested.
|
||||
|
||||
if ((ze._BitField & 0x0800) == 0x0800)
|
||||
{
|
||||
// workitem 12744
|
||||
ze.AlternateEncoding = System.Text.Encoding.UTF8;
|
||||
ze.AlternateEncodingUsage = ZipOption.Always;
|
||||
}
|
||||
|
||||
// need to use this form of GetString() for .NET CF
|
||||
ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length);
|
||||
|
||||
// workitem 6898
|
||||
if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory();
|
||||
|
||||
bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength);
|
||||
|
||||
ze._LengthOfTrailer = 0;
|
||||
|
||||
// workitem 6607 - don't read for directories
|
||||
// actually get the compressed size and CRC if necessary
|
||||
if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008)
|
||||
{
|
||||
// This descriptor exists only if bit 3 of the general
|
||||
// purpose bit flag is set (see below). It is byte aligned
|
||||
// and immediately follows the last byte of compressed data,
|
||||
// as well as any encryption trailer, as with AES.
|
||||
// This descriptor is used only when it was not possible to
|
||||
// seek in the output .ZIP file, e.g., when the output .ZIP file
|
||||
// was standard output or a non-seekable device. For ZIP64(tm) format
|
||||
// archives, the compressed and uncompressed sizes are 8 bytes each.
|
||||
|
||||
// workitem 8098: ok (restore)
|
||||
long posn = ze.ArchiveStream.Position;
|
||||
|
||||
// Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and
|
||||
// a consistent data record after that. To be consistent, the data record must
|
||||
// indicate the length of the entry data.
|
||||
bool wantMore = true;
|
||||
long SizeOfDataRead = 0;
|
||||
int tries = 0;
|
||||
while (wantMore)
|
||||
{
|
||||
tries++;
|
||||
// We call the FindSignature shared routine to find the specified signature
|
||||
// in the already-opened zip archive, starting from the current cursor
|
||||
// position in that filestream. If we cannot find the signature, then the
|
||||
// routine returns -1, and the ReadHeader() method returns false,
|
||||
// indicating we cannot read a legal entry header. If we have found it,
|
||||
// then the FindSignature() method returns the number of bytes in the
|
||||
// stream we had to seek forward, to find the sig. We need this to
|
||||
// determine if the zip entry is valid, later.
|
||||
|
||||
if (ze._container.ZipFile != null)
|
||||
ze._container.ZipFile.OnReadBytes(ze);
|
||||
|
||||
long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature);
|
||||
if (d == -1) return false;
|
||||
|
||||
// total size of data read (through all loops of this).
|
||||
SizeOfDataRead += d;
|
||||
|
||||
if (ze._InputUsesZip64)
|
||||
{
|
||||
// read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size)
|
||||
block = new byte[20];
|
||||
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
if (n != 20) return false;
|
||||
|
||||
// do not increment bytesRead - it is for entry header only.
|
||||
// the data we have just read is a footer (falls after the file data)
|
||||
//bytesRead += n;
|
||||
|
||||
i = 0;
|
||||
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._CompressedSize = BitConverter.ToInt64(block, i);
|
||||
i += 8;
|
||||
ze._UncompressedSize = BitConverter.ToInt64(block, i);
|
||||
i += 8;
|
||||
|
||||
ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes
|
||||
}
|
||||
else
|
||||
{
|
||||
// read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size)
|
||||
block = new byte[12];
|
||||
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
||||
if (n != 12) return false;
|
||||
|
||||
// do not increment bytesRead - it is for entry header only.
|
||||
// the data we have just read is a footer (falls after the file data)
|
||||
//bytesRead += n;
|
||||
|
||||
i = 0;
|
||||
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
||||
|
||||
ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes
|
||||
|
||||
}
|
||||
|
||||
wantMore = (SizeOfDataRead != ze._CompressedSize);
|
||||
|
||||
if (wantMore)
|
||||
{
|
||||
// Seek back to un-read the last 12 bytes - maybe THEY contain
|
||||
// the ZipEntryDataDescriptorSignature.
|
||||
// (12 bytes for the CRC, Comp and Uncomp size.)
|
||||
ze.ArchiveStream.Seek(-12, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
||||
|
||||
// Adjust the size to account for the false signature read in
|
||||
// FindSignature().
|
||||
SizeOfDataRead += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// seek back to previous position, to prepare to read file data
|
||||
// workitem 8098: ok (restore)
|
||||
ze.ArchiveStream.Seek(posn, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
||||
}
|
||||
|
||||
ze._CompressedFileDataSize = ze._CompressedSize;
|
||||
|
||||
|
||||
// bit 0 set indicates that some kind of encryption is in use
|
||||
if ((ze._BitField & 0x01) == 0x01)
|
||||
{
|
||||
#if AESCRYPTO
|
||||
if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 ||
|
||||
ze.Encryption == EncryptionAlgorithm.WinZipAes256)
|
||||
{
|
||||
int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile);
|
||||
// read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128.
|
||||
ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream);
|
||||
bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes)
|
||||
// according to WinZip, the CompressedSize includes the AES Crypto framing data.
|
||||
ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata;
|
||||
ze._LengthOfTrailer += 10; // MAC
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// read in the header data for "weak" encryption
|
||||
ze._WeakEncryptionHeader = new byte[12];
|
||||
bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader);
|
||||
// decrease the filedata size by 12 bytes
|
||||
ze._CompressedFileDataSize -= 12;
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the size of the blob for this entry.
|
||||
// We also have the starting position in the stream for this entry.
|
||||
ze._LengthOfHeader = bytesRead;
|
||||
ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer;
|
||||
|
||||
|
||||
// We've read in the regular entry header, the extra field, and any
|
||||
// encryption header. The pointer in the file is now at the start of the
|
||||
// filedata, which is potentially compressed and encrypted. Just ahead in
|
||||
// the file, there are _CompressedFileDataSize bytes of data, followed by
|
||||
// potentially a non-zero length trailer, consisting of optionally, some
|
||||
// encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24
|
||||
// bytes).
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer)
|
||||
{
|
||||
// PKZIP encrypts the compressed data stream. Encrypted files must
|
||||
// be decrypted before they can be extracted.
|
||||
|
||||
// Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data
|
||||
// area defining the encryption header for that file. The encryption header is
|
||||
// originally set to random values, and then itself encrypted, using three, 32-bit
|
||||
// keys. The key values are initialized using the supplied encryption password.
|
||||
// After each byte is encrypted, the keys are then updated using pseudo-random
|
||||
// number generation techniques in combination with the same CRC-32 algorithm used
|
||||
// in PKZIP and implemented in the CRC32.cs module in this project.
|
||||
|
||||
// read the 12-byte encryption header
|
||||
int additionalBytesRead = s.Read(buffer, 0, 12);
|
||||
if (additionalBytesRead != 12)
|
||||
throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position));
|
||||
|
||||
return additionalBytesRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool IsNotValidSig(int signature)
|
||||
{
|
||||
return (signature != ZipConstants.ZipEntrySignature);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads one <c>ZipEntry</c> from the given stream. The content for
|
||||
/// the entry does not get decompressed or decrypted. This method
|
||||
/// basically reads metadata, and seeks.
|
||||
/// </summary>
|
||||
/// <param name="zc">the ZipContainer this entry belongs to.</param>
|
||||
/// <param name="first">
|
||||
/// true of this is the first entry being read from the stream.
|
||||
/// </param>
|
||||
/// <returns>the <c>ZipEntry</c> read from the stream.</returns>
|
||||
internal static ZipEntry ReadEntry(ZipContainer zc, bool first)
|
||||
{
|
||||
ZipFile zf = zc.ZipFile;
|
||||
Stream s = zc.ReadStream;
|
||||
System.Text.Encoding defaultEncoding = zc.AlternateEncoding;
|
||||
ZipEntry entry = new ZipEntry();
|
||||
entry._Source = ZipEntrySource.ZipFile;
|
||||
entry._container = zc;
|
||||
entry._archiveStream = s;
|
||||
if (zf != null)
|
||||
zf.OnReadEntry(true, null);
|
||||
|
||||
if (first) HandlePK00Prefix(s);
|
||||
|
||||
// Read entry header, including any encryption header
|
||||
if (!ReadHeader(entry, defaultEncoding)) return null;
|
||||
|
||||
// Store the position in the stream for this entry
|
||||
// change for workitem 8098
|
||||
entry.__FileDataPosition = entry.ArchiveStream.Position;
|
||||
|
||||
// seek past the data without reading it. We will read on Extract()
|
||||
s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
|
||||
// ReadHeader moves the file pointer to the end of the entry header,
|
||||
// as well as any encryption header.
|
||||
|
||||
// CompressedFileDataSize includes:
|
||||
// the maybe compressed, maybe encrypted file data
|
||||
// the encryption trailer, if any
|
||||
// the bit 3 descriptor, if any
|
||||
|
||||
// workitem 5306
|
||||
// http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306
|
||||
HandleUnexpectedDataDescriptor(entry);
|
||||
|
||||
if (zf != null)
|
||||
{
|
||||
zf.OnReadBytes(entry);
|
||||
zf.OnReadEntry(false, entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
internal static void HandlePK00Prefix(Stream s)
|
||||
{
|
||||
// in some cases, the zip file begins with "PK00". This is a throwback and is rare,
|
||||
// but we handle it anyway. We do not change behavior based on it.
|
||||
uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (datum != ZipConstants.PackedToRemovableMedia)
|
||||
{
|
||||
s.Seek(-4, SeekOrigin.Current); // unread the block
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void HandleUnexpectedDataDescriptor(ZipEntry entry)
|
||||
{
|
||||
Stream s = entry.ArchiveStream;
|
||||
|
||||
// In some cases, the "data descriptor" is present, without a signature, even when
|
||||
// bit 3 of the BitField is NOT SET. This is the CRC, followed
|
||||
// by the compressed length and the uncompressed length (4 bytes for each
|
||||
// of those three elements). Need to check that here.
|
||||
//
|
||||
uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (datum == entry._Crc32)
|
||||
{
|
||||
int sz = Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (sz == entry._CompressedSize)
|
||||
{
|
||||
sz = Ionic.Zip.SharedUtilities.ReadInt(s);
|
||||
if (sz == entry._UncompressedSize)
|
||||
{
|
||||
// ignore everything and discard it.
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Seek(-12, SeekOrigin.Current); // unread the three blocks
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Seek(-8, SeekOrigin.Current); // unread the two blocks
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.Seek(-4, SeekOrigin.Current); // unread the block
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds a particular segment in the given extra field.
|
||||
/// This is used when modifying a previously-generated
|
||||
/// extra field, in particular when removing the AES crypto
|
||||
/// segment in the extra field.
|
||||
/// </summary>
|
||||
static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId)
|
||||
{
|
||||
int j = offx;
|
||||
while (j + 3 < extra.Length)
|
||||
{
|
||||
UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256);
|
||||
if (headerId == targetHeaderId) return j-2;
|
||||
|
||||
// else advance to next segment
|
||||
Int16 dataSize = (short)(extra[j++] + extra[j++] * 256);
|
||||
j+= dataSize;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// At current cursor position in the stream, read the extra
|
||||
/// field, and set the properties on the ZipEntry instance
|
||||
/// appropriately. This can be called when processing the
|
||||
/// Extra field in the Central Directory, or in the local
|
||||
/// header.
|
||||
/// </summary>
|
||||
internal int ProcessExtraField(Stream s, Int16 extraFieldLength)
|
||||
{
|
||||
int additionalBytesRead = 0;
|
||||
if (extraFieldLength > 0)
|
||||
{
|
||||
byte[] buffer = this._Extra = new byte[extraFieldLength];
|
||||
additionalBytesRead = s.Read(buffer, 0, buffer.Length);
|
||||
long posn = s.Position - additionalBytesRead;
|
||||
int j = 0;
|
||||
while (j + 3 < buffer.Length)
|
||||
{
|
||||
int start = j;
|
||||
UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256);
|
||||
Int16 dataSize = (short)(buffer[j++] + buffer[j++] * 256);
|
||||
|
||||
switch (headerId)
|
||||
{
|
||||
case 0x000a: // NTFS ctime, atime, mtime
|
||||
j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
case 0x5455: // Unix ctime, atime, mtime
|
||||
j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
case 0x5855: // Info-zip Extra field (outdated)
|
||||
// This is outdated, so the field is supported on
|
||||
// read only.
|
||||
j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
case 0x7855: // Unix uid/gid
|
||||
// ignored. DotNetZip does not handle this field.
|
||||
break;
|
||||
|
||||
case 0x7875: // ??
|
||||
// ignored. I could not find documentation on this field,
|
||||
// though it appears in some zip files.
|
||||
break;
|
||||
|
||||
case 0x0001: // ZIP64
|
||||
j = ProcessExtraFieldZip64(buffer, j, dataSize, posn);
|
||||
break;
|
||||
|
||||
#if AESCRYPTO
|
||||
case 0x9901: // WinZip AES encryption is in use. (workitem 6834)
|
||||
// we will handle this extra field only if compressionmethod is 0x63
|
||||
j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn);
|
||||
break;
|
||||
#endif
|
||||
case 0x0017: // workitem 7968: handle PKWare Strong encryption header
|
||||
j = ProcessExtraFieldPkwareStrongEncryption(buffer, j);
|
||||
break;
|
||||
}
|
||||
|
||||
// move to the next Header in the extra field
|
||||
j = start + dataSize + 4;
|
||||
}
|
||||
}
|
||||
return additionalBytesRead;
|
||||
}
|
||||
|
||||
private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j)
|
||||
{
|
||||
// Value Size Description
|
||||
// ----- ---- -----------
|
||||
// 0x0017 2 bytes Tag for this "extra" block type
|
||||
// TSize 2 bytes Size of data that follows
|
||||
// Format 2 bytes Format definition for this record
|
||||
// AlgID 2 bytes Encryption algorithm identifier
|
||||
// Bitlen 2 bytes Bit length of encryption key
|
||||
// Flags 2 bytes Processing flags
|
||||
// CertData TSize-8 Certificate decryption extra field data
|
||||
// (refer to the explanation for CertData
|
||||
// in the section describing the
|
||||
// Certificate Processing Method under
|
||||
// the Strong Encryption Specification)
|
||||
|
||||
j += 2;
|
||||
_UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256);
|
||||
_Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported;
|
||||
|
||||
// DotNetZip doesn't support this algorithm, but we don't need to throw
|
||||
// here. we might just be reading the archive, which is fine. We'll
|
||||
// need to throw if Extract() is called.
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
#if AESCRYPTO
|
||||
private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
if (this._CompressionMethod == 0x0063)
|
||||
{
|
||||
if ((this._BitField & 0x01) != 0x01)
|
||||
throw new BadReadException(String.Format(" Inconsistent metadata at position 0x{0:X16}", posn));
|
||||
|
||||
this._sourceIsEncrypted = true;
|
||||
|
||||
//this._aesCrypto = new WinZipAesCrypto(this);
|
||||
// see spec at http://www.winzip.com/aes_info.htm
|
||||
if (dataSize != 7)
|
||||
throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
this._WinZipAesMethod = BitConverter.ToInt16(buffer, j);
|
||||
j += 2;
|
||||
if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02)
|
||||
throw new BadReadException(String.Format(" Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}",
|
||||
this._WinZipAesMethod, posn));
|
||||
|
||||
Int16 vendorId = BitConverter.ToInt16(buffer, j);
|
||||
j += 2;
|
||||
if (vendorId != 0x4541)
|
||||
throw new BadReadException(String.Format(" Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn));
|
||||
|
||||
int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1;
|
||||
if (keystrength < 0)
|
||||
throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength));
|
||||
|
||||
_Encryption_FromZipFile = this._Encryption = (keystrength == 128)
|
||||
? EncryptionAlgorithm.WinZipAes128
|
||||
: EncryptionAlgorithm.WinZipAes256;
|
||||
|
||||
j++;
|
||||
|
||||
// set the actual compression method
|
||||
this._CompressionMethod_FromZipFile =
|
||||
this._CompressionMethod = BitConverter.ToInt16(buffer, j);
|
||||
j += 2; // for the next segment of the extra field
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private delegate T Func<T>();
|
||||
|
||||
private int ProcessExtraFieldZip64(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
// The PKWare spec says that any of {UncompressedSize, CompressedSize,
|
||||
// RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header,
|
||||
// and the ZIP64 header may contain one or more of those. If the
|
||||
// values are present, they will be found in the prescribed order.
|
||||
// There may also be a 4-byte "disk start number."
|
||||
// This means that the DataSize must be 28 bytes or less.
|
||||
|
||||
this._InputUsesZip64 = true;
|
||||
|
||||
// workitem 7941: check datasize before reading.
|
||||
if (dataSize > 28)
|
||||
throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}",
|
||||
dataSize, posn));
|
||||
int remainingData = dataSize;
|
||||
|
||||
var slurp = new Func<Int64>( () => {
|
||||
if (remainingData < 8)
|
||||
throw new BadReadException(String.Format(" Missing data for ZIP64 extra field, position 0x{0:X16}", posn));
|
||||
var x = BitConverter.ToInt64(buffer, j);
|
||||
j+= 8;
|
||||
remainingData -= 8;
|
||||
return x;
|
||||
});
|
||||
|
||||
if (this._UncompressedSize == 0xFFFFFFFF)
|
||||
this._UncompressedSize = slurp();
|
||||
|
||||
if (this._CompressedSize == 0xFFFFFFFF)
|
||||
this._CompressedSize = slurp();
|
||||
|
||||
if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF)
|
||||
this._RelativeOffsetOfLocalHeader = slurp();
|
||||
|
||||
// Ignore anything else. Potentially there are 4 more bytes for the
|
||||
// disk start number. DotNetZip currently doesn't handle multi-disk
|
||||
// archives.
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
if (dataSize != 12 && dataSize != 8)
|
||||
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
Int32 timet = BitConverter.ToInt32(buffer, j);
|
||||
this._Mtime = _unixEpoch.AddSeconds(timet);
|
||||
j += 4;
|
||||
|
||||
timet = BitConverter.ToInt32(buffer, j);
|
||||
this._Atime = _unixEpoch.AddSeconds(timet);
|
||||
j += 4;
|
||||
|
||||
this._Ctime = DateTime.UtcNow;
|
||||
|
||||
_ntfsTimesAreSet = true;
|
||||
_timestamp |= ZipEntryTimestamp.InfoZip1; return j;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
// The Unix filetimes are 32-bit unsigned integers,
|
||||
// storing seconds since Unix epoch.
|
||||
|
||||
if (dataSize != 13 && dataSize != 9 && dataSize != 5)
|
||||
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
int remainingData = dataSize;
|
||||
|
||||
var slurp = new Func<DateTime>( () => {
|
||||
Int32 timet = BitConverter.ToInt32(buffer, j);
|
||||
j += 4;
|
||||
remainingData -= 4;
|
||||
return _unixEpoch.AddSeconds(timet);
|
||||
});
|
||||
|
||||
if (dataSize == 13 || _readExtraDepth > 0)
|
||||
{
|
||||
byte flag = buffer[j++];
|
||||
remainingData--;
|
||||
|
||||
if ((flag & 0x0001) != 0 && remainingData >= 4)
|
||||
this._Mtime = slurp();
|
||||
|
||||
this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4)
|
||||
? slurp()
|
||||
: DateTime.UtcNow;
|
||||
|
||||
this._Ctime = ((flag & 0x0004) != 0 && remainingData >= 4)
|
||||
? slurp()
|
||||
:DateTime.UtcNow;
|
||||
|
||||
_timestamp |= ZipEntryTimestamp.Unix;
|
||||
_ntfsTimesAreSet = true;
|
||||
_emitUnixTimes = true;
|
||||
}
|
||||
else
|
||||
ReadExtraField(); // will recurse
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
||||
{
|
||||
// The NTFS filetimes are 64-bit unsigned integers, stored in Intel
|
||||
// (least significant byte first) byte order. They are expressed as the
|
||||
// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
|
||||
// which is "01-Jan-1601 00:00:00 UTC".
|
||||
//
|
||||
// HeaderId 2 bytes 0x000a == NTFS stuff
|
||||
// Datasize 2 bytes ?? (usually 32)
|
||||
// reserved 4 bytes ??
|
||||
// timetag 2 bytes 0x0001 == time
|
||||
// size 2 bytes 24 == 8 bytes each for ctime, mtime, atime
|
||||
// mtime 8 bytes win32 ticks since win32epoch
|
||||
// atime 8 bytes win32 ticks since win32epoch
|
||||
// ctime 8 bytes win32 ticks since win32epoch
|
||||
|
||||
if (dataSize != 32)
|
||||
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn));
|
||||
|
||||
j += 4; // reserved
|
||||
Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256);
|
||||
Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256);
|
||||
j += 4; // tag and size
|
||||
|
||||
if (timetag == 0x0001 && addlsize == 24)
|
||||
{
|
||||
Int64 z = BitConverter.ToInt64(buffer, j);
|
||||
this._Mtime = DateTime.FromFileTimeUtc(z);
|
||||
j += 8;
|
||||
|
||||
// At this point the library *could* set the LastModified value
|
||||
// to coincide with the Mtime value. In theory, they refer to
|
||||
// the same property of the file, and should be the same anyway,
|
||||
// allowing for differences in precision. But they are
|
||||
// independent quantities in the zip archive, and this library
|
||||
// will keep them separate in the object model. There is no ill
|
||||
// effect from this, because as files are extracted, the
|
||||
// higher-precision value (Mtime) is used if it is present.
|
||||
// Apps may wish to compare the Mtime versus LastModified
|
||||
// values, but any difference when both are present is not
|
||||
// germaine to the correctness of the library. but note: when
|
||||
// explicitly setting either value, both are set. See the setter
|
||||
// for LastModified or the SetNtfsTimes() method.
|
||||
|
||||
z = BitConverter.ToInt64(buffer, j);
|
||||
this._Atime = DateTime.FromFileTimeUtc(z);
|
||||
j += 8;
|
||||
|
||||
z = BitConverter.ToInt64(buffer, j);
|
||||
this._Ctime = DateTime.FromFileTimeUtc(z);
|
||||
j += 8;
|
||||
|
||||
_ntfsTimesAreSet = true;
|
||||
_timestamp |= ZipEntryTimestamp.Windows;
|
||||
_emitNtfsTimes = true;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
2582
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.Write.cs
Normal file
2582
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.Write.cs
Normal file
File diff suppressed because it is too large
Load diff
2968
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.cs
Normal file
2968
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipEntry.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,69 @@
|
|||
// ZipEntrySource.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-November-19 11:18:42>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum that specifies the source of the ZipEntry.
|
||||
/// </summary>
|
||||
public enum ZipEntrySource
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value. Invalid on a bonafide ZipEntry.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The entry was instantiated by calling AddFile() or another method that
|
||||
/// added an entry from the filesystem.
|
||||
/// </summary>
|
||||
FileSystem,
|
||||
|
||||
/// <summary>
|
||||
/// The entry was instantiated via <see cref="Ionic.Zip.ZipFile.AddEntry(string,string)"/> or
|
||||
/// <see cref="Ionic.Zip.ZipFile.AddEntry(string,System.IO.Stream)"/> .
|
||||
/// </summary>
|
||||
Stream,
|
||||
|
||||
/// <summary>
|
||||
/// The ZipEntry was instantiated by reading a zipfile.
|
||||
/// </summary>
|
||||
ZipFile,
|
||||
|
||||
/// <summary>
|
||||
/// The content for the ZipEntry will be or was provided by the WriteDelegate.
|
||||
/// </summary>
|
||||
WriteDelegate,
|
||||
|
||||
/// <summary>
|
||||
/// The content for the ZipEntry will be obtained from the stream dispensed by the <c>OpenDelegate</c>.
|
||||
/// The entry was instantiated via <see cref="Ionic.Zip.ZipFile.AddEntry(string,OpenDelegate,CloseDelegate)"/>.
|
||||
/// </summary>
|
||||
JitStream,
|
||||
|
||||
/// <summary>
|
||||
/// The content for the ZipEntry will be or was obtained from a <c>ZipOutputStream</c>.
|
||||
/// </summary>
|
||||
ZipOutputStream,
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// ZipErrorAction.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-September-01 18:43:20>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZipErrorAction enum, which provides
|
||||
// an action to take when errors occur when opening or reading
|
||||
// files to be added to a zip file.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum providing the options when an error occurs during opening or reading
|
||||
/// of a file or directory that is being saved to a zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This enum describes the actions that the library can take when an error occurs
|
||||
/// opening or reading a file, as it is being saved into a Zip archive.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// In some cases an error will occur when DotNetZip tries to open a file to be
|
||||
/// added to the zip archive. In other cases, an error might occur after the
|
||||
/// file has been successfully opened, while DotNetZip is reading the file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The first problem might occur when calling AddDirectory() on a directory
|
||||
/// that contains a Clipper .dbf file; the file is locked by Clipper and
|
||||
/// cannot be opened by another process. An example of the second problem is
|
||||
/// the ERROR_LOCK_VIOLATION that results when a file is opened by another
|
||||
/// process, but not locked, and a range lock has been taken on the file.
|
||||
/// Microsoft Outlook takes range locks on .PST files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public enum ZipErrorAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Throw an exception when an error occurs while zipping. This is the default
|
||||
/// behavior. (For COM clients, this is a 0 (zero).)
|
||||
/// </summary>
|
||||
Throw,
|
||||
|
||||
/// <summary>
|
||||
/// When an error occurs during zipping, for example a file cannot be opened,
|
||||
/// skip the file causing the error, and continue zipping. (For COM clients,
|
||||
/// this is a 1.)
|
||||
/// </summary>
|
||||
Skip,
|
||||
|
||||
/// <summary>
|
||||
/// When an error occurs during zipping, for example a file cannot be opened,
|
||||
/// retry the operation that caused the error. Be careful with this option. If
|
||||
/// the error is not temporary, the library will retry forever. (For COM
|
||||
/// clients, this is a 2.)
|
||||
/// </summary>
|
||||
Retry,
|
||||
|
||||
/// <summary>
|
||||
/// When an error occurs, invoke the zipError event. The event type used is
|
||||
/// <see cref="ZipProgressEventType.Error_Saving"/>. A typical use of this option:
|
||||
/// a GUI application may wish to pop up a dialog to allow the user to view the
|
||||
/// error that occurred, and choose an appropriate action. After your
|
||||
/// processing in the error event, if you want to skip the file, set <see
|
||||
/// cref="ZipEntry.ZipErrorAction"/> on the
|
||||
/// <c>ZipProgressEventArgs.CurrentEntry</c> to <c>Skip</c>. If you want the
|
||||
/// exception to be thrown, set <c>ZipErrorAction</c> on the <c>CurrentEntry</c>
|
||||
/// to <c>Throw</c>. If you want to cancel the zip, set
|
||||
/// <c>ZipProgressEventArgs.Cancel</c> to true. Cancelling differs from using
|
||||
/// Skip in that a cancel will not save any further entries, if there are any.
|
||||
/// (For COM clients, the value of this enum is a 3.)
|
||||
/// </summary>
|
||||
InvokeErrorEvent,
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,352 @@
|
|||
// ZipFile.Check.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:40:50>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the methods for doing Checks on zip files.
|
||||
// These are not necessary to include in the Reduced or CF
|
||||
// version of the library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
public partial class ZipFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks a zip file to see if its directory is consistent.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// In cases of data error, the directory within a zip file can get out
|
||||
/// of synch with the entries in the zip file. This method checks the
|
||||
/// given zip file and returns true if this has occurred.
|
||||
/// </para>
|
||||
///
|
||||
/// <para> This method may take a long time to run for large zip files. </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method is not supported in the Reduced or Compact Framework
|
||||
/// versions of DotNetZip.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Developers using COM can use the <see
|
||||
/// cref="ComHelper.CheckZip(String)">ComHelper.CheckZip(String)</see>
|
||||
/// method.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
|
||||
///
|
||||
/// <seealso cref="FixZipDirectory(string)"/>
|
||||
/// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/>
|
||||
public static bool CheckZip(string zipFileName)
|
||||
{
|
||||
return CheckZip(zipFileName, false, null);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks a zip file to see if its directory is consistent,
|
||||
/// and optionally fixes the directory if necessary.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// In cases of data error, the directory within a zip file can get out of
|
||||
/// synch with the entries in the zip file. This method checks the given
|
||||
/// zip file, and returns true if this has occurred. It also optionally
|
||||
/// fixes the zipfile, saving the fixed copy in <em>Name</em>_Fixed.zip.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method may take a long time to run for large zip files. It
|
||||
/// will take even longer if the file actually needs to be fixed, and if
|
||||
/// <c>fixIfNecessary</c> is true.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method is not supported in the Reduced or Compact
|
||||
/// Framework versions of DotNetZip.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to check.</param>
|
||||
///
|
||||
/// <param name="fixIfNecessary">If true, the method will fix the zip file if
|
||||
/// necessary.</param>
|
||||
///
|
||||
/// <param name="writer">
|
||||
/// a TextWriter in which messages generated while checking will be written.
|
||||
/// </param>
|
||||
///
|
||||
/// <returns>true if the named zip is OK; false if the file needs to be fixed.</returns>
|
||||
///
|
||||
/// <seealso cref="CheckZip(string)"/>
|
||||
/// <seealso cref="FixZipDirectory(string)"/>
|
||||
public static bool CheckZip(string zipFileName, bool fixIfNecessary,
|
||||
TextWriter writer)
|
||||
|
||||
{
|
||||
ZipFile zip1 = null, zip2 = null;
|
||||
bool isOk = true;
|
||||
try
|
||||
{
|
||||
zip1 = new ZipFile();
|
||||
zip1.FullScan = true;
|
||||
zip1.Initialize(zipFileName);
|
||||
|
||||
zip2 = ZipFile.Read(zipFileName);
|
||||
|
||||
foreach (var e1 in zip1)
|
||||
{
|
||||
foreach (var e2 in zip2)
|
||||
{
|
||||
if (e1.FileName == e2.FileName)
|
||||
{
|
||||
if (e1._RelativeOffsetOfLocalHeader != e2._RelativeOffsetOfLocalHeader)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in RelativeOffsetOfLocalHeader (0x{1:X16} != 0x{2:X16})",
|
||||
e1.FileName, e1._RelativeOffsetOfLocalHeader,
|
||||
e2._RelativeOffsetOfLocalHeader);
|
||||
}
|
||||
if (e1._CompressedSize != e2._CompressedSize)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in CompressedSize (0x{1:X16} != 0x{2:X16})",
|
||||
e1.FileName, e1._CompressedSize,
|
||||
e2._CompressedSize);
|
||||
}
|
||||
if (e1._UncompressedSize != e2._UncompressedSize)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in UncompressedSize (0x{1:X16} != 0x{2:X16})",
|
||||
e1.FileName, e1._UncompressedSize,
|
||||
e2._UncompressedSize);
|
||||
}
|
||||
if (e1.CompressionMethod != e2.CompressionMethod)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in CompressionMethod (0x{1:X4} != 0x{2:X4})",
|
||||
e1.FileName, e1.CompressionMethod,
|
||||
e2.CompressionMethod);
|
||||
}
|
||||
if (e1.Crc != e2.Crc)
|
||||
{
|
||||
isOk = false;
|
||||
if (writer != null)
|
||||
writer.WriteLine("{0}: mismatch in Crc32 (0x{1:X4} != 0x{2:X4})",
|
||||
e1.FileName, e1.Crc,
|
||||
e2.Crc);
|
||||
}
|
||||
|
||||
// found a match, so stop the inside loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zip2.Dispose();
|
||||
zip2 = null;
|
||||
|
||||
if (!isOk && fixIfNecessary)
|
||||
{
|
||||
string newFileName = Path.GetFileNameWithoutExtension(zipFileName);
|
||||
newFileName = System.String.Format("{0}_fixed.zip", newFileName);
|
||||
zip1.Save(newFileName);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (zip1 != null) zip1.Dispose();
|
||||
if (zip2 != null) zip2.Dispose();
|
||||
}
|
||||
return isOk;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Rewrite the directory within a zipfile.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// In cases of data error, the directory in a zip file can get out of
|
||||
/// synch with the entries in the zip file. This method attempts to fix
|
||||
/// the zip file if this has occurred.
|
||||
/// </para>
|
||||
///
|
||||
/// <para> This can take a long time for large zip files. </para>
|
||||
///
|
||||
/// <para> This won't work if the zip file uses a non-standard
|
||||
/// code page - neither IBM437 nor UTF-8. </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method is not supported in the Reduced or Compact Framework
|
||||
/// versions of DotNetZip.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Developers using COM can use the <see
|
||||
/// cref="ComHelper.FixZipDirectory(String)">ComHelper.FixZipDirectory(String)</see>
|
||||
/// method.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to fix.</param>
|
||||
///
|
||||
/// <seealso cref="CheckZip(string)"/>
|
||||
/// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/>
|
||||
public static void FixZipDirectory(string zipFileName)
|
||||
{
|
||||
using (var zip = new ZipFile())
|
||||
{
|
||||
zip.FullScan = true;
|
||||
zip.Initialize(zipFileName);
|
||||
zip.Save(zipFileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Verify the password on a zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Keep in mind that passwords in zipfiles are applied to
|
||||
/// zip entries, not to the entire zip file. So testing a
|
||||
/// zipfile for a particular password doesn't work in the
|
||||
/// general case. On the other hand, it's often the case
|
||||
/// that a single password will be used on all entries in a
|
||||
/// zip file. This method works for that case.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// There is no way to check a password without doing the
|
||||
/// decryption. So this code decrypts and extracts the given
|
||||
/// zipfile into <see cref="System.IO.Stream.Null"/>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="zipFileName">The filename to of the zip file to fix.</param>
|
||||
///
|
||||
/// <param name="password">The password to check.</param>
|
||||
///
|
||||
/// <returns>a bool indicating whether the password matches.</returns>
|
||||
public static bool CheckZipPassword(string zipFileName, string password)
|
||||
{
|
||||
// workitem 13664
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
using (ZipFile zip1 = ZipFile.Read(zipFileName))
|
||||
{
|
||||
foreach (var e in zip1)
|
||||
{
|
||||
if (!e.IsDirectory && e.UsesEncryption)
|
||||
{
|
||||
e.ExtractWithPassword(System.IO.Stream.Null, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
catch(Ionic.Zip.BadPasswordException) { }
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides a human-readable string with information about the ZipFile.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The information string contains 10 lines or so, about each ZipEntry,
|
||||
/// describing whether encryption is in use, the compressed and uncompressed
|
||||
/// length of the entry, the offset of the entry, and so on. As a result the
|
||||
/// information string can be very long for zip files that contain many
|
||||
/// entries.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This information is mostly useful for diagnostic purposes.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public string Info
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new System.Text.StringBuilder();
|
||||
builder.Append(string.Format(" ZipFile: {0}\n", this.Name));
|
||||
if (!string.IsNullOrEmpty(this._Comment))
|
||||
{
|
||||
builder.Append(string.Format(" Comment: {0}\n", this._Comment));
|
||||
}
|
||||
if (this._versionMadeBy != 0)
|
||||
{
|
||||
builder.Append(string.Format(" version made by: 0x{0:X4}\n", this._versionMadeBy));
|
||||
}
|
||||
if (this._versionNeededToExtract != 0)
|
||||
{
|
||||
builder.Append(string.Format("needed to extract: 0x{0:X4}\n", this._versionNeededToExtract));
|
||||
}
|
||||
|
||||
builder.Append(string.Format(" uses ZIP64: {0}\n", this.InputUsesZip64));
|
||||
|
||||
builder.Append(string.Format(" disk with CD: {0}\n", this._diskNumberWithCd));
|
||||
if (this._OffsetOfCentralDirectory == 0xFFFFFFFF)
|
||||
builder.Append(string.Format(" CD64 offset: 0x{0:X16}\n", this._OffsetOfCentralDirectory64));
|
||||
else
|
||||
builder.Append(string.Format(" CD offset: 0x{0:X8}\n", this._OffsetOfCentralDirectory));
|
||||
builder.Append("\n");
|
||||
foreach (ZipEntry entry in this._entries.Values)
|
||||
{
|
||||
builder.Append(entry.Info);
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1219
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Events.cs
Normal file
1219
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Events.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,298 @@
|
|||
// ZipFile.Extract.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:45:18>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the methods for Extract operations on zip files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
public partial class ZipFile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all of the items in the zip archive, to the specified path in the
|
||||
/// filesystem. The path can be relative or fully-qualified.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method will extract all entries in the <c>ZipFile</c> to the
|
||||
/// specified path.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// If an extraction of a file from the zip archive would overwrite an
|
||||
/// existing file in the filesystem, the action taken is dictated by the
|
||||
/// ExtractExistingFile property, which overrides any setting you may have
|
||||
/// made on individual ZipEntry instances. By default, if you have not
|
||||
/// set that property on the <c>ZipFile</c> instance, the entry will not
|
||||
/// be extracted, the existing file will not be overwritten and an
|
||||
/// exception will be thrown. To change this, set the property, or use the
|
||||
/// <see cref="ZipFile.ExtractAll(string,
|
||||
/// Ionic.Zip.ExtractExistingFileAction)" /> overload that allows you to
|
||||
/// specify an ExtractExistingFileAction parameter.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The action to take when an extract would overwrite an existing file
|
||||
/// applies to all entries. If you want to set this on a per-entry basis,
|
||||
/// then you must use one of the <see
|
||||
/// cref="ZipEntry.Extract()">ZipEntry.Extract</see> methods.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method will send verbose output messages to the <see
|
||||
/// cref="StatusMessageTextWriter"/>, if it is set on the <c>ZipFile</c>
|
||||
/// instance.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// You may wish to take advantage of the <c>ExtractProgress</c> event.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// About timestamps: When extracting a file entry from a zip archive, the
|
||||
/// extracted file gets the last modified time of the entry as stored in
|
||||
/// the archive. The archive may also store extended file timestamp
|
||||
/// information, including last accessed and created times. If these are
|
||||
/// present in the <c>ZipEntry</c>, then the extracted file will also get
|
||||
/// these times.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// A Directory entry is somewhat different. It will get the times as
|
||||
/// described for a file entry, but, if there are file entries in the zip
|
||||
/// archive that, when extracted, appear in the just-created directory,
|
||||
/// then when those file entries are extracted, the last modified and last
|
||||
/// accessed times of the directory will change, as a side effect. The
|
||||
/// result is that after an extraction of a directory and a number of
|
||||
/// files within the directory, the last modified and last accessed
|
||||
/// timestamps on the directory will reflect the time that the last file
|
||||
/// was extracted into the directory, rather than the time stored in the
|
||||
/// zip archive for the directory.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// To compensate, when extracting an archive with <c>ExtractAll</c>,
|
||||
/// DotNetZip will extract all the file and directory entries as described
|
||||
/// above, but it will then make a second pass on the directories, and
|
||||
/// reset the times on the directories to reflect what is stored in the
|
||||
/// zip archive.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This compensation is performed only within the context of an
|
||||
/// <c>ExtractAll</c>. If you call <c>ZipEntry.Extract</c> on a directory
|
||||
/// entry, the timestamps on directory in the filesystem will reflect the
|
||||
/// times stored in the zip. If you then call <c>ZipEntry.Extract</c> on
|
||||
/// a file entry, which is extracted into the directory, the timestamps on
|
||||
/// the directory will be updated to the current time.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example extracts all the entries in a zip archive file, to the
|
||||
/// specified target directory. The extraction will overwrite any
|
||||
/// existing files silently.
|
||||
///
|
||||
/// <code>
|
||||
/// String TargetDirectory= "unpack";
|
||||
/// using(ZipFile zip= ZipFile.Read(ZipFileToExtract))
|
||||
/// {
|
||||
/// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently;
|
||||
/// zip.ExtractAll(TargetDirectory);
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Dim TargetDirectory As String = "unpack"
|
||||
/// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
|
||||
/// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently
|
||||
/// zip.ExtractAll(TargetDirectory)
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.ExtractProgress"/>
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.ExtractExistingFile"/>
|
||||
///
|
||||
/// <param name="path">
|
||||
/// The path to which the contents of the zipfile will be extracted.
|
||||
/// The path can be relative or fully-qualified.
|
||||
/// </param>
|
||||
///
|
||||
public void ExtractAll(string path)
|
||||
{
|
||||
_InternalExtractAll(path, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all of the items in the zip archive, to the specified path in the
|
||||
/// filesystem, using the specified behavior when extraction would overwrite an
|
||||
/// existing file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This method will extract all entries in the <c>ZipFile</c> to the specified
|
||||
/// path. For an extraction that would overwrite an existing file, the behavior
|
||||
/// is dictated by <paramref name="extractExistingFile"/>, which overrides any
|
||||
/// setting you may have made on individual ZipEntry instances.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The action to take when an extract would overwrite an existing file
|
||||
/// applies to all entries. If you want to set this on a per-entry basis,
|
||||
/// then you must use <see cref="ZipEntry.Extract(String,
|
||||
/// ExtractExistingFileAction)" /> or one of the similar methods.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Calling this method is equivalent to setting the <see
|
||||
/// cref="ExtractExistingFile"/> property and then calling <see
|
||||
/// cref="ExtractAll(String)"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method will send verbose output messages to the
|
||||
/// <see cref="StatusMessageTextWriter"/>, if it is set on the <c>ZipFile</c> instance.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example extracts all the entries in a zip archive file, to the
|
||||
/// specified target directory. It does not overwrite any existing files.
|
||||
/// <code>
|
||||
/// String TargetDirectory= "c:\\unpack";
|
||||
/// using(ZipFile zip= ZipFile.Read(ZipFileToExtract))
|
||||
/// {
|
||||
/// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite);
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Dim TargetDirectory As String = "c:\unpack"
|
||||
/// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
|
||||
/// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite)
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="path">
|
||||
/// The path to which the contents of the zipfile will be extracted.
|
||||
/// The path can be relative or fully-qualified.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="extractExistingFile">
|
||||
/// The action to take if extraction would overwrite an existing file.
|
||||
/// </param>
|
||||
/// <seealso cref="ExtractSelectedEntries(String,ExtractExistingFileAction)"/>
|
||||
public void ExtractAll(string path, ExtractExistingFileAction extractExistingFile)
|
||||
{
|
||||
ExtractExistingFile = extractExistingFile;
|
||||
_InternalExtractAll(path, true);
|
||||
}
|
||||
|
||||
|
||||
private void _InternalExtractAll(string path, bool overrideExtractExistingProperty)
|
||||
{
|
||||
bool header = Verbose;
|
||||
_inExtractAll = true;
|
||||
try
|
||||
{
|
||||
OnExtractAllStarted(path);
|
||||
|
||||
int n = 0;
|
||||
foreach (ZipEntry e in _entries.Values)
|
||||
{
|
||||
if (header)
|
||||
{
|
||||
StatusMessageTextWriter.WriteLine("\n{1,-22} {2,-8} {3,4} {4,-8} {0}",
|
||||
"Name", "Modified", "Size", "Ratio", "Packed");
|
||||
StatusMessageTextWriter.WriteLine(new System.String('-', 72));
|
||||
header = false;
|
||||
}
|
||||
if (Verbose)
|
||||
{
|
||||
StatusMessageTextWriter.WriteLine("{1,-22} {2,-8} {3,4:F0}% {4,-8} {0}",
|
||||
e.FileName,
|
||||
e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
e.UncompressedSize,
|
||||
e.CompressionRatio,
|
||||
e.CompressedSize);
|
||||
if (!String.IsNullOrEmpty(e.Comment))
|
||||
StatusMessageTextWriter.WriteLine(" Comment: {0}", e.Comment);
|
||||
}
|
||||
e.Password = _Password; // this may be null
|
||||
OnExtractEntry(n, true, e, path);
|
||||
if (overrideExtractExistingProperty)
|
||||
e.ExtractExistingFile = this.ExtractExistingFile;
|
||||
e.Extract(path);
|
||||
n++;
|
||||
OnExtractEntry(n, false, e, path);
|
||||
if (_extractOperationCanceled)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_extractOperationCanceled)
|
||||
{
|
||||
// workitem 8264:
|
||||
// now, set times on directory entries, again.
|
||||
// The problem is, extracting a file changes the times on the parent
|
||||
// directory. So after all files have been extracted, we have to
|
||||
// run through the directories again.
|
||||
foreach (ZipEntry e in _entries.Values)
|
||||
{
|
||||
// check if it is a directory
|
||||
if ((e.IsDirectory) || (e.FileName.EndsWith("/")))
|
||||
{
|
||||
string outputFile = (e.FileName.StartsWith("/"))
|
||||
? Path.Combine(path, e.FileName.Substring(1))
|
||||
: Path.Combine(path, e.FileName);
|
||||
|
||||
e._SetTimes(outputFile, false);
|
||||
}
|
||||
}
|
||||
OnExtractAllCompleted(path);
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
_inExtractAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
1110
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Read.cs
Normal file
1110
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.Read.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,964 @@
|
|||
// ZipFile.Save.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-August-05 13:31:23>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the methods for Save operations on zip files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
public partial class ZipFile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Delete file with retry on UnauthorizedAccessException.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When calling File.Delete() on a file that has been "recently"
|
||||
/// created, the call sometimes fails with
|
||||
/// UnauthorizedAccessException. This method simply retries the Delete 3
|
||||
/// times with a sleep between tries.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name='filename'>the name of the file to be deleted</param>
|
||||
private void DeleteFileWithRetry(string filename)
|
||||
{
|
||||
bool done = false;
|
||||
int nRetries = 3;
|
||||
for (int i=0; i < nRetries && !done; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filename);
|
||||
done = true;
|
||||
}
|
||||
catch (System.UnauthorizedAccessException)
|
||||
{
|
||||
Console.WriteLine("************************************************** Retry delete.");
|
||||
System.Threading.Thread.Sleep(200+i*200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Saves the Zip archive to a file, specified by the Name property of the
|
||||
/// <c>ZipFile</c>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <c>ZipFile</c> instance is written to storage, typically a zip file
|
||||
/// in a filesystem, only when the caller calls <c>Save</c>. In the typical
|
||||
/// case, the Save operation writes the zip content to a temporary file, and
|
||||
/// then renames the temporary file to the desired name. If necessary, this
|
||||
/// method will delete a pre-existing file before the rename.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <see cref="ZipFile.Name"/> property is specified either explicitly,
|
||||
/// or implicitly using one of the parameterized ZipFile constructors. For
|
||||
/// COM Automation clients, the <c>Name</c> property must be set explicitly,
|
||||
/// because COM Automation clients cannot call parameterized constructors.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When using a filesystem file for the Zip output, it is possible to call
|
||||
/// <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each
|
||||
/// call the zip content is re-written to the same output file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Data for entries that have been added to the <c>ZipFile</c> instance is
|
||||
/// written to the output when the <c>Save</c> method is called. This means
|
||||
/// that the input streams for those entries must be available at the time
|
||||
/// the application calls <c>Save</c>. If, for example, the application
|
||||
/// adds entries with <c>AddEntry</c> using a dynamically-allocated
|
||||
/// <c>MemoryStream</c>, the memory stream must not have been disposed
|
||||
/// before the call to <c>Save</c>. See the <see
|
||||
/// cref="ZipEntry.InputStream"/> property for more discussion of the
|
||||
/// availability requirements of the input stream for an entry, and an
|
||||
/// approach for providing just-in-time stream lifecycle management.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/>
|
||||
///
|
||||
/// <exception cref="Ionic.Zip.BadStateException">
|
||||
/// Thrown if you haven't specified a location or stream for saving the zip,
|
||||
/// either in the constructor or by setting the Name property, or if you try
|
||||
/// to save a regular zip archive to a filename with a .exe extension.
|
||||
/// </exception>
|
||||
///
|
||||
/// <exception cref="System.OverflowException">
|
||||
/// Thrown if <see cref="MaxOutputSegmentSize"/> is non-zero, and the number
|
||||
/// of segments that would be generated for the spanned zip file during the
|
||||
/// save operation exceeds 99. If this happens, you need to increase the
|
||||
/// segment size.
|
||||
/// </exception>
|
||||
///
|
||||
public void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool thisSaveUsedZip64 = false;
|
||||
_saveOperationCanceled = false;
|
||||
_numberOfSegmentsForMostRecentSave = 0;
|
||||
OnSaveStarted();
|
||||
|
||||
if (WriteStream == null)
|
||||
throw new BadStateException("You haven't specified where to save the zip.");
|
||||
|
||||
if (_name != null && _name.EndsWith(".exe") && !_SavingSfx)
|
||||
throw new BadStateException("You specified an EXE for a plain zip file.");
|
||||
|
||||
// check if modified, before saving.
|
||||
if (!_contentsChanged)
|
||||
{
|
||||
OnSaveCompleted();
|
||||
if (Verbose) StatusMessageTextWriter.WriteLine("No save is necessary....");
|
||||
return;
|
||||
}
|
||||
|
||||
Reset(true);
|
||||
|
||||
if (Verbose) StatusMessageTextWriter.WriteLine("saving....");
|
||||
|
||||
// validate the number of entries
|
||||
if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
|
||||
throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
|
||||
|
||||
|
||||
// write an entry in the zip for each file
|
||||
int n = 0;
|
||||
// workitem 9831
|
||||
ICollection<ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
|
||||
foreach (ZipEntry e in c) // _entries.Values
|
||||
{
|
||||
OnSaveEntry(n, e, true);
|
||||
e.Write(WriteStream);
|
||||
if (_saveOperationCanceled)
|
||||
break;
|
||||
|
||||
n++;
|
||||
OnSaveEntry(n, e, false);
|
||||
if (_saveOperationCanceled)
|
||||
break;
|
||||
|
||||
// Some entries can be skipped during the save.
|
||||
if (e.IncludedInMostRecentSave)
|
||||
thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (_saveOperationCanceled)
|
||||
return;
|
||||
|
||||
var zss = WriteStream as ZipSegmentedStream;
|
||||
|
||||
_numberOfSegmentsForMostRecentSave = (zss!=null)
|
||||
? zss.CurrentSegment
|
||||
: 1;
|
||||
|
||||
bool directoryNeededZip64 =
|
||||
ZipOutput.WriteCentralDirectoryStructure
|
||||
(WriteStream,
|
||||
c,
|
||||
_numberOfSegmentsForMostRecentSave,
|
||||
_zip64,
|
||||
Comment,
|
||||
new ZipContainer(this));
|
||||
|
||||
OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
|
||||
|
||||
_hasBeenSaved = true;
|
||||
_contentsChanged = false;
|
||||
|
||||
thisSaveUsedZip64 |= directoryNeededZip64;
|
||||
_OutputUsesZip64 = new Nullable<bool>(thisSaveUsedZip64);
|
||||
|
||||
|
||||
// do the rename as necessary
|
||||
if (_name != null &&
|
||||
(_temporaryFileName!=null || zss != null))
|
||||
{
|
||||
// _temporaryFileName may remain null if we are writing to a stream.
|
||||
// only close the stream if there is a file behind it.
|
||||
#if NETCF
|
||||
WriteStream.Close();
|
||||
#else
|
||||
WriteStream.Dispose();
|
||||
#endif
|
||||
if (_saveOperationCanceled)
|
||||
return;
|
||||
|
||||
if (_fileAlreadyExists && this._readstream != null)
|
||||
{
|
||||
// This means we opened and read a zip file.
|
||||
// If we are now saving to the same file, we need to close the
|
||||
// orig file, first.
|
||||
this._readstream.Close();
|
||||
this._readstream = null;
|
||||
// the archiveStream for each entry needs to be null
|
||||
foreach (var e in c)
|
||||
{
|
||||
var zss1 = e._archiveStream as ZipSegmentedStream;
|
||||
if (zss1 != null)
|
||||
#if NETCF
|
||||
zss1.Close();
|
||||
#else
|
||||
zss1.Dispose();
|
||||
#endif
|
||||
e._archiveStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
string tmpName = null;
|
||||
if (File.Exists(_name))
|
||||
{
|
||||
// the steps:
|
||||
//
|
||||
// 1. Delete tmpName
|
||||
// 2. move existing zip to tmpName
|
||||
// 3. rename (File.Move) working file to name of existing zip
|
||||
// 4. delete tmpName
|
||||
//
|
||||
// This series of steps avoids the exception,
|
||||
// System.IO.IOException:
|
||||
// "Cannot create a file when that file already exists."
|
||||
//
|
||||
// Cannot just call File.Replace() here because
|
||||
// there is a possibility that the TEMP volume is different
|
||||
// that the volume for the final file (c:\ vs d:\).
|
||||
// So we need to do a Delete+Move pair.
|
||||
//
|
||||
// But, when doing the delete, Windows allows a process to
|
||||
// delete the file, even though it is held open by, say, a
|
||||
// virus scanner. It gets internally marked as "delete
|
||||
// pending". The file does not actually get removed from the
|
||||
// file system, it is still there after the File.Delete
|
||||
// call.
|
||||
//
|
||||
// Therefore, we need to move the existing zip, which may be
|
||||
// held open, to some other name. Then rename our working
|
||||
// file to the desired name, then delete (possibly delete
|
||||
// pending) the "other name".
|
||||
//
|
||||
// Ideally this would be transactional. It's possible that the
|
||||
// delete succeeds and the move fails. Lacking transactions, if
|
||||
// this kind of failure happens, we're hosed, and this logic will
|
||||
// throw on the next File.Move().
|
||||
//
|
||||
//File.Delete(_name);
|
||||
// workitem 10447
|
||||
#if NETCF || SILVERLIGHT
|
||||
tmpName = _name + "." + SharedUtilities.GenerateRandomStringImpl(8,0) + ".tmp";
|
||||
#else
|
||||
tmpName = _name + "." + Path.GetRandomFileName();
|
||||
#endif
|
||||
if (File.Exists(tmpName))
|
||||
DeleteFileWithRetry(tmpName);
|
||||
File.Move(_name, tmpName);
|
||||
}
|
||||
|
||||
OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
|
||||
File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName,
|
||||
_name);
|
||||
|
||||
OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);
|
||||
|
||||
if (tmpName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// not critical
|
||||
if (File.Exists(tmpName))
|
||||
File.Delete(tmpName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// don't care about exceptions here.
|
||||
}
|
||||
|
||||
}
|
||||
_fileAlreadyExists = true;
|
||||
}
|
||||
|
||||
NotifyEntriesSaveComplete(c);
|
||||
OnSaveCompleted();
|
||||
_JustSaved = true;
|
||||
}
|
||||
|
||||
// workitem 5043
|
||||
finally
|
||||
{
|
||||
CleanupAfterSaveOperation();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void NotifyEntriesSaveComplete(ICollection<ZipEntry> c)
|
||||
{
|
||||
foreach (ZipEntry e in c)
|
||||
{
|
||||
e.NotifySaveComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void RemoveTempFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_temporaryFileName))
|
||||
{
|
||||
File.Delete(_temporaryFileName);
|
||||
}
|
||||
}
|
||||
catch (IOException ex1)
|
||||
{
|
||||
if (Verbose)
|
||||
StatusMessageTextWriter.WriteLine("ZipFile::Save: could not delete temp file: {0}.", ex1.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CleanupAfterSaveOperation()
|
||||
{
|
||||
if (_name != null)
|
||||
{
|
||||
// close the stream if there is a file behind it.
|
||||
if (_writestream != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// workitem 7704
|
||||
#if NETCF
|
||||
_writestream.Close();
|
||||
#else
|
||||
_writestream.Dispose();
|
||||
#endif
|
||||
}
|
||||
catch (System.IO.IOException) { }
|
||||
}
|
||||
_writestream = null;
|
||||
|
||||
if (_temporaryFileName != null)
|
||||
{
|
||||
RemoveTempFile();
|
||||
_temporaryFileName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save the file to a new zipfile, with the given name.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method allows the application to explicitly specify the name of the zip
|
||||
/// file when saving. Use this when creating a new zip file, or when
|
||||
/// updating a zip archive.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// An application can also save a zip archive in several places by calling this
|
||||
/// method multiple times in succession, with different filenames.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The <c>ZipFile</c> instance is written to storage, typically a zip file in a
|
||||
/// filesystem, only when the caller calls <c>Save</c>. The Save operation writes
|
||||
/// the zip content to a temporary file, and then renames the temporary file
|
||||
/// to the desired name. If necessary, this method will delete a pre-existing file
|
||||
/// before the rename.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// Thrown if you specify a directory for the filename.
|
||||
/// </exception>
|
||||
///
|
||||
/// <param name="fileName">
|
||||
/// The name of the zip archive to save to. Existing files will
|
||||
/// be overwritten with great prejudice.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
/// This example shows how to create and Save a zip file.
|
||||
/// <code>
|
||||
/// using (ZipFile zip = new ZipFile())
|
||||
/// {
|
||||
/// zip.AddDirectory(@"c:\reports\January");
|
||||
/// zip.Save("January.zip");
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using zip As New ZipFile()
|
||||
/// zip.AddDirectory("c:\reports\January")
|
||||
/// zip.Save("January.zip")
|
||||
/// End Using
|
||||
/// </code>
|
||||
///
|
||||
/// </example>
|
||||
///
|
||||
/// <example>
|
||||
/// This example shows how to update a zip file.
|
||||
/// <code>
|
||||
/// using (ZipFile zip = ZipFile.Read("ExistingArchive.zip"))
|
||||
/// {
|
||||
/// zip.AddFile("NewData.csv");
|
||||
/// zip.Save("UpdatedArchive.zip");
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Using zip As ZipFile = ZipFile.Read("ExistingArchive.zip")
|
||||
/// zip.AddFile("NewData.csv")
|
||||
/// zip.Save("UpdatedArchive.zip")
|
||||
/// End Using
|
||||
/// </code>
|
||||
///
|
||||
/// </example>
|
||||
public void Save(String fileName)
|
||||
{
|
||||
// Check for the case where we are re-saving a zip archive
|
||||
// that was originally instantiated with a stream. In that case,
|
||||
// the _name will be null. If so, we set _writestream to null,
|
||||
// which insures that we'll cons up a new WriteStream (with a filesystem
|
||||
// file backing it) in the Save() method.
|
||||
if (_name == null)
|
||||
_writestream = null;
|
||||
|
||||
else _readName = _name; // workitem 13915
|
||||
|
||||
_name = fileName;
|
||||
if (Directory.Exists(_name))
|
||||
throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "fileName"));
|
||||
_contentsChanged = true;
|
||||
_fileAlreadyExists = File.Exists(_name);
|
||||
Save();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save the zip archive to the specified stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <c>ZipFile</c> instance is written to storage - typically a zip file
|
||||
/// in a filesystem, but using this overload, the storage can be anything
|
||||
/// accessible via a writable stream - only when the caller calls <c>Save</c>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Use this method to save the zip content to a stream directly. A common
|
||||
/// scenario is an ASP.NET application that dynamically generates a zip file
|
||||
/// and allows the browser to download it. The application can call
|
||||
/// <c>Save(Response.OutputStream)</c> to write a zipfile directly to the
|
||||
/// output stream, without creating a zip file on the disk on the ASP.NET
|
||||
/// server.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Be careful when saving a file to a non-seekable stream, including
|
||||
/// <c>Response.OutputStream</c>. When DotNetZip writes to a non-seekable
|
||||
/// stream, the zip archive is formatted in such a way that may not be
|
||||
/// compatible with all zip tools on all platforms. It's a perfectly legal
|
||||
/// and compliant zip file, but some people have reported problems opening
|
||||
/// files produced this way using the Mac OS archive utility.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example saves the zipfile content into a MemoryStream, and
|
||||
/// then gets the array of bytes from that MemoryStream.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// using (var zip = new Ionic.Zip.ZipFile())
|
||||
/// {
|
||||
/// zip.CompressionLevel= Ionic.Zlib.CompressionLevel.BestCompression;
|
||||
/// zip.Password = "VerySecret.";
|
||||
/// zip.Encryption = EncryptionAlgorithm.WinZipAes128;
|
||||
/// zip.AddFile(sourceFileName);
|
||||
/// MemoryStream output = new MemoryStream();
|
||||
/// zip.Save(output);
|
||||
///
|
||||
/// byte[] zipbytes = output.ToArray();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <example>
|
||||
/// <para>
|
||||
/// This example shows a pitfall you should avoid. DO NOT read
|
||||
/// from a stream, then try to save to the same stream. DO
|
||||
/// NOT DO THIS:
|
||||
/// </para>
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// using (var fs = new FileSteeam(filename, FileMode.Open))
|
||||
/// {
|
||||
/// using (var zip = Ionic.Zip.ZipFile.Read(inputStream))
|
||||
/// {
|
||||
/// zip.AddEntry("Name1.txt", "this is the content");
|
||||
/// zip.Save(inputStream); // NO NO NO!!
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para>
|
||||
/// Better like this:
|
||||
/// </para>
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// using (var zip = Ionic.Zip.ZipFile.Read(filename))
|
||||
/// {
|
||||
/// zip.AddEntry("Name1.txt", "this is the content");
|
||||
/// zip.Save(); // YES!
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// </example>
|
||||
///
|
||||
/// <param name="outputStream">
|
||||
/// The <c>System.IO.Stream</c> to write to. It must be
|
||||
/// writable. If you created the ZipFile instanct by calling
|
||||
/// ZipFile.Read(), this stream must not be the same stream
|
||||
/// you passed to ZipFile.Read().
|
||||
/// </param>
|
||||
public void Save(Stream outputStream)
|
||||
{
|
||||
if (outputStream == null)
|
||||
throw new ArgumentNullException("outputStream");
|
||||
if (!outputStream.CanWrite)
|
||||
throw new ArgumentException("Must be a writable stream.", "outputStream");
|
||||
|
||||
// if we had a filename to save to, we are now obliterating it.
|
||||
_name = null;
|
||||
|
||||
_writestream = new CountingStream(outputStream);
|
||||
|
||||
_contentsChanged = true;
|
||||
_fileAlreadyExists = false;
|
||||
Save();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static class ZipOutput
|
||||
{
|
||||
public static bool WriteCentralDirectoryStructure(Stream s,
|
||||
ICollection<ZipEntry> entries,
|
||||
uint numSegments,
|
||||
Zip64Option zip64,
|
||||
String comment,
|
||||
ZipContainer container)
|
||||
{
|
||||
var zss = s as ZipSegmentedStream;
|
||||
if (zss != null)
|
||||
zss.ContiguousWrite = true;
|
||||
|
||||
// write to a memory stream in order to keep the
|
||||
// CDR contiguous
|
||||
Int64 aLength = 0;
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
foreach (ZipEntry e in entries)
|
||||
{
|
||||
if (e.IncludedInMostRecentSave)
|
||||
{
|
||||
// this writes a ZipDirEntry corresponding to the ZipEntry
|
||||
e.WriteCentralDirectoryEntry(ms);
|
||||
}
|
||||
}
|
||||
var a = ms.ToArray();
|
||||
s.Write(a, 0, a.Length);
|
||||
aLength = a.Length;
|
||||
}
|
||||
|
||||
|
||||
// We need to keep track of the start and
|
||||
// Finish of the Central Directory Structure.
|
||||
|
||||
// Cannot always use WriteStream.Length or Position; some streams do
|
||||
// not support these. (eg, ASP.NET Response.OutputStream) In those
|
||||
// cases we have a CountingStream.
|
||||
|
||||
// Also, we cannot just set Start as s.Position bfore the write, and Finish
|
||||
// as s.Position after the write. In a split zip, the write may actually
|
||||
// flip to the next segment. In that case, Start will be zero. But we
|
||||
// don't know that til after we know the size of the thing to write. So the
|
||||
// answer is to compute the directory, then ask the ZipSegmentedStream which
|
||||
// segment that directory would fall in, it it were written. Then, include
|
||||
// that data into the directory, and finally, write the directory to the
|
||||
// output stream.
|
||||
|
||||
var output = s as CountingStream;
|
||||
long Finish = (output != null) ? output.ComputedPosition : s.Position; // BytesWritten
|
||||
long Start = Finish - aLength;
|
||||
|
||||
// need to know which segment the EOCD record starts in
|
||||
UInt32 startSegment = (zss != null)
|
||||
? zss.CurrentSegment
|
||||
: 0;
|
||||
|
||||
Int64 SizeOfCentralDirectory = Finish - Start;
|
||||
|
||||
int countOfEntries = CountEntries(entries);
|
||||
|
||||
bool needZip64CentralDirectory =
|
||||
zip64 == Zip64Option.Always ||
|
||||
countOfEntries >= 0xFFFF ||
|
||||
SizeOfCentralDirectory > 0xFFFFFFFF ||
|
||||
Start > 0xFFFFFFFF;
|
||||
|
||||
byte[] a2 = null;
|
||||
|
||||
// emit ZIP64 extensions as required
|
||||
if (needZip64CentralDirectory)
|
||||
{
|
||||
if (zip64 == Zip64Option.Never)
|
||||
{
|
||||
#if NETCF
|
||||
throw new ZipException("The archive requires a ZIP64 Central Directory. Consider enabling ZIP64 extensions.");
|
||||
#else
|
||||
System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame(1);
|
||||
if (sf.GetMethod().DeclaringType == typeof(ZipFile))
|
||||
throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipFile.UseZip64WhenSaving property.");
|
||||
else
|
||||
throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipOutputStream.EnableZip64 property.");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
var a = GenZip64EndOfCentralDirectory(Start, Finish, countOfEntries, numSegments);
|
||||
a2 = GenCentralDirectoryFooter(Start, Finish, zip64, countOfEntries, comment, container);
|
||||
if (startSegment != 0)
|
||||
{
|
||||
UInt32 thisSegment = zss.ComputeSegment(a.Length + a2.Length);
|
||||
int i = 16;
|
||||
// number of this disk
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
i += 4;
|
||||
// number of the disk with the start of the central directory
|
||||
//Array.Copy(BitConverter.GetBytes(startSegment), 0, a, i, 4);
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
|
||||
i = 60;
|
||||
// offset 60
|
||||
// number of the disk with the start of the zip64 eocd
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
i += 4;
|
||||
i += 8;
|
||||
|
||||
// offset 72
|
||||
// total number of disks
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4);
|
||||
}
|
||||
s.Write(a, 0, a.Length);
|
||||
}
|
||||
else
|
||||
a2 = GenCentralDirectoryFooter(Start, Finish, zip64, countOfEntries, comment, container);
|
||||
|
||||
|
||||
// now, the regular footer
|
||||
if (startSegment != 0)
|
||||
{
|
||||
// The assumption is the central directory is never split across
|
||||
// segment boundaries.
|
||||
|
||||
UInt16 thisSegment = (UInt16) zss.ComputeSegment(a2.Length);
|
||||
int i = 4;
|
||||
// number of this disk
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a2, i, 2);
|
||||
i += 2;
|
||||
// number of the disk with the start of the central directory
|
||||
//Array.Copy(BitConverter.GetBytes((UInt16)startSegment), 0, a2, i, 2);
|
||||
Array.Copy(BitConverter.GetBytes(thisSegment), 0, a2, i, 2);
|
||||
i += 2;
|
||||
}
|
||||
|
||||
s.Write(a2, 0, a2.Length);
|
||||
|
||||
// reset the contiguous write property if necessary
|
||||
if (zss != null)
|
||||
zss.ContiguousWrite = false;
|
||||
|
||||
return needZip64CentralDirectory;
|
||||
}
|
||||
|
||||
|
||||
private static System.Text.Encoding GetEncoding(ZipContainer container, string t)
|
||||
{
|
||||
switch (container.AlternateEncodingUsage)
|
||||
{
|
||||
case ZipOption.Always:
|
||||
return container.AlternateEncoding;
|
||||
case ZipOption.Never:
|
||||
return container.DefaultEncoding;
|
||||
}
|
||||
|
||||
// AsNecessary is in force
|
||||
var e = container.DefaultEncoding;
|
||||
if (t == null) return e;
|
||||
|
||||
var bytes = e.GetBytes(t);
|
||||
var t2 = e.GetString(bytes,0,bytes.Length);
|
||||
if (t2.Equals(t)) return e;
|
||||
return container.AlternateEncoding;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static byte[] GenCentralDirectoryFooter(long StartOfCentralDirectory,
|
||||
long EndOfCentralDirectory,
|
||||
Zip64Option zip64,
|
||||
int entryCount,
|
||||
string comment,
|
||||
ZipContainer container)
|
||||
{
|
||||
System.Text.Encoding encoding = GetEncoding(container, comment);
|
||||
int j = 0;
|
||||
int bufferLength = 22;
|
||||
byte[] block = null;
|
||||
Int16 commentLength = 0;
|
||||
if ((comment != null) && (comment.Length != 0))
|
||||
{
|
||||
block = encoding.GetBytes(comment);
|
||||
commentLength = (Int16)block.Length;
|
||||
}
|
||||
bufferLength += commentLength;
|
||||
byte[] bytes = new byte[bufferLength];
|
||||
|
||||
int i = 0;
|
||||
// signature
|
||||
byte[] sig = BitConverter.GetBytes(ZipConstants.EndOfCentralDirectorySignature);
|
||||
Array.Copy(sig, 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// number of this disk
|
||||
// (this number may change later)
|
||||
bytes[i++] = 0;
|
||||
bytes[i++] = 0;
|
||||
|
||||
// number of the disk with the start of the central directory
|
||||
// (this number may change later)
|
||||
bytes[i++] = 0;
|
||||
bytes[i++] = 0;
|
||||
|
||||
// handle ZIP64 extensions for the end-of-central-directory
|
||||
if (entryCount >= 0xFFFF || zip64 == Zip64Option.Always)
|
||||
{
|
||||
// the ZIP64 version.
|
||||
for (j = 0; j < 4; j++)
|
||||
bytes[i++] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the standard version.
|
||||
// total number of entries in the central dir on this disk
|
||||
bytes[i++] = (byte)(entryCount & 0x00FF);
|
||||
bytes[i++] = (byte)((entryCount & 0xFF00) >> 8);
|
||||
|
||||
// total number of entries in the central directory
|
||||
bytes[i++] = (byte)(entryCount & 0x00FF);
|
||||
bytes[i++] = (byte)((entryCount & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
// size of the central directory
|
||||
Int64 SizeOfCentralDirectory = EndOfCentralDirectory - StartOfCentralDirectory;
|
||||
|
||||
if (SizeOfCentralDirectory >= 0xFFFFFFFF || StartOfCentralDirectory >= 0xFFFFFFFF)
|
||||
{
|
||||
// The actual data is in the ZIP64 central directory structure
|
||||
for (j = 0; j < 8; j++)
|
||||
bytes[i++] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// size of the central directory (we just get the low 4 bytes)
|
||||
bytes[i++] = (byte)(SizeOfCentralDirectory & 0x000000FF);
|
||||
bytes[i++] = (byte)((SizeOfCentralDirectory & 0x0000FF00) >> 8);
|
||||
bytes[i++] = (byte)((SizeOfCentralDirectory & 0x00FF0000) >> 16);
|
||||
bytes[i++] = (byte)((SizeOfCentralDirectory & 0xFF000000) >> 24);
|
||||
|
||||
// offset of the start of the central directory (we just get the low 4 bytes)
|
||||
bytes[i++] = (byte)(StartOfCentralDirectory & 0x000000FF);
|
||||
bytes[i++] = (byte)((StartOfCentralDirectory & 0x0000FF00) >> 8);
|
||||
bytes[i++] = (byte)((StartOfCentralDirectory & 0x00FF0000) >> 16);
|
||||
bytes[i++] = (byte)((StartOfCentralDirectory & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
|
||||
// zip archive comment
|
||||
if ((comment == null) || (comment.Length == 0))
|
||||
{
|
||||
// no comment!
|
||||
bytes[i++] = (byte)0;
|
||||
bytes[i++] = (byte)0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the size of our buffer defines the max length of the comment we can write
|
||||
if (commentLength + i + 2 > bytes.Length) commentLength = (Int16)(bytes.Length - i - 2);
|
||||
bytes[i++] = (byte)(commentLength & 0x00FF);
|
||||
bytes[i++] = (byte)((commentLength & 0xFF00) >> 8);
|
||||
|
||||
if (commentLength != 0)
|
||||
{
|
||||
// now actually write the comment itself into the byte buffer
|
||||
for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++)
|
||||
{
|
||||
bytes[i + j] = block[j];
|
||||
}
|
||||
i += j;
|
||||
}
|
||||
}
|
||||
|
||||
// s.Write(bytes, 0, i);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static byte[] GenZip64EndOfCentralDirectory(long StartOfCentralDirectory,
|
||||
long EndOfCentralDirectory,
|
||||
int entryCount,
|
||||
uint numSegments)
|
||||
{
|
||||
const int bufferLength = 12 + 44 + 20;
|
||||
|
||||
byte[] bytes = new byte[bufferLength];
|
||||
|
||||
int i = 0;
|
||||
// signature
|
||||
byte[] sig = BitConverter.GetBytes(ZipConstants.Zip64EndOfCentralDirectoryRecordSignature);
|
||||
Array.Copy(sig, 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// There is a possibility to include "Extensible" data in the zip64
|
||||
// end-of-central-dir record. I cannot figure out what it might be used to
|
||||
// store, so the size of this record is always fixed. Maybe it is used for
|
||||
// strong encryption data? That is for another day.
|
||||
long DataSize = 44;
|
||||
Array.Copy(BitConverter.GetBytes(DataSize), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 12
|
||||
// VersionMadeBy = 45;
|
||||
bytes[i++] = 45;
|
||||
bytes[i++] = 0x00;
|
||||
|
||||
// VersionNeededToExtract = 45;
|
||||
bytes[i++] = 45;
|
||||
bytes[i++] = 0x00;
|
||||
|
||||
// offset 16
|
||||
// number of the disk, and the disk with the start of the central dir.
|
||||
// (this may change later)
|
||||
for (int j = 0; j < 8; j++)
|
||||
bytes[i++] = 0x00;
|
||||
|
||||
// offset 24
|
||||
long numberOfEntries = entryCount;
|
||||
Array.Copy(BitConverter.GetBytes(numberOfEntries), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
Array.Copy(BitConverter.GetBytes(numberOfEntries), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 40
|
||||
Int64 SizeofCentraldirectory = EndOfCentralDirectory - StartOfCentralDirectory;
|
||||
Array.Copy(BitConverter.GetBytes(SizeofCentraldirectory), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
Array.Copy(BitConverter.GetBytes(StartOfCentralDirectory), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 56
|
||||
// now, the locator
|
||||
// signature
|
||||
sig = BitConverter.GetBytes(ZipConstants.Zip64EndOfCentralDirectoryLocatorSignature);
|
||||
Array.Copy(sig, 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// offset 60
|
||||
// number of the disk with the start of the zip64 eocd
|
||||
// (this will change later) (it will?)
|
||||
uint x2 = (numSegments==0)?0:(uint)(numSegments-1);
|
||||
Array.Copy(BitConverter.GetBytes(x2), 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
// offset 64
|
||||
// relative offset of the zip64 eocd
|
||||
Array.Copy(BitConverter.GetBytes(EndOfCentralDirectory), 0, bytes, i, 8);
|
||||
i += 8;
|
||||
|
||||
// offset 72
|
||||
// total number of disks
|
||||
// (this will change later)
|
||||
Array.Copy(BitConverter.GetBytes(numSegments), 0, bytes, i, 4);
|
||||
i+=4;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static int CountEntries(ICollection<ZipEntry> _entries)
|
||||
{
|
||||
// Cannot just emit _entries.Count, because some of the entries
|
||||
// may have been skipped.
|
||||
int count = 0;
|
||||
foreach (var entry in _entries)
|
||||
if (entry.IncludedInMostRecentSave) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
3910
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.cs
Normal file
3910
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipFile.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,154 @@
|
|||
// ZipFile.x-IEnumerable.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2009-December-26 15:13:26>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines smoe methods for IEnumerable support. It is
|
||||
// particularly important for COM to have these things in a separate module.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
|
||||
// For some weird reason, the method with the DispId(-4) attribute, which is used as
|
||||
// the _NewEnum() method, and which is required to get enumeration to work from COM
|
||||
// environments like VBScript and Javascript (etc) must be the LAST MEMBER in the
|
||||
// source. In the event of Partial classes, it needs to be the last member defined
|
||||
// in the last source module. The source modules are ordered alphabetically by
|
||||
// filename. Not sure why this is true. In any case, we put the enumeration stuff
|
||||
// here in this oddly-named module, for this reason.
|
||||
//
|
||||
|
||||
|
||||
|
||||
public partial class ZipFile
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generic IEnumerator support, for use of a ZipFile in an enumeration.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// You probably do not want to call <c>GetEnumerator</c> explicitly. Instead
|
||||
/// it is implicitly called when you use a <see langword="foreach"/> loop in C#, or a
|
||||
/// <c>For Each</c> loop in VB.NET.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
/// This example reads a zipfile of a given name, then enumerates the
|
||||
/// entries in that zip file, and displays the information about each
|
||||
/// entry on the Console.
|
||||
/// <code>
|
||||
/// using (ZipFile zip = ZipFile.Read(zipfile))
|
||||
/// {
|
||||
/// bool header = true;
|
||||
/// foreach (ZipEntry e in zip)
|
||||
/// {
|
||||
/// if (header)
|
||||
/// {
|
||||
/// System.Console.WriteLine("Zipfile: {0}", zip.Name);
|
||||
/// System.Console.WriteLine("Version Needed: 0x{0:X2}", e.VersionNeeded);
|
||||
/// System.Console.WriteLine("BitField: 0x{0:X2}", e.BitField);
|
||||
/// System.Console.WriteLine("Compression Method: 0x{0:X2}", e.CompressionMethod);
|
||||
/// System.Console.WriteLine("\n{1,-22} {2,-6} {3,4} {4,-8} {0}",
|
||||
/// "Filename", "Modified", "Size", "Ratio", "Packed");
|
||||
/// System.Console.WriteLine(new System.String('-', 72));
|
||||
/// header = false;
|
||||
/// }
|
||||
///
|
||||
/// System.Console.WriteLine("{1,-22} {2,-6} {3,4:F0}% {4,-8} {0}",
|
||||
/// e.FileName,
|
||||
/// e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
/// e.UncompressedSize,
|
||||
/// e.CompressionRatio,
|
||||
/// e.CompressedSize);
|
||||
///
|
||||
/// e.Extract();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Dim ZipFileToExtract As String = "c:\foo.zip"
|
||||
/// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract)
|
||||
/// Dim header As Boolean = True
|
||||
/// Dim e As ZipEntry
|
||||
/// For Each e In zip
|
||||
/// If header Then
|
||||
/// Console.WriteLine("Zipfile: {0}", zip.Name)
|
||||
/// Console.WriteLine("Version Needed: 0x{0:X2}", e.VersionNeeded)
|
||||
/// Console.WriteLine("BitField: 0x{0:X2}", e.BitField)
|
||||
/// Console.WriteLine("Compression Method: 0x{0:X2}", e.CompressionMethod)
|
||||
/// Console.WriteLine(ChrW(10) & "{1,-22} {2,-6} {3,4} {4,-8} {0}", _
|
||||
/// "Filename", "Modified", "Size", "Ratio", "Packed" )
|
||||
/// Console.WriteLine(New String("-"c, 72))
|
||||
/// header = False
|
||||
/// End If
|
||||
/// Console.WriteLine("{1,-22} {2,-6} {3,4:F0}% {4,-8} {0}", _
|
||||
/// e.FileName, _
|
||||
/// e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), _
|
||||
/// e.UncompressedSize, _
|
||||
/// e.CompressionRatio, _
|
||||
/// e.CompressedSize )
|
||||
/// e.Extract
|
||||
/// Next
|
||||
/// End Using
|
||||
/// </code>
|
||||
/// </example>
|
||||
///
|
||||
/// <returns>A generic enumerator suitable for use within a foreach loop.</returns>
|
||||
public System.Collections.Generic.IEnumerator<ZipEntry> GetEnumerator()
|
||||
{
|
||||
foreach (ZipEntry e in _entries.Values)
|
||||
yield return e;
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An IEnumerator, for use of a ZipFile in a foreach construct.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This method is included for COM support. An application generally does not call
|
||||
/// this method directly. It is called implicitly by COM clients when enumerating
|
||||
/// the entries in the ZipFile instance. In VBScript, this is done with a <c>For Each</c>
|
||||
/// statement. In Javascript, this is done with <c>new Enumerator(zipfile)</c>.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <returns>
|
||||
/// The IEnumerator over the entries in the ZipFile.
|
||||
/// </returns>
|
||||
[System.Runtime.InteropServices.DispId(-4)]
|
||||
public System.Collections.IEnumerator GetNewEnum() // the name of this method is not significant
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,827 @@
|
|||
// ZipInputStream.cs
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2010 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-31 14:48:30>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines the ZipInputStream class, which is a stream metaphor for
|
||||
// reading zip files. This class does not depend on Ionic.Zip.ZipFile, but rather
|
||||
// stands alongside it as an alternative "container" for ZipEntry, when reading zips.
|
||||
//
|
||||
// It adds one interesting method to the normal "stream" interface: GetNextEntry.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Ionic.Zip;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a stream metaphor for reading zip files.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class provides an alternative programming model for reading zip files to
|
||||
/// the one enabled by the <see cref="ZipFile"/> class. Use this when reading zip
|
||||
/// files, as an alternative to the <see cref="ZipFile"/> class, when you would
|
||||
/// like to use a Stream class to read the file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Some application designs require a readable stream for input. This stream can
|
||||
/// be used to read a zip file, and extract entries.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Both the <c>ZipInputStream</c> class and the <c>ZipFile</c> class can be used
|
||||
/// to read and extract zip files. Both of them support many of the common zip
|
||||
/// features, including Unicode, different compression levels, and ZIP64. The
|
||||
/// programming models differ. For example, when extracting entries via calls to
|
||||
/// the <c>GetNextEntry()</c> and <c>Read()</c> methods on the
|
||||
/// <c>ZipInputStream</c> class, the caller is responsible for creating the file,
|
||||
/// writing the bytes into the file, setting the attributes on the file, and
|
||||
/// setting the created, last modified, and last accessed timestamps on the
|
||||
/// file. All of these things are done automatically by a call to <see
|
||||
/// cref="ZipEntry.Extract()">ZipEntry.Extract()</see>. For this reason, the
|
||||
/// <c>ZipInputStream</c> is generally recommended for when your application wants
|
||||
/// to extract the data, without storing that data into a file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Aside from the obvious differences in programming model, there are some
|
||||
/// differences in capability between the <c>ZipFile</c> class and the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
/// </para>
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <c>ZipFile</c> can be used to create or update zip files, or read and
|
||||
/// extract zip files. <c>ZipInputStream</c> can be used only to read and
|
||||
/// extract zip files. If you want to use a stream to create zip files, check
|
||||
/// out the <see cref="ZipOutputStream"/>.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> cannot read segmented or spanned
|
||||
/// zip files.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> will not read Zip file comments.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// When reading larger files, <c>ZipInputStream</c> will always underperform
|
||||
/// <c>ZipFile</c>. This is because the <c>ZipInputStream</c> does a full scan on the
|
||||
/// zip file, while the <c>ZipFile</c> class reads the central directory of the
|
||||
/// zip file.
|
||||
/// </item>
|
||||
///
|
||||
/// </list>
|
||||
///
|
||||
/// </remarks>
|
||||
public class ZipInputStream : Stream
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a <c>ZipInputStream</c>, wrapping it around an existing stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// While the <see cref="ZipFile"/> class is generally easier
|
||||
/// to use, this class provides an alternative to those
|
||||
/// applications that want to read from a zipfile directly,
|
||||
/// using a <see cref="System.IO.Stream"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Both the <c>ZipInputStream</c> class and the <c>ZipFile</c> class can be used
|
||||
/// to read and extract zip files. Both of them support many of the common zip
|
||||
/// features, including Unicode, different compression levels, and ZIP64. The
|
||||
/// programming models differ. For example, when extracting entries via calls to
|
||||
/// the <c>GetNextEntry()</c> and <c>Read()</c> methods on the
|
||||
/// <c>ZipInputStream</c> class, the caller is responsible for creating the file,
|
||||
/// writing the bytes into the file, setting the attributes on the file, and
|
||||
/// setting the created, last modified, and last accessed timestamps on the
|
||||
/// file. All of these things are done automatically by a call to <see
|
||||
/// cref="ZipEntry.Extract()">ZipEntry.Extract()</see>. For this reason, the
|
||||
/// <c>ZipInputStream</c> is generally recommended for when your application wants
|
||||
/// to extract the data, without storing that data into a file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Aside from the obvious differences in programming model, there are some
|
||||
/// differences in capability between the <c>ZipFile</c> class and the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
/// </para>
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <c>ZipFile</c> can be used to create or update zip files, or read and extract
|
||||
/// zip files. <c>ZipInputStream</c> can be used only to read and extract zip
|
||||
/// files. If you want to use a stream to create zip files, check out the <see
|
||||
/// cref="ZipOutputStream"/>.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> cannot read segmented or spanned
|
||||
/// zip files.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// <c>ZipInputStream</c> will not read Zip file comments.
|
||||
/// </item>
|
||||
///
|
||||
/// <item>
|
||||
/// When reading larger files, <c>ZipInputStream</c> will always underperform
|
||||
/// <c>ZipFile</c>. This is because the <c>ZipInputStream</c> does a full scan on the
|
||||
/// zip file, while the <c>ZipFile</c> class reads the central directory of the
|
||||
/// zip file.
|
||||
/// </item>
|
||||
///
|
||||
/// </list>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream to read. It must be readable. This stream will be closed at
|
||||
/// the time the <c>ZipInputStream</c> is closed.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to read a zip file, and extract entries, using the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// private void Unzip()
|
||||
/// {
|
||||
/// byte[] buffer= new byte[2048];
|
||||
/// int n;
|
||||
/// using (var raw = File.Open(inputFileName, FileMode.Open, FileAccess.Read))
|
||||
/// {
|
||||
/// using (var input= new ZipInputStream(raw))
|
||||
/// {
|
||||
/// ZipEntry e;
|
||||
/// while (( e = input.GetNextEntry()) != null)
|
||||
/// {
|
||||
/// if (e.IsDirectory) continue;
|
||||
/// string outputPath = Path.Combine(extractDir, e.FileName);
|
||||
/// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
|
||||
/// {
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// output.Write(buffer,0,n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Private Sub UnZip()
|
||||
/// Dim inputFileName As String = "MyArchive.zip"
|
||||
/// Dim extractDir As String = "extract"
|
||||
/// Dim buffer As Byte() = New Byte(2048) {}
|
||||
/// Using raw As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read)
|
||||
/// Using input As ZipInputStream = New ZipInputStream(raw)
|
||||
/// Dim e As ZipEntry
|
||||
/// Do While (Not e = input.GetNextEntry Is Nothing)
|
||||
/// If Not e.IsDirectory Then
|
||||
/// Using output As FileStream = File.Open(Path.Combine(extractDir, e.FileName), _
|
||||
/// FileMode.Create, FileAccess.ReadWrite)
|
||||
/// Dim n As Integer
|
||||
/// Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
|
||||
/// output.Write(buffer, 0, n)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End If
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Using
|
||||
/// End Sub
|
||||
/// </code>
|
||||
/// </example>
|
||||
public ZipInputStream(Stream stream) : this (stream, false) { }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZipInputStream</c>, given the name of an existing zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// This constructor opens a <c>FileStream</c> for the given zipfile, and
|
||||
/// wraps a <c>ZipInputStream</c> around that. See the documentation for the
|
||||
/// <see cref="ZipInputStream(Stream)"/> constructor for full details.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// While the <see cref="ZipFile"/> class is generally easier
|
||||
/// to use, this class provides an alternative to those
|
||||
/// applications that want to read from a zipfile directly,
|
||||
/// using a <see cref="System.IO.Stream"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="fileName">
|
||||
/// The name of the filesystem file to read.
|
||||
/// </param>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example shows how to read a zip file, and extract entries, using the
|
||||
/// <c>ZipInputStream</c> class.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// private void Unzip()
|
||||
/// {
|
||||
/// byte[] buffer= new byte[2048];
|
||||
/// int n;
|
||||
/// using (var input= new ZipInputStream(inputFileName))
|
||||
/// {
|
||||
/// ZipEntry e;
|
||||
/// while (( e = input.GetNextEntry()) != null)
|
||||
/// {
|
||||
/// if (e.IsDirectory) continue;
|
||||
/// string outputPath = Path.Combine(extractDir, e.FileName);
|
||||
/// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
|
||||
/// {
|
||||
/// while ((n= input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
/// {
|
||||
/// output.Write(buffer,0,n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <code lang="VB">
|
||||
/// Private Sub UnZip()
|
||||
/// Dim inputFileName As String = "MyArchive.zip"
|
||||
/// Dim extractDir As String = "extract"
|
||||
/// Dim buffer As Byte() = New Byte(2048) {}
|
||||
/// Using input As ZipInputStream = New ZipInputStream(inputFileName)
|
||||
/// Dim e As ZipEntry
|
||||
/// Do While (Not e = input.GetNextEntry Is Nothing)
|
||||
/// If Not e.IsDirectory Then
|
||||
/// Using output As FileStream = File.Open(Path.Combine(extractDir, e.FileName), _
|
||||
/// FileMode.Create, FileAccess.ReadWrite)
|
||||
/// Dim n As Integer
|
||||
/// Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
|
||||
/// output.Write(buffer, 0, n)
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End If
|
||||
/// Loop
|
||||
/// End Using
|
||||
/// End Sub
|
||||
/// </code>
|
||||
/// </example>
|
||||
public ZipInputStream(String fileName)
|
||||
{
|
||||
Stream stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read );
|
||||
_Init(stream, false, fileName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a <c>ZipInputStream</c>, explicitly specifying whether to
|
||||
/// keep the underlying stream open.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// See the documentation for the <see
|
||||
/// cref="ZipInputStream(Stream)">ZipInputStream(Stream)</see>
|
||||
/// constructor for a discussion of the class, and an example of how to use the class.
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">
|
||||
/// The stream to read from. It must be readable.
|
||||
/// </param>
|
||||
///
|
||||
/// <param name="leaveOpen">
|
||||
/// true if the application would like the stream
|
||||
/// to remain open after the <c>ZipInputStream</c> has been closed.
|
||||
/// </param>
|
||||
public ZipInputStream(Stream stream, bool leaveOpen)
|
||||
{
|
||||
_Init(stream, leaveOpen, null);
|
||||
}
|
||||
|
||||
private void _Init(Stream stream, bool leaveOpen, string name)
|
||||
{
|
||||
_inputStream = stream;
|
||||
if (!_inputStream.CanRead)
|
||||
throw new ZipException("The stream must be readable.");
|
||||
_container= new ZipContainer(this);
|
||||
_provisionalAlternateEncoding = System.Text.Encoding.GetEncoding("IBM437");
|
||||
_leaveUnderlyingStreamOpen = leaveOpen;
|
||||
_findRequired= true;
|
||||
_name = name ?? "(stream)";
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Provides a string representation of the instance.</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This can be useful for debugging purposes.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>a string representation of the instance.</returns>
|
||||
public override String ToString()
|
||||
{
|
||||
return String.Format ("ZipInputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The text encoding to use when reading entries into the zip archive, for
|
||||
/// those entries whose filenames or comments cannot be encoded with the
|
||||
/// default (IBM437) encoding.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">its
|
||||
/// zip specification</see>, PKWare describes two options for encoding
|
||||
/// filenames and comments: using IBM437 or UTF-8. But, some archiving tools
|
||||
/// or libraries do not follow the specification, and instead encode
|
||||
/// characters using the system default code page. For example, WinRAR when
|
||||
/// run on a machine in Shanghai may encode filenames with the Big-5 Chinese
|
||||
/// (950) code page. This behavior is contrary to the Zip specification, but
|
||||
/// it occurs anyway.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When using DotNetZip to read zip archives that use something other than
|
||||
/// UTF-8 or IBM437, set this property to specify the code page to use when
|
||||
/// reading encoded filenames and comments for each <c>ZipEntry</c> in the zip
|
||||
/// file.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This property is "provisional". When the entry in the zip archive is not
|
||||
/// explicitly marked as using UTF-8, then IBM437 is used to decode filenames
|
||||
/// and comments. If a loss of data would result from using IBM436 -
|
||||
/// specifically when encoding and decoding is not reflexive - the codepage
|
||||
/// specified here is used. It is possible, therefore, to have a given entry
|
||||
/// with a <c>Comment</c> encoded in IBM437 and a <c>FileName</c> encoded with
|
||||
/// the specified "provisional" codepage.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When a zip file uses an arbitrary, non-UTF8 code page for encoding, there
|
||||
/// is no standard way for the reader application - whether DotNetZip, WinZip,
|
||||
/// WinRar, or something else - to know which codepage has been used for the
|
||||
/// entries. Readers of zip files are not able to inspect the zip file and
|
||||
/// determine the codepage that was used for the entries contained within it.
|
||||
/// It is left to the application or user to determine the necessary codepage
|
||||
/// when reading zip files encoded this way. If you use an incorrect codepage
|
||||
/// when reading a zipfile, you will get entries with filenames that are
|
||||
/// incorrect, and the incorrect filenames may even contain characters that
|
||||
/// are not legal for use within filenames in Windows. Extracting entries with
|
||||
/// illegal characters in the filenames will lead to exceptions. It's too bad,
|
||||
/// but this is just the way things are with code pages in zip files. Caveat
|
||||
/// Emptor.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
public System.Text.Encoding ProvisionalAlternateEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return _provisionalAlternateEncoding;
|
||||
}
|
||||
set
|
||||
{
|
||||
_provisionalAlternateEncoding = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Size of the work buffer to use for the ZLIB codec during decompression.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Setting this affects the performance and memory efficiency of compression
|
||||
/// and decompression. For larger files, setting this to a larger size may
|
||||
/// improve performance, but the exact numbers vary depending on available
|
||||
/// memory, and a bunch of other variables. I don't have good firm
|
||||
/// recommendations on how to set it. You'll have to test it yourself. Or
|
||||
/// just leave it alone and accept the default.
|
||||
/// </remarks>
|
||||
public int CodecBufferSize
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the password to be used on the <c>ZipInputStream</c> instance.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
///
|
||||
/// <para>
|
||||
/// When reading a zip archive, this password is used to read and decrypt the
|
||||
/// entries that are encrypted within the zip file. When entries within a zip
|
||||
/// file use different passwords, set the appropriate password for the entry
|
||||
/// before the first call to <c>Read()</c> for each entry.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// When reading an entry that is not encrypted, the value of this property is
|
||||
/// ignored.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <example>
|
||||
///
|
||||
/// This example uses the ZipInputStream to read and extract entries from a
|
||||
/// zip file, using a potentially different password for each entry.
|
||||
///
|
||||
/// <code lang="C#">
|
||||
/// byte[] buffer= new byte[2048];
|
||||
/// int n;
|
||||
/// using (var raw = File.Open(_inputFileName, FileMode.Open, FileAccess.Read ))
|
||||
/// {
|
||||
/// using (var input= new ZipInputStream(raw))
|
||||
/// {
|
||||
/// ZipEntry e;
|
||||
/// while (( e = input.GetNextEntry()) != null)
|
||||
/// {
|
||||
/// input.Password = PasswordForEntry(e.FileName);
|
||||
/// if (e.IsDirectory) continue;
|
||||
/// string outputPath = Path.Combine(_extractDir, e.FileName);
|
||||
/// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite))
|
||||
/// {
|
||||
/// while ((n= input.Read(buffer,0,buffer.Length)) > 0)
|
||||
/// {
|
||||
/// output.Write(buffer,0,n);
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
public String Password
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new System.InvalidOperationException("The stream has been closed.");
|
||||
}
|
||||
_Password = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetupStream()
|
||||
{
|
||||
// Seek to the correct posn in the file, and open a
|
||||
// stream that can be read.
|
||||
_crcStream= _currentEntry.InternalOpenReader(_Password);
|
||||
_LeftToRead = _crcStream.Length;
|
||||
_needSetup = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal Stream ReadStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return _inputStream;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read the data from the stream into the buffer.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The data for the zipentry will be decrypted and uncompressed, as
|
||||
/// necessary, before being copied into the buffer.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// You must set the <see cref="Password"/> property before calling
|
||||
/// <c>Read()</c> the first time for an encrypted entry. To determine if an
|
||||
/// entry is encrypted and requires a password, check the <see
|
||||
/// cref="ZipEntry.Encryption">ZipEntry.Encryption</see> property.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="buffer">The buffer to hold the data read from the stream.</param>
|
||||
/// <param name="offset">the offset within the buffer to copy the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes read, after decryption and decompression.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new System.InvalidOperationException("The stream has been closed.");
|
||||
}
|
||||
|
||||
if (_needSetup)
|
||||
SetupStream();
|
||||
|
||||
if (_LeftToRead == 0) return 0;
|
||||
|
||||
int len = (_LeftToRead > count) ? count : (int)_LeftToRead;
|
||||
int n = _crcStream.Read(buffer, offset, len);
|
||||
|
||||
_LeftToRead -= n;
|
||||
|
||||
if (_LeftToRead == 0)
|
||||
{
|
||||
int CrcResult = _crcStream.Crc;
|
||||
_currentEntry.VerifyCrcAfterExtract(CrcResult);
|
||||
_inputStream.Seek(_endOfEntry, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read the next entry from the zip file.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Call this method just before calling <see cref="Read(byte[], int, int)"/>,
|
||||
/// to position the pointer in the zip file to the next entry that can be
|
||||
/// read. Subsequent calls to <c>Read()</c>, will decrypt and decompress the
|
||||
/// data in the zip file, until <c>Read()</c> returns 0.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Each time you call <c>GetNextEntry()</c>, the pointer in the wrapped
|
||||
/// stream is moved to the next entry in the zip file. If you call <see
|
||||
/// cref="Seek(long, SeekOrigin)"/>, and thus re-position the pointer within
|
||||
/// the file, you will need to call <c>GetNextEntry()</c> again, to insure
|
||||
/// that the file pointer is positioned at the beginning of a zip entry.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// This method returns the <c>ZipEntry</c>. Using a stream approach, you will
|
||||
/// read the raw bytes for an entry in a zip file via calls to <c>Read()</c>.
|
||||
/// Alternatively, you can extract an entry into a file, or a stream, by
|
||||
/// calling <see cref="ZipEntry.Extract()"/>, or one of its siblings.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <returns>
|
||||
/// The <c>ZipEntry</c> read. Returns null (or Nothing in VB) if there are no more
|
||||
/// entries in the zip file.
|
||||
/// </returns>
|
||||
///
|
||||
public ZipEntry GetNextEntry()
|
||||
{
|
||||
if (_findRequired)
|
||||
{
|
||||
// find the next signature
|
||||
long d = SharedUtilities.FindSignature(_inputStream, ZipConstants.ZipEntrySignature);
|
||||
if (d == -1) return null;
|
||||
// back up 4 bytes: ReadEntry assumes the file pointer is positioned before the entry signature
|
||||
_inputStream.Seek(-4, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
}
|
||||
// workitem 10923
|
||||
else if (_firstEntry)
|
||||
{
|
||||
// we've already read one entry.
|
||||
// Seek to the end of it.
|
||||
_inputStream.Seek(_endOfEntry, SeekOrigin.Begin);
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
}
|
||||
|
||||
_currentEntry = ZipEntry.ReadEntry(_container, !_firstEntry);
|
||||
// ReadEntry leaves the file position after all the entry
|
||||
// data and the optional bit-3 data descriptpr. This is
|
||||
// where the next entry would normally start.
|
||||
_endOfEntry = _inputStream.Position;
|
||||
_firstEntry = true;
|
||||
_needSetup = true;
|
||||
_findRequired= false;
|
||||
return _currentEntry;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method disposes the ZipInputStream. It may also close the
|
||||
/// underlying stream, depending on which constructor was used.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Typically the application will call <c>Dispose()</c> implicitly, via
|
||||
/// a <c>using</c> statement in C#, or a <c>Using</c> statement in VB.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Application code won't call this code directly. This method may
|
||||
/// be invoked in two distinct scenarios. If disposing == true, the
|
||||
/// method has been called directly or indirectly by a user's code,
|
||||
/// for example via the public Dispose() method. In this case, both
|
||||
/// managed and unmanaged resources can be referenced and disposed.
|
||||
/// If disposing == false, the method has been called by the runtime
|
||||
/// from inside the object finalizer and this method should not
|
||||
/// reference other objects; in that case only unmanaged resources
|
||||
/// must be referenced or disposed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="disposing">
|
||||
/// true if the Dispose method was invoked by user code.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_closed) return;
|
||||
|
||||
if (disposing) // not called from finalizer
|
||||
{
|
||||
// When ZipInputStream is used within a using clause, and an
|
||||
// exception is thrown, Close() is invoked. But we don't want to
|
||||
// try to write anything in that case. Eventually the exception
|
||||
// will be propagated to the application.
|
||||
if (_exceptionPending) return;
|
||||
|
||||
if (!_leaveUnderlyingStreamOpen)
|
||||
{
|
||||
#if NETCF
|
||||
_inputStream.Close();
|
||||
#else
|
||||
_inputStream.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
_closed= true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Always returns true.
|
||||
/// </summary>
|
||||
public override bool CanRead { get { return true; }}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of <c>CanSeek</c> for the underlying (wrapped) stream.
|
||||
/// </summary>
|
||||
public override bool CanSeek { get { return _inputStream.CanSeek; } }
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false.
|
||||
/// </summary>
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length { get { return _inputStream.Length; }}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the underlying stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting the position is equivalent to calling <c>Seek(value, SeekOrigin.Begin)</c>.
|
||||
/// </remarks>
|
||||
public override long Position
|
||||
{
|
||||
get { return _inputStream.Position;}
|
||||
set { Seek(value, SeekOrigin.Begin); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a no-op.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException("Flush");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws a NotSupportedException.
|
||||
/// </summary>
|
||||
/// <param name="buffer">ignored</param>
|
||||
/// <param name="offset">ignored</param>
|
||||
/// <param name="count">ignored</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException("Write");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This method seeks in the underlying stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Call this method if you want to seek around within the zip file for random access.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Applications can intermix calls to <c>Seek()</c> with calls to <see
|
||||
/// cref="GetNextEntry()"/>. After a call to <c>Seek()</c>,
|
||||
/// <c>GetNextEntry()</c> will get the next <c>ZipEntry</c> that falls after
|
||||
/// the current position in the input stream. You're on your own for finding
|
||||
/// out just where to seek in the stream, to get to the various entries.
|
||||
/// </para>
|
||||
///
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="offset">the offset point to seek to</param>
|
||||
/// <param name="origin">the reference point from which to seek</param>
|
||||
/// <returns>The new position</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
_findRequired= true;
|
||||
var x = _inputStream.Seek(offset, origin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method always throws a NotSupportedException.
|
||||
/// </summary>
|
||||
/// <param name="value">ignored</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
private Stream _inputStream;
|
||||
private System.Text.Encoding _provisionalAlternateEncoding;
|
||||
private ZipEntry _currentEntry;
|
||||
private bool _firstEntry;
|
||||
private bool _needSetup;
|
||||
private ZipContainer _container;
|
||||
private Ionic.Crc.CrcCalculatorStream _crcStream;
|
||||
private Int64 _LeftToRead;
|
||||
internal String _Password;
|
||||
private Int64 _endOfEntry;
|
||||
private string _name;
|
||||
|
||||
private bool _leaveUnderlyingStreamOpen;
|
||||
private bool _closed;
|
||||
private bool _findRequired;
|
||||
private bool _exceptionPending;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
1817
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipOutputStream.cs
Normal file
1817
MinecraftClient/Protocol/Handlers/Compression/Zip/ZipOutputStream.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,571 @@
|
|||
// ZipSegmentedStream.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2009-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// last saved (in emacs):
|
||||
// Time-stamp: <2011-July-13 22:25:45>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines logic for zip streams that span disk files.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
internal class ZipSegmentedStream : System.IO.Stream
|
||||
{
|
||||
enum RwMode
|
||||
{
|
||||
None = 0,
|
||||
ReadOnly = 1,
|
||||
Write = 2,
|
||||
//Update = 3
|
||||
}
|
||||
|
||||
private RwMode rwMode;
|
||||
private bool _exceptionPending; // **see note below
|
||||
private string _baseName;
|
||||
private string _baseDir;
|
||||
//private bool _isDisposed;
|
||||
private string _currentName;
|
||||
private string _currentTempName;
|
||||
private uint _currentDiskNumber;
|
||||
private uint _maxDiskNumber;
|
||||
private int _maxSegmentSize;
|
||||
private System.IO.Stream _innerStream;
|
||||
|
||||
// **Note regarding exceptions:
|
||||
//
|
||||
// When ZipSegmentedStream is employed within a using clause,
|
||||
// which is the typical scenario, and an exception is thrown
|
||||
// within the scope of the using, Dispose() is invoked
|
||||
// implicitly before processing the initial exception. If that
|
||||
// happens, this class sets _exceptionPending to true, and then
|
||||
// within the Dispose(bool), takes special action as
|
||||
// appropriate. Need to be careful: any additional exceptions
|
||||
// will mask the original one.
|
||||
|
||||
private ZipSegmentedStream() : base()
|
||||
{
|
||||
_exceptionPending = false;
|
||||
}
|
||||
|
||||
public static ZipSegmentedStream ForReading(string name,
|
||||
uint initialDiskNumber,
|
||||
uint maxDiskNumber)
|
||||
{
|
||||
ZipSegmentedStream zss = new ZipSegmentedStream()
|
||||
{
|
||||
rwMode = RwMode.ReadOnly,
|
||||
CurrentSegment = initialDiskNumber,
|
||||
_maxDiskNumber = maxDiskNumber,
|
||||
_baseName = name,
|
||||
};
|
||||
|
||||
// Console.WriteLine("ZSS: ForReading ({0})",
|
||||
// Path.GetFileName(zss.CurrentName));
|
||||
|
||||
zss._SetReadStream();
|
||||
|
||||
return zss;
|
||||
}
|
||||
|
||||
|
||||
public static ZipSegmentedStream ForWriting(string name, int maxSegmentSize)
|
||||
{
|
||||
ZipSegmentedStream zss = new ZipSegmentedStream()
|
||||
{
|
||||
rwMode = RwMode.Write,
|
||||
CurrentSegment = 0,
|
||||
_baseName = name,
|
||||
_maxSegmentSize = maxSegmentSize,
|
||||
_baseDir = Path.GetDirectoryName(name)
|
||||
};
|
||||
|
||||
// workitem 9522
|
||||
if (zss._baseDir=="") zss._baseDir=".";
|
||||
|
||||
zss._SetWriteStream(0);
|
||||
|
||||
// Console.WriteLine("ZSS: ForWriting ({0})",
|
||||
// Path.GetFileName(zss.CurrentName));
|
||||
|
||||
return zss;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sort-of like a factory method, ForUpdate is used only when
|
||||
/// the application needs to update the zip entry metadata for
|
||||
/// a segmented zip file, when the starting segment is earlier
|
||||
/// than the ending segment, for a particular entry.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The update is always contiguous, never rolls over. As a
|
||||
/// result, this method doesn't need to return a ZSS; it can
|
||||
/// simply return a FileStream. That's why it's "sort of"
|
||||
/// like a Factory method.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Caller must Close/Dispose the stream object returned by
|
||||
/// this method.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static Stream ForUpdate(string name, uint diskNumber)
|
||||
{
|
||||
if (diskNumber >= 99)
|
||||
throw new ArgumentOutOfRangeException("diskNumber");
|
||||
|
||||
string fname =
|
||||
String.Format("{0}.z{1:D2}",
|
||||
Path.Combine(Path.GetDirectoryName(name),
|
||||
Path.GetFileNameWithoutExtension(name)),
|
||||
diskNumber + 1);
|
||||
|
||||
// Console.WriteLine("ZSS: ForUpdate ({0})",
|
||||
// Path.GetFileName(fname));
|
||||
|
||||
// This class assumes that the update will not expand the
|
||||
// size of the segment. Update is used only for an in-place
|
||||
// update of zip metadata. It never will try to write beyond
|
||||
// the end of a segment.
|
||||
|
||||
return File.Open(fname,
|
||||
FileMode.Open,
|
||||
FileAccess.ReadWrite,
|
||||
FileShare.None);
|
||||
}
|
||||
|
||||
public bool ContiguousWrite
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public UInt32 CurrentSegment
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentDiskNumber;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_currentDiskNumber = value;
|
||||
_currentName = null; // it will get updated next time referenced
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the filesystem file corresponding to the current segment.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The name is not always the name currently being used in the
|
||||
/// filesystem. When rwMode is RwMode.Write, the filesystem file has a
|
||||
/// temporary name until the stream is closed or until the next segment is
|
||||
/// started.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public String CurrentName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentName==null)
|
||||
_currentName = _NameForSegment(CurrentSegment);
|
||||
|
||||
return _currentName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String CurrentTempName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _currentTempName;
|
||||
}
|
||||
}
|
||||
|
||||
private string _NameForSegment(uint diskNumber)
|
||||
{
|
||||
if (diskNumber >= 99)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new OverflowException("The number of zip segments would exceed 99.");
|
||||
}
|
||||
|
||||
return String.Format("{0}.z{1:D2}",
|
||||
Path.Combine(Path.GetDirectoryName(_baseName),
|
||||
Path.GetFileNameWithoutExtension(_baseName)),
|
||||
diskNumber + 1);
|
||||
}
|
||||
|
||||
|
||||
// Returns the segment that WILL be current if writing
|
||||
// a block of the given length.
|
||||
// This isn't exactly true. It could roll over beyond
|
||||
// this number.
|
||||
public UInt32 ComputeSegment(int length)
|
||||
{
|
||||
if (_innerStream.Position + length > _maxSegmentSize)
|
||||
// the block will go AT LEAST into the next segment
|
||||
return CurrentSegment + 1;
|
||||
|
||||
// it will fit in the current segment
|
||||
return CurrentSegment;
|
||||
}
|
||||
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
return String.Format("{0}[{1}][{2}], pos=0x{3:X})",
|
||||
"ZipSegmentedStream", CurrentName,
|
||||
rwMode.ToString(),
|
||||
this.Position);
|
||||
}
|
||||
|
||||
|
||||
private void _SetReadStream()
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (CurrentSegment + 1 == _maxDiskNumber)
|
||||
_currentName = _baseName;
|
||||
|
||||
// Console.WriteLine("ZSS: SRS ({0})",
|
||||
// Path.GetFileName(CurrentName));
|
||||
|
||||
_innerStream = File.OpenRead(CurrentName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Read from the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to read</param>
|
||||
/// <param name="offset">the offset at which to start</param>
|
||||
/// <param name="count">the number of bytes to read</param>
|
||||
/// <returns>the number of bytes actually read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (rwMode != RwMode.ReadOnly)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException("Stream Error: Cannot Read.");
|
||||
}
|
||||
|
||||
int r = _innerStream.Read(buffer, offset, count);
|
||||
int r1 = r;
|
||||
|
||||
while (r1 != count)
|
||||
{
|
||||
if (_innerStream.Position != _innerStream.Length)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new ZipException(String.Format("Read error in file {0}", CurrentName));
|
||||
|
||||
}
|
||||
|
||||
if (CurrentSegment + 1 == _maxDiskNumber)
|
||||
return r; // no more to read
|
||||
|
||||
CurrentSegment++;
|
||||
_SetReadStream();
|
||||
offset += r1;
|
||||
count -= r1;
|
||||
r1 = _innerStream.Read(buffer, offset, count);
|
||||
r += r1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void _SetWriteStream(uint increment)
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
if (File.Exists(CurrentName))
|
||||
File.Delete(CurrentName);
|
||||
File.Move(_currentTempName, CurrentName);
|
||||
// Console.WriteLine("ZSS: SWS close ({0})",
|
||||
// Path.GetFileName(CurrentName));
|
||||
}
|
||||
|
||||
if (increment > 0)
|
||||
CurrentSegment += increment;
|
||||
|
||||
SharedUtilities.CreateAndOpenUniqueTempFile(_baseDir,
|
||||
out _innerStream,
|
||||
out _currentTempName);
|
||||
|
||||
// Console.WriteLine("ZSS: SWS open ({0})",
|
||||
// Path.GetFileName(_currentTempName));
|
||||
|
||||
if (CurrentSegment == 0)
|
||||
_innerStream.Write(BitConverter.GetBytes(ZipConstants.SplitArchiveSignature), 0, 4);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write to the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer from which to write</param>
|
||||
/// <param name="offset">the offset at which to start writing</param>
|
||||
/// <param name="count">the number of bytes to write</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException("Stream Error: Cannot Write.");
|
||||
}
|
||||
|
||||
|
||||
if (ContiguousWrite)
|
||||
{
|
||||
// enough space for a contiguous write?
|
||||
if (_innerStream.Position + count > _maxSegmentSize)
|
||||
_SetWriteStream(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (_innerStream.Position + count > _maxSegmentSize)
|
||||
{
|
||||
int c = unchecked(_maxSegmentSize - (int)_innerStream.Position);
|
||||
_innerStream.Write(buffer, offset, c);
|
||||
_SetWriteStream(1);
|
||||
count -= c;
|
||||
offset += c;
|
||||
}
|
||||
}
|
||||
|
||||
_innerStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
public long TruncateBackward(uint diskNumber, long offset)
|
||||
{
|
||||
// Console.WriteLine("***ZSS.Trunc to disk {0}", diskNumber);
|
||||
// Console.WriteLine("***ZSS.Trunc: current disk {0}", CurrentSegment);
|
||||
if (diskNumber >= 99)
|
||||
throw new ArgumentOutOfRangeException("diskNumber");
|
||||
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new ZipException("bad state.");
|
||||
}
|
||||
|
||||
// Seek back in the segmented stream to a (maybe) prior segment.
|
||||
|
||||
// Check if it is the same segment. If it is, very simple.
|
||||
if (diskNumber == CurrentSegment)
|
||||
{
|
||||
var x =_innerStream.Seek(offset, SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Seeking back to a prior segment.
|
||||
// The current segment and any intervening segments must be removed.
|
||||
// First, close the current segment, and then remove it.
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
if (File.Exists(_currentTempName))
|
||||
File.Delete(_currentTempName);
|
||||
}
|
||||
|
||||
// Now, remove intervening segments.
|
||||
for (uint j= CurrentSegment-1; j > diskNumber; j--)
|
||||
{
|
||||
string s = _NameForSegment(j);
|
||||
// Console.WriteLine("***ZSS.Trunc: removing file {0}", s);
|
||||
if (File.Exists(s))
|
||||
File.Delete(s);
|
||||
}
|
||||
|
||||
// now, open the desired segment. It must exist.
|
||||
CurrentSegment = diskNumber;
|
||||
|
||||
// get a new temp file, try 3 times:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentTempName = SharedUtilities.InternalGetTempFileName();
|
||||
// move the .z0x file back to a temp name
|
||||
File.Move(CurrentName, _currentTempName);
|
||||
break; // workitem 12403
|
||||
}
|
||||
catch(IOException)
|
||||
{
|
||||
if (i == 2) throw;
|
||||
}
|
||||
}
|
||||
|
||||
// open it
|
||||
_innerStream = new FileStream(_currentTempName, FileMode.Open);
|
||||
|
||||
var r = _innerStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rwMode == RwMode.ReadOnly &&
|
||||
(_innerStream != null) &&
|
||||
_innerStream.CanRead);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_innerStream != null) &&
|
||||
_innerStream.CanSeek;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rwMode == RwMode.Write) &&
|
||||
(_innerStream != null) &&
|
||||
_innerStream.CanWrite;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_innerStream.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _innerStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return _innerStream.Position; }
|
||||
set { _innerStream.Position = value; }
|
||||
}
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
var x = _innerStream.Seek(offset, origin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream);
|
||||
return x;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
if (rwMode != RwMode.Write)
|
||||
{
|
||||
_exceptionPending = true;
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_innerStream.SetLength(value);
|
||||
}
|
||||
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
// this gets called by Stream.Close()
|
||||
|
||||
// if (_isDisposed) return;
|
||||
// _isDisposed = true;
|
||||
//Console.WriteLine("Dispose (mode={0})\n", rwMode.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
if (_innerStream != null)
|
||||
{
|
||||
#if NETCF
|
||||
_innerStream.Close();
|
||||
#else
|
||||
_innerStream.Dispose();
|
||||
#endif
|
||||
//_innerStream = null;
|
||||
if (rwMode == RwMode.Write)
|
||||
{
|
||||
if (_exceptionPending)
|
||||
{
|
||||
// possibly could try to clean up all the
|
||||
// temp files created so far...
|
||||
}
|
||||
else
|
||||
{
|
||||
// // move the final temp file to the .zNN name
|
||||
// if (File.Exists(CurrentName))
|
||||
// File.Delete(CurrentName);
|
||||
// if (File.Exists(_currentTempName))
|
||||
// File.Move(_currentTempName, CurrentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -173,27 +173,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
public Guid ReadNextUUID(Queue<byte> cache)
|
||||
{
|
||||
byte[] javaUUID = ReadData(16, cache);
|
||||
Guid guid;
|
||||
Guid guid = new Guid(javaUUID);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Convert big-endian Java UUID to little-endian .NET GUID
|
||||
byte[] netGUID = new byte[16];
|
||||
for (int i = 8; i < 16; i++)
|
||||
netGUID[i] = javaUUID[i];
|
||||
netGUID[3] = javaUUID[0];
|
||||
netGUID[2] = javaUUID[1];
|
||||
netGUID[1] = javaUUID[2];
|
||||
netGUID[0] = javaUUID[3];
|
||||
netGUID[5] = javaUUID[4];
|
||||
netGUID[4] = javaUUID[5];
|
||||
netGUID[6] = javaUUID[7];
|
||||
netGUID[7] = javaUUID[6];
|
||||
guid = new Guid(netGUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
guid = new Guid(javaUUID);
|
||||
}
|
||||
guid = guid.ToLittleEndian();
|
||||
return guid;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -617,6 +617,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return 100;
|
||||
}
|
||||
|
||||
public int GetProtocolVersion()
|
||||
{
|
||||
return protocolversion;
|
||||
}
|
||||
|
||||
public bool SendChatMessage(string message)
|
||||
{
|
||||
if (String.IsNullOrEmpty(message))
|
||||
|
|
|
|||
|
|
@ -212,6 +212,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
|
||||
packetID = dataTypes.ReadNextVarInt(packetData); //Packet ID
|
||||
|
||||
if (handler.GetNetworkPacketCaptureEnabled())
|
||||
{
|
||||
List<byte> clone = packetData.ToList();
|
||||
handler.OnNetworkPacket(packetID, clone, login_phase, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1050,6 +1056,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <param name="packetData">packet Data</param>
|
||||
private void SendPacket(int packetID, IEnumerable<byte> packetData)
|
||||
{
|
||||
if (handler.GetNetworkPacketCaptureEnabled())
|
||||
{
|
||||
List<byte> clone = packetData.ToList();
|
||||
handler.OnNetworkPacket(packetID, clone, login_phase, false);
|
||||
}
|
||||
|
||||
//The inner packet
|
||||
byte[] the_packet = dataTypes.ConcatBytes(dataTypes.GetVarInt(packetID), packetData.ToArray());
|
||||
|
||||
|
|
@ -1329,6 +1341,18 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
: 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current protocol version.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Version-specific operations should be handled inside the Protocol handled whenever possible.
|
||||
/// </remarks>
|
||||
/// <returns>Minecraft Protocol version number</returns>
|
||||
public int GetProtocolVersion()
|
||||
{
|
||||
return protocolversion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message to the server
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,15 @@ namespace MinecraftClient.Protocol
|
|||
/// <returns>Max length, in characters</returns>
|
||||
int GetMaxChatMessageLength();
|
||||
|
||||
/// <summary>
|
||||
/// Get the current protocol version.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Version-specific operations should be handled inside the Protocol handled whenever possible.
|
||||
/// </remarks>
|
||||
/// <returns>Minecraft Protocol version number</returns>
|
||||
int GetProtocolVersion();
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message or command to the server
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -33,8 +33,23 @@ namespace MinecraftClient.Protocol
|
|||
bool SetInventoryEnabled(bool enabled);
|
||||
bool GetEntityHandlingEnabled();
|
||||
bool SetEntityHandlingEnabled(bool enabled);
|
||||
bool GetNetworkPacketCaptureEnabled();
|
||||
void SetNetworkPacketCaptureEnabled(bool enabled);
|
||||
int GetProtocolVersion();
|
||||
Container GetInventory(int inventoryID);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a network packet received or sent
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only called if <see cref="McClient.networkPacketEventEnabled"/> is set to True
|
||||
/// </remarks>
|
||||
/// <param name="packetID">Packet ID</param>
|
||||
/// <param name="packetData">A copy of Packet Data</param>
|
||||
/// <param name="isLogin">The packet is login phase or playing phase</param>
|
||||
/// <param name="isInbound">The packet is received from server or sent by client</param>
|
||||
void OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a server was successfully joined
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -257,6 +257,52 @@ namespace MinecraftClient.Protocol
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a network protocol version number to human-readable Minecraft version number
|
||||
/// </summary>
|
||||
/// <remarks>Some Minecraft versions share the same protocol number. In that case, the lowest version for that protocol is returned.</remarks>
|
||||
/// <param name="protocol">The Minecraft protocol version number</param>
|
||||
/// <returns>The 1.X.X version number, or 0.0 if could not determine protocol version</returns>
|
||||
public static string ProtocolVersion2MCVer(int protocol)
|
||||
{
|
||||
switch (protocol)
|
||||
{
|
||||
case 51: return "1.4.6";
|
||||
case 60: return "1.5.1";
|
||||
case 62: return "1.5.2";
|
||||
case 72: return "1.6";
|
||||
case 73: return "1.6.1";
|
||||
case 4: return "1.7.2";
|
||||
case 5: return "1.7.6";
|
||||
case 47: return "1.8";
|
||||
case 107: return "1.9";
|
||||
case 108: return "1.9.1";
|
||||
case 109: return "1.9.2";
|
||||
case 110: return "1.9.3";
|
||||
case 210: return "1.10";
|
||||
case 315: return "1.11";
|
||||
case 316: return "1.11.1";
|
||||
case 335: return "1.12";
|
||||
case 338: return "1.12.1";
|
||||
case 340: return "1.12.2";
|
||||
case 393: return "1.13";
|
||||
case 401: return "1.13.1";
|
||||
case 404: return "1.13.2";
|
||||
case 477: return "1.14";
|
||||
case 480: return "1.14.1";
|
||||
case 485: return "1.14.2";
|
||||
case 490: return "1.14.3";
|
||||
case 498: return "1.14.4";
|
||||
case 573: return "1.15";
|
||||
case 575: return "1.15.1";
|
||||
case 578: return "1.15.2";
|
||||
case 735: return "1.16";
|
||||
case 736: return "1.16.1";
|
||||
case 751: return "1.16.2";
|
||||
default: return "0.0";
|
||||
}
|
||||
}
|
||||
|
||||
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, InvalidResponse, NullError };
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
497
MinecraftClient/Protocol/ReplayHandler.cs
Normal file
497
MinecraftClient/Protocol/ReplayHandler.cs
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using MinecraftClient.Protocol.Handlers;
|
||||
using System.Runtime.InteropServices;
|
||||
using Ionic.Zip;
|
||||
using MinecraftClient.Mapping;
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
using MinecraftClient.Protocol.Handlers.PacketPalettes;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Record and save replay file that can be used by Replay mod
|
||||
/// </summary>
|
||||
public class ReplayHandler
|
||||
{
|
||||
public string ReplayFileName = @"whhhh.mcpr";
|
||||
public string ReplayFileDirectory = @"replay_recordings";
|
||||
public MetaDataHandler MetaData;
|
||||
public bool RecordRunning { get { return !cleanedUp; } }
|
||||
|
||||
private readonly string recordingTmpFileName = @"recording.tmcpr";
|
||||
private readonly string temporaryCache = @"recording_cache";
|
||||
private DataTypes dataTypes;
|
||||
private PacketTypePalette packetType;
|
||||
private int protocolVersion;
|
||||
private BinaryWriter recordStream;
|
||||
private DateTime recordStartTime;
|
||||
private DateTime lastPacketTime;
|
||||
private bool cleanedUp = false;
|
||||
|
||||
private static bool logOutput = true;
|
||||
|
||||
private int playerEntityID;
|
||||
private Guid playerUUID;
|
||||
private Location playerLastPosition;
|
||||
private float playerLastYaw;
|
||||
private float playerLastPitch;
|
||||
|
||||
public ReplayHandler(int protocolVersion)
|
||||
{
|
||||
Initialize(protocolVersion);
|
||||
}
|
||||
|
||||
public ReplayHandler(int protocolVersion, string serverName, string recordingDirectory = @"replay_recordings")
|
||||
{
|
||||
Initialize(protocolVersion);
|
||||
this.MetaData.serverName = serverName;
|
||||
ReplayFileDirectory = recordingDirectory;
|
||||
}
|
||||
|
||||
private void Initialize(int protocolVersion)
|
||||
{
|
||||
this.dataTypes = new DataTypes(protocolVersion);
|
||||
this.packetType = new PacketTypeHandler().GetTypeHandler(protocolVersion);
|
||||
this.protocolVersion = protocolVersion;
|
||||
|
||||
if (!Directory.Exists(ReplayFileDirectory))
|
||||
Directory.CreateDirectory(ReplayFileDirectory);
|
||||
if (!Directory.Exists(temporaryCache))
|
||||
Directory.CreateDirectory(temporaryCache);
|
||||
|
||||
recordStream = new BinaryWriter(new FileStream(Path.Combine(temporaryCache, recordingTmpFileName), FileMode.Create, FileAccess.ReadWrite));
|
||||
recordStartTime = DateTime.Now;
|
||||
|
||||
MetaData = new MetaDataHandler();
|
||||
MetaData.date = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||
MetaData.protocol = protocolVersion;
|
||||
MetaData.mcversion = ProtocolHandler.ProtocolVersion2MCVer(protocolVersion);
|
||||
MetaData.SaveToFile();
|
||||
|
||||
playerLastPosition = new Location(0, 0, 0);
|
||||
WriteLog("Start recording.");
|
||||
}
|
||||
|
||||
~ReplayHandler()
|
||||
{
|
||||
OnShutDown();
|
||||
}
|
||||
|
||||
public void SetClientEntityID(int entityID)
|
||||
{
|
||||
playerEntityID = entityID;
|
||||
}
|
||||
|
||||
public void SetClientPlayerUUID(Guid uuid)
|
||||
{
|
||||
playerUUID = uuid;
|
||||
}
|
||||
|
||||
#region File and stream handling
|
||||
|
||||
public void CloseRecordStream()
|
||||
{
|
||||
try
|
||||
{
|
||||
recordStream.Flush();
|
||||
recordStream.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop recording and save replay file. Should called once before program exit
|
||||
/// </summary>
|
||||
public void OnShutDown()
|
||||
{
|
||||
if (!cleanedUp)
|
||||
{
|
||||
MetaData.duration = Convert.ToInt32((lastPacketTime - recordStartTime).TotalMilliseconds);
|
||||
MetaData.SaveToFile();
|
||||
CloseRecordStream();
|
||||
CreateReplayFile();
|
||||
cleanedUp = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the replay file for Replay mod to read
|
||||
/// </summary>
|
||||
public void CreateReplayFile()
|
||||
{
|
||||
string replayFileName = GetReplayDefaultName();
|
||||
CreateReplayFile(replayFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the replay file for Replay mod to read
|
||||
/// </summary>
|
||||
/// <param name="replayFileName">Replay file name</param>
|
||||
public void CreateReplayFile(string replayFileName)
|
||||
{
|
||||
WriteLog("Creating replay file.");
|
||||
|
||||
using (Stream recordingFile = new FileStream(Path.Combine(temporaryCache, recordingTmpFileName), FileMode.Open))
|
||||
{
|
||||
using (Stream metaDataFile = new FileStream(Path.Combine(temporaryCache, MetaData.MetaDataFileName), FileMode.Open))
|
||||
{
|
||||
using (ZipOutputStream zs = new ZipOutputStream(Path.Combine(ReplayFileDirectory, replayFileName)))
|
||||
{
|
||||
zs.PutNextEntry(recordingTmpFileName);
|
||||
recordingFile.CopyTo(zs);
|
||||
zs.PutNextEntry(MetaData.MetaDataFileName);
|
||||
metaDataFile.CopyTo(zs);
|
||||
zs.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(Path.Combine(temporaryCache, recordingTmpFileName));
|
||||
File.Delete(Path.Combine(temporaryCache, MetaData.MetaDataFileName));
|
||||
|
||||
WriteLog("Replay file created.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a backup replay file while recording
|
||||
/// </summary>
|
||||
/// <param name="replayFileName"></param>
|
||||
public void CreateBackupReplay(string replayFileName)
|
||||
{
|
||||
if (cleanedUp)
|
||||
return;
|
||||
WriteDebugLog("Creating backup replay file.");
|
||||
|
||||
MetaData.duration = Convert.ToInt32((lastPacketTime - recordStartTime).TotalMilliseconds);
|
||||
MetaData.SaveToFile();
|
||||
|
||||
using (Stream metaDataFile = new FileStream(Path.Combine(temporaryCache, MetaData.MetaDataFileName), FileMode.Open))
|
||||
{
|
||||
using (ZipOutputStream zs = new ZipOutputStream(replayFileName))
|
||||
{
|
||||
zs.PutNextEntry(recordingTmpFileName);
|
||||
// .CopyTo() method start from stream current position
|
||||
// We need to reset position in order to get full content
|
||||
var lastPosition = recordStream.BaseStream.Position;
|
||||
recordStream.BaseStream.Position = 0;
|
||||
recordStream.BaseStream.CopyTo(zs);
|
||||
recordStream.BaseStream.Position = lastPosition;
|
||||
|
||||
zs.PutNextEntry(MetaData.MetaDataFileName);
|
||||
metaDataFile.CopyTo(zs);
|
||||
zs.Close();
|
||||
}
|
||||
}
|
||||
|
||||
WriteDebugLog("Backup replay file created.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default mcpr file name by current time
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetReplayDefaultName()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
return string.Format("{0}_{1}_{2}_{3}_{4}_{5}.mcpr", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); // yyyy_mm_dd_hh_mm_ss
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Packet related method
|
||||
|
||||
/// <summary>
|
||||
/// Add a packet from network
|
||||
/// </summary>
|
||||
/// <param name="packetID"></param>
|
||||
/// <param name="packetData"></param>
|
||||
/// <param name="isLogin"></param>
|
||||
/// <param name="isInbound"></param>
|
||||
public void AddPacket(int packetID, IEnumerable<byte> packetData, bool isLogin, bool isInbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (isInbound)
|
||||
HandleInBoundPacket(packetID, packetData, isLogin);
|
||||
else return;
|
||||
|
||||
if (PacketShouldSave(packetID, isLogin, isInbound))
|
||||
AddPacket(packetID, packetData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
WriteDebugLog("Exception while adding packet: " + e.Message + "\n" + e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add packet directly without checking (internal use only)
|
||||
/// </summary>
|
||||
/// <param name="packetID"></param>
|
||||
/// <param name="packetData"></param>
|
||||
private void AddPacket(int packetID, IEnumerable<byte> packetData)
|
||||
{
|
||||
lastPacketTime = DateTime.Now;
|
||||
// build raw packet
|
||||
// format: packetID + packetData
|
||||
List<byte> rawPacket = new List<byte>();
|
||||
rawPacket.AddRange(dataTypes.GetVarInt(packetID).ToArray());
|
||||
rawPacket.AddRange(packetData.ToArray());
|
||||
// build format
|
||||
// format: timestamp + packetLength + RawPacket
|
||||
List<byte> line = new List<byte>();
|
||||
int nowTime = Convert.ToInt32((lastPacketTime - recordStartTime).TotalMilliseconds);
|
||||
line.AddRange(BitConverter.GetBytes((Int32)nowTime).Reverse().ToArray());
|
||||
line.AddRange(BitConverter.GetBytes((Int32)rawPacket.Count).Reverse().ToArray());
|
||||
line.AddRange(rawPacket.ToArray());
|
||||
// Write out to the file
|
||||
recordStream.Write(line.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a player's UUID to the MetaData
|
||||
/// </summary>
|
||||
/// <param name="uuid"></param>
|
||||
/// <param name="name"></param>
|
||||
public void OnPlayerSpawn(Guid uuid)
|
||||
{
|
||||
// Metadata has a field for storing uuid for all players entered client render range
|
||||
MetaData.AddPlayerUUID(uuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine a packet should be saved
|
||||
/// </summary>
|
||||
/// <param name="packetID"></param>
|
||||
/// <param name="isLogin"></param>
|
||||
/// <param name="isInbound"></param>
|
||||
/// <returns></returns>
|
||||
private bool PacketShouldSave(int packetID, bool isLogin, bool isInbound)
|
||||
{
|
||||
if (!isInbound) // save inbound only
|
||||
return false;
|
||||
if (!isLogin) // save all play state packet
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{ // is login
|
||||
if (packetID == 0x02) // login success
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to gather information needed
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Also for converting client side packet to server side packet
|
||||
/// </remarks>
|
||||
/// <param name="packetID"></param>
|
||||
/// <param name="packetData"></param>
|
||||
/// <param name="isLogin"></param>
|
||||
private void HandleInBoundPacket(int packetID, IEnumerable<byte> packetData, bool isLogin)
|
||||
{
|
||||
Queue<byte> p = new Queue<byte>(packetData);
|
||||
PacketTypesIn pType = packetType.GetIncommingTypeById(packetID);
|
||||
// Login success. Get player UUID
|
||||
if (isLogin && packetID == 0x02)
|
||||
{
|
||||
Guid uuid;
|
||||
if (protocolVersion < Protocol18Handler.MC116Version)
|
||||
{
|
||||
if (Guid.TryParse(dataTypes.ReadNextString(p), out uuid))
|
||||
{
|
||||
SetClientPlayerUUID(uuid);
|
||||
WriteDebugLog("User UUID: " + uuid.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var uuid2 = dataTypes.ReadNextUUID(p);
|
||||
SetClientPlayerUUID(uuid2);
|
||||
WriteDebugLog("User UUID: " + uuid2.ToString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLogin && pType == PacketTypesIn.JoinGame)
|
||||
{
|
||||
// Get client player entity ID
|
||||
SetClientEntityID(dataTypes.ReadNextInt(p));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLogin && pType == PacketTypesIn.SpawnPlayer)
|
||||
{
|
||||
dataTypes.ReadNextVarInt(p);
|
||||
OnPlayerSpawn(dataTypes.ReadNextUUID(p));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get client player location for calculating movement delta later
|
||||
if (pType == PacketTypesIn.PlayerPositionAndLook)
|
||||
{
|
||||
double x = dataTypes.ReadNextDouble(p);
|
||||
double y = dataTypes.ReadNextDouble(p);
|
||||
double z = dataTypes.ReadNextDouble(p);
|
||||
float yaw = dataTypes.ReadNextFloat(p);
|
||||
float pitch = dataTypes.ReadNextFloat(p);
|
||||
byte locMask = dataTypes.ReadNextByte(p);
|
||||
|
||||
playerLastPitch = pitch;
|
||||
playerLastYaw = yaw;
|
||||
if (protocolVersion >= Protocol18Handler.MC18Version)
|
||||
{
|
||||
playerLastPosition.X = (locMask & 1 << 0) != 0 ? playerLastPosition.X + x : x;
|
||||
playerLastPosition.Y = (locMask & 1 << 1) != 0 ? playerLastPosition.Y + y : y;
|
||||
playerLastPosition.Z = (locMask & 1 << 2) != 0 ? playerLastPosition.Z + z : z;
|
||||
}
|
||||
else
|
||||
{
|
||||
playerLastPosition.X = x;
|
||||
playerLastPosition.Y = y;
|
||||
playerLastPosition.Z = z;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle outbound packet (i.e. client player movement)
|
||||
/// </summary>
|
||||
/// <param name="packetID"></param>
|
||||
/// <param name="packetData"></param>
|
||||
/// <param name="isLogin"></param>
|
||||
private void HandleOutBoundPacket(int packetID, IEnumerable<byte> packetData, bool isLogin)
|
||||
{
|
||||
var packetType = this.packetType.GetOutgoingTypeById(packetID);
|
||||
if (packetType == PacketTypesOut.PlayerPosition
|
||||
|| packetType == PacketTypesOut.PlayerPositionAndRotation)
|
||||
{
|
||||
// translate them to incoming entitymovement packet then save them
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] GetSpawnPlayerPacket(int entityID, Guid playerUUID, Location location, double pitch, double yaw)
|
||||
{
|
||||
List<byte> packet = new List<byte>();
|
||||
packet.AddRange(dataTypes.GetVarInt(entityID));
|
||||
packet.AddRange(playerUUID.ToBigEndianBytes());
|
||||
packet.AddRange(dataTypes.GetDouble(location.X));
|
||||
packet.AddRange(dataTypes.GetDouble(location.Y));
|
||||
packet.AddRange(dataTypes.GetDouble(location.Z));
|
||||
packet.Add((byte)0);
|
||||
packet.Add((byte)0);
|
||||
return packet.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper method
|
||||
|
||||
private static void WriteLog(string t)
|
||||
{
|
||||
if (logOutput)
|
||||
ConsoleIO.WriteLogLine("[Replay] " + t);
|
||||
}
|
||||
|
||||
private static void WriteDebugLog(string t)
|
||||
{
|
||||
if (Settings.DebugMessages && logOutput)
|
||||
WriteLog(t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle MetaData used by Replay mod
|
||||
/// </summary>
|
||||
public class MetaDataHandler
|
||||
{
|
||||
public readonly string MetaDataFileName = @"metaData.json";
|
||||
public readonly string temporaryCache = @"recording_cache";
|
||||
|
||||
public bool singlePlayer = false;
|
||||
public string serverName;
|
||||
public int duration = 0; // duration of the whole replay
|
||||
public long date; // start time of the recording in unix timestamp milliseconds
|
||||
public string mcversion = "0.0"; // e.g. 1.15.2
|
||||
public string fileFormat = "MCPR";
|
||||
public int fileFormatVersion = 14; // 14 is what I found in metadata generated in 1.15.2 replay mod
|
||||
public int protocol;
|
||||
public string generator = "MCC"; // The program which generated the file (MCC have more popularity now :P)
|
||||
public int selfId = -1; // I saw -1 in medaData file generated by Replay mod. Not sure what is this for
|
||||
public List<string> players; // Array of UUIDs of all players which can be seen in the replay
|
||||
|
||||
public MetaDataHandler()
|
||||
{
|
||||
players = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a player's UUID who appeared in the replay
|
||||
/// </summary>
|
||||
/// <param name="uuid"></param>
|
||||
public void AddPlayerUUID(Guid uuid)
|
||||
{
|
||||
players.Add(uuid.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export metadata to JSON string
|
||||
/// </summary>
|
||||
/// <returns>JSON string</returns>
|
||||
public string ToJson()
|
||||
{
|
||||
return String.Concat(new[] { "{"
|
||||
, "\"singleplayer\":" , singlePlayer.ToString().ToLower() , ","
|
||||
, "\"serverName\":\"" , serverName , "\","
|
||||
, "\"duration\":" , duration.ToString() , ","
|
||||
, "\"date\":" , date.ToString() , ","
|
||||
, "\"mcversion\":\"" , mcversion , "\","
|
||||
, "\"fileFormat\":\"" , fileFormat , "\","
|
||||
, "\"fileFormatVersion\":" , fileFormatVersion.ToString() , ","
|
||||
, "\"protocol\":" , protocol.ToString() , ","
|
||||
, "\"generator\":\"" , generator , "\","
|
||||
, "\"selfId\":" , selfId.ToString() + ","
|
||||
, "\"player\":" , GetPlayersJsonArray()
|
||||
, "}"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save metadata to disk file
|
||||
/// </summary>
|
||||
public void SaveToFile()
|
||||
{
|
||||
File.WriteAllText(Path.Combine(temporaryCache, MetaDataFileName), ToJson());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get players UUID JSON array string
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string GetPlayersJsonArray()
|
||||
{
|
||||
if (players.Count == 0)
|
||||
return "[]";
|
||||
|
||||
// Place between brackets the comma-separated list of player names placed between quotes
|
||||
return String.Format("[{0}]",
|
||||
String.Join(",",
|
||||
players.Select(player => String.Format("\"{0}\"", player))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,6 +184,10 @@ namespace MinecraftClient
|
|||
public static string AutoDrop_Mode = "include";
|
||||
public static string AutoDrop_items = "";
|
||||
|
||||
// Replay Mod
|
||||
public static bool ReplayMod_Enabled = false;
|
||||
public static int ReplayMod_BackupInterval = 3000;
|
||||
|
||||
|
||||
//Custom app variables and Minecraft accounts
|
||||
private static readonly Dictionary<string, object> AppVars = new Dictionary<string, object>();
|
||||
|
|
@ -191,7 +195,7 @@ namespace MinecraftClient
|
|||
private static readonly Dictionary<string, KeyValuePair<string, ushort>> Servers = new Dictionary<string, KeyValuePair<string, ushort>>();
|
||||
|
||||
|
||||
private enum ParseMode { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer };
|
||||
private enum ParseMode { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer, ReplayMod };
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -239,6 +243,7 @@ namespace MinecraftClient
|
|||
case "autocraft": pMode = ParseMode.AutoCraft; break;
|
||||
case "mailer": pMode = ParseMode.Mailer; break;
|
||||
case "autodrop": pMode = ParseMode.AutoDrop; break;
|
||||
case "replaymod": pMode = ParseMode.ReplayMod; break;
|
||||
|
||||
default: pMode = ParseMode.Default; break;
|
||||
}
|
||||
|
|
@ -607,6 +612,13 @@ namespace MinecraftClient
|
|||
case "retentiondays": Mailer_MailRetentionDays = str2int(argValue); break;
|
||||
}
|
||||
break;
|
||||
case ParseMode.ReplayMod:
|
||||
switch (argName.ToLower())
|
||||
{
|
||||
case "enabled": ReplayMod_Enabled = str2bool(argValue); break;
|
||||
case "backupinterval": ReplayMod_BackupInterval = str2int(argValue); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -785,6 +797,12 @@ namespace MinecraftClient
|
|||
+ "items= # separate each item with comma ','\r\n"
|
||||
+ "# For the naming of the items, please see \r\n"
|
||||
+ "# https://github.com/ORelio/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs \r\n"
|
||||
+ "\r\n"
|
||||
+ "[ReplayMod]\r\n"
|
||||
+ "# Enable recording the game and replay it later using Replay Mod\r\n"
|
||||
+ "# You MUST exit the program using /quit command or the replay will NOT be saved!\r\n"
|
||||
+ "enabled=false\r\n"
|
||||
+ "backupinterval=300 # How long should replay file be auto-saved, in seconds. Use -1 for disabled\r\n"
|
||||
+ "\r\n", Encoding.UTF8);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,6 +176,12 @@ This is useful for reacting to messages eg when using the AutoRespond, Hangman g
|
|||
However, for unusual chat formats, so you may need to tinker with the ChatFormat section of the config file.
|
||||
Building regular expressions can be a bit tricky, so you might want to try them out eg on regex101.com
|
||||
|
||||
About Replay Mod feature
|
||||
------
|
||||
Replay Mod is _A Minecraft Mod to record, relive and share your experience._ You can see more at https://www.replaymod.com/
|
||||
|
||||
MCC support recording and saving your game to a file which can be used by Replay Mod. You can simply enable ReplayMod in the `.ini` setting to use this feature. The only limitation is the client player (you) will not be shown in the replay. Please be reminded that you MUST exit MCC with `/quit` command or use `/replay stop` command before closing MCC. Your replay will be lost if you force-closed MCC (i.e. Ctrl+C).
|
||||
|
||||
Using the Alerts bot
|
||||
------
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue