Moved resources related work to separate project, improved parsing logging, added support for parsing 'game' watchface, renamed several classes and attributes
parent
3dc14edfff
commit
6986268043
18
Amazfit.sln
18
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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WatchFace.Parser.Utils
|
||||
namespace Resources
|
||||
{
|
||||
public class BitReader
|
||||
{
|
|
@ -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();
|
|
@ -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")]
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Resources</RootNamespace>
|
||||
<AssemblyName>Resources</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BitReader.cs" />
|
||||
<Compile Include="ResourcesExtractor.cs" />
|
||||
<Compile Include="ResourcesHeader.cs" />
|
||||
<Compile Include="ImageReader.cs" />
|
||||
<Compile Include="ResourcesFileReader.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ResourcesReader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||
</packages>
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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}."
|
||||
) { }
|
||||
}
|
||||
}
|
|
@ -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}."
|
||||
) { }
|
||||
}
|
||||
}
|
|
@ -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<Parameter> Resources { get; private set; }
|
||||
public List<Bitmap> Images { get; private set; }
|
||||
public List<Parameter> 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<Parameter> parameters)
|
||||
private List<Parameter> ReadParameters(long coordinatesTableSize,
|
||||
IReadOnlyCollection<Parameter> parametersDescriptors)
|
||||
{
|
||||
var coordsStream = StreamBlock(_fileStream, (int) coordinatesTableSize);
|
||||
var parametersStream = StreamBlock(_fileStream, (int) coordinatesTableSize);
|
||||
|
||||
Resources = new List<Parameter>(parameters.Count);
|
||||
foreach (var parameter in parameters)
|
||||
var result = new List<Parameter>(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<Bitmap>((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)
|
||||
|
|
|
@ -31,26 +31,31 @@ namespace WatchFace.Parser.Utils
|
|||
return result;
|
||||
}
|
||||
|
||||
public static T Parse<T>(List<Parameter> descriptor, string path = "") where T : new()
|
||||
public static T Parse<T>(List<Parameter> descriptor, string path = "", int traceOffset = 0) where T : new()
|
||||
{
|
||||
var properties = SortedPropertiesDictionary<T>();
|
||||
|
||||
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)
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<Compile Include="Elements\DateElements\MonthAndDay.cs" />
|
||||
<Compile Include="Elements\DateElements\OneLineMonthAndDay.cs" />
|
||||
<Compile Include="Elements\DateElements\SeparateMonthAndDay.cs" />
|
||||
<Compile Include="Elements\Scales.cs" />
|
||||
<Compile Include="Elements\StepsProgress.cs" />
|
||||
<Compile Include="Elements\Status.cs" />
|
||||
<Compile Include="Elements\StatusElements\Flag.cs" />
|
||||
<Compile Include="Elements\StatusElements\Switch.cs" />
|
||||
|
@ -76,16 +76,12 @@
|
|||
<Compile Include="Elements\WeatherElements\TemperatureNumber.cs" />
|
||||
<Compile Include="Elements\WeatherElements\TodayTemperature.cs" />
|
||||
<Compile Include="Elements\WeatherElements\WeatherIcon.cs" />
|
||||
<Compile Include="Exceptions\DuplicateParameterException.cs" />
|
||||
<Compile Include="ImageReader.cs" />
|
||||
<Compile Include="Exceptions\InvalidParameterException.cs" />
|
||||
<Compile Include="JsonConverters\HexStringJsonConverter.cs" />
|
||||
<Compile Include="Models\Header.cs" />
|
||||
<Compile Include="Models\Parameter.cs" />
|
||||
<Compile Include="Models\ParameterFlags.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Reader.cs" />
|
||||
<Compile Include="Utils\BitReader.cs" />
|
||||
<Compile Include="Utils\BitWriter.cs" />
|
||||
<Compile Include="Utils\ParametersConverter.cs" />
|
||||
<Compile Include="Utils\RawParameterAttribute.cs" />
|
||||
|
@ -95,5 +91,11 @@
|
|||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Resources\Resources.csproj">
|
||||
<Project>{edd55d5d-9e80-451b-ac8a-0746ba6dc6e9}</Project>
|
||||
<Name>Resources</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -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; }
|
||||
|
|
|
@ -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 (args.Length > 0)
|
||||
Console.WriteLine("Multiple files unpacking.");
|
||||
|
||||
foreach (var inputFileName in args)
|
||||
{
|
||||
if (!File.Exists(inputFileName))
|
||||
{
|
||||
Console.WriteLine("File '{0}' doesn't exists.", inputFileName);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Path.GetExtension(inputFileName) == ".json")
|
||||
PackWatchFace(inputFileName);
|
||||
else
|
||||
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<Parser.WatchFace>(reader.Resources);
|
||||
return ParametersConverter.Parse<Parser.WatchFace>(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...");
|
||||
|
|
|
@ -57,6 +57,10 @@
|
|||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Resources\Resources.csproj">
|
||||
<Project>{edd55d5d-9e80-451b-ac8a-0746ba6dc6e9}</Project>
|
||||
<Name>Resources</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\WatchFace.Parser\WatchFace.Parser.csproj">
|
||||
<Project>{77d5f357-a1f3-4823-a328-b78b49ed870a}</Project>
|
||||
<Name>WatchFace.Parser</Name>
|
||||
|
|
Loading…
Reference in New Issue