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

View File

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

View File

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

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; }
[RawParameter(Id = 4)]
public long Unknown4 { get; set; }
public long AppendDegreesForBoth { get; set; }
[RawParameter(Id = 5)]
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 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)

View File

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

View File

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

View File

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

View File

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

View File

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