From c306dd6eb98e13b56edb68add002f48ad99d73fb Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:00:24 -0400 Subject: [PATCH 1/8] feat: Initial commit of IcdBinaryReader --- .../ICD.Common.Utils_SimplSharp.csproj | 1 + ICD.Common.Utils/IO/IcdBinaryReader.cs | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 ICD.Common.Utils/IO/IcdBinaryReader.cs diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 89b8d8a..253c0bd 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -98,6 +98,7 @@ + diff --git a/ICD.Common.Utils/IO/IcdBinaryReader.cs b/ICD.Common.Utils/IO/IcdBinaryReader.cs new file mode 100644 index 0000000..78f9e5e --- /dev/null +++ b/ICD.Common.Utils/IO/IcdBinaryReader.cs @@ -0,0 +1,69 @@ +using System; +#if SIMPLSHARP +using Crestron.SimplSharp.CrestronIO; +#else +using System.IO; +#endif + +namespace ICD.Common.Utils.IO +{ + public sealed class IcdBinaryReader : IDisposable + { + private readonly BinaryReader m_Reader; + + public BinaryReader WrappedReader { get { return m_Reader; } } + + /// + /// Constructor. + /// + /// + public IcdBinaryReader(IcdStream stream) + : this(new BinaryReader(stream.WrappedStream)) + { + } + + /// + /// Constructor. + /// + /// + public IcdBinaryReader(BinaryReader reader) + { + m_Reader = reader; + } + + public void Dispose() + { + m_Reader.Dispose(); + } + + public void Close() + { + m_Reader.Close(); + } + + public ushort ReadUInt16() + { + return m_Reader.ReadUInt16(); + } + + public int ReadInt32() + { + return m_Reader.ReadInt32(); + } + + public short ReadInt16() + { + return m_Reader.ReadInt16(); + } + + public uint ReadUInt32() + { + return m_Reader.ReadUInt32(); + } + + public byte[] ReadBytes(short numberOfBytesToRead) + { + return m_Reader.ReadBytes(numberOfBytesToRead); + } + } +} \ No newline at end of file From 38dd85d79df8e58bd23498c14587bd5cf0aced7b Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:00:46 -0400 Subject: [PATCH 2/8] feat: Exposing additional stream features in IcdStream --- ICD.Common.Utils/IO/IcdStream.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ICD.Common.Utils/IO/IcdStream.cs b/ICD.Common.Utils/IO/IcdStream.cs index a415110..9694d04 100644 --- a/ICD.Common.Utils/IO/IcdStream.cs +++ b/ICD.Common.Utils/IO/IcdStream.cs @@ -13,6 +13,10 @@ namespace ICD.Common.Utils.IO public Stream WrappedStream { get { return m_Stream; } } + public long Length { get { return m_Stream.Length; } } + + public long Position { get { return m_Stream.Position; } } + /// /// Constructor. /// @@ -29,5 +33,10 @@ namespace ICD.Common.Utils.IO { m_Stream.Dispose(); } + + public void Seek(long offset, SeekOrigin end) + { + m_Stream.Seek(offset, end); + } } } From e4b0c9f91a9566879c6019709c61974da4c9ed03 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:01:31 -0400 Subject: [PATCH 3/8] feat: Initial commit of eSeekOrigin enum --- .../ICD.Common.Utils_SimplSharp.csproj | 1 + ICD.Common.Utils/IO/eSeekOrigin.cs | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 ICD.Common.Utils/IO/eSeekOrigin.cs diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 253c0bd..ab52575 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -99,6 +99,7 @@ + diff --git a/ICD.Common.Utils/IO/eSeekOrigin.cs b/ICD.Common.Utils/IO/eSeekOrigin.cs new file mode 100644 index 0000000..ec0c0b0 --- /dev/null +++ b/ICD.Common.Utils/IO/eSeekOrigin.cs @@ -0,0 +1,40 @@ +using System; +#if SIMPLSHARP +using Crestron.SimplSharp.CrestronIO; +#else +using System.IO; +#endif + +namespace ICD.Common.Utils.IO +{ + public enum eSeekOrigin + { + Begin, + Current, + End, + } + + public static class SeekOriginExtensions + { + /// + /// Converts the seek origin enum to a system seek origin. + /// + /// + /// + public static SeekOrigin ToSeekOrigin(this eSeekOrigin extends) + { + switch (extends) + { + case eSeekOrigin.Begin: + return SeekOrigin.Begin; + case eSeekOrigin.Current: + return SeekOrigin.Current; + case eSeekOrigin.End: + return SeekOrigin.End; + + default: + throw new ArgumentOutOfRangeException("extends"); + } + } + } +} From da31fae213de337d54358bfa713dca5cd6c31458 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:02:00 -0400 Subject: [PATCH 4/8] feat: Exposing OpenRead method in IcdFile --- ICD.Common.Utils/IO/IcdFile.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ICD.Common.Utils/IO/IcdFile.cs b/ICD.Common.Utils/IO/IcdFile.cs index 0b90670..7e01e98 100644 --- a/ICD.Common.Utils/IO/IcdFile.cs +++ b/ICD.Common.Utils/IO/IcdFile.cs @@ -66,6 +66,11 @@ namespace ICD.Common.Utils.IO File.Delete(path); } + public static IcdStream OpenRead(string path) + { + return new IcdFileStream(File.OpenRead(path)); + } + [PublicAPI] public static IcdFileStream OpenWrite(string path) { From 2f3b1ef57d9728b7dcca2062e1f32d7bb37b0b84 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:02:39 -0400 Subject: [PATCH 5/8] feat: Adding methods for inspecting contents of a ZIP file --- .../ICD.Common.Utils_SimplSharp.csproj | 3 +- ICD.Common.Utils/IO/Compression/IcdZip.cs | 163 ++++++++++++++++++ .../IO/Compression/IcdZipEntry.cs | 59 +++++++ ICD.Common.Utils/IcdZip.cs | 43 ----- 4 files changed, 224 insertions(+), 44 deletions(-) create mode 100644 ICD.Common.Utils/IO/Compression/IcdZip.cs create mode 100644 ICD.Common.Utils/IO/Compression/IcdZipEntry.cs delete mode 100644 ICD.Common.Utils/IcdZip.cs diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index ab52575..b199d67 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -98,6 +98,7 @@ + @@ -124,7 +125,7 @@ - + diff --git a/ICD.Common.Utils/IO/Compression/IcdZip.cs b/ICD.Common.Utils/IO/Compression/IcdZip.cs new file mode 100644 index 0000000..b9701db --- /dev/null +++ b/ICD.Common.Utils/IO/Compression/IcdZip.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +#if SIMPLSHARP + +#else +using System.IO.Compression; +#endif + +namespace ICD.Common.Utils.IO.Compression +{ + /// + /// Utils for managing archives. + /// + public static class IcdZip + { + private const int DIRECTORY_SIGNATURE = 0x06054B50; + private const int ENTRY_SIGNATURE = 0x02014B50; + + /// + /// Unzips the archive at the given path. + /// + /// + /// + /// + public static bool Unzip(string path, string outputPath, out string message) + { + try + { +#if SIMPLSHARP + CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath); + message = result.ToString(); + return result == CrestronZIP.ResultCode.ZR_OK; +#else + using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read)) + archive.ExtractToDirectory(outputPath); + message = "Success"; + return true; +#endif + } + catch (Exception e) + { + message = e.Message; + return false; + } + } + + /// + /// Gets the sequence of files names in the archive at the given path. + /// + public static IEnumerable GetFileNames(string path) + { + return GetEntries(path).Select(e => e.Name) + .Where(f => !f.EndsWith("/")) + .OrderBy(f => f); + } + + /// + /// Gets sequence of zip file entries in the archive at the given path. + /// + public static IEnumerable GetEntries(string path) + { + using (IcdStream stream = IcdFile.OpenRead(path)) + { + using (IcdBinaryReader reader = new IcdBinaryReader(stream)) + { + if (stream.Length < 22) + yield break; + + stream.Seek(-22, eSeekOrigin.End.ToSeekOrigin()); + + // find directory signature + while (reader.ReadInt32() != DIRECTORY_SIGNATURE) + { + if (stream.Position <= 5) + yield break; + + // move 1 byte back + stream.Seek(-5, eSeekOrigin.Current.ToSeekOrigin()); + } + + // read directory properties + stream.Seek(6, eSeekOrigin.Current.ToSeekOrigin()); + ushort entries = reader.ReadUInt16(); + int difSize = reader.ReadInt32(); + uint dirOffset = reader.ReadUInt32(); + stream.Seek(dirOffset, eSeekOrigin.Begin.ToSeekOrigin()); + + // read directory entries + for (int i = 0; i < entries; i++) + { + if (reader.ReadInt32() != ENTRY_SIGNATURE) + continue; + + // read file properties + reader.ReadInt32(); + bool utf8 = (reader.ReadInt16() & 0x0800) != 0; + short method = reader.ReadInt16(); + int timestamp = reader.ReadInt32(); + uint crc32 = reader.ReadUInt32(); + int compressedSize = reader.ReadInt32(); + int fileSize = reader.ReadInt32(); + short fileNameSize = reader.ReadInt16(); + short extraSize = reader.ReadInt16(); + short commentSize = reader.ReadInt16(); + int headerOffset = reader.ReadInt32(); + reader.ReadInt32(); + int fileHeaderOffset = reader.ReadInt32(); + byte[] fileNameBytes = reader.ReadBytes(fileNameSize); + stream.Seek(extraSize, eSeekOrigin.Current.ToSeekOrigin()); + byte[] fileCommentBytes = reader.ReadBytes(commentSize); + int fileDataOffset = CalculateFileDataOffset(stream, reader, fileHeaderOffset); + + // decode zip file entry + Encoding encoder = utf8 ? Encoding.UTF8 : Encoding.Default; + + yield return new IcdZipEntry + { + Name = encoder.GetString(fileNameBytes, 0, fileNameBytes.Length), + Comment = encoder.GetString(fileCommentBytes, 0, fileCommentBytes.Length), + Crc32 = crc32, + CompressedSize = compressedSize, + OriginalSize = fileSize, + HeaderOffset = fileHeaderOffset, + DataOffset = fileDataOffset, + Deflated = method == 8, + Timestamp = ConvertToDateTime(timestamp) + }; + } + } + } + } + + private static int CalculateFileDataOffset(IcdStream stream, IcdBinaryReader reader, int fileHeaderOffset) + { + long position = stream.Position; + stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin.ToSeekOrigin()); + short fileNameSize = reader.ReadInt16(); + short extraSize = reader.ReadInt16(); + + int fileOffset = (int)stream.Position + fileNameSize + extraSize; + stream.Seek(position, eSeekOrigin.Begin.ToSeekOrigin()); + return fileOffset; + } + + /// + /// Converts DOS timestamp to a instance. + /// + /// The DOS timestamp. + /// The instance. + private static DateTime ConvertToDateTime(int dosTimestamp) + { + return new DateTime((dosTimestamp >> 25) + 1980, + (dosTimestamp >> 21) & 15, + (dosTimestamp >> 16) & 31, + (dosTimestamp >> 11) & 31, + (dosTimestamp >> 5) & 63, + (dosTimestamp & 31) * 2); + } + } +} diff --git a/ICD.Common.Utils/IO/Compression/IcdZipEntry.cs b/ICD.Common.Utils/IO/Compression/IcdZipEntry.cs new file mode 100644 index 0000000..1646ab0 --- /dev/null +++ b/ICD.Common.Utils/IO/Compression/IcdZipEntry.cs @@ -0,0 +1,59 @@ +using System; + +namespace ICD.Common.Utils.IO.Compression +{ + /// + /// Zip archive entry. + /// + public sealed class IcdZipEntry + { + /// + /// Gets or sets the name of a file or a directory. + /// + public string Name { get; set; } + + /// + /// Gets or sets the comment. + /// + public string Comment { get; set; } + + /// + /// Gets or sets the CRC32. + /// + public uint Crc32 { get; set; } + + /// + /// Gets or sets the compressed size of the file. + /// + public int CompressedSize { get; set; } + + /// + /// Gets or sets the original size of the file. + /// + public int OriginalSize { get; set; } + + /// + /// Gets or sets a value indicating whether this is deflated. + /// + public bool Deflated { get; set; } + + /// + /// Gets a value indicating whether this is a directory. + /// + public bool IsDirectory { get { return Name.EndsWith("/"); } } + + /// + /// Gets or sets the timestamp. + /// + public DateTime Timestamp { get; set; } + + /// + /// Gets a value indicating whether this is a file. + /// + public bool IsFile { get { return !IsDirectory; } } + + public int HeaderOffset { get; set; } + + public int DataOffset { get; set; } + } +} diff --git a/ICD.Common.Utils/IcdZip.cs b/ICD.Common.Utils/IcdZip.cs deleted file mode 100644 index b7f1c51..0000000 --- a/ICD.Common.Utils/IcdZip.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -#if SIMPLSHARP -using Crestron.SimplSharp; -#else -using System.IO.Compression; -#endif - -namespace ICD.Common.Utils -{ - /// - /// Utils for managing archives. - /// - public static class IcdZip - { - /// - /// Unzips the archive at the given path. - /// - /// - /// - /// - public static bool Unzip(string path, string outputPath, out string message) - { - try - { -#if SIMPLSHARP - CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath); - message = result.ToString(); - return result == CrestronZIP.ResultCode.ZR_OK; -#else - using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read)) - archive.ExtractToDirectory(outputPath); - message = "Success"; - return true; -#endif - } - catch (Exception e) - { - message = e.Message; - return false; - } - } - } -} From 381b19f2e622c3376c7389f1ca8a6ff3b41e6c2b Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:39:54 -0400 Subject: [PATCH 6/8] fix: Fixing net standard build --- ICD.Common.Utils/IO/Compression/IcdZip.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ICD.Common.Utils/IO/Compression/IcdZip.cs b/ICD.Common.Utils/IO/Compression/IcdZip.cs index b9701db..5e7877b 100644 --- a/ICD.Common.Utils/IO/Compression/IcdZip.cs +++ b/ICD.Common.Utils/IO/Compression/IcdZip.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Crestron.SimplSharp; #if SIMPLSHARP - +using Crestron.SimplSharp; #else using System.IO.Compression; #endif From 1ae6a55c7db003b3166283d6bc1dc9d94129385b Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:40:15 -0400 Subject: [PATCH 7/8] docs: Adding MIT license to ZIP entries method --- ICD.Common.Utils/IO/Compression/IcdZip.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ICD.Common.Utils/IO/Compression/IcdZip.cs b/ICD.Common.Utils/IO/Compression/IcdZip.cs index 5e7877b..39bc1f3 100644 --- a/ICD.Common.Utils/IO/Compression/IcdZip.cs +++ b/ICD.Common.Utils/IO/Compression/IcdZip.cs @@ -61,6 +61,28 @@ namespace ICD.Common.Utils.IO.Compression /// public static IEnumerable GetEntries(string path) { + /* + Copyright (c) 2012-2013 Alexey Yakovlev + + 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. + */ + using (IcdStream stream = IcdFile.OpenRead(path)) { using (IcdBinaryReader reader = new IcdBinaryReader(stream)) From da58379dfa01817f8f84d7cb3a4cbf1df6316dfd Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 6 Jun 2018 10:54:39 -0400 Subject: [PATCH 8/8] refactor: Tidying --- ICD.Common.Utils/IO/Compression/IcdZip.cs | 14 +++++++------- ICD.Common.Utils/IO/IcdStream.cs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ICD.Common.Utils/IO/Compression/IcdZip.cs b/ICD.Common.Utils/IO/Compression/IcdZip.cs index 39bc1f3..2e10201 100644 --- a/ICD.Common.Utils/IO/Compression/IcdZip.cs +++ b/ICD.Common.Utils/IO/Compression/IcdZip.cs @@ -90,7 +90,7 @@ namespace ICD.Common.Utils.IO.Compression if (stream.Length < 22) yield break; - stream.Seek(-22, eSeekOrigin.End.ToSeekOrigin()); + stream.Seek(-22, eSeekOrigin.End); // find directory signature while (reader.ReadInt32() != DIRECTORY_SIGNATURE) @@ -99,15 +99,15 @@ namespace ICD.Common.Utils.IO.Compression yield break; // move 1 byte back - stream.Seek(-5, eSeekOrigin.Current.ToSeekOrigin()); + stream.Seek(-5, eSeekOrigin.Current); } // read directory properties - stream.Seek(6, eSeekOrigin.Current.ToSeekOrigin()); + stream.Seek(6, eSeekOrigin.Current); ushort entries = reader.ReadUInt16(); int difSize = reader.ReadInt32(); uint dirOffset = reader.ReadUInt32(); - stream.Seek(dirOffset, eSeekOrigin.Begin.ToSeekOrigin()); + stream.Seek(dirOffset, eSeekOrigin.Begin); // read directory entries for (int i = 0; i < entries; i++) @@ -130,7 +130,7 @@ namespace ICD.Common.Utils.IO.Compression reader.ReadInt32(); int fileHeaderOffset = reader.ReadInt32(); byte[] fileNameBytes = reader.ReadBytes(fileNameSize); - stream.Seek(extraSize, eSeekOrigin.Current.ToSeekOrigin()); + stream.Seek(extraSize, eSeekOrigin.Current); byte[] fileCommentBytes = reader.ReadBytes(commentSize); int fileDataOffset = CalculateFileDataOffset(stream, reader, fileHeaderOffset); @@ -157,12 +157,12 @@ namespace ICD.Common.Utils.IO.Compression private static int CalculateFileDataOffset(IcdStream stream, IcdBinaryReader reader, int fileHeaderOffset) { long position = stream.Position; - stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin.ToSeekOrigin()); + stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin); short fileNameSize = reader.ReadInt16(); short extraSize = reader.ReadInt16(); int fileOffset = (int)stream.Position + fileNameSize + extraSize; - stream.Seek(position, eSeekOrigin.Begin.ToSeekOrigin()); + stream.Seek(position, eSeekOrigin.Begin); return fileOffset; } diff --git a/ICD.Common.Utils/IO/IcdStream.cs b/ICD.Common.Utils/IO/IcdStream.cs index 9694d04..56cb11b 100644 --- a/ICD.Common.Utils/IO/IcdStream.cs +++ b/ICD.Common.Utils/IO/IcdStream.cs @@ -34,9 +34,9 @@ namespace ICD.Common.Utils.IO m_Stream.Dispose(); } - public void Seek(long offset, SeekOrigin end) + public void Seek(long offset, eSeekOrigin seekOrigin) { - m_Stream.Seek(offset, end); + m_Stream.Seek(offset, seekOrigin.ToSeekOrigin()); } } }