From 4ad3537a2605ba6d0f41b1af233038c3993cc271 Mon Sep 17 00:00:00 2001 From: Valeriy Mironov Date: Sat, 4 May 2019 11:44:19 +0300 Subject: [PATCH] Added support for non image resources --- Resources/Extractor.cs | 10 ++- Resources/FileReader.cs | 2 +- Resources/FileWriter.cs | 4 +- Resources/Image/Reader.cs | 6 +- Resources/ImageLoader.cs | 61 +++++++++++++------ Resources/InvalidResourceException.cs | 9 +++ Resources/Models/Blob.cs | 27 ++++++++ Resources/Models/FileDescriptor.cs | 3 +- Resources/Models/IResource.cs | 11 ++++ Resources/Models/Image.cs | 29 +++++++++ Resources/Reader.cs | 61 +++++++++++++------ Resources/Resource.cs | 12 ++++ Resources/Resources.csproj | 4 ++ Resources/Writer.cs | 23 +++---- WatchFace.Parser/Reader.cs | 9 ++- .../{ImagesLoader.cs => ResourcesLoader.cs} | 27 ++++---- WatchFace.Parser/WatchFace.Parser.csproj | 2 +- WatchFace.Parser/Writer.cs | 6 +- WatchFace/Program.cs | 21 ++++--- 19 files changed, 243 insertions(+), 84 deletions(-) create mode 100644 Resources/InvalidResourceException.cs create mode 100644 Resources/Models/Blob.cs create mode 100644 Resources/Models/IResource.cs create mode 100644 Resources/Models/Image.cs create mode 100644 Resources/Resource.cs rename WatchFace.Parser/Utils/{ImagesLoader.cs => ResourcesLoader.cs} (85%) diff --git a/Resources/Extractor.cs b/Resources/Extractor.cs index aa885ec..96769b8 100644 --- a/Resources/Extractor.cs +++ b/Resources/Extractor.cs @@ -18,12 +18,16 @@ namespace Resources public void Extract(string outputDirectory) { - for (var i = 0; i < _descriptor.Images.Count; i++) + for (var i = 0; i < _descriptor.Resources.Count; i++) { + var resource = _descriptor.Resources[i]; var numericPart = i.ToString().PadLeft(ImageLoader.NumericPartLength, '0'); - var fileName = Path.Combine(outputDirectory, numericPart + ".png"); + + var fileName = Path.Combine(outputDirectory, numericPart + resource.Extension); Logger.Debug("Extracting {0}...", fileName); - _descriptor.Images[i].Save(fileName, ImageFormat.Png); + + using (var fileStream = File.OpenWrite(fileName)) + resource.ExportTo(fileStream); } } } diff --git a/Resources/FileReader.cs b/Resources/FileReader.cs index a8bdd05..f9c98cf 100644 --- a/Resources/FileReader.cs +++ b/Resources/FileReader.cs @@ -40,7 +40,7 @@ namespace Resources ResourcesCount = header.ResourcesCount, Version = header.Version, Unknown = (header as NewHeader)?.Unknown, - Images = new Reader(stream).Read(header.ResourcesCount) + Resources = new Reader(stream).Read(header.ResourcesCount) }; } } diff --git a/Resources/FileWriter.cs b/Resources/FileWriter.cs index 86c5a39..b4bfb62 100644 --- a/Resources/FileWriter.cs +++ b/Resources/FileWriter.cs @@ -29,14 +29,14 @@ namespace Resources WriteOldHeader(descriptor); Logger.Trace("Writing images..."); - new Writer(_stream).Write(descriptor.Images); + new Writer(_stream).Write(descriptor.Resources); } private void WriteOldHeader(FileDescriptor descriptor) { var header = new Header { - ResourcesCount = (uint)descriptor.Images.Count, + ResourcesCount = (uint)descriptor.Resources.Count, Version = descriptor.Version.Value }; Logger.Trace("Writing resources header..."); diff --git a/Resources/Image/Reader.cs b/Resources/Image/Reader.cs index 8e1e4fb..9fcd63d 100644 --- a/Resources/Image/Reader.cs +++ b/Resources/Image/Reader.cs @@ -1,5 +1,4 @@ -using System; -using System.Drawing; +using System.Drawing; using System.IO; using BumpKit; using NLog; @@ -29,7 +28,7 @@ namespace Resources.Image { var signature = _reader.ReadChars(4); if (signature[0] != 'B' || signature[1] != 'M') - throw new ArgumentException("Image signature doesn't match."); + throw new InvalidResourceException("Image signature doesn't match."); ReadHeader(); ReadPalette(); @@ -94,6 +93,7 @@ namespace Resources.Image } } } + return image; } } diff --git a/Resources/ImageLoader.cs b/Resources/ImageLoader.cs index a52c90c..b08ae1e 100644 --- a/Resources/ImageLoader.cs +++ b/Resources/ImageLoader.cs @@ -1,9 +1,11 @@ -using System.Drawing; +using System.Collections.Generic; +using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using NLog; using Resources.Image; +using Resources.Models; namespace Resources { @@ -12,33 +14,58 @@ namespace Resources public static readonly int NumericPartLength = 4; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public static Bitmap LoadImageForNumber(string directory, long index) + public static IResource LoadResourceForNumber(string directory, long index) { + var strIndex = index.ToString(); var numericParts = new[] { - index.ToString().PadLeft(4, '0'), - index.ToString().PadLeft(3, '0'), - index.ToString().PadLeft(2, '0'), - index.ToString() - }; + strIndex.PadLeft(4, '0'), strIndex.PadLeft(3, '0'), strIndex.PadLeft(2, '0'), strIndex + }.Distinct(); - foreach (var numericPart in numericParts.Distinct()) + foreach (var numericPart in numericParts) { - var fullFileName = Path.Combine(directory, numericPart + ".png"); - if (!File.Exists(fullFileName)) - { - Logger.Trace("File {0} doesn't exists.", fullFileName); - continue; - } + var resource = TryLoadBitmap(directory, numericPart); + if (resource != null) return resource; - var image = (Bitmap) System.Drawing.Image.FromFile(fullFileName); - Logger.Trace("Image was loaded from file {0}", fullFileName); - return ApplyDithering(image); + resource = TryLoadBlob(directory, numericPart); + if (resource != null) return resource; } throw new FileNotFoundException($"File referenced by index {index} not found."); } + private static IResource TryLoadBitmap(string directory, string numericPart) + { + var fullFileName = Path.Combine(directory, numericPart + Models.Image.ResourceExtension); + if (!File.Exists(fullFileName)) + { + Logger.Trace("File {0} doesn't exists.", fullFileName); + return null; + } + + var image = (Bitmap) System.Drawing.Image.FromFile(fullFileName); + Logger.Trace("Image was loaded from file {0}", fullFileName); + var ditheredBitmap = ApplyDithering(image); + return new Models.Image(ditheredBitmap); + } + + private static IResource TryLoadBlob(string directory, string numericPart) + { + var fullFileName = Path.Combine(directory, numericPart + Blob.ResourceExtension); + if (!File.Exists(fullFileName)) + { + Logger.Trace("File {0} doesn't exists.", fullFileName); + return null; + } + + using (var fileStream = File.OpenRead(fullFileName)) + { + var data = new byte[fileStream.Length]; + fileStream.Read(data, 0, data.Length); + return new Blob(data); + } + } + private static Bitmap ApplyDithering(Bitmap image) { var clone = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb); diff --git a/Resources/InvalidResourceException.cs b/Resources/InvalidResourceException.cs new file mode 100644 index 0000000..726d21d --- /dev/null +++ b/Resources/InvalidResourceException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Resources +{ + public class InvalidResourceException : Exception + { + public InvalidResourceException(string message) : base(message) { } + } +} \ No newline at end of file diff --git a/Resources/Models/Blob.cs b/Resources/Models/Blob.cs new file mode 100644 index 0000000..489983f --- /dev/null +++ b/Resources/Models/Blob.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace Resources.Models +{ + public class Blob : IResource + { + public static string ResourceExtension = ".dat"; + private readonly byte[] _data; + + public Blob(byte[] data) + { + _data = data; + } + + public string Extension => ResourceExtension; + + public void WriteTo(Stream stream) + { + stream.Write(_data, 0, _data.Length); + } + + public void ExportTo(Stream stream) + { + stream.Write(_data, 0, _data.Length); + } + } +} \ No newline at end of file diff --git a/Resources/Models/FileDescriptor.cs b/Resources/Models/FileDescriptor.cs index 7d87289..e7fffd4 100644 --- a/Resources/Models/FileDescriptor.cs +++ b/Resources/Models/FileDescriptor.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Drawing; +using System.Linq; using System.Runtime.Serialization; namespace Resources.Models @@ -12,6 +13,6 @@ namespace Resources.Models public byte? Version { get; set; } [IgnoreDataMember] - public List Images { get; set; } = new List(); + public List Resources { get; set; } = new List(); } } \ No newline at end of file diff --git a/Resources/Models/IResource.cs b/Resources/Models/IResource.cs new file mode 100644 index 0000000..e4d1a15 --- /dev/null +++ b/Resources/Models/IResource.cs @@ -0,0 +1,11 @@ +using System.IO; + +namespace Resources.Models +{ + public interface IResource + { + string Extension { get; } + void WriteTo(Stream stream); + void ExportTo(Stream stream); + } +} \ No newline at end of file diff --git a/Resources/Models/Image.cs b/Resources/Models/Image.cs new file mode 100644 index 0000000..677aa65 --- /dev/null +++ b/Resources/Models/Image.cs @@ -0,0 +1,29 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; + +namespace Resources.Models +{ + public class Image: IResource + { + public Bitmap Bitmap { get; } + + public Image(Bitmap bitmap) + { + Bitmap = bitmap; + } + + public static string ResourceExtension = ".png"; + public string Extension => ResourceExtension; + + public void WriteTo(Stream stream) + { + new Resources.Image.Writer(stream).Write(Bitmap); + } + + public void ExportTo(Stream stream) + { + Bitmap.Save(stream, ImageFormat.Png); + } + } +} \ No newline at end of file diff --git a/Resources/Reader.cs b/Resources/Reader.cs index 7afaa35..dea361c 100644 --- a/Resources/Reader.cs +++ b/Resources/Reader.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Drawing; +using System.Collections.Generic; using System.IO; using NLog; +using Resources.Models; namespace Resources { @@ -20,32 +19,54 @@ namespace Resources _binaryReader = new BinaryReader(_stream); } - public List Read(uint imagesTableLength) + public List Read(uint resourcesCount) { - var offsetsTableLength = (int) (imagesTableLength * OffsetTableItemLength); + var offsetsTableLength = (int) (resourcesCount * OffsetTableItemLength); Logger.Trace("Reading resources offsets table with {0} elements ({1} bytes)", - imagesTableLength, offsetsTableLength + resourcesCount, offsetsTableLength ); - var imagesOffsets = _binaryReader.ReadBytes(offsetsTableLength); - var imagesOffset = _stream.Position; - Logger.Debug("Reading {0} images...", imagesTableLength); - var images = new List((int) imagesTableLength); - for (var i = 0; i < imagesTableLength; i++) + var offsets = new int[resourcesCount]; + for (var i = 0; i < resourcesCount; i++) + offsets[i] = _binaryReader.ReadInt32(); + + var resourcesOffset = (int) _stream.Position; + var fileSize = (int) _stream.Length; + + Logger.Debug("Reading {0} resources...", resourcesCount); + var resources = new List((int) resourcesCount); + for (var i = 0; i < resourcesCount; i++) { - var imageOffset = BitConverter.ToUInt32(imagesOffsets, i * OffsetTableItemLength); - var realOffset = imageOffset + imagesOffset; - Logger.Trace("Image {0} offset is {1}...", i, imageOffset); - if (_stream.Position != realOffset) + var offset = offsets[i] + resourcesOffset; + var nextOffset = i + 1 < resourcesCount ? offsets[i + 1] + resourcesOffset : fileSize; + var length = nextOffset - offset; + Logger.Trace("Resource {0} offset: {1}, length: {2}...", i, offset, length); + if (_stream.Position != offset) { - var bytesGap = realOffset - _stream.Position; + var bytesGap = offset - _stream.Position; Logger.Warn("Found {0} bytes gap before resource number {1}", bytesGap, i); - _stream.Seek(realOffset, SeekOrigin.Begin); + _stream.Seek(offset, SeekOrigin.Begin); + } + + Logger.Debug("Reading resource {0}...", i); + try + { + var bitmap = new Image.Reader(_stream).Read(); + var image = new Models.Image(bitmap); + resources.Add(image); + } + catch (InvalidResourceException) + { + Logger.Warn("Resource is not an image"); + _stream.Seek(offset, SeekOrigin.Begin); + var data = new byte[length]; + _stream.Read(data, 0, length); + var blob = new Blob(data); + resources.Add(blob); } - Logger.Debug("Reading image {0}...", i); - images.Add(new Image.Reader(_stream).Read()); } - return images; + + return resources; } } } \ No newline at end of file diff --git a/Resources/Resource.cs b/Resources/Resource.cs new file mode 100644 index 0000000..78aaf2c --- /dev/null +++ b/Resources/Resource.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Resources +{ + interface IResource + { + + } +} diff --git a/Resources/Resources.csproj b/Resources/Resources.csproj index cd27aa1..1129791 100644 --- a/Resources/Resources.csproj +++ b/Resources/Resources.csproj @@ -53,6 +53,10 @@ + + + + diff --git a/Resources/Writer.cs b/Resources/Writer.cs index 39550f1..e1c0ec4 100644 --- a/Resources/Writer.cs +++ b/Resources/Writer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using NLog; +using Resources.Models; namespace Resources { @@ -18,30 +19,30 @@ namespace Resources _stream = stream; } - public void Write(List images) + public void Write(List resources) { - var offsetsTable = new byte[images.Count * OffsetTableItemLength]; - var encodedImages = new MemoryStream[images.Count]; + var offsetsTable = new byte[resources.Count * OffsetTableItemLength]; + var encodedResources = new MemoryStream[resources.Count]; var offset = (uint) 0; - for (var i = 0; i < images.Count; i++) + for (var i = 0; i < resources.Count; i++) { - Logger.Trace("Image {0} offset is {1}...", i, offset); + Logger.Trace("Resource {0} offset is {1}...", i, offset); var offsetBytes = BitConverter.GetBytes(offset); offsetBytes.CopyTo(offsetsTable, i * OffsetTableItemLength); var encodedImage = new MemoryStream(); - Logger.Debug("Encoding image {0}...", i); - new Image.Writer(encodedImage).Write(images[i]); + Logger.Debug("Encoding resource {0}...", i); + resources[i].WriteTo(encodedImage); offset += (uint) encodedImage.Length; - encodedImages[i] = encodedImage; + encodedResources[i] = encodedImage; } - Logger.Trace("Writing images offsets table"); + Logger.Trace("Writing resources offsets table"); _stream.Write(offsetsTable, 0, offsetsTable.Length); - Logger.Debug("Writing images"); - foreach (var encodedImage in encodedImages) + Logger.Debug("Writing resources"); + foreach (var encodedImage in encodedResources) { encodedImage.Seek(0, SeekOrigin.Begin); encodedImage.CopyTo(_stream); diff --git a/WatchFace.Parser/Reader.cs b/WatchFace.Parser/Reader.cs index 9c59a15..5c3fe4b 100644 --- a/WatchFace.Parser/Reader.cs +++ b/WatchFace.Parser/Reader.cs @@ -1,8 +1,12 @@ using System.Collections.Generic; using System.Drawing; using System.IO; +using System.Linq; using NLog; +using Resources.Models; using WatchFace.Parser.Models; +using Header = WatchFace.Parser.Models.Header; +using Image = Resources.Models.Image; namespace WatchFace.Parser { @@ -18,7 +22,8 @@ namespace WatchFace.Parser } public List Parameters { get; private set; } - public List Images { get; private set; } + public List Resources { get; private set; } + public Bitmap[] Images => Resources.OfType().Select(i => i.Bitmap).ToArray(); public void Read() { @@ -46,7 +51,7 @@ namespace WatchFace.Parser Logger.Trace("Watch face parameters locations were read:"); Parameters = ReadParameters(parametrsTableLength, parametersLocations); - Images = new Resources.Reader(_stream).Read((uint) imagesCount); + Resources = new Resources.Reader(_stream).Read((uint) imagesCount); } private List ReadParameters(long coordinatesTableSize, ICollection parametersDescriptors) diff --git a/WatchFace.Parser/Utils/ImagesLoader.cs b/WatchFace.Parser/Utils/ResourcesLoader.cs similarity index 85% rename from WatchFace.Parser/Utils/ImagesLoader.cs rename to WatchFace.Parser/Utils/ResourcesLoader.cs index 8242956..81bdf08 100644 --- a/WatchFace.Parser/Utils/ImagesLoader.cs +++ b/WatchFace.Parser/Utils/ResourcesLoader.cs @@ -1,31 +1,35 @@ -using System; +using NLog; +using Resources; +using System; using System.Collections.Generic; using System.Drawing; -using NLog; -using Resources; +using System.Linq; +using Resources.Models; using WatchFace.Parser.Attributes; +using Image = Resources.Models.Image; namespace WatchFace.Parser.Utils { - public class ImagesLoader + public class ResourcesLoader { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly string _imagesDirectory; private readonly Dictionary _mapping; - public ImagesLoader(string imagesDirectory) + public ResourcesLoader(string imagesDirectory) { - Images = new List(); + Resources = new List(); _mapping = new Dictionary(); _imagesDirectory = imagesDirectory; } - public List Images { get; } + public List Resources { get; } + public Bitmap[] Images => Resources.OfType().Select(i => i.Bitmap).ToArray(); public void Process(T serializable, string path = "") { - if (!string.IsNullOrEmpty(path)) Logger.Trace("Loading images for {0} '{1}'", path, typeof(T).Name); + if (!string.IsNullOrEmpty(path)) Logger.Trace("Loading resources for {0} '{1}'", path, typeof(T).Name); long? lastImageIndexValue = null; @@ -67,7 +71,7 @@ namespace WatchFace.Parser.Utils { if (lastImageIndexValue == null) throw new ArgumentException( - $"Property {propertyInfo.Name} can't be processed becuase ImageIndex isn't present or it is zero" + $"Property {propertyInfo.Name} can't be processed because ImageIndex isn't present or it is zero" ); var imagesCount = propertyType.IsGenericType @@ -102,9 +106,10 @@ namespace WatchFace.Parser.Utils if (_mapping.ContainsKey(index)) return _mapping[index]; - var newImageIndex = Images.Count; + var newImageIndex = Resources.Count; Logger.Trace("Loading image {0}...", newImageIndex); - Images.Add(ImageLoader.LoadImageForNumber(_imagesDirectory, index)); + var resource = ImageLoader.LoadResourceForNumber(_imagesDirectory, index); + Resources.Add(resource); _mapping[index] = newImageIndex; return newImageIndex; } diff --git a/WatchFace.Parser/WatchFace.Parser.csproj b/WatchFace.Parser/WatchFace.Parser.csproj index 06a799c..497a33e 100644 --- a/WatchFace.Parser/WatchFace.Parser.csproj +++ b/WatchFace.Parser/WatchFace.Parser.csproj @@ -160,7 +160,7 @@ - + diff --git a/WatchFace.Parser/Writer.cs b/WatchFace.Parser/Writer.cs index a06e6dd..152b630 100644 --- a/WatchFace.Parser/Writer.cs +++ b/WatchFace.Parser/Writer.cs @@ -2,18 +2,20 @@ using System.Drawing; using System.IO; using NLog; +using Resources.Models; using WatchFace.Parser.Models; +using Header = WatchFace.Parser.Models.Header; namespace WatchFace.Parser { public class Writer { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private readonly List _images; + private readonly List _images; private readonly Stream _stream; - public Writer(Stream stream, List images) + public Writer(Stream stream, List images) { _stream = stream; _images = images; diff --git a/WatchFace/Program.cs b/WatchFace/Program.cs index 301c5bc..f32c045 100644 --- a/WatchFace/Program.cs +++ b/WatchFace/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; +using System.Linq; using System.Reflection; using BumpKit; using Newtonsoft.Json; @@ -14,6 +15,7 @@ using Resources.Models; using WatchFace.Parser; using WatchFace.Parser.Models; using WatchFace.Parser.Utils; +using Image = System.Drawing.Image; using Reader = WatchFace.Parser.Reader; using Writer = WatchFace.Parser.Writer; @@ -132,11 +134,10 @@ namespace WatchFace var watchFace = ParseResources(reader); if (watchFace == null) return; - - GeneratePreviews(reader.Parameters, reader.Images.ToArray(), outputDirectory, baseName); + GeneratePreviews(reader.Parameters, reader.Images, outputDirectory, baseName); Logger.Debug("Exporting resources to '{0}'", outputDirectory); - var reDescriptor = new FileDescriptor {Images = reader.Images}; + var reDescriptor = new FileDescriptor {Resources = reader.Resources}; new Extractor(reDescriptor).Extract(outputDirectory); ExportWatchFaceConfig(watchFace, Path.Combine(outputDirectory, $"{baseName}.json")); } @@ -173,13 +174,13 @@ namespace WatchFace } var i = 0; - var images = new List(); + var images = new List(); while (resDescriptor.ResourcesCount == null || i < resDescriptor.ResourcesCount.Value) { try { - var image = ImageLoader.LoadImageForNumber(inputDirectory, i); - images.Add(image); + var resource = ImageLoader.LoadResourceForNumber(inputDirectory, i); + images.Add(resource); } catch (FileNotFoundException) { @@ -194,7 +195,7 @@ namespace WatchFace throw new ArgumentException( $"The .res-file should contain {resDescriptor.ResourcesCount.Value} images but was loaded {images.Count} images."); - resDescriptor.Images = images; + resDescriptor.Resources = images; using (var stream = File.OpenWrite(outputFileName)) { @@ -223,19 +224,19 @@ namespace WatchFace try { Logger.Debug("Reading referenced images from '{0}'", imagesDirectory); - var imagesReader = new ImagesLoader(imagesDirectory); + var imagesReader = new ResourcesLoader(imagesDirectory); imagesReader.Process(watchFace); Logger.Trace("Building parameters for watch face..."); var descriptor = ParametersConverter.Build(watchFace); var baseFilename = Path.GetFileNameWithoutExtension(outputFileName); - GeneratePreviews(descriptor, imagesReader.Images.ToArray(), outputDirectory, baseFilename); + GeneratePreviews(descriptor, imagesReader.Images, outputDirectory, baseFilename); Logger.Debug("Writing watch face to '{0}'", outputFileName); using (var fileStream = File.OpenWrite(outputFileName)) { - var writer = new Writer(fileStream, imagesReader.Images); + var writer = new Writer(fileStream, imagesReader.Resources); writer.Write(descriptor); fileStream.Flush(); }