diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj
index aa01ed9d1..d87dbf9f3 100644
--- a/ClassicalSharp/ClassicalSharp.csproj
+++ b/ClassicalSharp/ClassicalSharp.csproj
@@ -159,11 +159,12 @@
+
+
+
-
-
@@ -190,6 +191,7 @@
+
diff --git a/ClassicalSharp/Utils/TerrainAtlas1D.cs b/ClassicalSharp/TexturePack/TerrainAtlas1D.cs
similarity index 100%
rename from ClassicalSharp/Utils/TerrainAtlas1D.cs
rename to ClassicalSharp/TexturePack/TerrainAtlas1D.cs
diff --git a/ClassicalSharp/Utils/TerrainAtlas2D.cs b/ClassicalSharp/TexturePack/TerrainAtlas2D.cs
similarity index 100%
rename from ClassicalSharp/Utils/TerrainAtlas2D.cs
rename to ClassicalSharp/TexturePack/TerrainAtlas2D.cs
diff --git a/ClassicalSharp/TexturePack/ZipExtractor.cs b/ClassicalSharp/TexturePack/ZipExtractor.cs
new file mode 100644
index 000000000..ce2b1e20f
--- /dev/null
+++ b/ClassicalSharp/TexturePack/ZipExtractor.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+
+namespace ClassicalSharp.TexturePack {
+
+ public sealed class ZipExtractor {
+
+ static Encoding enc = Encoding.ASCII;
+ public void Extract( Stream stream ) {
+
+ BinaryReader reader = new BinaryReader( stream );
+ while( true ) {
+ uint sig = reader.ReadUInt32();
+ if( sig == 0x04034b50 ) { // local file header
+ ReadLocalFileHeader( reader );
+ } else if( sig == 0x02014b50 ) { // central directory header
+ break;
+ } else {
+ throw new NotSupportedException( "Unsupported signature: " + sig.ToString( "X8" ) );
+ }
+ }
+ }
+
+ void ReadLocalFileHeader( BinaryReader reader ) {
+ ushort versionNeeded = reader.ReadUInt16();
+ ushort flags = reader.ReadUInt16();
+ ushort compressionMethod = reader.ReadUInt16();
+ reader.ReadUInt32(); // last modified
+ reader.ReadUInt32(); // CRC 32
+ int compressedSize = reader.ReadInt32();
+ int uncompressedSize = reader.ReadInt32();
+ ushort fileNameLen = reader.ReadUInt16();
+ ushort extraFieldLen = reader.ReadUInt16();
+ string fileName = enc.GetString( reader.ReadBytes( fileNameLen ) );
+ reader.ReadBytes( extraFieldLen );
+
+ if( versionNeeded > 20 )
+ Utils.LogWarning( "May not be able to properly extract a .zip enty with a version later than 2.0" );
+
+ byte[] data = DecompressEntry( reader, compressionMethod, compressedSize, uncompressedSize );
+ if( data != null )
+ HandleZipEntry( fileName, data );
+ }
+
+ byte[] DecompressEntry( BinaryReader reader, ushort compressionMethod, int compressedSize, int uncompressedSize ) {
+ if( compressionMethod == 0 ) { // Store/Raw
+ return reader.ReadBytes( uncompressedSize );
+ } else if( compressionMethod == 8 ) { // Deflate
+ byte[] data = new byte[uncompressedSize];
+ int index = 0, read = 0;
+ DeflateStream deflater = new DeflateStream( reader.BaseStream, CompressionMode.Decompress );
+ while( index < uncompressedSize &&
+ ( read = deflater.Read( data, index, data.Length - index ) ) > 0 ) {
+ index += read;
+ }
+ return data;
+ } else {
+ Utils.LogWarning( "Unsupported .zip entry compression method: " + compressionMethod );
+ reader.ReadBytes( compressedSize );
+ return null;
+ }
+ }
+
+ void HandleZipEntry( string filename, byte[] data ) {
+ Console.WriteLine( filename );
+ }
+ }
+}