Moved resources related work to separate project, improved parsing logging, added support for parsing 'game' watchface, renamed several classes and attributes

fonts_experiment
Valeriy Mironov 2017-11-25 01:03:47 +02:00
parent 3dc14edfff
commit 6986268043
21 changed files with 392 additions and 132 deletions

View File

@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchFace.Parser", "WatchFa
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchFace", "WatchFace\WatchFace.csproj", "{963BCF47-8C24-4219-BC84-8EC3670205B3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchFace", "WatchFace\WatchFace.csproj", "{963BCF47-8C24-4219-BC84-8EC3670205B3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resources", "Resources\Resources.csproj", "{EDD55D5D-9E80-451B-AC8A-0746BA6DC6E9}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|x64.Build.0 = Release|Any CPU
{963BCF47-8C24-4219-BC84-8EC3670205B3}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,7 +1,7 @@
using System; using System;
using System.IO; using System.IO;
namespace WatchFace.Parser.Utils namespace Resources
{ {
public class BitReader public class BitReader
{ {

View File

@ -2,9 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using WatchFace.Parser.Utils;
namespace WatchFace.Parser namespace Resources
{ {
public class ImageReader public class ImageReader
{ {
@ -26,7 +25,7 @@ namespace WatchFace.Parser
{ {
var signature = _reader.ReadChars(4); var signature = _reader.ReadChars(4);
if (signature[0] != 'B' || signature[1] != 'M') if (signature[0] != 'B' || signature[1] != 'M')
throw new ArgumentOutOfRangeException("signature", "Signature doesn't match."); throw new ArgumentException("Image signature doesn't match.");
ReadHeader(); ReadHeader();
ReadPalette(); ReadPalette();

View File

@ -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")]

View File

@ -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>

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.4.12" targetFramework="net461" />
</packages>

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -15,7 +15,7 @@ namespace WatchFace.Parser.Elements.WeatherElements
public long DelimiterImageIndex { get; set; } public long DelimiterImageIndex { get; set; }
[RawParameter(Id = 4)] [RawParameter(Id = 4)]
public long Unknown4 { get; set; } public long AppendDegreesForBoth { get; set; }
[RawParameter(Id = 5)] [RawParameter(Id = 5)]
public long DegreesImageIndex { get; set; } public long DegreesImageIndex { get; set; }

View File

@ -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}."
) { }
}
}

View File

@ -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}."
) { }
}
}

View File

@ -1,8 +1,8 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Drawing;
using System.IO; using System.IO;
using NLog; using NLog;
using System.Drawing; using Resources;
using WatchFace.Parser.Models; using WatchFace.Parser.Models;
namespace WatchFace.Parser namespace WatchFace.Parser
@ -18,8 +18,8 @@ namespace WatchFace.Parser
_fileStream = streamReader; _fileStream = streamReader;
} }
public List<Parameter> Resources { get; private set; } public List<Parameter> Parameters { get; private set; }
public List<Bitmap> Images { get; private set; } public Bitmap[] Images { get; private set; }
public void Read() public void Read()
{ {
@ -38,51 +38,36 @@ namespace WatchFace.Parser
Logger.Trace("Reading parameters descriptor..."); Logger.Trace("Reading parameters descriptor...");
var mainParam = Parameter.ReadFrom(parametersStream); var mainParam = Parameter.ReadFrom(parametersStream);
Logger.Trace("Parameters descriptor was read:"); Logger.Trace("Parameters descriptor was read:");
var coordinatesTableSize = mainParam.Children[0].Value; var parametrsTableLength = mainParam.Children[0].Value;
var imagesTableLength = mainParam.Children[1].Value; var imagesCount = mainParam.Children[1].Value;
Logger.Trace($"CoordinatesTableSize: {coordinatesTableSize}, ImagesTableLength: {imagesTableLength}"); Logger.Trace($"ParametrsTableLength: {parametrsTableLength}, ImagesCount: {imagesCount}");
Logger.Trace("Reading face parameters..."); Logger.Trace("Reading parameters locations...");
var parameters = Parameter.ReadList(parametersStream); var parametersLocations = Parameter.ReadList(parametersStream);
Logger.Trace("Watch face parameters were read:"); Logger.Trace("Watch face parameters locations were read:");
ParseResources(coordinatesTableSize, parameters); Parameters = ReadParameters(parametrsTableLength, parametersLocations);
ParseImages(imagesTableLength); 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); var result = new List<Parameter>(parametersDescriptors.Count);
foreach (var parameter in parameters) foreach (var prameterDescriptor in parametersDescriptors)
{ {
Logger.Trace("Reading descriptor for parameter {0}", parameter.Id); var descriptorOffset = prameterDescriptor.Children[0].Value;
var descriptorOffset = parameter.Children[0].Value; var descriptorLength = prameterDescriptor.Children[1].Value;
var descriptorLength = parameter.Children[1].Value; Logger.Trace("Reading descriptor for parameter {0}", prameterDescriptor.Id);
Logger.Trace("Descriptor offset: {0}, Descriptor length: {1}", descriptorOffset, descriptorLength); Logger.Trace("Descriptor offset: {0}, Descriptor length: {1}", descriptorOffset, descriptorLength);
coordsStream.Seek(descriptorOffset, SeekOrigin.Begin); parametersStream.Seek(descriptorOffset, SeekOrigin.Begin);
var descriptorStream = StreamBlock(coordsStream, (int) descriptorLength); var descriptorStream = StreamBlock(parametersStream, (int) descriptorLength);
Logger.Trace("Parsing descriptor for parameter {0}...", parameter.Id); Logger.Trace("Parsing descriptor for parameter {0}...", prameterDescriptor.Id);
Resources.Add(new Parameter(parameter.Id, Parameter.ReadList(descriptorStream))); result.Add(new Parameter(prameterDescriptor.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);
} }
return result;
} }
private static Stream StreamBlock(Stream stream, int size) private static Stream StreamBlock(Stream stream, int size)

View File

@ -31,26 +31,31 @@ namespace WatchFace.Parser.Utils
return result; 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 properties = SortedPropertiesDictionary<T>();
var result = new 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) foreach (var parameter in descriptor)
{ {
var currentPath = string.Concat(path, '.', parameter.Id.ToString()); var currentPath = string.IsNullOrEmpty(path)
var propertyInfo = properties[parameter.Id]; ? parameter.Id.ToString()
if (propertyInfo == null) : string.Concat(path, '.', parameter.Id.ToString());
throw new InvalidParameterException(parameter, path); 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; var propertyType = propertyInfo.PropertyType;
if (propertyType == typeof(long)) if (propertyType == typeof(long))
{ {
Logger.Trace("{0} '{1}': {2}", currentPath, propertyInfo.Name, parameter.Value);
dynamic propertyValue = propertyInfo.GetValue(result); dynamic propertyValue = propertyInfo.GetValue(result);
if (propertyValue != 0) 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); propertyInfo.SetValue(result, parameter.Value);
} }
@ -68,7 +73,8 @@ namespace WatchFace.Parser.Utils
var method = typeof(ParametersConverter).GetMethod(nameof(Parse)); var method = typeof(ParametersConverter).GetMethod(nameof(Parse));
var itemType = propertyType.GetGenericArguments()[0]; var itemType = propertyType.GetGenericArguments()[0];
var generic = method.MakeGenericMethod(itemType); 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); propertyValue.Add(parsedValue);
} }
catch (TargetInvocationException e) catch (TargetInvocationException e)
@ -80,13 +86,14 @@ namespace WatchFace.Parser.Utils
{ {
dynamic propertyValue = propertyInfo.GetValue(result); dynamic propertyValue = propertyInfo.GetValue(result);
if (propertyValue != null) if (propertyValue != null)
throw new DuplicateParameterException(parameter, path); throw new ArgumentException($"Parameter {parameter.Id} is already set for {currentType.Name}");
try try
{ {
var method = typeof(ParametersConverter).GetMethod(nameof(Parse)); var method = typeof(ParametersConverter).GetMethod(nameof(Parse));
var generic = method.MakeGenericMethod(propertyType); 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); propertyInfo.SetValue(result, parsedValue);
} }
catch (TargetInvocationException e) catch (TargetInvocationException e)

View File

@ -61,7 +61,7 @@
<Compile Include="Elements\DateElements\MonthAndDay.cs" /> <Compile Include="Elements\DateElements\MonthAndDay.cs" />
<Compile Include="Elements\DateElements\OneLineMonthAndDay.cs" /> <Compile Include="Elements\DateElements\OneLineMonthAndDay.cs" />
<Compile Include="Elements\DateElements\SeparateMonthAndDay.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\Status.cs" />
<Compile Include="Elements\StatusElements\Flag.cs" /> <Compile Include="Elements\StatusElements\Flag.cs" />
<Compile Include="Elements\StatusElements\Switch.cs" /> <Compile Include="Elements\StatusElements\Switch.cs" />
@ -76,16 +76,12 @@
<Compile Include="Elements\WeatherElements\TemperatureNumber.cs" /> <Compile Include="Elements\WeatherElements\TemperatureNumber.cs" />
<Compile Include="Elements\WeatherElements\TodayTemperature.cs" /> <Compile Include="Elements\WeatherElements\TodayTemperature.cs" />
<Compile Include="Elements\WeatherElements\WeatherIcon.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="JsonConverters\HexStringJsonConverter.cs" />
<Compile Include="Models\Header.cs" /> <Compile Include="Models\Header.cs" />
<Compile Include="Models\Parameter.cs" /> <Compile Include="Models\Parameter.cs" />
<Compile Include="Models\ParameterFlags.cs" /> <Compile Include="Models\ParameterFlags.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reader.cs" /> <Compile Include="Reader.cs" />
<Compile Include="Utils\BitReader.cs" />
<Compile Include="Utils\BitWriter.cs" /> <Compile Include="Utils\BitWriter.cs" />
<Compile Include="Utils\ParametersConverter.cs" /> <Compile Include="Utils\ParametersConverter.cs" />
<Compile Include="Utils\RawParameterAttribute.cs" /> <Compile Include="Utils\RawParameterAttribute.cs" />
@ -95,5 +91,11 @@
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </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" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -21,7 +21,7 @@ namespace WatchFace.Parser
public Weather Weather { get; set; } public Weather Weather { get; set; }
[RawParameter(Id = 7)] [RawParameter(Id = 7)]
public Scales Scales { get; set; } public StepsProgress StepsProgress { get; set; }
[RawParameter(Id = 8)] [RawParameter(Id = 8)]
public Status Status { get; set; } public Status Status { get; set; }

View File

@ -1,12 +1,15 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog; using NLog;
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
using WatchFace.Parser.Utils; using Resources;
using WatchFace.Parser; using WatchFace.Parser;
using WatchFace.Parser.Utils;
namespace WatchFace namespace WatchFace
{ {
@ -21,21 +24,47 @@ namespace WatchFace
if (args.Length == 0 || args[0] == null) if (args.Length == 0 || args[0] == null)
{ {
Console.WriteLine("{0} unpacks Amazfit Bip downloadable watch faces.", AppName); 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; return;
} }
var inputFileName = args[0]; if (args.Length > 0)
if (!File.Exists(inputFileName)) Console.WriteLine("Multiple files unpacking.");
foreach (var inputFileName in args)
{ {
Console.WriteLine("File '{0}' doesn't exists.", inputFileName); if (!File.Exists(inputFileName))
return; {
} Console.WriteLine("File '{0}' doesn't exists.", inputFileName);
continue;
}
if (Path.GetExtension(inputFileName) == ".json") Console.WriteLine("Processing file '{0}'", inputFileName);
PackWatchFace(inputFileName); var inputFileExtension = Path.GetExtension(inputFileName);
else try
UnpackWatchFace(inputFileName); {
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) private static void PackWatchFace(string inputFileName)
@ -59,10 +88,25 @@ namespace WatchFace
if (watchFace == null) return; if (watchFace == null) return;
Logger.Debug("Exporting resources to '{0}'", outputDirectory); Logger.Debug("Exporting resources to '{0}'", outputDirectory);
ExportImages(reader, outputDirectory); new ResourcesExtractor(reader.Images).Extract(outputDirectory);
ExportConfig(watchFace, Path.Combine(outputDirectory, $"{baseName}.json")); 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) private static void WriteWatchFace(string outputFileName, Parser.WatchFace watchFace)
{ {
Logger.Debug("Writing watch face to '{0}'", outputFileName); Logger.Debug("Writing watch face to '{0}'", outputFileName);
@ -106,7 +150,7 @@ namespace WatchFace
Logger.Debug("Parsing parameters..."); Logger.Debug("Parsing parameters...");
try try
{ {
return ParametersConverter.Parse<Parser.WatchFace>(reader.Resources); return ParametersConverter.Parse<Parser.WatchFace>(reader.Parameters);
} }
catch (Exception e) catch (Exception e)
{ {
@ -124,24 +168,6 @@ namespace WatchFace
return unpackedPath; 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) private static Parser.WatchFace ReadConfig(string jsonFileName)
{ {
Logger.Debug("Reading config..."); Logger.Debug("Reading config...");

View File

@ -57,6 +57,10 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<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"> <ProjectReference Include="..\WatchFace.Parser\WatchFace.Parser.csproj">
<Project>{77d5f357-a1f3-4823-a328-b78b49ed870a}</Project> <Project>{77d5f357-a1f3-4823-a328-b78b49ed870a}</Project>
<Name>WatchFace.Parser</Name> <Name>WatchFace.Parser</Name>