Repository: jaime-olivares/zipstorer
Branch: master
Commit: fcc839b6d6fe
Files: 16
Total size: 132.5 KB
Directory structure:
gitextract_50um_gkx/
├── .github/
│ └── workflows/
│ └── main.yml
├── .gitignore
├── .vscode/
│ ├── launch.json
│ ├── settings.json
│ └── tasks.json
├── LICENSE.md
├── README.md
├── src/
│ ├── ZipCrc32.cs
│ ├── ZipDate.cs
│ ├── ZipFileEntry.cs
│ ├── ZipStorer.cs
│ └── ZipStorer.csproj
├── test/
│ ├── Program.cs
│ ├── ZipStorerTest.csproj
│ └── lorem_ipsum.txt
└── zipstorer.sln
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/main.yml
================================================
name: CI
on:
push:
branches:
- master
paths-ignore:
- README.md
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Compile source
run: dotnet build src/*.csproj --configuration Release
test:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Mono
run: |
sudo apt update
sudo apt install -y dirmngr gnupg apt-transport-https ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
sudo apt install -y mono-complete
- name: Verify Mono Installation
run: mono --version
- name: Restore source
run: dotnet build src/*.csproj --configuration Release --framework net8.0
- name: Run unit tests
run: dotnet test test/*.csproj --logger:trx --configuration Release --framework net8.0
env:
TEST_TITLE: ZipStorer unit test
deploy:
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Prepare NuGet package
run: dotnet pack **/ZipStorer.csproj --configuration Release
- name: Publish NuGet package
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: dotnet nuget push **/*.nupkg -k $NUGET_API_KEY -s https://api.nuget.org/v3/index.json
================================================
FILE: .gitignore
================================================
.DS_Store
.suo
**/bin
**/obj
**/*.nupkg
test/sample1.zip
test/.vs/*
/.vs
*.ini
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/test/bin/Debug/net8.0/ZipStorerTest.dll",
"args": [],
"cwd": "${workspaceFolder}/test",
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"files.exclude": {
"**/bin": true,
"**/obj": true,
"**/TestResults": true
}
}
================================================
FILE: .vscode/tasks.json
================================================
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/test/ZipStorerTest.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/test/ZipStorerTest.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/test/ZipStorerTest.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
================================================
FILE: LICENSE.md
================================================
# The MIT License (MIT)
Copyright (c) 2016 Jaime Olivares
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ZipStorer
A Pure C# class for storing files in Zip format
[](https://www.nuget.org/packages/ZipStorer/)



ZipStorer is a minimalistic cross-platform .net class to create Zip files and store/retrieve files to/from it, by using the Deflate algorithm. No other compression methods supported.
## :warning: Breaking changes
- Since version 4.1, as part of a major reorganization of the source code, `ZipFileEntry` is no longer a nested class inside `ZipStorer`.
Therefore, you should not prefix it with `ZipStorer.` anymore.
## Advantages and usage
ZipStorer has the following advantages:
* No Interop calls, increments portability to Mono and other non-Windows platforms
* Async methods for storing and extracting files
* Support for Zip64 (file sizes > 4GB)
* Support for UTF8 and CP 437 Encodings
* Available as a [nuget package](https://www.nuget.org/packages/ZipStorer/)
## Using the code
The ZipStorer class is the unique one needed to create the zip file. It contains a nested structure *(ZipFileEntry)* for collecting each directory entry. The class has been declared inside the System.IO.Compression namespace.
There is no default constructor. There are two ways to construct a new ZipStorer instance, depending on specific needs: use either the *Create()* or the *Open()* static method. To create a new Zip file, use the *Create()* method like this:
````csharp
ZipStorer zip = ZipStorer.Create(filename, comment); // file-oriented version
ZipStorer zip = ZipStorer.Create(stream, comment); // stream-oriented version
````
It is required to specify the full path for the new zip file, or pass a valid stream, and optionally add a comment. For opening an existing zip file for appending, the *Open()* method is required, like the following:
````csharp
ZipStorer zip = ZipStorer.Open(filename, fileaccess); // file-oriented version
ZipStorer zip = ZipStorer.Open(stream, fileaccess); // stream-oriented version
````
Where *fileaccess* should be of type *System.IO.FileAccess* enumeration type. Also, as now ZipStorer is derived from *IDisposable* interface, the using keyword can be used to ensure proper disposing of the storage resource:
````csharp
using (ZipStorer zip = ZipStorer.Create(filename, comment))
{
// some operations with zip object
//
} // automatic close operation here
````
For adding files into an opened zip storage, there are two available methods:
````csharp
public void AddFile(ZipStorer.Compress method, string pathname, string filenameInZip, string comment);
public void AddStream(ZipStorer.Compress method, string filenameInZip, Stream source, DateTime modTime, string comment);
````
The first method allows adding an existing file to the storage. The first argument receives the compression method; it can be *Store* or *Deflate* enum values. The second argument admits the physical path name, the third one allows to change the path or file name to be stored in the Zip, and the last argument inserts a comment in the storage. Notice that the folder path in the *pathname* argument is not saved in the Zip file. Use the *filenameInZip* argument instead to specify the folder path and filename. It can be expressed with both slashes or backslashes.
The second method allows adding data from any kind of stream object derived from the *System.IO.Stream class*. Internally, the first method opens a *FileStream* and calls the second method.
Finally, it is required to close the storage with the *Close()* method. This will save the central directory information too. Alternatively, the *Dispose()* method can be used.
## Extracting stored files
For extracting a file, the zip directory shall be read first, by using the *ReadCentralDir()* method, and then the *ExtractFile()* method, like in the following minimal sample code:
````csharp
// Open an existing zip file for reading
ZipStorer zip = ZipStorer.Open(@"c:\data\sample.zip", FileAccess.Read);
// Read the central directory collection
List<ZipFileEntry> dir = zip.ReadCentralDir();
// Look for the desired file
foreach (ZipFileEntry entry in dir)
{
if (Path.GetFileName(entry.FilenameInZip) == "sample.jpg")
{
// File found, extract it
zip.ExtractFile(entry, @"c:\data\sample.jpg");
break;
}
}
zip.Close();
````
## Removal of entries
Removal of entries in a zip file is a resource-consuming task. The simplest way is to copy all non-removed files into a new zip storage. The *RemoveEntries()* static method will do this exactly and will construct the ZipStorer object again. For the sake of efficiency, *RemoveEntries()* will accept many entry references in a single call, as in the following example:
````csharp
List<ZipFileEntry> removeList = new List<ZipFileEntry>();
foreach (object sel in listBox4.SelectedItems)
{
removeList.Add((ZipFileEntry)sel);
}
ZipStorer.RemoveEntries(ref zip, removeList);
````
## File and stream usage
The current release of ZipStorer supports both files and streams for creating and opening a zip storage. Several methods are overloaded for this dual support. The advantage of file-oriented methods is simplicity, since those methods will open or create files internally. On the other hand, stream-oriented methods are more flexible by allowing to manage zip storages in streams different than files. File-oriented methods will invoke internally to equivalent stream-oriented methods. Notice that not all streams will apply, because the library requires the streams to be randomly accessed (CanSeek = true). The RemoveEntries method will work only if the zip storage is a file.
````csharp
// File-oriented methods:
public static ZipStorer Create(string filename, string comment);
public static ZipStorer Open(string filename, FileAccess access);
public ZipFileEntry AddFile(Compression method, string pathname, string filenameInZip, string comment);
public bool ExtractFile(ZipFileEntry zfe, string filename);
public static bool RemoveEntries(ref ZipStorer zip, List<zipfileentry> zfes);
// Stream-oriented methods:
public static ZipStorer Create(Stream stream, string comment, bool leaveOpen);
public static ZipStorer Open(Stream stream, FileAccess access, bool leaveOpen);
public ZipFileEntry AddStream(Compression method, string filenameInZip, Stream source, DateTime modTime, string comment);
public bool ExtractFile(ZipFileEntry zfe, Stream stream);
// Async methods
public async Task<ZipFileEntry> AddStreamAsync(Compression method, string filenameInZip, Stream source, DateTime modTime, string comment)
public async Task<bool> ExtractFileAsync(ZipFileEntry zfe, Stream stream);
````
The *leaveOpen* argument will prevent the stream to be closed after completing the generation of the zip package.
## Filename encoding
Traditionally, the ZIP format supported DOS encoding system (a.k.a. IBM Code Page 437) for filenames in header records, which is a serious limitation for using non-occidental and even some occidental characters. Since 2007, the ZIP format specification was improved to support Unicode's UTF-8 encoding system.
ZipStorer class detects UTF-8 encoding by reading the proper flag in each file's header information. For enforcing filenames to be encoded with UTF-8 system, set the *EncodeUTF8* member of ZipStorer class to true. All new filenames added will be encoded with UTF8. Notice this doesn't affect stored file contents at all. Also be aware that Windows Explorer's embedded Zip format facility does not recognize well the UTF-8 encoding system, as WinZip or WinRAR do.
## Zip64 compatibility
This library has been compatible with native macOS zip support for large files.
However, it hasn't been compatible with the native Windows zip tools and it rather required the use of third party tools like WinZip or similar.
At this moment, the support for Zip64 on Windows is in beta test. If you want to test it, look for the latest beta in nuget.
================================================
FILE: src/ZipCrc32.cs
================================================
namespace System.IO.Compression
{
/* CRC32 algorithm
The 'magic number' for the CRC is 0xdebb20e3.
The proper CRC pre and post conditioning is used, meaning that the CRC register is
pre-conditioned with all ones (a starting value of 0xffffffff) and the value is post-conditioned by
taking the one's complement of the CRC residual.
If bit 3 of the general purpose flag is set, this field is set to zero in the local header and the correct
value is put in the data descriptor and in the central directory.
*/
public static class ZipCrc32
{
private static UInt32[] CrcTable = null;
static ZipCrc32()
{
// Generate CRC32 table
CrcTable = new UInt32[256];
for (int i = 0; i < CrcTable.Length; i++)
{
UInt32 c = (UInt32)i;
for (int j = 0; j < 8; j++)
{
if ((c & 1) != 0)
c = 3988292384 ^ (c >> 1);
else
c >>= 1;
}
CrcTable[i] = c;
}
}
public static UInt32 UpdateCRC(UInt32 init, byte[] buffer, int count)
{
UInt32 crc = init;
for (uint i = 0; i < count; i++)
{
crc = CrcTable[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8);
}
return crc;
}
}
}
================================================
FILE: src/ZipDate.cs
================================================
namespace System.IO.Compression
{
public static class ZipDate
{
/* DOS Date and time:
MS-DOS date. The date is a packed value with the following format. Bits Description
0-4 Day of the month (131)
5-8 Month (1 = January, 2 = February, and so on)
9-15 Year offset from 1980 (add 1980 to get actual year)
MS-DOS time. The time is a packed value with the following format. Bits Description
0-4 Second divided by 2
5-10 Minute (059)
11-15 Hour (023 on a 24-hour clock)
*/
public static uint DateTimeToDosTime(DateTime dt)
{
return (uint)(
(dt.Second / 2) | (dt.Minute << 5) | (dt.Hour << 11) |
(dt.Day << 16) | (dt.Month << 21) | ((dt.Year - 1980) << 25));
}
public static DateTime? DosTimeToDateTime(uint dt)
{
int year = (int)(dt >> 25) + 1980;
int month = (int)(dt >> 21) & 15;
int day = (int)(dt >> 16) & 31;
int hours = (int)(dt >> 11) & 31;
int minutes = (int)(dt >> 5) & 63;
int seconds = (int)(dt & 31) * 2;
if (month == 0 || day == 0 || year >= 2107)
return DateTime.Now;
return new DateTime(year, month, day, hours, minutes, seconds);
}
}
}
================================================
FILE: src/ZipFileEntry.cs
================================================
// ZipStorer, by Jaime Olivares
// Website: http://github.com/jaime-olivares/zipstorer
namespace System.IO.Compression
{
/// <summary>
/// Represents an entry in Zip file directory
/// </summary>
public class ZipFileEntry
{
/// <summary>Compression method</summary>
public ZipStorer.Compression Method { get; set; }
/// <summary>Full path and filename as stored in Zip</summary>
public string FilenameInZip { get; set; }
/// <summary>Original file size</summary>
public long FileSize { get; set; }
/// <summary>Compressed file size</summary>
public long CompressedSize { get; set; }
/// <summary>Offset of header information inside Zip storage</summary>
public long HeaderOffset { get; set; }
/// <summary>Offset of file inside Zip storage</summary>
public long FileOffset { get; set; }
/// <summary>Size of header information</summary>
public uint HeaderSize { get; set; }
/// <summary>32-bit checksum of entire file</summary>
public uint Crc32 { get; set; }
/// <summary>Last modification time of file</summary>
public DateTime ModifyTime { get; set; }
/// <summary>Creation time of file</summary>
public DateTime CreationTime { get; set; }
/// <summary>Last access time of file</summary>
public DateTime AccessTime { get; set; }
/// <summary>User comment for file</summary>
public string Comment { get; set; }
/// <summary>True if UTF8 encoding for filename and comments, false if default (CP 437)</summary>
public bool EncodeUTF8 { get; set; }
/// <summary>Overriden method</summary>
/// <returns>Filename in Zip</returns>
public override string ToString()
{
return this.FilenameInZip;
}
public override bool Equals(object obj)
{
ZipFileEntry o = obj as ZipFileEntry;
if (o is null)
return false;
return this.HeaderOffset == o.HeaderOffset;
}
public override int GetHashCode()
{
return this.FilenameInZip.GetHashCode() + this.FileSize.GetHashCode();
}
public bool IsZip64ExtNeeded(byte mask)
{
bool zip64 = false;
if ((mask & 1) != 0)
zip64 = this.FileSize >= 0xFFFFFFFF || this.CompressedSize >= 0xFFFFFFFF;
if (!zip64 && (mask & 2) != 0)
zip64 = this.HeaderOffset >= 0xFFFFFFFF;
return zip64;
}
public byte[] CreateExtraInfo(bool localHeader)
{
var zip64FileSize = this.FileSize >= 0xFFFFFFFF || localHeader && this.FileSize == 0;
var zip64CompSize = this.CompressedSize >= 0xFFFFFFFF || localHeader && (this.FileSize == 0 || this.FileSize >= 0xFFFFFFFF);
var zip64Offset = !localHeader && this.HeaderOffset >= 0xFFFFFFFF;
int offset = (zip64FileSize ? 8 : 0) + (zip64CompSize ? 8 : 0) + (zip64Offset ? 8 : 0);
if (offset != 0)
offset += 4;
byte[] buffer = new byte[offset + 36];
if (offset > 0)
{
BitConverter.GetBytes((ushort)0x0001).CopyTo(buffer, 0); // ZIP64 Information
BitConverter.GetBytes((ushort)(offset - 4)).CopyTo(buffer, 2); // Length
BitConverter.GetBytes(zip64FileSize ? this.FileSize : zip64CompSize ? this.CompressedSize : this.HeaderOffset).CopyTo(buffer, 4);
if (zip64CompSize || zip64Offset)
BitConverter.GetBytes(zip64CompSize ? this.CompressedSize : this.HeaderOffset).CopyTo(buffer, 12);
if (zip64Offset)
BitConverter.GetBytes(this.HeaderOffset).CopyTo(buffer, 20);
}
BitConverter.GetBytes((ushort)0x000A).CopyTo(buffer, offset); // NTFS FileTime
BitConverter.GetBytes((ushort)32).CopyTo(buffer, offset + 2); // Length
BitConverter.GetBytes((uint)0).CopyTo(buffer, offset + 4); // Reserved
BitConverter.GetBytes((ushort)0x0001).CopyTo(buffer, offset + 8); // Tag 1
BitConverter.GetBytes((ushort)24).CopyTo(buffer, offset + 10); // Size 1
BitConverter.GetBytes(this.ModifyTime.ToFileTime()).CopyTo(buffer, offset + 12); // MTime
BitConverter.GetBytes(this.AccessTime.ToFileTime()).CopyTo(buffer, offset + 20); // ATime
BitConverter.GetBytes(this.CreationTime.ToFileTime()).CopyTo(buffer, offset + 28); // CTime
return buffer;
}
public void ReadExtraInfo(byte[] buffer, int offset, int extraSize)
{
if (buffer.Length < 4)
return;
int start = offset;
int pos = offset;
uint tag, size;
while (pos < buffer.Length - 4 && pos - start < extraSize)
{
uint extraId = BitConverter.ToUInt16(buffer, pos);
uint length = BitConverter.ToUInt16(buffer, pos + 2);
if (extraId == 0x0001) // ZIP64 Information
{
int fieldOffset = 0;
while (fieldOffset < Math.Min(length, 24))
{
var data = BitConverter.ToInt64(buffer, pos + 4 + fieldOffset);
if (this.FileSize == 0xFFFFFFFF)
this.FileSize = data;
else if (this.CompressedSize == 0xFFFFFFFF)
this.CompressedSize = data;
else if (this.HeaderOffset == 0xFFFFFFFF)
this.HeaderOffset = data;
fieldOffset += 8;
}
}
if (extraId == 0x000A) // NTFS FileTime
{
tag = BitConverter.ToUInt16(buffer, pos + 8);
size = BitConverter.ToUInt16(buffer, pos + 10);
if (tag == 1 && size == 24)
{
this.ModifyTime = DateTime.FromFileTime(BitConverter.ToInt64(buffer, pos + 12));
this.AccessTime = DateTime.FromFileTime(BitConverter.ToInt64(buffer, pos + 20));
this.CreationTime = DateTime.FromFileTime(BitConverter.ToInt64(buffer, pos + 28));
}
}
pos += (int)length + 4;
}
}
}
}
================================================
FILE: src/ZipStorer.cs
================================================
// ZipStorer, by Jaime Olivares
// Website: http://github.com/jaime-olivares/zipstorer
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace System.IO.Compression
{
/// <summary>
/// Unique class for compression/decompression file. Represents a Zip file.
/// </summary>
public class ZipStorer : IDisposable
{
private const uint LocalFileHeaderSignature = 0x04034b50;
private const uint CentralDirHeaderSignature = 0x02014b50;
private const uint EndOfCentralDirSignature = 0x06054b50;
private const uint Zip64EndOfCentralDirSignature = 0x06064b50;
private const uint Zip64EndOfCentralDirLocatorSignature = 0x07064b50;
/// <summary>
/// Compression method enumeration
/// </summary>
public enum Compression : ushort
{
/// <summary>Uncompressed storage</summary>
Store = 0,
/// <summary>Deflate compression method</summary>
Deflate = 8
}
#region Public properties
/// <summary>True if UTF8 encoding for filename and comments, false if default (CP 437)</summary>
public bool EncodeUTF8 { get; set; } = false;
/// <summary>Force deflate algotithm even if it inflates the stored file. Off by default.</summary>
public bool ForceDeflating { get; set; } = false;
#endregion
#region Private fields
// List of files to store
private readonly List<ZipFileEntry> Files = new List<ZipFileEntry>();
// List of files in Central Directory
private readonly List<ZipFileEntry> CentralDirectoryFiles = new List<ZipFileEntry>();
// Filename of storage file
private string FileName;
// Stream object of storage file
private Stream ZipFileStream;
// General comment
private string Comment = string.Empty;
// Central dir image
private byte[] CentralDirImage = null;
// Existing files in zip
private long ExistingFiles = 0;
// File access for Open method
private FileAccess Access;
// Dispose control
private bool IsDisposed = false;
// Default filename encoder
private static Encoding DefaultEncoding;
// leave the stream open after the ZipStorer object is disposed
private bool LeaveOpen;
#endregion
#region Public static methods
static ZipStorer()
{
// Configure CP 437 encoding
#if NET5_0_OR_GREATER
CodePagesEncodingProvider.Instance.GetEncoding(437);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#endif
DefaultEncoding = Encoding.GetEncoding(437);
}
/// <summary>
/// Method to create a new storage file
/// </summary>
/// <param name="filename">Full path of Zip file to create</param>
/// <param name="comment">General comment for Zip file</param>
/// <returns>A valid ZipStorer object</returns>
public static ZipStorer Create(string filename, string comment = null)
{
Stream stream = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite);
ZipStorer zip = Create(stream, comment);
zip.Comment = comment ?? string.Empty;
zip.FileName = filename;
return zip;
}
/// <summary>
/// Method to create a new zip storage in a stream
/// </summary>
/// <param name="stream"></param>
/// <param name="comment"></param>
/// <param name="leaveOpen">true to leave the stream open after the ZipStorer object is disposed; otherwise, false (default).</param>
/// <returns>A valid ZipStorer object</returns>
public static ZipStorer Create(Stream stream, string comment = null, bool leaveOpen = false)
{
ZipStorer zip = new ZipStorer()
{
Comment = comment ?? string.Empty,
ZipFileStream = stream,
Access = FileAccess.Write,
LeaveOpen = leaveOpen,
CentralDirImage = Array.Empty<byte>()
};
return zip;
}
/// <summary>
/// Method to open an existing storage file
/// </summary>
/// <param name="filename">Full path of Zip file to open</param>
/// <param name="access">File access mode as used in FileStream constructor</param>
/// <returns>A valid ZipStorer object</returns>
public static ZipStorer Open(string filename, FileAccess access)
{
Stream stream = null;
ZipStorer zip = null;
try
{
stream = new FileStream(filename, FileMode.Open, access == FileAccess.Read ? FileAccess.Read : FileAccess.ReadWrite);
zip = Open(stream, access);
zip.FileName = filename;
}
catch (Exception)
{
if (stream != null)
{
stream.Dispose();
stream = null;
}
if (zip != null)
{
zip.Dispose();
zip = null;
}
throw;
}
return zip;
}
/// <summary>
/// Method to open an existing storage from stream
/// </summary>
/// <param name="stream">Already opened stream with zip contents</param>
/// <param name="access">File access mode for stream operations</param>
/// <param name="leaveOpen">true to leave the stream open after the ZipStorer object is disposed; otherwise, false (default).</param>
/// <returns>A valid ZipStorer object</returns>
public static ZipStorer Open(Stream stream, FileAccess access, bool leaveOpen = false)
{
if (!stream.CanSeek && access != FileAccess.Read)
throw new InvalidOperationException("Stream cannot seek");
ZipStorer zip = new ZipStorer()
{
ZipFileStream = stream,
Access = access,
LeaveOpen = leaveOpen
};
if (zip.readFileInfo())
{
zip.ReadCentralDir(true);
return zip;
}
if (!leaveOpen)
zip.Close();
throw new System.IO.InvalidDataException();
}
#endregion
#region Public methods
/// <summary>
/// Add full contents of a file into the Zip storage
/// </summary>
/// <param name="method">Compression method</param>
/// <param name="pathname">Full path of file to add to Zip storage</param>
/// <param name="filenameInZip">Filename and path as desired in Zip directory</param>
/// <param name="comment">Comment for stored file</param>
public ZipFileEntry AddFile(Compression method, string pathname, string filenameInZip, string comment = null)
{
if (Access == FileAccess.Read)
throw new InvalidOperationException("Writing is not allowed");
using (var stream = new FileStream(pathname, FileMode.Open, FileAccess.Read))
{
return this.AddStream(method, filenameInZip, stream, File.GetLastWriteTime(pathname), comment);
}
}
/// <summary>
/// Add full contents of a stream into the Zip storage
/// </summary>
/// <remarks>Same parameters and return value as AddStreamAsync()</remarks>
public ZipFileEntry AddStream(Compression method, string filenameInZip, Stream source, DateTime modTime, string comment = null)
{
// return this.AddStreamAsync(method, filenameInZip, source, modTime, comment);
return Task.Run(() => this.AddStreamAsync(method, filenameInZip, source, modTime, comment)).Result;
}
/// <summary>
/// Add full contents of a stream into the Zip storage
/// </summary>
/// <param name="method">Compression method</param>
/// <param name="filenameInZip">Filename and path as desired in Zip directory</param>
/// <param name="source">Stream object containing the data to store in Zip</param>
/// <param name="modTime">Modification time of the data to store</param>
/// <param name="comment">Comment for stored file</param>
public async Task<ZipFileEntry> AddStreamAsync(Compression method, string filenameInZip, Stream source, DateTime modTime, string comment = null)
{
if (Access == FileAccess.Read)
throw new InvalidOperationException("Writing is not allowed");
// Prepare the fileinfo
ZipFileEntry zfe = new ZipFileEntry()
{
Method = method,
EncodeUTF8 = this.EncodeUTF8,
FilenameInZip = normalizedFilename(filenameInZip),
FileSize = !source.CanSeek || ForceDeflating ? 0 : source.Length,
Comment = comment ?? string.Empty,
Crc32 = 0, // to be updated later
HeaderOffset = this.ZipFileStream.Position, // offset within file of the start of this local record
CreationTime = modTime,
ModifyTime = modTime,
AccessTime = modTime
};
// Write local header
this.writeLocalHeader(zfe);
zfe.FileOffset = this.ZipFileStream.Position;
// Write file to zip (store)
await storeAsync(zfe, source);
source.Close();
this.updateCrcAndSizes(zfe);
Files.Add(zfe);
return zfe;
}
/// <summary>
/// Add full contents of a directory into the Zip storage
/// </summary>
/// <param name="method">Compression method</param>
/// <param name="pathname">Full path of directory to add to Zip storage</param>
/// <param name="pathnameInZip">Path name as desired in Zip directory</param>
/// <param name="comment">Comment for stored directory</param>
public void AddDirectory(Compression method, string pathname, string pathnameInZip, string comment = null)
{
if (Access == FileAccess.Read)
throw new InvalidOperationException("Writing is not allowed");
string foldername;
int pos = pathname.LastIndexOf(Path.DirectorySeparatorChar);
string separator = Path.DirectorySeparatorChar.ToString();
if (pos >= 0)
foldername = pathname.Remove(0, pos + 1);
else
foldername = pathname;
if (!string.IsNullOrEmpty(pathnameInZip))
foldername = pathnameInZip + foldername;
if (!foldername.EndsWith(separator, StringComparison.CurrentCulture))
foldername = foldername + separator;
// this.AddStream(method, foldername, null, File.GetLastWriteTime(pathname), comment);
// Process the list of files found in the directory.
string[] fileEntries = Directory.GetFiles(pathname);
foreach (string fileName in fileEntries)
this.AddFile(method, fileName, foldername + Path.GetFileName(fileName), "");
// Recurse into subdirectories of this directory.
string[] subdirectoryEntries = Directory.GetDirectories(pathname);
foreach (string subdirectory in subdirectoryEntries)
this.AddDirectory(method, subdirectory, foldername, "");
}
/// <summary>
/// Updates central directory (if pertinent) and close the Zip storage
/// </summary>
/// <remarks>This is a required step, unless automatic dispose is used</remarks>
public void Close()
{
if (this.Access != FileAccess.Read && Files.Count > 0)
{
long centralOffset = this.ZipFileStream.Position;
long centralSize = 0;
if (this.CentralDirImage != null)
this.ZipFileStream.Write(CentralDirImage, 0, CentralDirImage.Length);
for (int i = 0; i < Files.Count; i++)
{
long pos = this.ZipFileStream.Position;
this.writeCentralDirRecord(Files[i]);
centralSize += this.ZipFileStream.Position - pos;
}
if (this.CentralDirImage != null)
this.writeEndRecord(centralSize + (uint)CentralDirImage.Length, centralOffset);
else
this.writeEndRecord(centralSize, centralOffset);
}
if (this.ZipFileStream != null && !this.LeaveOpen)
{
this.ZipFileStream.Flush();
this.ZipFileStream.Dispose();
this.ZipFileStream = null;
}
}
/// <summary>
/// Read all the file records in the central directory
/// </summary>
/// <param name="skipFileOffsetCalculation">Delay file offset calculation</param>
/// <returns>List of all entries in directory</returns>
/// <remarks>The calculation of the file offsets can be delayed until files are actually extracted.
/// This results in better performance, if only a few files are extracted.</remarks>
public List<ZipFileEntry> ReadCentralDir(bool skipFileOffsetCalculation = false)
{
var lastPos = this.ZipFileStream.Position;
if (this.CentralDirImage == null)
throw new InvalidOperationException("Central directory currently does not exist");
CentralDirectoryFiles.Clear();
for (int pointer = 0; pointer < this.CentralDirImage.Length;)
{
uint signature = BitConverter.ToUInt32(CentralDirImage, pointer);
if (signature != CentralDirHeaderSignature)
break;
bool encodeUTF8 = (BitConverter.ToUInt16(CentralDirImage, pointer + 8) & 0x0800) != 0;
ushort method = BitConverter.ToUInt16(CentralDirImage, pointer + 10);
uint modifyTime = BitConverter.ToUInt32(CentralDirImage, pointer + 12);
uint crc32 = BitConverter.ToUInt32(CentralDirImage, pointer + 16);
long comprSize = BitConverter.ToUInt32(CentralDirImage, pointer + 20);
long fileSize = BitConverter.ToUInt32(CentralDirImage, pointer + 24);
ushort filenameSize = BitConverter.ToUInt16(CentralDirImage, pointer + 28);
ushort extraSize = BitConverter.ToUInt16(CentralDirImage, pointer + 30);
ushort commentSize = BitConverter.ToUInt16(CentralDirImage, pointer + 32);
uint headerOffset = BitConverter.ToUInt32(CentralDirImage, pointer + 42);
uint headerSize = (uint)(46 + filenameSize + extraSize + commentSize);
DateTime modifyTimeDT = ZipDate.DosTimeToDateTime(modifyTime) ?? DateTime.Now;
EncodeUTF8 |= encodeUTF8;
Encoding encoder = encodeUTF8 ? Encoding.UTF8 : DefaultEncoding;
ZipFileEntry zfe = new ZipFileEntry()
{
Method = (Compression)method,
EncodeUTF8 = encodeUTF8,
Comment = string.Empty,
FilenameInZip = encoder.GetString(CentralDirImage, pointer + 46, filenameSize),
FileOffset = skipFileOffsetCalculation ? 0 : headerOffset == 0xFFFFFFFF ? 0 : this.getFileOffset(headerOffset),
FileSize = fileSize,
CompressedSize = comprSize,
HeaderOffset = headerOffset,
HeaderSize = headerSize,
Crc32 = crc32,
ModifyTime = modifyTimeDT,
CreationTime = modifyTimeDT,
AccessTime = DateTime.Now,
};
if (commentSize > 0)
zfe.Comment = encoder.GetString(CentralDirImage, pointer + 46 + filenameSize + extraSize, commentSize);
if (extraSize > 0)
{
zfe.ReadExtraInfo(CentralDirImage, pointer + 46 + filenameSize, extraSize);
if (!skipFileOffsetCalculation && headerOffset == 0xFFFFFFFF)
zfe.FileOffset = this.getFileOffset(zfe.HeaderOffset);
}
CentralDirectoryFiles.Add(zfe);
pointer += 46 + filenameSize + extraSize + commentSize;
}
this.ZipFileStream.Position = lastPos;
return CentralDirectoryFiles;
}
/// <summary>
/// Copy the contents of a stored file into a physical file
/// </summary>
/// <param name="zfe">Entry information of file to extract</param>
/// <param name="filename">Name of file to store uncompressed data</param>
/// <returns>True if success, false if not.</returns>
/// <remarks>Unique compression methods are Store and Deflate</remarks>
public bool ExtractFile(ZipFileEntry zfe, string filename)
{
// Make sure the parent directory exist
string path = Path.GetDirectoryName(filename);
if (!string.IsNullOrWhiteSpace(path) && !Directory.Exists(path))
Directory.CreateDirectory(path);
// Check if it is a directory. If so, do nothing.
if (Directory.Exists(filename))
return true;
bool result;
using (var output = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
result = this.ExtractFile(zfe, output);
}
if (result)
{
File.SetCreationTime(filename, zfe.CreationTime);
File.SetLastWriteTime(filename, zfe.ModifyTime);
File.SetLastAccessTime(filename, zfe.AccessTime);
}
return result;
}
/// <summary>
/// Copy the contents of a stored file into an opened stream
/// </summary>
/// <remarks>Same parameters and return value as ExtractFileAsync</remarks>
public bool ExtractFile(ZipFileEntry zfe, Stream stream)
{
return Task.Run(() => ExtractFileAsync(zfe, stream)).Result;
}
/// <summary>
/// Copy the contents of a stored file into an opened stream
/// </summary>
/// <param name="zfe">Entry information of file to extract</param>
/// <param name="stream">Stream to store the uncompressed data</param>
/// <returns>True if success, false if not.</returns>
/// <remarks>Unique compression methods are Store and Deflate</remarks>
public async Task<bool> ExtractFileAsync(ZipFileEntry zfe, Stream stream)
{
if (!stream.CanWrite)
throw new InvalidOperationException("Stream cannot be written");
// check signature
byte[] signature = new byte[4];
this.ZipFileStream.Seek(zfe.HeaderOffset, SeekOrigin.Begin);
await this.ZipFileStream.ReadAsync(signature, 0, 4);
if (BitConverter.ToUInt32(signature, 0) != LocalFileHeaderSignature)
return false;
if (zfe.FileOffset == 0)
zfe.FileOffset = this.getFileOffset(zfe.HeaderOffset);
// Buffered copy
byte[] buffer = new byte[65535];
this.ZipFileStream.Seek(zfe.FileOffset, SeekOrigin.Begin);
long bytesPending = zfe.FileSize;
if (zfe.Method == Compression.Store)
{
while (bytesPending > 0)
{
int bytesRead = await this.ZipFileStream.ReadAsync(buffer, 0, (int)Math.Min(bytesPending, buffer.Length));
await stream.WriteAsync(buffer, 0, bytesRead);
bytesPending -= bytesRead;
}
}
else if (zfe.Method == Compression.Deflate)
{
// Use 'using' to ensure DeflateStream is disposed even if an exception occurs
using (var inStream = new DeflateStream(this.ZipFileStream, CompressionMode.Decompress, true))
{
while (bytesPending > 0)
{
int bytesRead = await inStream.ReadAsync(buffer, 0, (int)Math.Min(bytesPending, buffer.Length));
await stream.WriteAsync(buffer, 0, bytesRead);
bytesPending -= bytesRead;
}
}
}
else
{
return false;
}
await stream.FlushAsync();
return true;
}
/// <summary>
/// Copy the contents of a stored file into a byte array
/// </summary>
/// <param name="zfe">Entry information of file to extract</param>
/// <param name="file">Byte array with uncompressed data</param>
/// <returns>True if success, false if not.</returns>
/// <remarks>Unique compression methods are Store and Deflate</remarks>
public bool ExtractFile(ZipFileEntry zfe, out byte[] file)
{
using (MemoryStream ms = new MemoryStream())
{
if (ExtractFile(zfe, ms))
{
file = ms.ToArray();
return true;
}
else
{
file = null;
return false;
}
}
}
/// <summary>
/// Removes one of many files in storage. It creates a new Zip file.
/// </summary>
/// <param name="zip">Reference to the current Zip object</param>
/// <param name="zfes">List of Entries to remove from storage</param>
/// <returns>True if success, false if not</returns>
/// <remarks>This method only works for storage of type FileStream</remarks>
public static bool RemoveEntries(ref ZipStorer zip, List<ZipFileEntry> zfes)
{
if (!(zip.ZipFileStream is FileStream))
throw new InvalidOperationException("RemoveEntries is allowed just over streams of type FileStream");
// Just remove affected entries from the newly added files list
zip.Files.RemoveAll(x => zfes.Contains(x));
// either here if needed or in Create/Open for (file) streams
zip.FileName = zip.FileName ?? ((FileStream)zip.ZipFileStream).Name;
// In order to delete we need to create a copy of the zip file excluding the selected items
var tempZipName = zip.FileName + ".tmp";
try
{
var tempZip = Create(tempZipName, zip.Comment);
tempZip.EncodeUTF8 = zip.EncodeUTF8;
var br = new BinaryReader(zip.ZipFileStream);
long offset = 0;
for (int listIndex = 0; listIndex <= 1; listIndex++)
{
var list = listIndex == 0 ? zip.CentralDirectoryFiles : zip.Files;
for (int index = 0; index < list.Count; index++)
{
var zfe = list[index];
if (zfe.FileOffset == 0)
zfe.FileOffset = zip.getFileOffset(zfe.HeaderOffset);
if (!zfes.Contains(zfe))
{
// Copy local header and consider filename and extra field lengths
zip.ZipFileStream.Position = zfe.HeaderOffset;
var bytes = br.ReadBytes(26);
tempZip.ZipFileStream.Write(bytes, 0, bytes.Length);
var filenameLength = br.ReadInt16();
var extraFieldLength = br.ReadInt16();
tempZip.ZipFileStream.Write(BitConverter.GetBytes((ushort)filenameLength), 0, 2);
tempZip.ZipFileStream.Write(BitConverter.GetBytes((ushort)extraFieldLength), 0, 2);
bytes = br.ReadBytes(filenameLength + extraFieldLength);
tempZip.ZipFileStream.Write(bytes, 0, bytes.Length);
// Buffered copy
byte[] buffer = new byte[65535];
long bytesPending = zfe.CompressedSize;
while (bytesPending > 0)
{
int bytesRead = zip.ZipFileStream.Read(buffer, 0, (int)Math.Min(bytesPending, buffer.Length));
tempZip.ZipFileStream.Write(buffer, 0, bytesRead);
bytesPending -= bytesRead;
}
tempZip.ZipFileStream.Flush();
// Adjust offsets
zfe.HeaderOffset += offset;
zfe.FileOffset += offset;
// Add to new files list
tempZip.Files.Add(zfe);
}
else
{
// offset correction
offset -= (zfe.FileOffset - zfe.HeaderOffset) + zfe.CompressedSize;
}
}
}
zip.Close();
tempZip.Close();
File.Delete(zip.FileName);
File.Move(tempZipName, zip.FileName);
zip = Open(zip.FileName, zip.Access);
}
catch
{
return false;
}
finally
{
if (File.Exists(tempZipName))
File.Delete(tempZipName);
}
return true;
}
/// <summary>
/// Returns a list of all file entries.
/// </summary>
/// <returns>List of all entries (central directory and newly added)</returns>
public List<ZipFileEntry> GetEntries()
{
var list = new List<ZipFileEntry>(CentralDirectoryFiles);
list.AddRange(Files);
return list;
}
/// <summary>
/// Returns the entry with the specified name.
/// </summary>
/// <param name="name">Name to search for</param>
/// <param name="comparisonType">StringComparison enum value</param>
/// <returns>ZipFileEntry found or null</returns>
/// <remarks>Searching with IgnoreCase in a Linux zip archive returns only the first entry found.</remarks>
public ZipFileEntry GetEntry(string name, StringComparison comparisonType = StringComparison.CurrentCultureIgnoreCase)
{
var entry = CentralDirectoryFiles.Where(x => x.FilenameInZip.Equals(name, comparisonType)).FirstOrDefault();
if (entry is null) entry = Files.Where(x => x.FilenameInZip.Equals(name, comparisonType)).FirstOrDefault();
return entry;
}
#endregion
#region Private methods
// Calculate the file offset by reading the corresponding local header
private long getFileOffset(long headerOffset)
{
byte[] buffer = new byte[2];
this.ZipFileStream.Seek(headerOffset + 26, SeekOrigin.Begin);
this.ZipFileStream.Read(buffer, 0, 2);
ushort filenameSize = BitConverter.ToUInt16(buffer, 0);
this.ZipFileStream.Read(buffer, 0, 2);
ushort extraSize = BitConverter.ToUInt16(buffer, 0);
return 30 + filenameSize + extraSize + headerOffset;
}
/* Local file header:
local file header signature 4 bytes (0x04034b50)
version needed to extract 2 bytes
general purpose bit flag 2 bytes
compression method 2 bytes
last mod file time 2 bytes
last mod file date 2 bytes
crc-32 4 bytes
compressed size 4 bytes
uncompressed size 4 bytes
filename length 2 bytes
extra field length 2 bytes
filename (variable size)
extra field (variable size)
*/
private void writeLocalHeader(ZipFileEntry zfe)
{
long pos = this.ZipFileStream.Position;
Encoding encoder = zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding;
byte[] encodedFilename = encoder.GetBytes(zfe.FilenameInZip);
byte[] extraInfo = zfe.CreateExtraInfo(true);
this.ZipFileStream.Write(BitConverter.GetBytes(LocalFileHeaderSignature), 0, 4); // header signature
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(zfe.IsZip64ExtNeeded(3) ? 45 : zfe.Method == Compression.Deflate ? 20 : 10)), 0, 2); // version needed to extract
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(zfe.EncodeUTF8 ? 0x0800 : 0)), 0, 2); // filename and comment encoding
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)zfe.Method), 0, 2); // zipping method
this.ZipFileStream.Write(BitConverter.GetBytes(ZipDate.DateTimeToDosTime(zfe.ModifyTime)), 0, 4); // zipping date and time
this.ZipFileStream.Write(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 12); // unused CRC, un/compressed size, updated later
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedFilename.Length), 0, 2); // filename length
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)extraInfo.Length), 0, 2); // extra length
this.ZipFileStream.Write(encodedFilename, 0, encodedFilename.Length);
this.ZipFileStream.Write(extraInfo, 0, extraInfo.Length);
zfe.HeaderSize = (uint)(this.ZipFileStream.Position - pos);
}
/* Central directory's File header:
central file header signature 4 bytes (0x02014b50)
version made by 2 bytes
version needed to extract 2 bytes
general purpose bit flag 2 bytes
compression method 2 bytes
last mod file time 2 bytes
last mod file date 2 bytes
crc-32 4 bytes
compressed size 4 bytes
uncompressed size 4 bytes
filename length 2 bytes
extra field length 2 bytes
file comment length 2 bytes
disk number start 2 bytes
internal file attributes 2 bytes
external file attributes 4 bytes
relative offset of local header 4 bytes
filename (variable size)
extra field (variable size)
file comment (variable size)
*/
private void writeCentralDirRecord(ZipFileEntry zfe)
{
Encoding encoder = zfe.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding;
byte[] encodedFilename = encoder.GetBytes(zfe.FilenameInZip);
byte[] encodedComment = encoder.GetBytes(zfe.Comment);
byte[] extraInfo = zfe.CreateExtraInfo(false);
var isZip64 = zfe.IsZip64ExtNeeded(3);
this.ZipFileStream.Write(BitConverter.GetBytes(CentralDirHeaderSignature), 0, 4); // header signature
this.ZipFileStream.Write(new byte[] { (byte)(isZip64 ? 45 : 23), 0x0, (byte)(isZip64 ? 45 : zfe.Method == Compression.Deflate ? 20 : 10), 0 }, 0, 4); // version made by / needed to extract
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(zfe.EncodeUTF8 ? 0x0800 : 0)), 0, 2); // filename and comment encoding
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)zfe.Method), 0, 2); // zipping method
this.ZipFileStream.Write(BitConverter.GetBytes(ZipDate.DateTimeToDosTime(zfe.ModifyTime)), 0, 4); // zipping date and time
this.ZipFileStream.Write(BitConverter.GetBytes(zfe.Crc32), 0, 4); // file CRC
this.ZipFileStream.Write(BitConverter.GetBytes(get32bitSize(zfe.CompressedSize)), 0, 4); // compressed file size
this.ZipFileStream.Write(BitConverter.GetBytes(get32bitSize(zfe.FileSize)), 0, 4); // uncompressed file size
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedFilename.Length), 0, 2); // Filename in zip
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)extraInfo.Length), 0, 2); // extra length
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedComment.Length), 0, 2);
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // disk=0
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)0), 0, 2); // Internal file attributes
this.ZipFileStream.Write(BitConverter.GetBytes((uint)0), 0, 4); // External file attributes (normal/readable)
this.ZipFileStream.Write(BitConverter.GetBytes(get32bitSize(zfe.HeaderOffset)), 0, 4); // Offset of header
this.ZipFileStream.Write(encodedFilename, 0, encodedFilename.Length);
this.ZipFileStream.Write(extraInfo, 0, extraInfo.Length);
this.ZipFileStream.Write(encodedComment, 0, encodedComment.Length);
}
/*
Zip64 end of central directory record
zip64 end of central dir
signature 4 bytes (0x06064b50)
size of zip64 end of central
directory record 8 bytes
version made by 2 bytes
version needed to extract 2 bytes
number of this disk 4 bytes
number of the disk with the
start of the central directory 4 bytes
total number of entries in the
central directory on this disk 8 bytes
total number of entries in the
central directory 8 bytes
size of the central directory 8 bytes
offset of start of central
directory with respect to
the starting disk number 8 bytes
zip64 extensible data sector (variable size)
Zip64 end of central directory locator
zip64 end of central dir locator
signature 4 bytes (0x07064b50)
number of the disk with the
start of the zip64 end of
central directory 4 bytes
relative offset of the zip64
end of central directory record 8 bytes
total number of disks 4 bytes
End of central dir record:
end of central dir signature 4 bytes (0x06054b50)
number of this disk 2 bytes
number of the disk with the
start of the central directory 2 bytes
total number of entries in
the central dir on this disk 2 bytes
total number of entries in
the central dir 2 bytes
size of the central directory 4 bytes
offset of start of central
directory with respect to
the starting disk number 4 bytes
zipfile comment length 2 bytes
zipfile comment (variable size)
*/
private void writeEndRecord(long size, long offset)
{
long dirOffset = ZipFileStream.Length;
// Zip64 end of central directory record
this.ZipFileStream.Position = dirOffset;
this.ZipFileStream.Write(new byte[] { 80, 75, 6, 6 }, 0, 4);
this.ZipFileStream.Write(BitConverter.GetBytes((Int64)44), 0, 8); // size of zip64 end of central directory
this.ZipFileStream.Write(BitConverter.GetBytes((UInt16)45), 0, 2); // version made by
this.ZipFileStream.Write(BitConverter.GetBytes((UInt16)45), 0, 2); // version needed to extract
this.ZipFileStream.Write(BitConverter.GetBytes((UInt32)0), 0, 4); // current disk
this.ZipFileStream.Write(BitConverter.GetBytes((UInt32)0), 0, 4); // start of central directory
this.ZipFileStream.Write(BitConverter.GetBytes((Int64)Files.Count + ExistingFiles), 0, 8); // total number of entries in the central directory in disk
this.ZipFileStream.Write(BitConverter.GetBytes((Int64)Files.Count + ExistingFiles), 0, 8); // total number of entries in the central directory
this.ZipFileStream.Write(BitConverter.GetBytes(size), 0, 8); // size of the central directory
this.ZipFileStream.Write(BitConverter.GetBytes(offset), 0, 8); // offset of start of central directory with respect to the starting disk number
// Zip64 end of central directory locator
this.ZipFileStream.Write(new byte[] { 80, 75, 6, 7 }, 0, 4);
this.ZipFileStream.Write(BitConverter.GetBytes((UInt32)0), 0, 4); // number of the disk
this.ZipFileStream.Write(BitConverter.GetBytes(dirOffset), 0, 8); // relative offset of the zip64 end of central directory record
this.ZipFileStream.Write(BitConverter.GetBytes((UInt32)1), 0, 4); // total number of disks
Encoding encoder = this.EncodeUTF8 ? Encoding.UTF8 : DefaultEncoding;
byte[] encodedComment = encoder.GetBytes(this.Comment);
this.ZipFileStream.Write(new byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }, 0, 8);
this.ZipFileStream.Write(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0, 12);
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)encodedComment.Length), 0, 2);
this.ZipFileStream.Write(encodedComment, 0, encodedComment.Length);
}
// Copies all the source file into the zip storage
private async Task<Compression> storeAsync(ZipFileEntry zfe, Stream source)
{
if (source.Length == 0)
{
zfe.FileSize = 0;
zfe.CompressedSize = 0;
zfe.Crc32 = 0;
zfe.Method = Compression.Store;
return zfe.Method;
}
byte[] buffer = new byte[16384];
int bytesRead;
ulong totalRead = 0;
Stream outStream;
long posStart = this.ZipFileStream.Position;
long sourceStart = source.CanSeek ? source.Position : 0;
if (zfe.Method == Compression.Store)
outStream = this.ZipFileStream;
else
outStream = new DeflateStream(this.ZipFileStream, CompressionMode.Compress, true);
zfe.Crc32 = 0 ^ 0xffffffff;
do
{
bytesRead = await source.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead > 0)
await outStream.WriteAsync(buffer, 0, bytesRead);
zfe.Crc32 = ZipCrc32.UpdateCRC(zfe.Crc32, buffer, bytesRead);
totalRead += (uint)bytesRead;
} while (bytesRead > 0);
outStream.Flush();
if (zfe.Method == Compression.Deflate)
outStream.Dispose();
zfe.Crc32 ^= 0xFFFFFFFF;
zfe.FileSize = (long)totalRead;
zfe.CompressedSize = this.ZipFileStream.Position - posStart;
// Verify for real compression
if (zfe.Method == Compression.Deflate && !this.ForceDeflating && source.CanSeek && zfe.CompressedSize > zfe.FileSize)
{
// Start operation again with Store algorithm
zfe.Method = Compression.Store;
this.ZipFileStream.Position = posStart;
this.ZipFileStream.SetLength(posStart);
source.Position = sourceStart;
return await this.storeAsync(zfe, source);
}
return zfe.Method;
}
private void updateCrcAndSizes(ZipFileEntry zfe)
{
long lastPos = this.ZipFileStream.Position; // remember position
this.ZipFileStream.Position = zfe.HeaderOffset + 4;
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)(zfe.IsZip64ExtNeeded(3) ? 45 : zfe.Method == Compression.Deflate ? 20 : 10)), 0, 2); // version needed
this.ZipFileStream.Position = zfe.HeaderOffset + 8;
this.ZipFileStream.Write(BitConverter.GetBytes((ushort)zfe.Method), 0, 2); // zipping method
var zip64Sizes = updateLocalHeaderExtraFields(zfe);
this.ZipFileStream.Position = zfe.HeaderOffset + 14;
this.ZipFileStream.Write(BitConverter.GetBytes(zfe.Crc32), 0, 4); // Update CRC
this.ZipFileStream.Write(BitConverter.GetBytes(zip64Sizes ? 0xFFFFFFFF : zfe.CompressedSize), 0, 4); // Compressed size
this.ZipFileStream.Write(BitConverter.GetBytes(zip64Sizes ? 0xFFFFFFFF : zfe.FileSize), 0, 4); // Uncompressed size
this.ZipFileStream.Position = lastPos; // restore position
}
private bool updateLocalHeaderExtraFields(ZipFileEntry zfe)
{
this.ZipFileStream.Position = zfe.HeaderOffset + 26;
bool zip64Sizes = false;
var buffer = new byte[4];
this.ZipFileStream.Read(buffer, 0, 4);
var fileNameLength = BitConverter.ToUInt16(buffer, 0);
var extraFieldLength = BitConverter.ToUInt16(buffer, 2);
if (extraFieldLength > 0)
{
this.ZipFileStream.Seek(fileNameLength, SeekOrigin.Current);
var extraFieldsBuffer = new byte[extraFieldLength];
this.ZipFileStream.Read(extraFieldsBuffer, 0, extraFieldLength);
int pos = 0;
while (pos < extraFieldsBuffer.Length - 4)
{
uint extraId = BitConverter.ToUInt16(extraFieldsBuffer, pos);
uint length = BitConverter.ToUInt16(extraFieldsBuffer, pos + 2);
if (extraId == 0x0001) // ZIP64 Information
{
zip64Sizes = true;
this.ZipFileStream.Position = zfe.HeaderOffset + 30 + fileNameLength + 4 + pos;
this.ZipFileStream.Write(BitConverter.GetBytes((ulong)zfe.FileSize), 0, 8);
this.ZipFileStream.Write(BitConverter.GetBytes((ulong)zfe.CompressedSize), 0, 8);
}
pos += (int)length + 4;
}
}
return zip64Sizes;
}
// Reads the end-of-central-directory record
private bool readFileInfo()
{
if (this.ZipFileStream.Length < 22)
return false;
this.ZipFileStream.Seek(0, SeekOrigin.Begin);
var br = new BinaryReader(this.ZipFileStream);
UInt32 headerSig = br.ReadUInt32();
if (headerSig != LocalFileHeaderSignature && headerSig != EndOfCentralDirSignature)
{
// not PK.. signature header and not 'end of central directory record' (empty ZIP files)
return false;
}
var end = this.ZipFileStream.Seek(-17, SeekOrigin.End); // Will start seeking from -22
try
{
do
{
this.ZipFileStream.Seek(-5, SeekOrigin.Current);
UInt32 sig = br.ReadUInt32();
if (sig == EndOfCentralDirSignature) // It is central dir
{
long dirPosition = ZipFileStream.Position - 4;
this.ZipFileStream.Seek(6, SeekOrigin.Current);
long entries = br.ReadUInt16();
long centralSize = br.ReadInt32();
long centralDirOffset = br.ReadUInt32();
UInt16 commentSize = br.ReadUInt16();
var commentPosition = ZipFileStream.Position;
if (centralDirOffset == 0xffffffff) // It is a Zip64 file
{
this.ZipFileStream.Position = dirPosition - 20;
sig = br.ReadUInt32();
if (sig != Zip64EndOfCentralDirLocatorSignature) // Not a Zip64 central dir locator
return false;
this.ZipFileStream.Seek(4, SeekOrigin.Current);
long dir64Position = br.ReadInt64();
this.ZipFileStream.Position = dir64Position;
sig = br.ReadUInt32();
if (sig != Zip64EndOfCentralDirSignature) // Not a Zip64 central dir record
return false;
this.ZipFileStream.Seek(28, SeekOrigin.Current);
entries = br.ReadInt64();
centralSize = br.ReadInt64();
centralDirOffset = br.ReadInt64();
}
// check if comment field is the very last data in file
if (commentPosition + commentSize != this.ZipFileStream.Length)
return false;
// Copy entire central directory to a memory buffer
this.ExistingFiles = entries;
this.CentralDirImage = new byte[centralSize];
this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin);
this.ZipFileStream.Read(this.CentralDirImage, 0, (int)centralSize);
// Leave the pointer at the begining of central dir, to append new files
this.ZipFileStream.Seek(centralDirOffset, SeekOrigin.Begin);
return true;
}
} while (this.ZipFileStream.Position > end - 65535);
}
catch { }
return false;
}
// Replaces backslashes with slashes to store in zip header
private static string normalizedFilename(string filename)
{
string filename1 = filename.Replace('\\', '/');
int pos = filename1.IndexOf(':');
if (pos >= 0)
filename1 = filename1.Remove(0, pos + 1);
return filename1.Trim('/');
}
// Returns filesize or 0xFFFFFFFF, if greater
private static uint get32bitSize(long size)
{
return size >= 0xFFFFFFFF ? 0xFFFFFFFF : (uint)size;
}
#endregion
#region IDisposable implementation
/// <summary>
/// Closes the Zip file stream
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
this.Close();
IsDisposed = true;
}
}
#endregion
}
}
================================================
FILE: src/ZipStorer.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A Pure C# class to handle Zip files</Description>
<Authors>Jaime Olivares</Authors>
<VersionPrefix>4.2.0</VersionPrefix>
<TargetFrameworks>netstandard2.0;net8.0;net48</TargetFrameworks>
<AssemblyName>ZipStorer</AssemblyName>
<AssemblyTitle>ZipStorer</AssemblyTitle>
<PackageId>ZipStorer</PackageId>
<PackageTags>zip;archive;deflate;dotnet</PackageTags>
<PackageProjectUrl>https://github.com/jaime-olivares/zipstorer</PackageProjectUrl>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/jaime-olivares/zipstorer</RepositoryUrl>
<Configuration>Release</Configuration>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
<PropertyGroup>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>Recommended</AnalysisMode>
<NoWarn>CA1805,CA1835</NoWarn>
</PropertyGroup>
<PropertyGroup>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="../LICENSE.md" Pack="true" PackagePath="$(PackageLicenseFile)" />
<None Include="../README.md" Pack="true" PackagePath="$(PackageReadmeFile)" />
</ItemGroup>
</Project>
================================================
FILE: test/Program.cs
================================================
using System;
using System.Collections;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ZipStorerTest
{
[TestClass]
public class Program
{
static void Main()
{
// var test = new UnitTestFolder();
// UnitTestFolder.Initialize(null);
// test.Folder_Test();
}
const string sampleFile1 = "sample1.zip";
const string sampleFile3 = "sample3.zip";
const string sampleFile4a = "sample4a.zip";
const string sampleFile4b = "sample4b.zip";
const string sampleFile5 = "sample5.zip";
const string sampleFile = "sample.zip";
private readonly DateTime baseDate = new DateTime(2019, 1, 1);
private readonly byte[] loremIpsum;
public Program()
{
this.loremIpsum = File.ReadAllBytes("lorem_ipsum.txt");
}
// [TestMethod]
public void Folder_Test()
{
File.Delete(sampleFile3);
using (ZipStorer zip = ZipStorer.Create(sampleFile3))
{
zip.AddDirectory(ZipStorer.Compression.Deflate, "/some/folder", null);
}
File.Delete(sampleFile);
}
[TestMethod]
public void OpenRead_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
}
}
[TestMethod]
public void ReadCentralDir_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.AreEqual(dir.Count, 10);
}
}
[TestMethod]
public void ExtractFolder_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsFalse(dir.Count == 0);
zip.ExtractFile(dir[0], out byte[] output);
Assert.AreEqual(output.Length, 0);
}
}
[TestMethod]
public void ExtractFile_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsFalse(dir.Count == 0);
zip.ExtractFile(dir[4], out byte[] output);
Assert.IsFalse(output.Length == 0);
}
}
[TestMethod]
public void ReadModifyTime_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsTrue(dir[0].ModifyTime > baseDate);
}
}
[TestMethod]
public void ReadAccessTime_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsTrue(dir[0].AccessTime > DateTime.Today);
}
}
[TestMethod]
public void ReadCreationTime_Test()
{
using (ZipStorer zip = ZipStorer.Open(sampleFile, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsTrue(dir[0].CreationTime > baseDate);
Assert.IsTrue(dir[0].CreationTime <= dir[0].ModifyTime);
}
}
[TestMethod]
public void CreateFile_Test()
{
File.Delete(sampleFile1);
using (ZipStorer zip = ZipStorer.Create(sampleFile1)) { }
;
}
[TestMethod]
public void AddStream_Test()
{
this.createSampleFile();
var now1 = DateTime.Now;
using (ZipStorer zip = ZipStorer.Open(sampleFile1, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsFalse(dir.Count == 0);
Assert.IsTrue(dir[0].FilenameInZip == "Lorem.txt");
}
}
[TestMethod]
public void AddStreamDate_Test()
{
var now = DateTime.Now;
this.createSampleFile();
using (ZipStorer zip = ZipStorer.Open(sampleFile1, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsFalse(dir.Count == 0);
Assert.IsTrue(dir[0].CreationTime >= now, "Creation Time failed");
Assert.IsTrue(dir[0].ModifyTime >= now, "Modify Time failed");
Assert.IsTrue(dir[0].AccessTime >= now, "Access Time failed");
}
}
[TestMethod]
public void Compression_Test()
{
var now = DateTime.Now;
this.createSampleFile();
using (ZipStorer zip = ZipStorer.Open(sampleFile1, FileAccess.Read))
{
var dir = zip.ReadCentralDir();
Assert.IsFalse(dir.Count == 0);
Assert.IsTrue(dir[0].Method == ZipStorer.Compression.Deflate);
Assert.IsTrue(dir[0].CompressedSize < loremIpsum.Length);
}
}
[TestMethod]
public void RemoveEntries_Test()
{
using (ZipStorer zip = ZipStorer.Create(sampleFile4a))
{
using (var mem = new MemoryStream(loremIpsum))
{
zip.AddStream(ZipStorer.Compression.Deflate, "Lorem1.txt", mem, baseDate);
}
using (var mem = new MemoryStream(loremIpsum))
{
zip.AddStream(ZipStorer.Compression.Deflate, "Lorem2.txt", mem, baseDate);
}
using (var mem = new MemoryStream(loremIpsum))
{
zip.AddStream(ZipStorer.Compression.Deflate, "Lorem3.txt", mem, baseDate);
}
}
ZipStorer zip1 = ZipStorer.Open(sampleFile4a, FileAccess.ReadWrite);
var entries = zip1.ReadCentralDir();
var entry = entries[1];
ZipStorer.RemoveEntries(ref zip1, new System.Collections.Generic.List<ZipFileEntry> { entry });
zip1.Close();
using (ZipStorer zip = ZipStorer.Create(sampleFile4b))
{
using (var mem = new MemoryStream(loremIpsum))
{
zip.AddStream(ZipStorer.Compression.Deflate, "Lorem1.txt", mem, baseDate);
}
using (var mem = new MemoryStream(loremIpsum))
{
zip.AddStream(ZipStorer.Compression.Deflate, "Lorem3.txt", mem, baseDate);
}
}
using (var stream1 = new FileStream(sampleFile4a, FileMode.Open, FileAccess.Read))
{
using (var stream2 = new FileStream(sampleFile4b, FileMode.Open, FileAccess.Read))
{
Assert.IsTrue(streamsAreEqual(stream1, stream2));
}
}
}
// [TestMethod]
public void Zip64_Test()
{
var dir = Path.Combine(Environment.CurrentDirectory, "SampleFiles5");
#if WINDOWS
if (new DriveInfo(dir).AvailableFreeSpace < ((long)16496 * 1024 * 1024))
throw new Exception("Not enough disk space (16.1 GB) for test!");
#endif
if (Directory.Exists(dir))
Directory.Delete(dir, true);
if (Directory.Exists(dir + "_2"))
Directory.Delete(dir + "_2", true);
Directory.CreateDirectory(dir);
Directory.CreateDirectory(dir + "_2");
File.Delete(Path.Combine(dir, "..", sampleFile5));
// Create one test file larger than 0xFFFFFFFF and one with 0xFFFFFFFE bytes
for (int n = 2; n <= 3; n++)
{
using (var fs = new FileStream(Path.Combine(dir, $"File{n}.txt"), FileMode.Create))
{
int max = (int)Math.Floor(0x100000000L / (float)this.loremIpsum.Length) + (n==2 ? 1 : 0);
for (var i = 0; i < max; i++)
{
fs.Write(loremIpsum.AsSpan());
}
if (n == 3)
{
int remainder = (int)(0xFFFFFFFE - fs.Length);
fs.Write(this.loremIpsum, 0, remainder);
}
}
}
File.Copy(Path.Combine(dir, "..", "lorem_ipsum.txt"), Path.Combine(dir, "File1.txt"));
// zip them
using (ZipStorer zip = ZipStorer.Create(Path.Combine(dir, "..", sampleFile5)))
{
zip.AddFile(ZipStorer.Compression.Deflate, Path.Combine(dir, "File1.txt"), "File1.txt"); // normal file
zip.AddFile(ZipStorer.Compression.Deflate, Path.Combine(dir, "File2.txt"), "File2.txt"); // Zip64 file size, normal compressed size and offset
zip.AddFile(ZipStorer.Compression.Store, Path.Combine(dir, "File3.txt"), "File3.txt"); // normal file size and offset
zip.AddFile(ZipStorer.Compression.Deflate, Path.Combine(dir, "File2.txt"), "File4.txt"); // Zip64 file size and offset
}
// unzip and compare them
using (ZipStorer zip = ZipStorer.Open(Path.Combine(dir, "..", sampleFile5), FileAccess.Read))
{
var entries = zip.ReadCentralDir();
for (var n = 0; n < 4; n++)
{
zip.ExtractFile(entries[n], Path.Combine(dir + "_2", entries[n].FilenameInZip));
using (var fs1 = new FileStream(Path.Combine(dir, entries[n == 3 ? 1 : n].FilenameInZip), FileMode.Open))
{
using (var fs2 = new FileStream(Path.Combine(dir + "_2", entries[n].FilenameInZip), FileMode.Open))
{
Assert.IsTrue(streamsAreEqual(fs1, fs2));
}
}
File.Delete(Path.Combine(dir + "_2", entries[n].FilenameInZip));
}
}
}
private bool streamsAreEqual(Stream s1, Stream s2)
{
using var sha256 = SHA256.Create();
byte[] hash1 = sha256.ComputeHash(s1);
byte[] hash2 = sha256.ComputeHash(s2);
return StructuralComparisons.StructuralEqualityComparer.Equals(hash1, hash2);
}
private void createSampleFile()
{
using (var mem = new MemoryStream(this.loremIpsum))
{
File.Delete(sampleFile1);
using (ZipStorer zip = ZipStorer.Create(sampleFile1))
{
zip.AddStream(ZipStorer.Compression.Deflate, "Lorem.txt", mem, DateTime.Now);
}
}
}
}
}
================================================
FILE: test/ZipStorerTest.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>Jaime Olivares</Authors>
<VersionPrefix>4.2.0</VersionPrefix>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>ZipStorerTest</AssemblyName>
<PackageId>ZipStorerTest</PackageId>
<OutputType>exe</OutputType>
<StartupObject>ZipStorerTest.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
<PackageReference Include="MSTest.TestAdapter" Version="3.4.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../src/ZipStorer.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="*.zip">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="*.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
================================================
FILE: test/lorem_ipsum.txt
================================================
Lorem ipsum odor amet, consectetuer adipiscing elit. Eros fringilla vitae, faucibus rhoncus faucibus malesuada non magnis? Proin euismod finibus ornare dignissim ridiculus. Tortor maximus faucibus magnis scelerisque vulputate. Velit rutrum volutpat convallis, maecenas quis nisi amet. Habitasse elit curabitur quisque porttitor neque auctor.
Sollicitudin inceptos justo primis, platea conubia cursus. Conubia mi congue penatibus urna nullam ligula. Turpis faucibus metus eget phasellus lorem nascetur. Interdum libero commodo felis sollicitudin leo. Malesuada iaculis arcu ut tempor rhoncus nec. Vestibulum justo quam ut morbi sed sapien dapibus habitant. Suscipit posuere fermentum tristique class ridiculus facilisis.
Est viverra in habitasse consequat tristique lobortis. Interdum sed et, nisi blandit maecenas platea arcu pulvinar. Viverra class auctor egestas lobortis elementum bibendum. Cursus tellus elit dictum fringilla lacus luctus etiam duis. Ac sapien elementum cubilia ultrices, orci pellentesque nam. Dapibus convallis congue duis platea varius phasellus vel. Nascetur lorem odio mi; montes aptent per.
Rhoncus dolor ante quam ac lobortis nam purus. Habitasse sed euismod aliquet lacinia neque. Accumsan curae auctor sit orci adipiscing. Massa maecenas accumsan non finibus suspendisse; commodo volutpat faucibus. Porttitor nascetur adipiscing nisl quam nisl mi ultrices! Eleifend maximus quam odio adipiscing primis luctus libero morbi. Arcu nisl odio pharetra morbi aliquam congue class quis. Accumsan libero montes luctus rhoncus netus.
Praesent aenean bibendum primis sodales bibendum vivamus lobortis velit metus. Risus sodales ex curabitur velit, cras senectus libero. Molestie posuere porttitor sodales suscipit lectus morbi. Odio cubilia non ac, congue metus amet molestie nullam. Sit suscipit nisl senectus pulvinar hac aliquam risus faucibus. Nec congue faucibus facilisi; ut mus accumsan placerat. Nisl interdum malesuada sagittis ex cubilia tempus magna dignissim sem.
Aliquam vehicula mattis tortor fringilla suspendisse sagittis. Dignissim pretium tortor ridiculus fames porttitor. Blandit felis mus leo fringilla fusce duis. Aliquam risus dictum litora nec, erat eleifend duis. Iaculis placerat maecenas curae mattis finibus nullam venenatis elementum porttitor. Aliquam natoque efficitur magnis dictum risus semper accumsan vel cubilia. Feugiat etiam eleifend primis pretium bibendum eu ornare.
Dolor auctor lacinia habitant sociosqu vivamus ut! Odio nisi duis fusce ullamcorper id duis tortor. Ut etiam vestibulum quam convallis bibendum. Dolor euismod lobortis odio consequat elementum conubia dictumst luctus. Volutpat elit tellus aptent eu venenatis sodales ultricies. Afusce nisl per nam litora molestie. Sollicitudin sit vel mattis integer platea ipsum. Amet maximus risus maximus vulputate penatibus cubilia ex integer. Quam cubilia sapien nisi dis velit natoque quam aliquet. Sapien odio aliquet iaculis consequat mollis id litora rhoncus.
Cursus maximus varius donec vestibulum dui, natoque adipiscing varius. Amet etiam dolor amet morbi imperdiet euismod neque penatibus aliquet? Felis risus inceptos amet fames, conubia nam nibh velit molestie. Dui imperdiet ad condimentum gravida potenti dui id eget. Viverra eros in maximus suspendisse venenatis faucibus ridiculus semper egestas. Efficitur fames libero lobortis parturient nibh posuere duis. Cursus quam aliquet parturient bibendum quisque in.
Sed phasellus montes proin imperdiet commodo vitae sollicitudin nibh. Orci eu velit litora at ultrices. Lectus etiam sagittis tempor efficitur consequat ad. Mauris in facilisis sodales vulputate phasellus mus. Gravida volutpat luctus porta in velit elementum dapibus metus sapien. Accumsan nibh penatibus netus lobortis a duis. Rhoncus inceptos non mus; nisi elementum eu. Etiam montes interdum eros nec urna risus sapien nulla. Rutrum faucibus risus sociosqu dignissim enim porta scelerisque.
Rutrum ex lorem nisi massa efficitur. Iaculis molestie sapien sollicitudin euismod elit. Aenean vel nisi nam justo consectetur luctus orci posuere. Velit senectus magnis elementum cubilia fermentum class curae. Vehicula facilisis aenean sapien, pharetra luctus phasellus nec. Velit risus venenatis blandit orci enim dis sodales commodo id! Vestibulum neque urna mollis vel placerat.
Praesent primis ac ultrices nisl consectetur eget. Nunc efficitur cubilia semper sem ornare sollicitudin platea ut. Pulvinar mollis quam dictum facilisis nec diam consectetur suspendisse et. Sociosqu rutrum quisque dis aenean himenaeos dignissim molestie venenatis dapibus. Habitasse scelerisque elit libero sollicitudin; euismod gravida. Sem amet sagittis sodales felis cras hac nam hac. Torquent nulla pellentesque hac quam tempus fames sit senectus.
Euismod magna commodo lacus nascetur ad. Dignissim mollis velit amet sit convallis habitant senectus vitae. Iaculis adipiscing faucibus aptent nunc pulvinar facilisi nascetur et. Sit litora accumsan sagittis nam conubia, mollis penatibus. Gravida venenatis maximus sapien donec dolor enim id aenean. Eros arcu mus mus maximus elementum.
Et lacinia porta primis facilisi ornare habitasse. Erat sit vivamus per inceptos posuere euismod. Aenean est fermentum a pulvinar nisl iaculis efficitur tempor. Cubilia dignissim tempus convallis elit auctor eleifend a sagittis nunc. Dui sodales scelerisque magna curae eget mauris integer. Montes commodo a ultrices condimentum auctor mollis litora nec. Potenti accumsan mus leo nascetur semper tortor. Cursus facilisi nisl nostra phasellus leo consectetur. Erat ligula sagittis natoque eleifend proin. Ante vivamus himenaeos elit conubia commodo gravida.
Laoreet vivamus volutpat imperdiet pulvinar donec nam magnis pulvinar. Ultrices imperdiet lectus justo ac interdum rhoncus elit fringilla. Potenti eu primis fames urna ipsum maximus ridiculus viverra. Viverra inceptos quis pharetra vitae varius conubia sit. Magna potenti pulvinar scelerisque neque duis luctus tempor fermentum ac. Auctor facilisis donec lacus diam consectetur id orci. Dictum curae pulvinar cursus viverra magna.
Sociosqu torquent metus feugiat justo litora luctus. Rutrum cubilia sed lobortis, curabitur duis venenatis auctor fames dictum. Platea tincidunt id aptent ridiculus venenatis mollis. Maecenas auctor massa tellus erat orci duis faucibus blandit proin. Dictum primis nunc fusce hac at netus volutpat. Ante bibendum lorem lobortis semper neque himenaeos fames elit iaculis. Lectus fermentum eros interdum natoque commodo aliquet aliquet scelerisque.
Accumsan vitae interdum maecenas mattis blandit. Nascetur feugiat tempus; hendrerit montes dolor augue. Lectus suscipit egestas ullamcorper purus varius; blandit dapibus. Enim magna aenean eget quisque ex potenti. Mus sollicitudin faucibus orci dapibus conubia mattis dis. Proin platea quam luctus dictumst hac et sapien per ornare. Eros suspendisse montes litora donec aliquet congue. Ipsum curabitur sodales magnis turpis penatibus.
Per dolor nibh fusce eros id dictum dis. Ultricies euismod mattis facilisi class leo cursus platea. Non in integer eu accumsan consequat pharetra ad taciti odio. Mauris interdum nisi libero ipsum nec. Senectus magna dictumst magna class dui maecenas scelerisque odio. Platea aenean varius malesuada odio vulputate fusce tempor risus. Interdum consequat mus eget ante sed pellentesque. Convallis fusce duis vehicula iaculis per morbi tincidunt duis. Cursus mauris nam pharetra himenaeos nibh.
Vehicula pretium tristique nullam ut natoque libero nascetur. Nunc potenti eleifend sapien montes lobortis. Malesuada et pellentesque et volutpat odio neque? Vel at sociosqu viverra parturient nascetur tortor. Eros torquent nisl venenatis ac, amet feugiat purus. Conubia lacinia parturient commodo fermentum amet neque bibendum.
Fringilla cubilia sollicitudin rhoncus nisl erat finibus nec. Aliquam condimentum imperdiet class sit faucibus montes. Potenti habitasse curae vel consequat diam. Montes feugiat placerat nec sit scelerisque primis interdum phasellus. Dictum justo mauris ad; fermentum proin cursus rutrum. Nunc fermentum aenean ad duis dictum.
Venenatis nec sed praesent hac vehicula fermentum venenatis. Vivamus quis parturient tempus litora integer cubilia. Auctor sociosqu vel, inceptos natoque duis ac rhoncus. Non efficitur ullamcorper integer imperdiet arcu luctus varius. Nec laoreet non interdum volutpat in hendrerit. Lobortis potenti ipsum maximus ultricies nostra phasellus nostra hendrerit rhoncus. Interdum volutpat pharetra quam; pellentesque montes eu fringilla. Venenatis quisque tempus eu dictum parturient gravida pharetra? Lectus auctor vel dictumst est eros leo. Ligula risus tristique; condimentum fermentum class at.
Senectus magna primis ante scelerisque consequat dignissim dictum. Massa neque imperdiet urna dapibus elit mus donec. Amollis dapibus porta metus lorem erat porta. Dui himenaeos nostra donec tellus dapibus hendrerit. Fames hac velit lectus rutrum posuere sem nam. Per dictum commodo pulvinar non; gravida integer finibus inceptos himenaeos. Ut lacinia pellentesque nec vehicula nullam malesuada. Montes facilisis odio laoreet senectus bibendum vehicula suspendisse. Sed nullam velit interdum tortor sagittis tincidunt. Donec nisl scelerisque lobortis diam rhoncus efficitur mi eu.
Facilisis cras arcu habitant eu nullam ipsum. Curae habitant turpis maecenas dictum nisl porta pellentesque interdum. Eu aptent habitasse dictum eget mus efficitur justo taciti! Ex fames feugiat fringilla dolor massa nibh, sodales euismod augue. Dui pulvinar curae sed facilisi phasellus dis. Tempor tincidunt facilisis inceptos molestie proin diam convallis eleifend. Quis sapien amet convallis enim habitant tellus mattis euismod.
Finibus potenti primis duis convallis maximus. Platea hac primis augue, elit lobortis auctor. Est placerat sollicitudin mollis libero commodo porta faucibus pharetra. Proin lacinia ante est, massa hac laoreet accumsan euismod. Urna duis non feugiat semper iaculis ante arcu feugiat! Enim nibh iaculis porttitor condimentum mi platea. Nisi class facilisis duis magnis lacinia nam aliquam nam. Vulputate primis accumsan velit mus aenean.
Ultrices ut tellus fringilla sit at non placerat. Eget ridiculus vivamus convallis, malesuada purus cras facilisis. Suscipit consectetur vulputate dignissim est porta lectus in augue. Cras tincidunt nibh quis conubia; pellentesque molestie risus finibus. Vulputate luctus imperdiet vivamus finibus erat amet. Nam mi cursus ligula ipsum morbi porttitor ligula conubia. Dignissim nostra amet diam donec eros eros.
Sodales class semper iaculis a adipiscing nisi phasellus. Pellentesque congue convallis suspendisse nibh semper. Dolor curabitur nisl vestibulum, nibh integer rhoncus amet. Fames amet convallis volutpat euismod at. Ante montes vestibulum nibh natoque vehicula dis congue tempus interdum. Metus justo purus amet felis morbi netus sed. Vehicula faucibus eu consectetur pharetra quis gravida aptent pulvinar.
Est curabitur tristique vel; eu commodo ac. Ullamcorper congue interdum arcu mattis luctus. Fermentum rutrum habitasse orci primis tortor laoreet eleifend lectus. Dui parturient mi habitasse phasellus parturient fringilla risus tortor? Mus sed platea lorem suscipit posuere semper cubilia sagittis. Vehicula et ante ipsum est, aliquam potenti natoque. Donec interdum massa condimentum; imperdiet integer class pellentesque. Non pellentesque ligula molestie interdum; amet sapien gravida. Ac vehicula et dis faucibus eros egestas nibh litora amet.
Interdum cursus rhoncus sed lacinia nulla, facilisis nec. Maximus ultrices praesent parturient lorem nisl molestie integer adipiscing. Vitae cursus in ornare netus tortor, augue fames sit. Sodales viverra aenean praesent nisi ridiculus neque. Senectus adipiscing ut ut purus dapibus mollis sed adipiscing. Enim ullamcorper faucibus penatibus quam urna. Dis pharetra cubilia ornare dictumst primis mi. Consectetur tempus gravida tortor himenaeos gravida vivamus. Nisl ridiculus porttitor placerat ipsum integer.
Pretium rhoncus egestas risus viverra erat. Aptent fames blandit bibendum lacus etiam suspendisse vitae rhoncus. Duis efficitur ullamcorper sem ligula suscipit. Penatibus lacinia urna vulputate montes conubia. Inceptos varius torquent duis finibus; sem non dis. Pulvinar ut ad dapibus tincidunt nam. Quam fermentum natoque; egestas lacus auctor quis.
Mus amet maecenas pretium interdum semper hendrerit volutpat. Efficitur penatibus per rutrum; at quis purus phasellus. Aenean non ex enim adipiscing tortor pharetra. Natoque euismod fusce dapibus ad adipiscing aenean. Maecenas quis ad ultricies; id aptent fusce sagittis. Massa nascetur curae venenatis lacus est. Tempus nullam vel mattis fermentum pulvinar interdum est sodales.
Mollis porta velit blandit morbi aptent eu. Convallis luctus sapien inceptos neque vulputate auctor blandit quam. Rutrum et ullamcorper nisl cubilia posuere sed integer. Aliquam eros commodo semper penatibus suscipit facilisi fames. Ad sagittis ultrices consequat turpis, imperdiet fringilla nisi. Donec volutpat tempor aliquet viverra diam curae finibus. Urna pretium in congue ridiculus eros. Laoreet vulputate eu interdum dictumst maximus urna porta sed! Aptent consectetur scelerisque pellentesque dui molestie; donec taciti leo ullamcorper.
Magna potenti aliquet curae ridiculus nulla aptent. Dictum eros risus scelerisque ac mauris. Sodales molestie nulla venenatis mattis erat aliquam suspendisse bibendum ridiculus. Primis fames sed elit magnis aliquet fermentum cras nascetur dignissim. Sapien phasellus suspendisse consectetur posuere scelerisque iaculis commodo suspendisse. Pellentesque bibendum praesent leo habitant aptent diam. Diam facilisi egestas quis urna dis ullamcorper montes morbi.
Ridiculus eu justo porttitor quis aliquet hac vitae justo? Congue vehicula quam nunc adipiscing elit duis diam. Himenaeos consectetur condimentum iaculis dictum; mus odio ad potenti. Ut praesent ipsum ullamcorper tristique habitasse accumsan dui. Proin nostra tempor quis volutpat ac eros. Fringilla nascetur sed iaculis sem hac. Aenean finibus gravida fringilla natoque, vehicula sagittis amet ullamcorper. Aenean consequat elementum est lobortis sociosqu consequat. Nascetur faucibus commodo neque morbi convallis. Vestibulum dolor sollicitudin nec mauris adipiscing sodales.
Vitae sollicitudin est fringilla habitasse conubia mattis, consectetur nisl. Et nulla potenti rutrum orci at eros eleifend. Neque integer interdum tempus neque commodo eu. Dictumst accumsan a facilisi litora litora rhoncus, nibh luctus. In quisque venenatis quis et dignissim placerat libero congue per. Suscipit gravida sit fusce fusce elit tristique. Scelerisque neque curae consectetur diam ad euismod neque.
Laoreet amet a pellentesque purus condimentum dictumst. Viverra fringilla penatibus risus placerat tempor leo litora accumsan ornare. Cubilia tincidunt nisi aptent odio praesent. Enim cursus malesuada interdum porta molestie fames tristique litora pharetra. Torquent risus porta, tempor massa cursus integer neque. At dictumst ullamcorper et inceptos ac phasellus potenti finibus. Nibh mi quam mus rhoncus aliquam tellus, massa tellus. Libero dictumst tortor dictumst facilisis turpis massa. Ante blandit fusce quam id posuere libero praesent nibh.
Accumsan dignissim vulputate lobortis sapien malesuada lacinia. Vulputate odio suspendisse ut nisi habitasse taciti integer. Dapibus quam commodo dapibus senectus duis praesent eget vehicula ex. Fames conubia dignissim habitant; gravida aliquam viverra interdum cras rutrum. Dis donec consequat consectetur lorem, dui fermentum felis? Lacus sociosqu quam donec sem cursus semper. Suspendisse nisl velit sed sodales sem. Suspendisse orci condimentum fermentum ridiculus tempus eget molestie. Aliquet mattis ultrices ornare magna, maximus hac. Senectus at nunc amet fringilla at.
Dictum per purus arcu sollicitudin condimentum nullam commodo magna. Pretium netus magna eu, per per sagittis netus tristique. Purus sapien enim varius; curabitur etiam dignissim. Metus habitant quisque interdum litora consectetur sem nam. Erat rutrum augue vivamus arcu lacinia placerat. Urna vel enim diam porta; suscipit curabitur. Fringilla faucibus mollis nisl odio etiam sed.
Nullam tincidunt elit donec finibus id penatibus a. Commodo eros mauris maximus pharetra vehicula dignissim litora venenatis elit? Proin egestas tempor eleifend nam proin mattis iaculis. Dignissim dignissim taciti amet elit velit. Nec non nam a efficitur dignissim platea hac scelerisque pretium. Dapibus sem sodales magnis blandit venenatis habitasse. Gravida penatibus sem sit porttitor quam habitasse quisque nisi.
Ad ullamcorper nostra nisl efficitur ornare natoque. Ligula morbi donec nam congue habitasse dis nam. Facilisi pulvinar fermentum sem conubia eu. Ridiculus habitant est mollis risus etiam vitae amet morbi tempor. Aconvallis inceptos sit bibendum tortor semper auctor. Ante vehicula eget placerat a, dapibus eros ipsum. Quis mi suspendisse nec finibus vehicula augue inceptos senectus lectus. Egestas morbi pretium dapibus aptent libero nam.
Pretium sagittis hendrerit erat magnis dolor tempor curae turpis. Faucibus litora dis facilisis sociosqu; urna aenean curae vitae. Magnis cursus tincidunt aptent dis tristique torquent interdum varius. Interdum dictumst suspendisse; elit mus aliquet massa. Dui sem fames lectus conubia mi torquent. Velit placerat molestie nunc imperdiet tortor faucibus. Hac sociosqu efficitur aliquam suscipit suspendisse; ultricies vel mattis. At sem sodales ac sociosqu mi.
Dui massa enim eu pharetra est nisi dictum laoreet. Netus mus sapien amet condimentum vivamus massa fames ultrices. Quis finibus quisque duis facilisis, ornare pulvinar et. Venenatis conubia augue; urna faucibus scelerisque torquent imperdiet aptent sodales. Apretium lectus risus platea luctus accumsan. Nibh tincidunt venenatis duis lacinia; condimentum duis. Torquent habitasse tincidunt varius curabitur in arcu. Malesuada eleifend ipsum etiam eleifend; platea auctor malesuada magna.
Bibendum lacus nullam viverra nullam cras orci dignissim. Sit mus rhoncus mattis eget cras hac. Dictumst finibus euismod ac quisque tincidunt? Commodo facilisis efficitur finibus nunc; praesent lobortis faucibus. Adipiscing vel consequat eu mus sapien rutrum luctus nulla lacus. Tincidunt est leo; platea nisi fringilla cursus a quisque. Volutpat praesent tempus blandit consequat diam magna eu quisque curae.
Non facilisi etiam turpis, rhoncus etiam eros. Sem porttitor pellentesque neque at curabitur viverra. Rhoncus molestie molestie commodo magnis lobortis. Odio lacus habitasse erat risus elementum quis ipsum sed. Eget nunc dapibus, urna lectus tempus justo. Dui lectus metus, auctor porta sem nostra habitant. Hac nibh purus integer euismod vitae pharetra rhoncus.
Risus habitant neque dolor erat bibendum. Etiam praesent taciti commodo hendrerit ullamcorper penatibus vitae malesuada. Euismod vitae erat suspendisse diam, urna vivamus diam. Bibendum ultrices rutrum aliquam augue volutpat risus magnis; habitasse natoque. Elementum etiam aliquet nisl ex lobortis, netus gravida nam. Imperdiet nam quis sollicitudin blandit nisi suscipit. Parturient per mus ornare tincidunt augue nullam. Vitae elit aliquam augue faucibus parturient porta egestas enim nostra. Finibus conubia dapibus proin semper senectus adipiscing. Faucibus sapien porta mus posuere elit netus eu mauris.
Ligula ac facilisi sagittis condimentum; congue amet est class. Platea vehicula risus vel porttitor eleifend porta potenti sagittis curabitur. Vehicula suspendisse elit orci inceptos at cras aenean habitasse nam. Elit ad morbi; morbi bibendum urna eget urna. Pretium platea phasellus facilisis dictumst taciti non. Amet ut augue sapien tellus; hendrerit in? Ad laoreet neque duis torquent cursus risus. Feugiat suspendisse nascetur viverra natoque nascetur platea commodo. Etiam malesuada convallis consequat lectus scelerisque porttitor erat venenatis massa.
Enim urna arcu turpis tristique proin vivamus. In nascetur risus sapien, felis mi hac pulvinar feugiat. Eros quis ut quis mi diam ridiculus turpis cubilia. Venenatis elit libero ridiculus hac elit hac libero tortor. Faucibus torquent penatibus ut; viverra hendrerit arcu. Taciti rutrum eget ultricies facilisi accumsan eget senectus. Magna curabitur blandit eros ornare vestibulum suscipit ac. Orci fermentum pharetra interdum laoreet aptent ex volutpat accumsan. Adipiscing quam tristique fames arcu convallis tortor pellentesque. Turpis pharetra rhoncus pulvinar non dapibus.
Mi sodales fermentum dui potenti elementum auctor volutpat fusce. Maecenas primis cursus vitae vestibulum mollis. Nisl id efficitur nostra maximus accumsan, semper aptent hendrerit a. Tristique luctus sit tortor metus id facilisi sit. Maecenas augue praesent aenean etiam erat sollicitudin. Consectetur viverra consequat blandit urna rutrum elit congue suscipit.
Luctus fames morbi torquent scelerisque euismod. Taciti pellentesque posuere dis pellentesque vestibulum magnis arcu euismod. Congue nullam vivamus pretium, blandit sem torquent maximus. Ex tincidunt duis dictum maecenas ornare nam molestie. Mattis viverra elit ex facilisi urna metus. Eu dignissim rhoncus potenti nulla, mollis enim finibus nam. Sit tincidunt vestibulum tincidunt dis cursus class; platea ac. Efficitur natoque lobortis a id curabitur malesuada nunc varius. Tortor dapibus aenean leo lobortis, dis porttitor ad.
Class nec mattis rhoncus; blandit sapien rutrum. Vehicula vulputate tortor lacinia est mollis; nisi diam. Montes eu cubilia diam donec dapibus neque nunc aliquet nibh. Conubia massa justo phasellus, ullamcorper etiam ante vulputate class et. Accumsan ante rutrum primis; massa sociosqu ex. Sapien tristique tortor velit netus eu urna elit vel. Justo aenean etiam aenean molestie pharetra curabitur sem aenean. Maecenas praesent commodo malesuada eu curae luctus habitant.
Varius ex consequat etiam vehicula himenaeos? Velit libero aenean facilisis quis cras torquent enim volutpat. Lorem ullamcorper consequat ornare massa leo nulla. Efficitur vulputate lorem orci; porttitor rhoncus mattis. Aliquet ut conubia hac efficitur rhoncus mus metus. Laoreet ornare augue dui lacinia facilisi dictum. Magna augue molestie metus eros efficitur. Tristique venenatis maecenas dolor bibendum a pretium sollicitudin porta id?
Leo phasellus dictum faucibus sollicitudin platea netus. Venenatis ultrices ridiculus libero potenti in; vel luctus tortor tempor. Torquent fusce turpis adipiscing egestas magna. Semper non torquent vitae curae platea. Morbi tellus curabitur sociosqu tincidunt diam. Libero bibendum libero habitasse condimentum mi conubia commodo et taciti.
Hac lobortis fermentum nibh suscipit dolor? Pharetra nisl maximus sed natoque potenti sed facilisi sit. Fermentum vel augue ullamcorper eleifend ligula sed. Dapibus sagittis mattis volutpat magnis fermentum. Accumsan finibus lobortis consequat, dui parturient sagittis. Senectus dui massa interdum placerat eros congue curabitur. Taciti augue pharetra conubia ex velit. Libero posuere dis, lorem duis sociosqu mattis quisque. Pellentesque habitant efficitur fames pellentesque vehicula. Nibh lorem tincidunt netus accumsan eget.
Auctor amet elementum erat orci ad egestas. Pulvinar enim nunc, ultricies felis primis ut! Vehicula euismod sociosqu est donec quisque faucibus metus. Maximus imperdiet curae praesent augue magna praesent etiam aliquam. Neque proin facilisis phasellus dignissim praesent imperdiet odio lorem convallis. Eu himenaeos pharetra porta pellentesque curabitur suscipit. Malesuada odio mi quisque bibendum aenean tempor nec nec amet.
Nullam hac morbi sagittis est tortor cras platea nostra. Dui pretium orci nulla commodo maximus. Cursus sapien pretium arcu nisl enim fringilla ante. Non lobortis elit; dui mus litora eget. Enim morbi sem ridiculus eget ultricies leo imperdiet? Sollicitudin pulvinar vulputate torquent dolor fames inceptos sollicitudin. Rhoncus inceptos habitant donec consectetur malesuada fringilla metus. Non ad nascetur venenatis nisi eleifend sagittis nullam cubilia. Bibendum condimentum nulla cursus ultrices sed. Congue curae morbi tempor vehicula penatibus turpis.
Ornare enim massa blandit blandit amet id augue at tellus. Primis sit sodales accumsan montes sagittis ridiculus. Himenaeos sociosqu suspendisse ipsum nulla penatibus mauris cursus. Conubia sit sed sollicitudin curae rhoncus torquent. Dictumst porta magnis potenti non nulla justo metus mauris. Eleifend netus varius enim dignissim nisl, massa neque. Placerat luctus convallis per maecenas fringilla dui facilisi parturient. Nibh eleifend enim ultricies amet himenaeos. Libero neque pellentesque lobortis, nisl felis habitasse suspendisse consectetur? Facilisis nulla feugiat magna ac lectus porta hendrerit cubilia imperdiet.
Ut convallis pellentesque montes nam sodales bibendum nec aenean sapien. Dignissim elementum ut faucibus congue aliquet dictum sit turpis. In duis cubilia iaculis fusce nisi parturient commodo sit. Senectus pharetra ad cubilia praesent vulputate imperdiet est taciti. Sem sociosqu enim arcu faucibus tortor taciti malesuada. Dignissim eros bibendum maecenas orci id congue posuere. Eros sodales rutrum penatibus hendrerit sagittis faucibus. Sapien rhoncus cubilia molestie potenti rhoncus est ante dignissim habitant.
Condimentum placerat mauris posuere sed curabitur ac. Turpis in neque facilisi vulputate ullamcorper. Tincidunt lacus faucibus nascetur auctor aptent ad dignissim ridiculus eros. Magnis phasellus praesent massa praesent ac metus lectus torquent. Aliquam amet mattis gravida vel ante sagittis mollis penatibus. Posuere proin potenti, eget efficitur primis tincidunt sodales at. Diam lacus ultricies integer tempor ultricies; penatibus ullamcorper erat tincidunt. Bibendum dictum donec consequat vel amet auctor sagittis. Habitant est elementum massa platea porta ac primis est. Primis mollis nisl venenatis gravida; aptent platea in bibendum.
Dapibus porttitor pretium dapibus nibh commodo. Convallis bibendum sed nullam senectus penatibus sapien. Ex velit sem sagittis orci viverra lectus. Velit in consectetur, vulputate bibendum parturient posuere quam dictumst vulputate. Felis dis hendrerit id venenatis mattis. Aaliquet mattis arcu fusce platea tincidunt euismod. Potenti purus tincidunt varius mi class litora aliquet morbi et. Ultrices laoreet tellus augue vehicula facilisi. Nec tincidunt per morbi netus nec. Ante fringilla rhoncus congue; sodales magna ultrices.
Fringilla blandit efficitur curae ornare morbi ac. Felis nulla quam primis enim augue morbi. Eu interdum condimentum senectus, dictumst platea justo auctor phasellus. Maximus ad hendrerit a condimentum class curae. Sodales ex faucibus aliquam pharetra; phasellus sed velit fringilla. Class massa velit nullam himenaeos torquent scelerisque nulla accumsan cubilia? Suscipit sagittis congue per euismod eget accumsan velit.
Consectetur sem cras vestibulum turpis sem pharetra ipsum cras aliquet. Turpis lacus mus accumsan nec tellus montes. Fermentum hendrerit luctus potenti ac finibus. Porttitor mattis maximus accumsan blandit ligula congue nascetur. Mauris rutrum erat eleifend molestie non sollicitudin hac et. Orci eros integer praesent accumsan congue per mattis. Cursus elementum platea donec class lobortis praesent. Maximus vivamus magnis lacus mauris interdum egestas montes duis.
Tempor posuere sem neque quis venenatis hac purus. Orci ultrices quisque libero ante; a tristique. Nibh cursus pretium class ridiculus leo eu molestie phasellus neque. Montes vitae netus class dui elit. Penatibus ex tempor senectus tortor curabitur nisl. Rhoncus pretium etiam urna ante volutpat tristique. Nisi est enim maecenas bibendum ultricies felis duis tempor integer. Elementum finibus imperdiet gravida penatibus luctus inceptos arcu felis.
Vitae dui ut vulputate himenaeos urna primis in semper. Justo nec viverra dui eu tincidunt! Penatibus nibh semper varius eleifend ridiculus fusce aliquet. Aptent diam natoque ex primis nibh posuere. Parturient vitae dis ligula ligula sit ipsum nam. Et tincidunt pretium porta; nullam condimentum litora imperdiet. Purus vivamus ullamcorper finibus, aliquet nulla diam turpis. Aenean vulputate ultricies nisl primis nullam rutrum amet.
Cursus maximus velit habitant nascetur parturient aenean ultricies torquent scelerisque. Suscipit risus sed turpis et justo laoreet tortor consectetur conubia. Nascetur orci dignissim dolor proin himenaeos porta. Tempus adipiscing pretium amet condimentum nascetur at donec donec. Accumsan pretium metus pulvinar elementum molestie vestibulum? Lorem aptent mattis vulputate malesuada ridiculus; sed diam mattis. Volutpat ligula egestas, pellentesque pharetra a nisl elit.
Mus ornare venenatis duis eu fringilla justo. Aliquam potenti turpis habitasse efficitur ipsum lobortis semper pharetra. Dictum consequat mi tristique facilisis sagittis nostra eu. Etiam massa rhoncus rutrum dui; tortor quisque. Penatibus risus accumsan aenean semper urna. Elementum ipsum iaculis habitasse dignissim dapibus dictumst. Tortor dictum fringilla mattis potenti sagittis; tempus libero.
Pellentesque lacinia mattis ipsum risus dictum eget vitae. Platea quisque ligula risus volutpat praesent; inceptos litora dolor. Dictumst ornare habitasse himenaeos malesuada nisi; cras mauris. Et vestibulum tellus fames porta mus ut. Integer suscipit potenti eleifend sapien ipsum senectus nisi. Convallis sollicitudin class mollis inceptos; a commodo tellus proin. Felis fames vitae libero auctor luctus. Proin platea luctus porta ultricies conubia ullamcorper donec augue. Vehicula magna facilisis nisl porttitor placerat.
Dapibus malesuada penatibus libero facilisis; quisque rutrum vehicula ridiculus pharetra. Suspendisse maximus nascetur, nisl blandit felis nibh quis. Curabitur montes phasellus volutpat elit nam convallis. Primis adipiscing nam eleifend, aptent magna volutpat. Dapibus pellentesque purus mauris varius felis malesuada. Risus sollicitudin arcu eros sem efficitur sem pretium. Class velit torquent ante curabitur facilisis ante ultrices. Finibus dapibus libero consequat scelerisque; tincidunt semper. Quis porta maecenas; facilisi mauris sem ipsum. Commodo non pellentesque leo inceptos mus; nisl lacus.
Pretium interdum suscipit ante ad venenatis eget magnis finibus lorem. Mauris id tortor velit erat, sagittis dui sociosqu. Fusce aenean vulputate augue potenti ridiculus erat dis. Augue auctor enim aliquet vel ultricies amet consectetur. Fusce vitae interdum varius himenaeos leo rhoncus. Laoreet velit ipsum consequat rutrum lacinia adipiscing. Faucibus pellentesque convallis magna; pellentesque varius netus! Laoreet phasellus torquent arcu nostra; vehicula aliquet enim.
Imperdiet ad felis arcu quis nostra. Potenti purus luctus nostra integer venenatis eros id. Purus cursus facilisis maecenas primis lobortis litora consectetur maecenas laoreet. Blandit at magnis ad libero nisi tempor vehicula feugiat ridiculus! Massa gravida dapibus phasellus iaculis aptent nibh hendrerit faucibus. Vehicula justo tincidunt maecenas ad hendrerit commodo. Vehicula libero class natoque sagittis metus donec.
Duis proin bibendum malesuada ullamcorper consectetur? Dolor habitant pretium inceptos semper mauris sit mi ante ut. Sociosqu magna vehicula molestie vestibulum laoreet posuere dictum. Ullamcorper justo molestie dapibus tortor morbi porta. Eu sagittis montes hendrerit magnis senectus fringilla etiam. Ornare hendrerit fermentum porttitor leo tempus. Porttitor efficitur vivamus purus felis dui condimentum, tempor inceptos elementum. Sodales nam ornare dignissim per tempor primis. Nisi sagittis viverra vestibulum vivamus porttitor ridiculus pulvinar turpis.
At volutpat tincidunt proin tempus fusce interdum. Neque id rhoncus cras tempus urna auctor duis. Donec congue molestie molestie faucibus amet posuere vel mus. Accumsan integer urna convallis ullamcorper rhoncus habitant. Ante montes donec consectetur ad at dignissim. Vehicula habitant elementum quisque blandit malesuada tristique magnis vivamus nulla.
Tristique aliquet a dis viverra suspendisse cubilia sapien feugiat. Sollicitudin velit vivamus maximus, fames ante dis. Massa dis lectus erat natoque leo. Pharetra maecenas lorem habitasse libero scelerisque. Nulla aptent sollicitudin euismod scelerisque parturient, dignissim bibendum montes. Euismod suspendisse cubilia elementum fermentum a sed facilisi malesuada nisl. Mauris parturient hac facilisi tempor sollicitudin rutrum aliquam ligula convallis. Magnis fringilla vestibulum per risus semper aliquet congue. Habitasse eleifend mi ut pulvinar pellentesque nostra.
Hendrerit ad elit ligula hendrerit consectetur amet neque. Malesuada nullam interdum quis id elementum; suscipit nullam sollicitudin odio. Vulputate leo fringilla fringilla dapibus ac. Himenaeos parturient sem commodo amet luctus sem risus parturient pretium. Tempor curabitur habitasse efficitur montes nisl id donec. Placerat aliquet diam posuere pellentesque euismod. Nisi purus nascetur; tortor torquent magna commodo. Curae vivamus cras auctor sagittis aenean sed. Fusce aenean id praesent condimentum aliquet suscipit aliquet.
Potenti platea dictum eleifend integer torquent platea. Ipsum in commodo donec dignissim natoque mi elementum. Dui cras tincidunt nibh rutrum vestibulum nec est tortor. Ipsum per placerat magna rhoncus enim fermentum. Torquent diam imperdiet nibh efficitur posuere dis; interdum libero. Eros volutpat leo, est risus fames consequat.
Elementum felis quam class; convallis pellentesque consectetur nec. Nam ipsum vulputate finibus auctor fermentum quam ad. Faucibus sodales lobortis rhoncus venenatis rutrum est libero. Hendrerit vestibulum ultricies sodales nibh; dui habitant sollicitudin mi. Gravida metus nullam sollicitudin donec consequat rutrum. Ligula semper curabitur vulputate blandit maximus. Dis eget massa proin faucibus maximus. Ut platea suspendisse rutrum imperdiet hendrerit porta curabitur. Litora metus malesuada metus fringilla urna duis urna.
Ridiculus risus pulvinar eleifend accumsan ac arcu facilisis. Nisi consectetur platea sem netus volutpat pretium. Mauris eget cubilia integer vitae non eros curae sapien. Nullam eleifend semper facilisis curae in. Turpis cubilia magnis congue dictum congue. Magnis dictum ultricies habitasse parturient eleifend cubilia natoque ultricies enim. Praesent elementum rutrum per pretium enim pulvinar lacinia netus enim. Efficitur est enim penatibus metus a hac auctor.
Felis dignissim et odio augue euismod urna phasellus. At ultrices et nullam tincidunt ipsum ultricies tempor. Etiam sit tellus eu massa vestibulum lacinia. Felis himenaeos magnis curabitur massa magnis efficitur taciti odio. Nibh aptent ornare habitant fusce malesuada turpis faucibus. Nunc viverra nunc hac porta eget commodo faucibus erat. Congue leo nec sed primis elit mattis pharetra posuere.
Facilisi lectus phasellus consectetur mauris odio curae ultrices nibh. Tortor malesuada vitae sagittis aptent ad blandit. Dictumst habitasse convallis, fames euismod auctor ac ridiculus convallis. Aliquam phasellus iaculis pulvinar aliquam vel vel enim quisque. Fusce morbi maecenas praesent arcu vehicula non eleifend aptent. Mauris curae tortor ante venenatis aliquet curabitur. Proin semper sapien faucibus parturient ipsum. Rutrum nisl venenatis placerat a etiam.
Pellentesque quam vehicula posuere metus cras ex risus. Ac id rutrum montes elit vestibulum class quisque. Sit eros mi integer duis vivamus natoque cras mattis. Mauris arcu augue tempor vitae sollicitudin morbi duis. Vitae posuere odio ante massa fermentum parturient venenatis primis hendrerit. Posuere turpis id consectetur nostra pretium; curabitur amet.
Non duis turpis hendrerit donec consectetur. Sed cras vehicula dolor nostra aliquam. Conubia dictumst ullamcorper maximus ipsum at quis. Sit sociosqu nisl ante orci blandit vehicula. Dis dignissim mus aenean diam hendrerit ac. Ridiculus auctor consectetur porttitor suspendisse dictum porta est id. Eu aenean orci consectetur sapien facilisis dignissim. Ultrices fusce quis ad consectetur arcu ante. Tortor lacinia quisque sem maximus vestibulum tempus penatibus. Id conubia dapibus hendrerit vitae taciti, lacus morbi montes.
Vestibulum ante sodales etiam ex cubilia felis magnis rutrum. Finibus pharetra sed malesuada tincidunt facilisi duis augue bibendum urna! Sed mus ultricies libero sit; nostra maecenas est ad fringilla. Est ultrices donec quis vulputate; condimentum posuere. Nascetur orci fusce lorem sociosqu placerat sociosqu. Natoque senectus sollicitudin malesuada accumsan dolor luctus. Tempor vulputate torquent adipiscing feugiat vulputate eget; torquent sollicitudin.
Feugiat justo lectus torquent vitae rutrum integer vivamus justo. Sem ultricies et vel nibh erat suscipit. Ex est nascetur quam, a purus ex. Pharetra pulvinar convallis himenaeos mus porttitor ultrices. Senectus eu natoque euismod felis orci montes massa pulvinar. Sit suspendisse hac rutrum iaculis; interdum aenean orci. Finibus tempor fermentum fringilla phasellus, faucibus taciti mi rhoncus. Curae diam himenaeos orci sapien viverra tempus; vitae fusce.
Conubia lorem ad placerat sociosqu metus ullamcorper. Mus nec vulputate diam ligula nascetur litora. Justo pharetra diam hac fusce mus fames. Diam at magna ridiculus potenti curae primis. Felis odio dolor felis condimentum sed leo dictum rhoncus sed. Dolor proin eleifend placerat, pulvinar volutpat adipiscing?
Senectus est proin a dignissim efficitur eleifend. Semper felis sem posuere; suspendisse mollis quis. Vivamus venenatis vehicula facilisi ornare scelerisque ullamcorper bibendum. Neque dapibus ultrices litora hendrerit sed finibus. Vulputate varius volutpat class mattis nec iaculis pharetra a sapien. Lobortis pulvinar nisi metus eu sociosqu libero habitasse et. Eget convallis velit dignissim molestie fermentum quisque quam ornare. Felis maecenas risus primis imperdiet lacus accumsan diam.
Integer eu lacus imperdiet vivamus, natoque dolor faucibus sit. Iaculis nam pellentesque erat gravida sociosqu lobortis amet nibh facilisi. Bibendum rutrum laoreet elementum dolor, leo metus urna. Mollis pellentesque est ultrices ultrices libero urna. Sagittis pretium montes luctus sapien nibh commodo quis himenaeos arcu. Integer quis curabitur orci potenti at torquent? Id condimentum nunc vel massa lectus arcu. Lacinia venenatis pharetra vivamus euismod facilisi blandit est? Potenti consequat sit rhoncus tincidunt ridiculus nulla laoreet quam.
Nibh lacus proin phasellus est mattis congue mi. Imperdiet feugiat aptent orci commodo justo felis. Donec convallis ex purus praesent, vivamus netus fermentum. Neque tortor in laoreet adipiscing non venenatis. Rutrum nec cursus; luctus suspendisse turpis etiam. Primis curae nulla himenaeos tellus; cubilia egestas. Diam neque aliquam netus senectus eget ac. Risus semper quisque eros, mauris rutrum commodo. Aenean sem nec suspendisse tincidunt turpis ex semper dolor. Nunc ex mollis lectus dignissim vitae et risus.
Fames tellus potenti fringilla ac semper mi hendrerit nostra. Senectus bibendum per cursus nulla tempor aenean congue nulla nam. Velit integer pulvinar nec, ante primis platea maximus. Potenti cursus ultrices quis tempus euismod finibus feugiat potenti parturient. Finibus vel diam porta ad a sagittis. In aliquam lacinia curae ridiculus nibh neque dui magna duis. Lacus habitasse aenean porttitor velit cras in sodales habitasse.
Sem odio eget taciti auctor lectus est efficitur et. Neque cubilia dis suscipit torquent velit faucibus inceptos natoque. Condimentum semper senectus turpis ante egestas; quis viverra facilisi malesuada? Penatibus maximus euismod imperdiet vitae duis ornare. Mauris vel lacinia vivamus tempus id gravida nisi sodales nostra. Lacinia varius tristique turpis vehicula ipsum felis consequat blandit. Dis torquent eleifend interdum turpis dolor aptent accumsan dictumst.
Ante laoreet erat; praesent risus amet dis arcu fringilla. Augue porttitor dapibus dictumst rutrum pulvinar metus. Enim porttitor elit accumsan class aenean ullamcorper class. Sagittis justo scelerisque dictumst diam lobortis a. Dis amet ornare commodo phasellus eleifend; tempus class netus. Dolor mi hendrerit augue porta tortor fusce. Dolor laoreet placerat ad morbi himenaeos efficitur, ut lacinia ac. Sed eget ultricies consectetur posuere magna dapibus nulla odio. Enim cubilia suscipit tincidunt sociosqu augue fames sem ridiculus facilisi. Egestas lacinia elementum porta pretium nibh sapien hendrerit leo.
Inceptos amet suscipit velit iaculis pellentesque, pellentesque at est purus. Ad auctor metus vel, per suspendisse porttitor consequat. Aenean nisi auctor; dignissim suspendisse vehicula proin. Arcu risus convallis et suscipit maximus phasellus molestie. Tristique erat donec adipiscing tellus vulputate elementum molestie placerat semper. Erat dictum leo mattis, dictum volutpat himenaeos volutpat! Fames ornare pharetra elementum massa etiam quam purus magnis. Lectus fermentum accumsan nisi; non hac himenaeos ligula risus proin.
Platea justo porta hendrerit malesuada platea pellentesque id sollicitudin. Nisl rutrum maecenas habitant tempor diam massa. Senectus vehicula cubilia penatibus laoreet penatibus curabitur phasellus pellentesque mattis. Sagittis sit purus posuere praesent scelerisque; sit pellentesque? Cras velit inceptos libero tortor pulvinar. Dolor erat dolor massa mattis blandit at in purus. Placerat enim sapien nunc posuere mi tortor fringilla. Erat rhoncus ultrices justo tincidunt class imperdiet primis? Semper ut elit est nulla morbi.
Ante fermentum accumsan natoque placerat quam at. Iaculis posuere ut quam porta porta faucibus feugiat ligula ultrices. Velit lacus conubia cubilia morbi vitae litora ullamcorper per taciti. Urna hac turpis ultrices lacus vestibulum lobortis nulla sagittis a. Rhoncus rhoncus dapibus tristique efficitur ullamcorper molestie. Diam fringilla vestibulum netus morbi mus. Cursus tortor massa lectus gravida suscipit fames mi accumsan vitae. Luctus molestie ut habitant ultrices feugiat duis parturient vehicula vehicula.
Non sagittis litora neque laoreet rhoncus ullamcorper quis. Integer interdum taciti tellus ultrices sagittis vulputate himenaeos. Fermentum ridiculus lacinia lacinia hendrerit taciti suspendisse dignissim. Dis sagittis consectetur rhoncus id posuere faucibus taciti habitasse. Eget scelerisque lacinia pretium id; non nec. Accumsan platea egestas curabitur massa semper donec ullamcorper facilisi porta? Penatibus ut vulputate convallis magna ex eleifend. Nunc parturient morbi sollicitudin etiam eu diam.
Cubilia facilisi accumsan inceptos auctor, nisl platea magnis. Nostra nulla hac ut aliquet sollicitudin. Egestas non rutrum pretium risus rutrum tristique gravida in. Curae ornare aenean porttitor ac vestibulum. Conubia ridiculus ridiculus parturient vitae neque. Libero mi platea sit curabitur justo sollicitudin suscipit ante? Enim amet proin parturient, per tempor montes.
Turpis id eu eget pretium, tempus praesent vehicula. Class elit eleifend amet lacinia etiam sollicitudin mollis. Nam sociosqu arcu odio velit aliquam vestibulum risus euismod? Nunc fermentum sodales ut placerat habitant lectus ultrices lacinia mi. Eu felis aliquam id mollis id consectetur mus. Vehicula dui nam fames vel mattis rhoncus ex mattis. Nibh curabitur et penatibus placerat vehicula mi. Curae nam suscipit vel molestie, diam penatibus. Magnis interdum purus quam feugiat vel.
Ante odio senectus lectus nunc hendrerit. Dolor sociosqu mattis facilisis proin egestas. Nostra etiam euismod purus porta primis. Facilisis parturient porttitor ornare senectus platea ac, nisi penatibus. Himenaeos quis blandit viverra porta netus. Venenatis mi suspendisse nullam justo nulla fames condimentum fringilla. Massa imperdiet rutrum nullam; facilisi nulla augue curae.
Netus felis ante et rhoncus eget aliquet scelerisque scelerisque dictum. Nisi eu tempor, gravida mattis lobortis nisi. Avolutpat dapibus viverra quisque sodales fermentum donec dictum. Diam pulvinar quis nec eleifend ultricies quisque parturient elementum commodo. Blandit felis mauris class mauris porttitor. Tellus mollis cras nisl nulla amet? Potenti euismod vel; senectus cras primis libero. Integer sodales amet; suspendisse tristique feugiat vel semper. Vulputate suscipit tincidunt molestie dignissim maecenas platea varius a consequat. Feugiat ex vulputate nostra elementum potenti ultrices pulvinar.
Blandit pulvinar penatibus nec eros lacus accumsan. Urna lobortis vitae natoque maximus odio montes litora. Semper adipiscing dis metus orci nostra sagittis. Bibendum tellus nunc felis; nascetur venenatis accumsan erat. Tempor id dui mus metus ut nec. Phasellus tempus rhoncus lorem eget dis sit per. Dui ullamcorper senectus aptent condimentum nisi efficitur metus? Auctor aliquet quisque viverra aliquet eu semper consectetur nam.
Vivamus dictum montes placerat massa nullam maximus mi. Aptent hendrerit fringilla platea etiam nisl bibendum sagittis id. Nunc suspendisse dis nostra pulvinar ridiculus integer placerat elit enim. Habitant ex leo vestibulum vehicula nunc vivamus. Proin blandit pretium scelerisque molestie habitant. Ac venenatis sodales volutpat orci erat enim. Himenaeos justo cursus accumsan tellus placerat tempor. Magna potenti tempor at accumsan morbi placerat feugiat.
Natoque proin commodo suscipit dictumst sodales varius interdum aliquet. Nec ridiculus aliquam hendrerit neque proin aliquet aliquam nostra. Odio ad tincidunt habitasse pharetra aliquet consectetur. Senectus ut porta tellus scelerisque turpis eleifend turpis. Orci vulputate nulla nibh netus nibh euismod platea sodales amet. Maximus eget vestibulum pulvinar dignissim mollis litora. Ad arcu pellentesque dolor interdum feugiat ad.
Vestibulum faucibus diam ipsum quisque dui. Ante hendrerit mus euismod penatibus metus ullamcorper aliquam phasellus. Nunc netus tincidunt porttitor arcu pulvinar dictum enim volutpat. Velit torquent fusce mus; penatibus commodo lacinia. Mi cubilia quam nulla luctus ullamcorper lacus lobortis egestas. Aptent ultricies in suscipit ex pellentesque hac interdum tempus. Montes nostra vivamus netus congue aenean, nascetur sodales maximus. Dis euismod sapien dis sed vitae fames at libero. Dolor mattis feugiat nullam morbi class nostra fusce.
Nulla facilisis curae lacinia donec quam. Bibendum parturient tortor congue at efficitur ex tristique mi. In vestibulum parturient consectetur rutrum euismod finibus auctor luctus. Lacinia felis ridiculus inceptos taciti ipsum neque penatibus. Parturient ante nec porta metus odio fames. Dis eget proin pretium, pulvinar inceptos sollicitudin erat. Iaculis ligula rutrum dolor netus ac class felis.
================================================
FILE: zipstorer.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.10
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZipStorer", "src\ZipStorer.csproj", "{AE002F5E-F361-4F5A-84C0-446C6FB19687}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZipStorerTest", "test\ZipStorerTest.csproj", "{B3B98D4B-E399-4677-81E9-E50B83E65D52}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Debug|x64.ActiveCfg = Debug|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Debug|x64.Build.0 = Debug|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Debug|x86.ActiveCfg = Debug|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Debug|x86.Build.0 = Debug|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Release|Any CPU.Build.0 = Release|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Release|x64.ActiveCfg = Release|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Release|x64.Build.0 = Release|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Release|x86.ActiveCfg = Release|Any CPU
{AE002F5E-F361-4F5A-84C0-446C6FB19687}.Release|x86.Build.0 = Release|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Debug|x64.ActiveCfg = Debug|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Debug|x64.Build.0 = Debug|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Debug|x86.ActiveCfg = Debug|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Debug|x86.Build.0 = Debug|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Release|Any CPU.Build.0 = Release|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Release|x64.ActiveCfg = Release|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Release|x64.Build.0 = Release|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Release|x86.ActiveCfg = Release|Any CPU
{B3B98D4B-E399-4677-81E9-E50B83E65D52}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
gitextract_50um_gkx/ ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── .vscode/ │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── LICENSE.md ├── README.md ├── src/ │ ├── ZipCrc32.cs │ ├── ZipDate.cs │ ├── ZipFileEntry.cs │ ├── ZipStorer.cs │ └── ZipStorer.csproj ├── test/ │ ├── Program.cs │ ├── ZipStorerTest.csproj │ └── lorem_ipsum.txt └── zipstorer.sln
SYMBOL INDEX (64 symbols across 5 files)
FILE: src/ZipCrc32.cs
class ZipCrc32 (line 11) | public static class ZipCrc32
method ZipCrc32 (line 15) | static ZipCrc32()
method UpdateCRC (line 33) | public static UInt32 UpdateCRC(UInt32 init, byte[] buffer, int count)
FILE: src/ZipDate.cs
class ZipDate (line 3) | public static class ZipDate
method DateTimeToDosTime (line 15) | public static uint DateTimeToDosTime(DateTime dt)
method DosTimeToDateTime (line 22) | public static DateTime? DosTimeToDateTime(uint dt)
FILE: src/ZipFileEntry.cs
class ZipFileEntry (line 9) | public class ZipFileEntry
method ToString (line 40) | public override string ToString()
method Equals (line 45) | public override bool Equals(object obj)
method GetHashCode (line 55) | public override int GetHashCode()
method IsZip64ExtNeeded (line 60) | public bool IsZip64ExtNeeded(byte mask)
method CreateExtraInfo (line 73) | public byte[] CreateExtraInfo(bool localHeader)
method ReadExtraInfo (line 110) | public void ReadExtraInfo(byte[] buffer, int offset, int extraSize)
FILE: src/ZipStorer.cs
class ZipStorer (line 14) | public class ZipStorer : IDisposable
type Compression (line 25) | public enum Compression : ushort
method ZipStorer (line 66) | static ZipStorer()
method Create (line 82) | public static ZipStorer Create(string filename, string comment = null)
method Create (line 100) | public static ZipStorer Create(Stream stream, string comment = null, b...
method Open (line 120) | public static ZipStorer Open(string filename, FileAccess access)
method Open (line 159) | public static ZipStorer Open(Stream stream, FileAccess access, bool le...
method AddFile (line 192) | public ZipFileEntry AddFile(Compression method, string pathname, strin...
method AddStream (line 207) | public ZipFileEntry AddStream(Compression method, string filenameInZip...
method AddStreamAsync (line 221) | public async Task<ZipFileEntry> AddStreamAsync(Compression method, str...
method AddDirectory (line 262) | public void AddDirectory(Compression method, string pathname, string p...
method Close (line 301) | public void Close()
method ReadCentralDir (line 339) | public List<ZipFileEntry> ReadCentralDir(bool skipFileOffsetCalculatio...
method ExtractFile (line 415) | public bool ExtractFile(ZipFileEntry zfe, string filename)
method ExtractFile (line 448) | public bool ExtractFile(ZipFileEntry zfe, Stream stream)
method ExtractFileAsync (line 460) | public async Task<bool> ExtractFileAsync(ZipFileEntry zfe, Stream stream)
method ExtractFile (line 519) | public bool ExtractFile(ZipFileEntry zfe, out byte[] file)
method RemoveEntries (line 543) | public static bool RemoveEntries(ref ZipStorer zip, List<ZipFileEntry>...
method GetEntries (line 642) | public List<ZipFileEntry> GetEntries()
method GetEntry (line 656) | public ZipFileEntry GetEntry(string name, StringComparison comparisonT...
method getFileOffset (line 666) | private long getFileOffset(long headerOffset)
method writeLocalHeader (line 695) | private void writeLocalHeader(ZipFileEntry zfe)
method writeCentralDirRecord (line 739) | private void writeCentralDirRecord(ZipFileEntry zfe)
method writeEndRecord (line 817) | private void writeEndRecord(long size, long offset)
method storeAsync (line 850) | private async Task<Compression> storeAsync(ZipFileEntry zfe, Stream so...
method updateCrcAndSizes (line 912) | private void updateCrcAndSizes(ZipFileEntry zfe)
method updateLocalHeaderExtraFields (line 933) | private bool updateLocalHeaderExtraFields(ZipFileEntry zfe)
method readFileInfo (line 969) | private bool readFileInfo()
method normalizedFilename (line 1054) | private static string normalizedFilename(string filename)
method get32bitSize (line 1067) | private static uint get32bitSize(long size)
method Dispose (line 1077) | public void Dispose()
method Dispose (line 1084) | protected virtual void Dispose(bool disposing)
FILE: test/Program.cs
class Program (line 10) | [TestClass]
method Main (line 13) | static void Main()
method Program (line 30) | public Program()
method Folder_Test (line 36) | public void Folder_Test()
method OpenRead_Test (line 48) | [TestMethod]
method ReadCentralDir_Test (line 56) | [TestMethod]
method ExtractFolder_Test (line 66) | [TestMethod]
method ExtractFile_Test (line 78) | [TestMethod]
method ReadModifyTime_Test (line 90) | [TestMethod]
method ReadAccessTime_Test (line 100) | [TestMethod]
method ReadCreationTime_Test (line 110) | [TestMethod]
method CreateFile_Test (line 121) | [TestMethod]
method AddStream_Test (line 130) | [TestMethod]
method AddStreamDate_Test (line 144) | [TestMethod]
method Compression_Test (line 161) | [TestMethod]
method RemoveEntries_Test (line 177) | [TestMethod]
method Zip64_Test (line 227) | public void Zip64_Test()
method streamsAreEqual (line 299) | private bool streamsAreEqual(Stream s1, Stream s2)
method createSampleFile (line 308) | private void createSampleFile()
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (140K chars).
[
{
"path": ".github/workflows/main.yml",
"chars": 1964,
"preview": "name: CI\n\non:\n push:\n branches:\n - master\n paths-ignore:\n - README.md\n\njobs:\n build:\n runs-on: ubun"
},
{
"path": ".gitignore",
"chars": 79,
"preview": ".DS_Store\n.suo\n**/bin\n**/obj\n**/*.nupkg\ntest/sample1.zip\ntest/.vs/*\n/.vs\n*.ini\n"
},
{
"path": ".vscode/launch.json",
"chars": 1151,
"preview": "{\n // Use IntelliSense to find out which attributes exist for C# debugging\n // Use hover for the description of the "
},
{
"path": ".vscode/settings.json",
"chars": 111,
"preview": "{\n \"files.exclude\": {\n \"**/bin\": true,\n \"**/obj\": true,\n \"**/TestResults\": true\n }\n}"
},
{
"path": ".vscode/tasks.json",
"chars": 923,
"preview": "{\n \"version\": \"2.0.0\",\n \"tasks\": [\n {\n \"label\": \"build\",\n \"command\": \"dotnet\",\n "
},
{
"path": "LICENSE.md",
"chars": 1084,
"preview": "# The MIT License (MIT)\n\nCopyright (c) 2016 Jaime Olivares\n\nPermission is hereby granted, free of charge, to any person "
},
{
"path": "README.md",
"chars": 8250,
"preview": "# ZipStorer\nA Pure C# class for storing files in Zip format\n\n[](ht"
},
{
"path": "src/ZipCrc32.cs",
"chars": 1464,
"preview": "namespace System.IO.Compression\n{\n /* CRC32 algorithm\n The 'magic number' for the CRC is 0xdebb20e3. \n "
},
{
"path": "src/ZipDate.cs",
"chars": 1413,
"preview": "namespace System.IO.Compression\n{\n public static class ZipDate\n {\n /* DOS Date and time:\n MS-DOS"
},
{
"path": "src/ZipFileEntry.cs",
"chars": 6653,
"preview": "// ZipStorer, by Jaime Olivares\n// Website: http://github.com/jaime-olivares/zipstorer\n\nnamespace System.IO.Compression\n"
},
{
"path": "src/ZipStorer.cs",
"chars": 49201,
"preview": "// ZipStorer, by Jaime Olivares\r\n// Website: http://github.com/jaime-olivares/zipstorer\r\n\r\nusing System.Collections.Gene"
},
{
"path": "src/ZipStorer.csproj",
"chars": 1563,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n <PropertyGroup>\n <Description>A Pure C# class to handle Zip files</Descrip"
},
{
"path": "test/Program.cs",
"chars": 11103,
"preview": "using System;\nusing System.Collections;\nusing System.IO;\nusing System.IO.Compression;\nusing System.Security.Cryptography"
},
{
"path": "test/ZipStorerTest.csproj",
"chars": 995,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n <PropertyGroup>\r\n <Authors>Jaime Olivares</Authors>\r\n <VersionPrefix>4.2.0"
},
{
"path": "test/lorem_ipsum.txt",
"chars": 46980,
"preview": "Lorem ipsum odor amet, consectetuer adipiscing elit. Eros fringilla vitae, faucibus rhoncus faucibus malesuada non magni"
},
{
"path": "zipstorer.sln",
"chars": 2794,
"preview": "Microsoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26228.10\nMini"
}
]
About this extraction
This page contains the full source code of the jaime-olivares/zipstorer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (132.5 KB), approximately 31.3k tokens, and a symbol index with 64 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.