-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1f5b297
Showing
5 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0"?> | ||
<configuration> | ||
<startup> | ||
|
||
<supportedRuntime version="v2.0.50727" sku="Client"/></startup> | ||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
// Unzip class for .NET 3.5 | ||
// Written by Alexey Yakovlev <[email protected]> | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.IO; | ||
using System.IO.Compression; | ||
using System.Linq; | ||
|
||
namespace Internals | ||
{ | ||
public class Unzip : IDisposable | ||
{ | ||
private const int EntrySignature = 0x02014B50; | ||
|
||
private const int FileSignature = 0x04034b50; | ||
|
||
private const int DirectorySignature = 0x06054B50; | ||
|
||
private const int BufferSize = 16 * 1024; | ||
|
||
public Unzip(string fileName) | ||
: this(File.OpenRead(fileName)) | ||
{ | ||
} | ||
|
||
public Unzip(Stream stream) | ||
{ | ||
Stream = stream; | ||
Reader = new BinaryReader(Stream); | ||
} | ||
|
||
private Stream Stream { get; set; } | ||
|
||
private BinaryReader Reader { get; set; } | ||
|
||
public void Dispose() | ||
{ | ||
if (Stream != null) | ||
{ | ||
Stream.Dispose(); | ||
Stream = null; | ||
} | ||
|
||
if (Reader != null) | ||
{ | ||
Reader.Close(); | ||
Reader = null; | ||
} | ||
} | ||
|
||
public void Decompress(string fileName, Stream outputStream) | ||
{ | ||
var entry = Entries.Where(e => e.FileName == fileName).First(); | ||
Decompress(entry, outputStream); | ||
} | ||
|
||
public void Decompress(ZipEntry entry, Stream outputStream) | ||
{ | ||
// check file signature | ||
Stream.Seek(entry.FileHeaderOffset, SeekOrigin.Begin); | ||
if (Reader.ReadInt32() != FileSignature) | ||
{ | ||
throw new InvalidOperationException("File signature don't match."); | ||
} | ||
|
||
// move to file data | ||
Stream.Seek(entry.FileDataOffset, SeekOrigin.Begin); | ||
var inputStream = Stream; | ||
if (entry.Deflated) | ||
{ | ||
Console.WriteLine("entry: {0} is deflated.", entry.FileName); | ||
inputStream = new DeflateStream(Stream, CompressionMode.Decompress, true); | ||
} | ||
|
||
// allocate buffer | ||
var count = entry.FileSize; | ||
var bufferSize = Math.Min(BufferSize, entry.FileSize); | ||
var buffer = new byte[bufferSize]; | ||
|
||
while (count > 0) | ||
{ | ||
// decompress data | ||
var read = inputStream.Read(buffer, 0, bufferSize); | ||
if (read == 0) | ||
{ | ||
break; | ||
} | ||
|
||
// copy to the output stream | ||
outputStream.Write(buffer, 0, read); | ||
count -= read; | ||
} | ||
} | ||
|
||
public IEnumerable<string> FileNames | ||
{ | ||
get | ||
{ | ||
return Entries.Select(e => e.FileName).Where(f => !f.EndsWith("/")); | ||
} | ||
} | ||
|
||
private ZipEntry[] entries; | ||
|
||
public IEnumerable<ZipEntry> Entries | ||
{ | ||
get | ||
{ | ||
if (entries == null) | ||
{ | ||
entries = ReadZipEntries().ToArray(); | ||
} | ||
|
||
return entries; | ||
} | ||
} | ||
|
||
private IEnumerable<ZipEntry> ReadZipEntries() | ||
{ | ||
if (Stream.Length < 22) | ||
{ | ||
yield break; | ||
} | ||
|
||
Stream.Seek(-22, SeekOrigin.End); | ||
|
||
// find directory signature | ||
while (Reader.ReadInt32() != DirectorySignature) | ||
{ | ||
if (Stream.Position <= 5) | ||
{ | ||
yield break; | ||
} | ||
|
||
// move 1 byte back | ||
Stream.Seek(-5, SeekOrigin.Current); | ||
} | ||
|
||
// read directory properties | ||
Stream.Seek(6, SeekOrigin.Current); | ||
var entries = Reader.ReadUInt16(); | ||
var difSize = Reader.ReadInt32(); | ||
var dirOffset = Reader.ReadUInt32(); | ||
Stream.Seek(dirOffset, SeekOrigin.Begin); | ||
|
||
// read directory entries | ||
for (int i = 0; i < entries; i++) | ||
{ | ||
if (Reader.ReadInt32() != EntrySignature) | ||
{ | ||
continue; | ||
} | ||
|
||
// read file properties | ||
Reader.ReadInt32(); | ||
bool utf8 = (Reader.ReadInt16() & 0x0800) != 0; | ||
short method = Reader.ReadInt16(); | ||
int timestamp = Reader.ReadInt32(); | ||
int crc32 = Reader.ReadInt32(); | ||
int compressedSize = Reader.ReadInt32(); | ||
int fileSize = Reader.ReadInt32(); | ||
short fileNameSize = Reader.ReadInt16(); | ||
short extraSize = Reader.ReadInt16(); | ||
short commentSize = Reader.ReadInt16(); | ||
int headerOffset = Reader.ReadInt32(); | ||
Reader.ReadInt32(); | ||
int fileHeaderOffset = Reader.ReadInt32(); | ||
var fileNameBytes = Reader.ReadBytes(fileNameSize); | ||
Stream.Seek(extraSize, SeekOrigin.Current); | ||
var fileCommentBytes = Reader.ReadBytes(commentSize); | ||
var fileDataOffset = CalculateFileDataOffset(fileHeaderOffset); | ||
|
||
// decode zip file entry | ||
var encoder = utf8 ? Encoding.UTF8 : Encoding.Default; | ||
yield return new ZipEntry | ||
{ | ||
FileName = encoder.GetString(fileNameBytes), | ||
FileComment = encoder.GetString(fileCommentBytes), | ||
Crc32 = crc32, | ||
CompressedSize = compressedSize, | ||
FileSize = fileSize, | ||
FileHeaderOffset = fileHeaderOffset, | ||
FileDataOffset = fileDataOffset, | ||
Deflated = method == 8 | ||
}; | ||
} | ||
} | ||
|
||
private int CalculateFileDataOffset(int fileHeaderOffset) | ||
{ | ||
var position = Stream.Position; | ||
Stream.Seek(fileHeaderOffset + 26, SeekOrigin.Begin); | ||
var fileNameSize = Reader.ReadInt16(); | ||
var extraSize = Reader.ReadInt16(); | ||
|
||
var fileOffset = (int)Stream.Position + fileNameSize + extraSize; | ||
Stream.Seek(position, SeekOrigin.Begin); | ||
return fileOffset; | ||
} | ||
} | ||
|
||
public class ZipEntry | ||
{ | ||
public string FileName { get; set; } | ||
|
||
public string FileComment { get; set; } | ||
|
||
public int Crc32 { get; set; } | ||
|
||
public int CompressedSize { get; set; } | ||
|
||
public int FileSize { get; set; } | ||
|
||
public int FileHeaderOffset { get; set; } | ||
|
||
public int FileDataOffset { get; set; } | ||
|
||
public bool Deflated { get; set; } | ||
|
||
public bool IsDirectory { get { return FileName.EndsWith("/"); } } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProjectGuid>{219BB6A2-8DF5-4A85-BD38-EA09293FDDEE}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>Internals</RootNamespace> | ||
<AssemblyName>Unzip</AssemblyName> | ||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
<TargetFrameworkProfile>Client</TargetFrameworkProfile> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Data.DataSetExtensions" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="Unzip.cs" /> | ||
<Compile Include="UsageExample.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="App.config" /> | ||
</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. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio 2012 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unzip", "Unzip.csproj", "{219BB6A2-8DF5-4A85-BD38-EA09293FDDEE}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{219BB6A2-8DF5-4A85-BD38-EA09293FDDEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{219BB6A2-8DF5-4A85-BD38-EA09293FDDEE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{219BB6A2-8DF5-4A85-BD38-EA09293FDDEE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{219BB6A2-8DF5-4A85-BD38-EA09293FDDEE}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Unzip class usage example | ||
// Written by Alexey Yakovlev <[email protected]> | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace Internals | ||
{ | ||
struct Program | ||
{ | ||
static void Main() | ||
{ | ||
using (var unzip = new Unzip("zssample.zip")) | ||
{ | ||
Console.WriteLine("Files: {0}", string.Join(", ", unzip.FileNames.ToArray())); | ||
|
||
foreach (var entry in unzip.Entries.Where(e => !e.IsDirectory)) | ||
{ | ||
Console.WriteLine("{0}: {1} -> {2}", entry.FileName, entry.FileSize, entry.CompressedSize); | ||
|
||
var fileName = Path.Combine("Unzipped", entry.FileName); | ||
var dirName = Path.GetDirectoryName(fileName); | ||
Directory.CreateDirectory(dirName); | ||
|
||
using (var outStream = File.Create(fileName)) | ||
{ | ||
unzip.Decompress(entry.FileName, outStream); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |