diff --git a/Amazfit.sln b/Amazfit.sln index 7721e16..8661403 100644 --- a/Amazfit.sln +++ b/Amazfit.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchFace.Parser", "WatchFa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchFace", "WatchFace\WatchFace.csproj", "{963BCF47-8C24-4219-BC84-8EC3670205B3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resources", "Resources\Resources.csproj", "{EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +53,22 @@ Global {963BCF47-8C24-4219-BC84-8EC3670205B3}.Release|x64.Build.0 = Release|Any CPU {963BCF47-8C24-4219-BC84-8EC3670205B3}.Release|x86.ActiveCfg = Release|Any CPU {963BCF47-8C24-4219-BC84-8EC3670205B3}.Release|x86.Build.0 = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|ARM.ActiveCfg = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|ARM.Build.0 = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|x64.ActiveCfg = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|x64.Build.0 = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|x86.ActiveCfg = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Debug|x86.Build.0 = Debug|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|Any CPU.Build.0 = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|ARM.ActiveCfg = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|ARM.Build.0 = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|x64.ActiveCfg = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|x64.Build.0 = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|x86.ActiveCfg = Release|Any CPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WatchFace.Parser/Utils/BitReader.cs b/Resources/BitReader.cs similarity index 98% rename from WatchFace.Parser/Utils/BitReader.cs rename to Resources/BitReader.cs index 34134ce..da803e3 100644 --- a/WatchFace.Parser/Utils/BitReader.cs +++ b/Resources/BitReader.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace WatchFace.Parser.Utils +namespace Resources { public class BitReader { diff --git a/WatchFace.Parser/ImageReader.cs b/Resources/ImageReader.cs similarity index 93% rename from WatchFace.Parser/ImageReader.cs rename to Resources/ImageReader.cs index b0cb182..03fb27b 100644 --- a/WatchFace.Parser/ImageReader.cs +++ b/Resources/ImageReader.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Drawing; using System.IO; -using WatchFace.Parser.Utils; -namespace WatchFace.Parser +namespace Resources { public class ImageReader { @@ -26,7 +25,7 @@ namespace WatchFace.Parser { var signature = _reader.ReadChars(4); if (signature[0] != 'B' || signature[1] != 'M') - throw new ArgumentOutOfRangeException("signature", "Signature doesn't match."); + throw new ArgumentException("Image signature doesn't match."); ReadHeader(); ReadPalette(); diff --git a/Resources/Properties/AssemblyInfo.cs b/Resources/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..eb4d018 --- /dev/null +++ b/Resources/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ResourcesReader")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ResourcesReader")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("edd55d5d-9e80-451b-ac8a-0746ba6dc6e9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Resources/Resources.csproj b/Resources/Resources.csproj new file mode 100644 index 0000000..4095418 --- /dev/null +++ b/Resources/Resources.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9} + Library + Properties + Resources + Resources + v4.6.1 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\NLog.4.4.12\lib\net45\NLog.dll + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/ResourcesExtractor.cs b/Resources/ResourcesExtractor.cs new file mode 100644 index 0000000..fc3b92d --- /dev/null +++ b/Resources/ResourcesExtractor.cs @@ -0,0 +1,29 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using NLog; + +namespace Resources +{ + public class ResourcesExtractor + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly Bitmap[] _images; + + public ResourcesExtractor(Bitmap[] images) + { + _images = images; + } + + public void Extract(string outputDirectory) + { + for (var i = 0; i < _images.Length; i++) + { + var fileName = Path.Combine(outputDirectory, $"{i}.bmp"); + Logger.Debug("Exporting {0}...", fileName); + _images[i].Save(fileName, ImageFormat.Bmp); + } + } + } +} \ No newline at end of file diff --git a/Resources/ResourcesFileReader.cs b/Resources/ResourcesFileReader.cs new file mode 100644 index 0000000..0cc4f2b --- /dev/null +++ b/Resources/ResourcesFileReader.cs @@ -0,0 +1,36 @@ +using System; +using System.Drawing; +using System.IO; +using NLog; + +namespace Resources +{ + public class ResourcesFileReader + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly BinaryReader _binaryReader; + private readonly Stream _stream; + + public ResourcesFileReader(Stream stream) + { + _stream = stream; + _binaryReader = new BinaryReader(stream); + } + + public Bitmap[] Read() + { + Logger.Trace("Reading resources header"); + var header = ResourcesHeader.ReadFrom(_binaryReader); + Logger.Trace("Resources header was read:"); + Logger.Trace("Signature: {0}, Version: {1}, ResourcesCount: {2}, IsValid: {3}", + header.Signature, header.Version, header.ResourcesCount, header.IsValid + ); + + if (!header.IsValid) + throw new ArgumentException("Invalid resources header"); + + return new ResourcesReader(_stream).Read(header.ResourcesCount); + } + } +} \ No newline at end of file diff --git a/Resources/ResourcesHeader.cs b/Resources/ResourcesHeader.cs new file mode 100644 index 0000000..3a3617f --- /dev/null +++ b/Resources/ResourcesHeader.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using System.Text; + +namespace Resources +{ + public class ResourcesHeader + { + public const int HeaderSize = 20; + private const string ResHeaderSignature = "HMRES"; + private readonly byte[] _header; + + public ResourcesHeader(byte[] header) + { + _header = header; + } + + public string Signature => Encoding.ASCII.GetString(_header, 0, 5); + public bool IsValid => Signature == ResHeaderSignature; + public uint Version => _header[5]; + public uint ResourcesCount => BitConverter.ToUInt32(_header, 16); + + public static ResourcesHeader ReadFrom(BinaryReader reader) + { + var buffer = reader.ReadBytes(HeaderSize); + return new ResourcesHeader(buffer); + } + } +} \ No newline at end of file diff --git a/Resources/ResourcesReader.cs b/Resources/ResourcesReader.cs new file mode 100644 index 0000000..14c5020 --- /dev/null +++ b/Resources/ResourcesReader.cs @@ -0,0 +1,47 @@ +using System; +using System.Drawing; +using System.IO; +using NLog; + +namespace Resources +{ + public class ResourcesReader + { + private const int OffsetTableItemLength = 4; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly BinaryReader _binaryReader; + private readonly Stream _stream; + + public ResourcesReader(Stream stream) + { + _stream = stream; + _binaryReader = new BinaryReader(_stream); + } + + public Bitmap[] Read(uint imagesTableLength) + { + var offsetsTableLength = (int) (imagesTableLength * OffsetTableItemLength); + Logger.Trace("Reading resources offsets table with {0} elements ({1} bytes)", + imagesTableLength, offsetsTableLength + ); + var imagesOffsets = _binaryReader.ReadBytes(offsetsTableLength); + var imagesOffset = _stream.Position; + + Logger.Trace("Reading {0} resources", imagesTableLength); + var images = new Bitmap[imagesTableLength]; + for (var i = 0; i < imagesTableLength; i++) + { + var imageOffset = BitConverter.ToUInt32(imagesOffsets, i * OffsetTableItemLength) + imagesOffset; + if (_stream.Position != imageOffset) + { + var bytesGap = imageOffset - _stream.Position; + Logger.Warn("Found {0} bytes gap before resource number {1}", bytesGap, i); + _stream.Seek(imageOffset, SeekOrigin.Begin); + } + images[i] = new ImageReader(_stream).Read(); + } + return images; + } + } +} \ No newline at end of file diff --git a/Resources/packages.config b/Resources/packages.config new file mode 100644 index 0000000..80757f3 --- /dev/null +++ b/Resources/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/WatchFace.Parser/Elements/Scales.cs b/WatchFace.Parser/Elements/Scales.cs deleted file mode 100644 index b8e2eac..0000000 --- a/WatchFace.Parser/Elements/Scales.cs +++ /dev/null @@ -1,14 +0,0 @@ -using WatchFace.Parser.Elements.BasicElements; -using WatchFace.Parser.Utils; - -namespace WatchFace.Parser.Elements -{ - public class Scales - { - [RawParameter(Id = 2)] - public Scale LinearSteps { get; set; } - - [RawParameter(Id = 3)] - public CircleScale CircleSteps { get; set; } - } -} \ No newline at end of file diff --git a/WatchFace.Parser/Elements/StepsProgress.cs b/WatchFace.Parser/Elements/StepsProgress.cs new file mode 100644 index 0000000..c94d432 --- /dev/null +++ b/WatchFace.Parser/Elements/StepsProgress.cs @@ -0,0 +1,17 @@ +using WatchFace.Parser.Elements.BasicElements; +using WatchFace.Parser.Utils; + +namespace WatchFace.Parser.Elements +{ + public class StepsProgress + { + [RawParameter(Id = 1)] + public Image GoalImage { get; set; } + + [RawParameter(Id = 2)] + public Scale Linear { get; set; } + + [RawParameter(Id = 3)] + public CircleScale Circle { get; set; } + } +} \ No newline at end of file diff --git a/WatchFace.Parser/Elements/WeatherElements/OneLineTemperature.cs b/WatchFace.Parser/Elements/WeatherElements/OneLineTemperature.cs index 30954af..32c6aed 100644 --- a/WatchFace.Parser/Elements/WeatherElements/OneLineTemperature.cs +++ b/WatchFace.Parser/Elements/WeatherElements/OneLineTemperature.cs @@ -15,7 +15,7 @@ namespace WatchFace.Parser.Elements.WeatherElements public long DelimiterImageIndex { get; set; } [RawParameter(Id = 4)] - public long Unknown4 { get; set; } + public long AppendDegreesForBoth { get; set; } [RawParameter(Id = 5)] public long DegreesImageIndex { get; set; } diff --git a/WatchFace.Parser/Exceptions/DuplicateParameterException.cs b/WatchFace.Parser/Exceptions/DuplicateParameterException.cs deleted file mode 100644 index 4ef5e3c..0000000 --- a/WatchFace.Parser/Exceptions/DuplicateParameterException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using WatchFace.Models; - -namespace WatchFace -{ - public class DuplicateParameterException : Exception - { - public DuplicateParameterException(Parameter paramter, string path) : base( - $"Parameter with Id: {paramter.Id} was already set in this position: {path}." - ) { } - } -} \ No newline at end of file diff --git a/WatchFace.Parser/Exceptions/InvalidParameterException.cs b/WatchFace.Parser/Exceptions/InvalidParameterException.cs deleted file mode 100644 index ab8603d..0000000 --- a/WatchFace.Parser/Exceptions/InvalidParameterException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using WatchFace.Parser.Models; - -namespace WatchFace -{ - public class InvalidParameterException : Exception - { - public InvalidParameterException(Parameter paramter, string path) : base( - $"Parameter with Id: {paramter.Id} is not supported in this position: {path}." - ) { } - } -} \ No newline at end of file diff --git a/WatchFace.Parser/Reader.cs b/WatchFace.Parser/Reader.cs index 84410ed..e651287 100644 --- a/WatchFace.Parser/Reader.cs +++ b/WatchFace.Parser/Reader.cs @@ -1,8 +1,8 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Drawing; using System.IO; using NLog; -using System.Drawing; +using Resources; using WatchFace.Parser.Models; namespace WatchFace.Parser @@ -18,8 +18,8 @@ namespace WatchFace.Parser _fileStream = streamReader; } - public List Resources { get; private set; } - public List Images { get; private set; } + public List Parameters { get; private set; } + public Bitmap[] Images { get; private set; } public void Read() { @@ -38,51 +38,36 @@ namespace WatchFace.Parser Logger.Trace("Reading parameters descriptor..."); var mainParam = Parameter.ReadFrom(parametersStream); Logger.Trace("Parameters descriptor was read:"); - var coordinatesTableSize = mainParam.Children[0].Value; - var imagesTableLength = mainParam.Children[1].Value; - Logger.Trace($"CoordinatesTableSize: {coordinatesTableSize}, ImagesTableLength: {imagesTableLength}"); + var parametrsTableLength = mainParam.Children[0].Value; + var imagesCount = mainParam.Children[1].Value; + Logger.Trace($"ParametrsTableLength: {parametrsTableLength}, ImagesCount: {imagesCount}"); - Logger.Trace("Reading face parameters..."); - var parameters = Parameter.ReadList(parametersStream); - Logger.Trace("Watch face parameters were read:"); + Logger.Trace("Reading parameters locations..."); + var parametersLocations = Parameter.ReadList(parametersStream); + Logger.Trace("Watch face parameters locations were read:"); - ParseResources(coordinatesTableSize, parameters); - ParseImages(imagesTableLength); + Parameters = ReadParameters(parametrsTableLength, parametersLocations); + Images = new ResourcesReader(_fileStream).Read((uint) imagesCount); } - private void ParseResources(long coordinatesTableSize, IReadOnlyCollection parameters) + private List ReadParameters(long coordinatesTableSize, + IReadOnlyCollection parametersDescriptors) { - var coordsStream = StreamBlock(_fileStream, (int) coordinatesTableSize); + var parametersStream = StreamBlock(_fileStream, (int) coordinatesTableSize); - Resources = new List(parameters.Count); - foreach (var parameter in parameters) + var result = new List(parametersDescriptors.Count); + foreach (var prameterDescriptor in parametersDescriptors) { - Logger.Trace("Reading descriptor for parameter {0}", parameter.Id); - var descriptorOffset = parameter.Children[0].Value; - var descriptorLength = parameter.Children[1].Value; + var descriptorOffset = prameterDescriptor.Children[0].Value; + var descriptorLength = prameterDescriptor.Children[1].Value; + Logger.Trace("Reading descriptor for parameter {0}", prameterDescriptor.Id); Logger.Trace("Descriptor offset: {0}, Descriptor length: {1}", descriptorOffset, descriptorLength); - coordsStream.Seek(descriptorOffset, SeekOrigin.Begin); - var descriptorStream = StreamBlock(coordsStream, (int) descriptorLength); - Logger.Trace("Parsing descriptor for parameter {0}...", parameter.Id); - Resources.Add(new Parameter(parameter.Id, Parameter.ReadList(descriptorStream))); - } - } - - private void ParseImages(long imagesTableLength) - { - var imagesOffsets = new byte[imagesTableLength * 4]; - _fileStream.Read(imagesOffsets, 0, imagesOffsets.Length); - - var imagesOffset = _fileStream.Position; - - Images = new List((int) imagesTableLength); - for (var i = 0; i < (int) imagesTableLength; i++) - { - var imageOffset = BitConverter.ToUInt32(imagesOffsets, i * 4) + imagesOffset; - _fileStream.Seek(imageOffset, SeekOrigin.Begin); - var image = new ImageReader(_fileStream).Read(); - Images.Add(image); + parametersStream.Seek(descriptorOffset, SeekOrigin.Begin); + var descriptorStream = StreamBlock(parametersStream, (int) descriptorLength); + Logger.Trace("Parsing descriptor for parameter {0}...", prameterDescriptor.Id); + result.Add(new Parameter(prameterDescriptor.Id, Parameter.ReadList(descriptorStream))); } + return result; } private static Stream StreamBlock(Stream stream, int size) diff --git a/WatchFace.Parser/Utils/ParametersConverter.cs b/WatchFace.Parser/Utils/ParametersConverter.cs index 9e74a84..b0ab9ac 100644 --- a/WatchFace.Parser/Utils/ParametersConverter.cs +++ b/WatchFace.Parser/Utils/ParametersConverter.cs @@ -31,26 +31,31 @@ namespace WatchFace.Parser.Utils return result; } - public static T Parse(List descriptor, string path = "") where T : new() + public static T Parse(List descriptor, string path = "", int traceOffset = 0) where T : new() { var properties = SortedPropertiesDictionary(); - var result = new T(); - Logger.Trace("Reading {0} {1}", typeof(T).Name, path); + var currentType = typeof(T); + + if (!string.IsNullOrEmpty(path)) + Logger.Trace("{0} '{1}'", path, currentType.Name); foreach (var parameter in descriptor) { - var currentPath = string.Concat(path, '.', parameter.Id.ToString()); - var propertyInfo = properties[parameter.Id]; - if (propertyInfo == null) - throw new InvalidParameterException(parameter, path); + var currentPath = string.IsNullOrEmpty(path) + ? parameter.Id.ToString() + : string.Concat(path, '.', parameter.Id.ToString()); + if (!properties.ContainsKey(parameter.Id)) + throw new ArgumentException($"Parameter {parameter.Id} isn't supported for {currentType.Name}"); + var propertyInfo = properties[parameter.Id]; var propertyType = propertyInfo.PropertyType; if (propertyType == typeof(long)) { + Logger.Trace("{0} '{1}': {2}", currentPath, propertyInfo.Name, parameter.Value); dynamic propertyValue = propertyInfo.GetValue(result); if (propertyValue != 0) - throw new DuplicateParameterException(parameter, path); + throw new ArgumentException($"Parameter {parameter.Id} is already set for {currentType.Name}"); propertyInfo.SetValue(result, parameter.Value); } @@ -68,7 +73,8 @@ namespace WatchFace.Parser.Utils var method = typeof(ParametersConverter).GetMethod(nameof(Parse)); var itemType = propertyType.GetGenericArguments()[0]; var generic = method.MakeGenericMethod(itemType); - dynamic parsedValue = generic.Invoke(null, new dynamic[] {parameter.Children, currentPath}); + dynamic parsedValue = generic.Invoke(null, + new dynamic[] {parameter.Children, currentPath, traceOffset + 1}); propertyValue.Add(parsedValue); } catch (TargetInvocationException e) @@ -80,13 +86,14 @@ namespace WatchFace.Parser.Utils { dynamic propertyValue = propertyInfo.GetValue(result); if (propertyValue != null) - throw new DuplicateParameterException(parameter, path); + throw new ArgumentException($"Parameter {parameter.Id} is already set for {currentType.Name}"); try { var method = typeof(ParametersConverter).GetMethod(nameof(Parse)); var generic = method.MakeGenericMethod(propertyType); - dynamic parsedValue = generic.Invoke(null, new dynamic[] {parameter.Children, currentPath}); + dynamic parsedValue = generic.Invoke(null, + new dynamic[] {parameter.Children, currentPath, traceOffset + 1}); propertyInfo.SetValue(result, parsedValue); } catch (TargetInvocationException e) diff --git a/WatchFace.Parser/WatchFace.Parser.csproj b/WatchFace.Parser/WatchFace.Parser.csproj index 7a12800..85072f8 100644 --- a/WatchFace.Parser/WatchFace.Parser.csproj +++ b/WatchFace.Parser/WatchFace.Parser.csproj @@ -61,7 +61,7 @@ - + @@ -76,16 +76,12 @@ - - - - @@ -95,5 +91,11 @@ + + + {edd55d5d-9e80-451b-ac8a-0746ba6dc6e9} + Resources + + \ No newline at end of file diff --git a/WatchFace.Parser/WatchFace.cs b/WatchFace.Parser/WatchFace.cs index 5c413de..ef7dd94 100644 --- a/WatchFace.Parser/WatchFace.cs +++ b/WatchFace.Parser/WatchFace.cs @@ -21,7 +21,7 @@ namespace WatchFace.Parser public Weather Weather { get; set; } [RawParameter(Id = 7)] - public Scales Scales { get; set; } + public StepsProgress StepsProgress { get; set; } [RawParameter(Id = 8)] public Status Status { get; set; } diff --git a/WatchFace/Program.cs b/WatchFace/Program.cs index 595c5b7..cca9173 100644 --- a/WatchFace/Program.cs +++ b/WatchFace/Program.cs @@ -1,12 +1,15 @@ using System; +using System.Collections.Generic; +using System.Drawing; using System.Drawing.Imaging; using System.IO; using Newtonsoft.Json; using NLog; using NLog.Config; using NLog.Targets; -using WatchFace.Parser.Utils; +using Resources; using WatchFace.Parser; +using WatchFace.Parser.Utils; namespace WatchFace { @@ -21,21 +24,47 @@ namespace WatchFace if (args.Length == 0 || args[0] == null) { Console.WriteLine("{0} unpacks Amazfit Bip downloadable watch faces.", AppName); - Console.WriteLine("Usage: {0}.exe watchface.bin", AppName); + Console.WriteLine("Usage: {0}.exe wf.bin [wf2.bin] ...", AppName); return; } - var inputFileName = args[0]; - if (!File.Exists(inputFileName)) + if (args.Length > 0) + Console.WriteLine("Multiple files unpacking."); + + foreach (var inputFileName in args) { - Console.WriteLine("File '{0}' doesn't exists.", inputFileName); - return; - } + if (!File.Exists(inputFileName)) + { + Console.WriteLine("File '{0}' doesn't exists.", inputFileName); + continue; + } - if (Path.GetExtension(inputFileName) == ".json") - PackWatchFace(inputFileName); - else - UnpackWatchFace(inputFileName); + Console.WriteLine("Processing file '{0}'", inputFileName); + var inputFileExtension = Path.GetExtension(inputFileName); + try + { + switch (inputFileExtension) + { + case ".bin": + UnpackWatchFace(inputFileName); + break; + case ".json": + PackWatchFace(inputFileName); + break; + case ".res": + UnpackResources(inputFileName); + break; + default: + Console.WriteLine("The app doesn't support files with extension {0}.", inputFileExtension); + Console.WriteLine("Only 'bin', 'res' and 'json' files are supported at this time."); + break; + } + } + catch (Exception e) + { + Logger.Fatal(e); + } + } } private static void PackWatchFace(string inputFileName) @@ -59,10 +88,25 @@ namespace WatchFace if (watchFace == null) return; Logger.Debug("Exporting resources to '{0}'", outputDirectory); - ExportImages(reader, outputDirectory); + new ResourcesExtractor(reader.Images).Extract(outputDirectory); ExportConfig(watchFace, Path.Combine(outputDirectory, $"{baseName}.json")); } + private static void UnpackResources(string inputFileName) + { + var outputDirectory = CreateOutputDirectory(inputFileName); + var baseName = Path.GetFileNameWithoutExtension(inputFileName); + SetupLogger(Path.Combine(outputDirectory, $"{baseName}.log")); + + Bitmap[] images; + using (var stream = File.OpenRead(inputFileName)) + { + images = new ResourcesFileReader(stream).Read(); + } + + new ResourcesExtractor(images).Extract(outputDirectory); + } + private static void WriteWatchFace(string outputFileName, Parser.WatchFace watchFace) { Logger.Debug("Writing watch face to '{0}'", outputFileName); @@ -106,7 +150,7 @@ namespace WatchFace Logger.Debug("Parsing parameters..."); try { - return ParametersConverter.Parse(reader.Resources); + return ParametersConverter.Parse(reader.Parameters); } catch (Exception e) { @@ -124,24 +168,6 @@ namespace WatchFace return unpackedPath; } - private static void ExportImages(Reader reader, string outputDirectory) - { - Logger.Debug("Exporting images..."); - try - { - var index = 0; - foreach (var image in reader.Images) - { - image.Save(Path.Combine(outputDirectory, $"{index}.bmp"), ImageFormat.Bmp); - index++; - } - } - catch (Exception e) - { - Logger.Fatal(e); - } - } - private static Parser.WatchFace ReadConfig(string jsonFileName) { Logger.Debug("Reading config..."); diff --git a/WatchFace/WatchFace.csproj b/WatchFace/WatchFace.csproj index d2d099b..798382d 100644 --- a/WatchFace/WatchFace.csproj +++ b/WatchFace/WatchFace.csproj @@ -57,6 +57,10 @@ + + {edd55d5d-9e80-451b-ac8a-0746ba6dc6e9} + Resources + {77d5f357-a1f3-4823-a328-b78b49ed870a} WatchFace.Parser