Compare commits
26 Commits
Author | SHA1 | Date |
---|---|---|
Valeriy Mironov | 9c028a9c07 | |
Valeriy Mironov | 4ad3537a26 | |
Valeriy Mironov | baa352661d | |
Valeriy Mironov | 948fd521cc | |
Valeriy Mironov | 1c3663b65d | |
Valeriy Mironov | 10273531cf | |
Valeriy Mironov | 7d0938b569 | |
Valeriy Mironov | 37ca2a8142 | |
Valeriy Mironov | f2cc221072 | |
Valeriy Mironov | b09c78a2a2 | |
Valeriy Mironov | dd2421c7b4 | |
Valeriy Mironov | cfff020bc0 | |
Valeriy Mironov | 985fd1688e | |
Valeriy Mironov | 631b90fcb5 | |
Valeriy Mironov | 320be4c269 | |
Valeriy Mironov | e906ed4ff2 | |
Valeriy Mironov | 2398ab8086 | |
Valeriy Mironov | 950462fb90 | |
Valeriy Mironov | 7ef631924d | |
Valeriy Mironov | 3e6f8c44fe | |
Valeriy Mironov | 6074eeb335 | |
Valeriy Mironov | 20f7155838 | |
Valeriy Mironov | bb8388e95c | |
Valeriy Mironov | 1957abecbc | |
Valeriy Mironov | c28196305f | |
Valeriy Mironov | ee9660dc5a |
|
@ -4,6 +4,7 @@
|
|||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/Environment/Hierarchy/PsiConfigurationSettingsKey/CustomLocation/@EntryValue">C:\Users\valeronm\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_048a238b\SolutionCaches</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
59
Changelog.md
|
@ -3,7 +3,53 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [Unreleased]
|
||||
## [1.0.2.12] - 2018-05-01
|
||||
### Added
|
||||
- Added support for air quality index value;
|
||||
- Aanimated preview now shows discharging battery instead of charging.
|
||||
|
||||
## [1.0.2.11] - 2018-05-01
|
||||
### Changed
|
||||
- Improved speed of all operations by tuning logging options
|
||||
|
||||
## [1.0.2.10] - 2018-05-01
|
||||
### Added
|
||||
- Skipped storing empty paramter lists. It was possible to brick watches with this :( (Thanks to Luca Venturini for the report)
|
||||
|
||||
## [1.0.2.9] - 2018-03-25
|
||||
### Added
|
||||
- Added sorting of image palette for correct repacking font images added in 0.1.1.15 firmware (RES 32) (#23)
|
||||
|
||||
## [1.0.2.8] - 2018-02-02
|
||||
### Added
|
||||
- Added support for unpacking/packing new format of .res-file from firmware 0.1.0.66 (#18)
|
||||
### Changed
|
||||
- Fixed drawing order of AM/PM element.
|
||||
|
||||
## [1.0.2.7] - 2018-01-18
|
||||
### Added
|
||||
- Added support for custom weather icons. New element `CustomIcon` was added to `Weather.Icon` element.
|
||||
It contains `X`, `Y`, `ImageIndex` and `ImagesCount` parameters.
|
||||
`X` and `Y` are the coordinates of the icon on the screen.
|
||||
`ImageIndex` and `ImagesCount` describe the images set used for weather icon.
|
||||
|
||||
## [1.0.2.6] - 2017-12-25
|
||||
### Changed
|
||||
- Fixed adding zero `DrawingOrder` when it's not present in config
|
||||
|
||||
## [1.0.2.5] - 2017-12-25
|
||||
### Added
|
||||
- Added support for digits drawing order in Time block used by new official watchface (Winter).
|
||||
|
||||
### Changed
|
||||
- Renamed `Unknown3` property of `Weather.Icon` element to `CurrentAlt`.
|
||||
- Renamed `Unknown3` and `Unknown4` properties of `Weather.Temperature.Today.Separate` element to `DayAlt` and `NightAlt`.
|
||||
|
||||
## [1.0.2.4] - 2017-12-14
|
||||
### Changed
|
||||
- Changed calculated block element position according to watches behavior. Text with width bigger than block width will be rendered with left alignment and text with height bigger than block height will be rendered with top alighnment.
|
||||
- Fixed error "The image has (5,6,7...) bit/pixel". It was caused by not applying dithering to alpha-channel (#8).
|
||||
- Dithering is now applied right after image load, so preview uses dithered instead of original image.
|
||||
|
||||
## [1.0.2.3] - 2017-12-10
|
||||
### Changed
|
||||
|
@ -55,7 +101,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
|||
- Implemented watchfaces unpacking and packing.
|
||||
- Implemented .res file unpacking.
|
||||
|
||||
[Unreleased]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/HEAD..1.0.2.3
|
||||
[Unreleased]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/HEAD..1.0.2.12
|
||||
[1.0.2.12]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.12..1.0.2.11
|
||||
[1.0.2.11]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.11..1.0.2.10
|
||||
[1.0.2.10]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.10..1.0.2.9
|
||||
[1.0.2.9]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.9..1.0.2.8
|
||||
[1.0.2.8]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.8..1.0.2.7
|
||||
[1.0.2.7]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.7..1.0.2.6
|
||||
[1.0.2.6]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.6..1.0.2.5
|
||||
[1.0.2.5]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.5..1.0.2.4
|
||||
[1.0.2.4]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.4..1.0.2.3
|
||||
[1.0.2.3]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.3..1.0.2.2
|
||||
[1.0.2.2]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.2..1.0.2.1
|
||||
[1.0.2.1]: https://bitbucket.org/valeronm/amazfitbiptools/branches/compare/1.0.2.1..1.0.2.0
|
||||
|
|
|
@ -18,22 +18,16 @@ namespace Resources
|
|||
|
||||
public void Extract(string outputDirectory)
|
||||
{
|
||||
if (_descriptor.Version != null)
|
||||
{
|
||||
var fileName = Path.Combine(outputDirectory, "version");
|
||||
using (var stream = File.OpenWrite(fileName))
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
{
|
||||
writer.Write(_descriptor.Version.Value);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < _descriptor.Images.Count; i++)
|
||||
for (var i = 0; i < _descriptor.Resources.Count; i++)
|
||||
{
|
||||
var resource = _descriptor.Resources[i];
|
||||
var numericPart = i.ToString().PadLeft(ImageLoader.NumericPartLength, '0');
|
||||
var fileName = Path.Combine(outputDirectory, numericPart + ".png");
|
||||
|
||||
var fileName = Path.Combine(outputDirectory, numericPart + resource.Extension);
|
||||
Logger.Debug("Extracting {0}...", fileName);
|
||||
_descriptor.Images[i].Save(fileName, ImageFormat.Png);
|
||||
|
||||
using (var fileStream = File.OpenWrite(fileName))
|
||||
resource.ExportTo(fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using Resources.Models;
|
||||
|
||||
|
@ -13,20 +14,33 @@ namespace Resources
|
|||
{
|
||||
var binaryReader = new BinaryReader(stream);
|
||||
|
||||
Logger.Trace("Reading resources header");
|
||||
var header = Header.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
|
||||
);
|
||||
var signature = Encoding.ASCII.GetString(binaryReader.ReadBytes(5));
|
||||
Logger.Trace("Resources signature was read:");
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
Logger.Trace("Signature: {0}", signature);
|
||||
|
||||
if (!header.IsValid)
|
||||
throw new ArgumentException("Invalid resources header");
|
||||
Header header;
|
||||
switch (signature) {
|
||||
case Header.ResSignature:
|
||||
header = Header.ReadFrom(binaryReader);
|
||||
break;
|
||||
case NewHeader.ResSignature:
|
||||
header = NewHeader.ReadFrom(binaryReader);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Signature '{signature}' is no recognized.");
|
||||
}
|
||||
|
||||
Logger.Trace("Resources header was read:");
|
||||
Logger.Trace("Version: {0}, ResourcesCount: {1}", header.Version, header.ResourcesCount);
|
||||
|
||||
return new FileDescriptor
|
||||
{
|
||||
HasNewHeader = header is NewHeader,
|
||||
ResourcesCount = header.ResourcesCount,
|
||||
Version = header.Version,
|
||||
Images = new Reader(stream).Read(header.ResourcesCount)
|
||||
Unknown = (header as NewHeader)?.Unknown,
|
||||
Resources = new Reader(stream).Read(header.ResourcesCount)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,19 +23,43 @@ namespace Resources
|
|||
if (descriptor.Version == null)
|
||||
throw new ArgumentException("Res file version required");
|
||||
|
||||
if (descriptor.HasNewHeader)
|
||||
WriteNewHeader(descriptor);
|
||||
else
|
||||
WriteOldHeader(descriptor);
|
||||
|
||||
Logger.Trace("Writing images...");
|
||||
new Writer(_stream).Write(descriptor.Resources);
|
||||
}
|
||||
|
||||
private void WriteOldHeader(FileDescriptor descriptor)
|
||||
{
|
||||
var header = new Header
|
||||
{
|
||||
ResourcesCount = (uint) descriptor.Images.Count,
|
||||
ResourcesCount = (uint)descriptor.Resources.Count,
|
||||
Version = descriptor.Version.Value
|
||||
};
|
||||
Logger.Trace("Writing resources header...");
|
||||
Logger.Trace("Signature: {0}, Version: {1}, ResourcesCount: {2}, IsValid: {3}",
|
||||
header.Signature, header.Version, header.ResourcesCount, header.IsValid
|
||||
Logger.Trace("Signature: {0}, Version: {1}, ResourcesCount: {2}",
|
||||
header.Signature, header.Version, header.ResourcesCount
|
||||
);
|
||||
header.WriteTo(_binaryWriter);
|
||||
|
||||
Logger.Trace("Writing images...");
|
||||
new Writer(_stream).Write(descriptor.Images);
|
||||
}
|
||||
|
||||
private void WriteNewHeader(FileDescriptor descriptor)
|
||||
{
|
||||
var header = new NewHeader
|
||||
{
|
||||
ResourcesCount = descriptor.ResourcesCount.Value,
|
||||
Version = descriptor.Version.Value,
|
||||
Unknown = descriptor.Unknown.Value
|
||||
};
|
||||
Logger.Trace("Writing resources header...");
|
||||
Logger.Trace("Signature: {0}, Version: {1}, ResourcesCount: {2}, Unknown: {3}",
|
||||
header.Signature, header.Version, header.ResourcesCount, header.Unknown
|
||||
);
|
||||
header.WriteTo(_binaryWriter);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using BumpKit;
|
||||
using NLog;
|
||||
|
@ -29,10 +28,20 @@ namespace Resources.Image
|
|||
{
|
||||
var signature = _reader.ReadChars(4);
|
||||
if (signature[0] != 'B' || signature[1] != 'M')
|
||||
throw new ArgumentException("Image signature doesn't match.");
|
||||
throw new InvalidResourceException("Image signature doesn't match.");
|
||||
|
||||
ReadHeader();
|
||||
ReadPalette();
|
||||
if (_paletteColors > 256)
|
||||
throw new InvalidResourceException(
|
||||
"Too many palette colors.");
|
||||
|
||||
if (_paletteColors > 0)
|
||||
ReadPalette();
|
||||
else if (_bitsPerPixel == 8 || _bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32)
|
||||
Logger.Trace("The image doesn't use a palette.");
|
||||
else
|
||||
throw new InvalidResourceException(
|
||||
"The image format is not supported. Please report the issue on https://bitbucket.org/valeronm/amazfitbiptools");
|
||||
return ReadImage();
|
||||
}
|
||||
|
||||
|
@ -66,7 +75,6 @@ namespace Resources.Image
|
|||
if (padding != 0) Logger.Warn("Palette item {0} last byte is not zero: {1:X2}", i, padding);
|
||||
|
||||
var isColorValid = (r == 0 || r == 0xff) && (g == 0 || g == 0xff) && (b == 0 || b == 0xff);
|
||||
|
||||
if (isColorValid)
|
||||
Logger.Trace("Palette item {0}: R {1:X2}, G {2:X2}, B {3:X2}", i, r, g, b);
|
||||
else
|
||||
|
@ -78,6 +86,16 @@ namespace Resources.Image
|
|||
}
|
||||
|
||||
private Bitmap ReadImage()
|
||||
{
|
||||
if (_paletteColors > 0) return ReadPaletteImage();
|
||||
if (_bitsPerPixel == 8) return Read8BitImage();
|
||||
if (_bitsPerPixel == 16) return Read16BitImage();
|
||||
if (_bitsPerPixel == 24) return Read24BitImage();
|
||||
if (_bitsPerPixel == 32) return Read32BitImage();
|
||||
throw new InvalidResourceException($"Unsupported bits per pixel value: {_bitsPerPixel}");
|
||||
}
|
||||
|
||||
private Bitmap ReadPaletteImage()
|
||||
{
|
||||
var image = new Bitmap(_width, _height);
|
||||
using (var context = image.CreateUnsafeContext())
|
||||
|
@ -89,11 +107,104 @@ namespace Resources.Image
|
|||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var pixelColorIndex = bitReader.ReadBits(_bitsPerPixel);
|
||||
var color = _palette[(int) pixelColorIndex];
|
||||
var color = _palette[(int)pixelColorIndex];
|
||||
context.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private Bitmap Read8BitImage()
|
||||
{
|
||||
var image = new Bitmap(_width, _height);
|
||||
using (var context = image.CreateUnsafeContext())
|
||||
{
|
||||
for (var y = 0; y < _height; y++)
|
||||
{
|
||||
var rowBytes = _reader.ReadBytes(_rowLengthInBytes);
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var data = rowBytes[x];
|
||||
var color = Color.FromArgb(0xff, data, data, data);
|
||||
context.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private Bitmap Read16BitImage()
|
||||
{
|
||||
var image = new Bitmap(_width, _height);
|
||||
using (var context = image.CreateUnsafeContext())
|
||||
{
|
||||
for (var y = 0; y < _height; y++)
|
||||
{
|
||||
var rowBytes = _reader.ReadBytes(_rowLengthInBytes);
|
||||
var bitReader = new BitReader(rowBytes);
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var firstByte = (int)bitReader.ReadByte();
|
||||
var secondByte = (int)bitReader.ReadByte();
|
||||
var b = (byte)((secondByte >> 3) & 0x1f) << 3;
|
||||
var g = (byte)(((firstByte >> 5) & 0x7) | ((secondByte & 0x07) << 3)) << 2;
|
||||
var r = (byte)(firstByte & 0x1f) << 3;
|
||||
var color = Color.FromArgb(0xff, r, g, b);
|
||||
context.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private Bitmap Read24BitImage()
|
||||
{
|
||||
var image = new Bitmap(_width, _height);
|
||||
using (var context = image.CreateUnsafeContext())
|
||||
{
|
||||
for (var y = 0; y < _height; y++)
|
||||
{
|
||||
var rowBytes = _reader.ReadBytes(_rowLengthInBytes);
|
||||
var bitReader = new BitReader(rowBytes);
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var alpha = (int)bitReader.ReadByte();
|
||||
var b = (int)(bitReader.ReadBits(5) << 3);
|
||||
var g = (int)(bitReader.ReadBits(6) << 2);
|
||||
var r = (int)(bitReader.ReadBits(5) << 3);
|
||||
var color = Color.FromArgb(0xff - alpha, r, g, b);
|
||||
context.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private Bitmap Read32BitImage()
|
||||
{
|
||||
var image = new Bitmap(_width, _height);
|
||||
using (var context = image.CreateUnsafeContext())
|
||||
{
|
||||
for (var y = 0; y < _height; y++)
|
||||
{
|
||||
var rowBytes = _reader.ReadBytes(_rowLengthInBytes);
|
||||
for (var x = 0; x < _width; x++)
|
||||
{
|
||||
var r = rowBytes[x * 4];
|
||||
var g = rowBytes[x * 4 + 1];
|
||||
var b = rowBytes[x * 4 + 2];
|
||||
var alpha = rowBytes[x * 4 + 3];
|
||||
var color = Color.FromArgb(0xff - alpha, r, g, b);
|
||||
context.SetPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace Resources.Image
|
|||
|
||||
if (_bitsPerPixel > 4)
|
||||
throw new ArgumentException(
|
||||
$"The image has {_bitsPerPixel} bit/pixel and can't be packed for using on the watches. Looks like dithering works wincorrectly on the image."
|
||||
$"The image has {_bitsPerPixel} bit/pixel and can't be packed for using on the watches. Looks like dithering works incorrectly on the image."
|
||||
);
|
||||
|
||||
_rowLengthInBytes = (ushort) Math.Ceiling(_width * _bitsPerPixel / 8.0);
|
||||
|
@ -85,6 +85,29 @@ namespace Resources.Image
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var startIndex = _transparency == 0 ? 0 : 1;
|
||||
|
||||
for (var i = startIndex; i < _palette.Count - 1; i++)
|
||||
{
|
||||
var minColor = (uint) _palette[i].ToArgb();
|
||||
var minIndex = i;
|
||||
for (var j = i + 1; j < _palette.Count; j++)
|
||||
{
|
||||
var color = (uint) _palette[j].ToArgb();
|
||||
if (color >= minColor) continue;
|
||||
|
||||
minColor = color;
|
||||
minIndex = j;
|
||||
}
|
||||
|
||||
if (minIndex == i) continue;
|
||||
|
||||
var tmp = _palette[i];
|
||||
_palette[i] = _palette[minIndex];
|
||||
_palette[minIndex] = tmp;
|
||||
}
|
||||
|
||||
_paletteColors = (ushort) _palette.Count;
|
||||
_bitsPerPixel = (ushort) Math.Ceiling(Math.Log(_paletteColors, 2));
|
||||
}
|
||||
|
@ -149,6 +172,7 @@ namespace Resources.Image
|
|||
bitWriter.WriteBits(paletteIndex, _bitsPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
bitWriter.Flush();
|
||||
_writer.Write(rowData);
|
||||
}
|
||||
|
|
|
@ -1,42 +1,71 @@
|
|||
using System.Drawing;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using Resources.Image;
|
||||
using Resources.Models;
|
||||
|
||||
namespace Resources
|
||||
{
|
||||
public class ImageLoader
|
||||
{
|
||||
public static readonly int NumericPartLength = 3;
|
||||
public static readonly int NumericPartLength = 4;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static Bitmap LoadImageForNumber(string directory, long index)
|
||||
public static IResource LoadResourceForNumber(string directory, long index)
|
||||
{
|
||||
var strIndex = index.ToString();
|
||||
var numericParts = new[]
|
||||
{
|
||||
index.ToString().PadLeft(NumericPartLength, '0'),
|
||||
index.ToString()
|
||||
};
|
||||
strIndex.PadLeft(4, '0'), strIndex.PadLeft(3, '0'), strIndex.PadLeft(2, '0'), strIndex
|
||||
}.Distinct();
|
||||
|
||||
foreach (var numericPart in numericParts.Distinct())
|
||||
foreach (var numericPart in numericParts)
|
||||
{
|
||||
var fullFileName = Path.Combine(directory, numericPart + ".png");
|
||||
if (!File.Exists(fullFileName))
|
||||
{
|
||||
Logger.Trace("File {0} doesn't exists.", fullFileName);
|
||||
continue;
|
||||
}
|
||||
var resource = TryLoadBitmap(directory, numericPart);
|
||||
if (resource != null) return resource;
|
||||
|
||||
var image = (Bitmap) System.Drawing.Image.FromFile(fullFileName);
|
||||
Logger.Trace("Image was loaded from file {0}", fullFileName);
|
||||
return ApplyDithering(image);
|
||||
resource = TryLoadBlob(directory, numericPart);
|
||||
if (resource != null) return resource;
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"File referenced by index {index} not found.");
|
||||
}
|
||||
|
||||
private static IResource TryLoadBitmap(string directory, string numericPart)
|
||||
{
|
||||
var fullFileName = Path.Combine(directory, numericPart + Models.Image.ResourceExtension);
|
||||
if (!File.Exists(fullFileName))
|
||||
{
|
||||
Logger.Trace("File {0} doesn't exists.", fullFileName);
|
||||
return null;
|
||||
}
|
||||
|
||||
var image = (Bitmap) System.Drawing.Image.FromFile(fullFileName);
|
||||
Logger.Trace("Image was loaded from file {0}", fullFileName);
|
||||
var ditheredBitmap = ApplyDithering(image);
|
||||
return new Models.Image(ditheredBitmap);
|
||||
}
|
||||
|
||||
private static IResource TryLoadBlob(string directory, string numericPart)
|
||||
{
|
||||
var fullFileName = Path.Combine(directory, numericPart + Blob.ResourceExtension);
|
||||
if (!File.Exists(fullFileName))
|
||||
{
|
||||
Logger.Trace("File {0} doesn't exists.", fullFileName);
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var fileStream = File.OpenRead(fullFileName))
|
||||
{
|
||||
var data = new byte[fileStream.Length];
|
||||
fileStream.Read(data, 0, data.Length);
|
||||
return new Blob(data);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap ApplyDithering(Bitmap image)
|
||||
{
|
||||
var clone = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);
|
||||
|
@ -46,7 +75,6 @@ namespace Resources
|
|||
}
|
||||
|
||||
FloydSteinbergDitherer.Process(clone);
|
||||
clone.Save("tmp.png");
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Resources
|
||||
{
|
||||
public class InvalidResourceException : Exception
|
||||
{
|
||||
public InvalidResourceException(string message) : base(message) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Resources.Models
|
||||
{
|
||||
public class Blob : IResource
|
||||
{
|
||||
public static string ResourceExtension = ".dat";
|
||||
private readonly byte[] _data;
|
||||
|
||||
public Blob(byte[] data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public string Extension => ResourceExtension;
|
||||
|
||||
public void WriteTo(Stream stream)
|
||||
{
|
||||
stream.Write(_data, 0, _data.Length);
|
||||
}
|
||||
|
||||
public void ExportTo(Stream stream)
|
||||
{
|
||||
stream.Write(_data, 0, _data.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Resources.Models
|
||||
{
|
||||
public class FileDescriptor
|
||||
{
|
||||
public bool HasNewHeader { get; set; }
|
||||
public uint? ResourcesCount { get; set; }
|
||||
public uint? Unknown { get; set; }
|
||||
public byte? Version { get; set; }
|
||||
public List<Bitmap> Images { get; set; } = new List<Bitmap>();
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<IResource> Resources { get; set; } = new List<IResource>();
|
||||
}
|
||||
}
|
|
@ -7,15 +7,13 @@ namespace Resources.Models
|
|||
public class Header
|
||||
{
|
||||
public const int HeaderSize = 20;
|
||||
private const string ResSignature = "HMRES";
|
||||
public const string ResSignature = "HMRES";
|
||||
|
||||
public string Signature { get; private set; } = ResSignature;
|
||||
public string Signature { get; protected set; } = ResSignature;
|
||||
public byte Version { get; set; }
|
||||
public uint ResourcesCount { get; set; }
|
||||
|
||||
public bool IsValid => Signature == ResSignature;
|
||||
|
||||
public void WriteTo(BinaryWriter writer)
|
||||
public virtual void WriteTo(BinaryWriter writer)
|
||||
{
|
||||
var buffer = new byte[HeaderSize];
|
||||
for (var i = 0; i < buffer.Length; i++) buffer[i] = 0xff;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Resources.Models
|
||||
{
|
||||
public interface IResource
|
||||
{
|
||||
string Extension { get; }
|
||||
void WriteTo(Stream stream);
|
||||
void ExportTo(Stream stream);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
|
||||
namespace Resources.Models
|
||||
{
|
||||
public class Image: IResource
|
||||
{
|
||||
public Bitmap Bitmap { get; }
|
||||
|
||||
public Image(Bitmap bitmap)
|
||||
{
|
||||
Bitmap = bitmap;
|
||||
}
|
||||
|
||||
public static string ResourceExtension = ".png";
|
||||
public string Extension => ResourceExtension;
|
||||
|
||||
public void WriteTo(Stream stream)
|
||||
{
|
||||
new Resources.Image.Writer(stream).Write(Bitmap);
|
||||
}
|
||||
|
||||
public void ExportTo(Stream stream)
|
||||
{
|
||||
Bitmap.Save(stream, ImageFormat.Png);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Resources.Models
|
||||
{
|
||||
public class NewHeader : Header
|
||||
{
|
||||
public new const int HeaderSize = 0x24;
|
||||
public new const string ResSignature = "NERES";
|
||||
|
||||
public NewHeader()
|
||||
{
|
||||
Signature = ResSignature;
|
||||
}
|
||||
|
||||
public uint Unknown { get; set; }
|
||||
|
||||
public override void WriteTo(BinaryWriter writer)
|
||||
{
|
||||
var buffer = new byte[HeaderSize];
|
||||
for (var i = 0; i < buffer.Length; i++) buffer[i] = 0xff;
|
||||
|
||||
Encoding.ASCII.GetBytes(ResSignature).CopyTo(buffer, 0);
|
||||
buffer[5] = Version;
|
||||
BitConverter.GetBytes(Unknown).CopyTo(buffer, 0xa);
|
||||
BitConverter.GetBytes(ResourcesCount).CopyTo(buffer, 0x20);
|
||||
writer.Write(buffer);
|
||||
}
|
||||
|
||||
public new static NewHeader ReadFrom(BinaryReader reader)
|
||||
{
|
||||
var buffer = reader.ReadBytes(HeaderSize);
|
||||
return new NewHeader
|
||||
{
|
||||
Signature = Encoding.ASCII.GetString(buffer, 0, 0x5),
|
||||
Version = buffer[0x5],
|
||||
Unknown = BitConverter.ToUInt32(buffer, 0xa),
|
||||
ResourcesCount = BitConverter.ToUInt32(buffer, 0x20)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using Resources.Models;
|
||||
|
||||
namespace Resources
|
||||
{
|
||||
|
@ -20,32 +19,54 @@ namespace Resources
|
|||
_binaryReader = new BinaryReader(_stream);
|
||||
}
|
||||
|
||||
public List<Bitmap> Read(uint imagesTableLength)
|
||||
public List<IResource> Read(uint resourcesCount)
|
||||
{
|
||||
var offsetsTableLength = (int) (imagesTableLength * OffsetTableItemLength);
|
||||
var offsetsTableLength = (int) (resourcesCount * OffsetTableItemLength);
|
||||
Logger.Trace("Reading resources offsets table with {0} elements ({1} bytes)",
|
||||
imagesTableLength, offsetsTableLength
|
||||
resourcesCount, offsetsTableLength
|
||||
);
|
||||
var imagesOffsets = _binaryReader.ReadBytes(offsetsTableLength);
|
||||
var imagesOffset = _stream.Position;
|
||||
|
||||
Logger.Debug("Reading {0} images...", imagesTableLength);
|
||||
var images = new List<Bitmap>((int) imagesTableLength);
|
||||
for (var i = 0; i < imagesTableLength; i++)
|
||||
var offsets = new int[resourcesCount];
|
||||
for (var i = 0; i < resourcesCount; i++)
|
||||
offsets[i] = _binaryReader.ReadInt32();
|
||||
|
||||
var resourcesOffset = (int) _stream.Position;
|
||||
var fileSize = (int) _stream.Length;
|
||||
|
||||
Logger.Debug("Reading {0} resources...", resourcesCount);
|
||||
var resources = new List<IResource>((int) resourcesCount);
|
||||
for (var i = 0; i < resourcesCount; i++)
|
||||
{
|
||||
var imageOffset = BitConverter.ToUInt32(imagesOffsets, i * OffsetTableItemLength);
|
||||
var realOffset = imageOffset + imagesOffset;
|
||||
Logger.Trace("Image {0} offset is {1}...", i, imageOffset);
|
||||
if (_stream.Position != realOffset)
|
||||
var offset = offsets[i] + resourcesOffset;
|
||||
var nextOffset = i + 1 < resourcesCount ? offsets[i + 1] + resourcesOffset : fileSize;
|
||||
var length = nextOffset - offset;
|
||||
Logger.Trace("Resource {0} offset: {1}, length: {2}...", i, offset, length);
|
||||
if (_stream.Position != offset)
|
||||
{
|
||||
var bytesGap = realOffset - _stream.Position;
|
||||
var bytesGap = offset - _stream.Position;
|
||||
Logger.Warn("Found {0} bytes gap before resource number {1}", bytesGap, i);
|
||||
_stream.Seek(realOffset, SeekOrigin.Begin);
|
||||
_stream.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
Logger.Debug("Reading resource {0}...", i);
|
||||
try
|
||||
{
|
||||
var bitmap = new Image.Reader(_stream).Read();
|
||||
var image = new Models.Image(bitmap);
|
||||
resources.Add(image);
|
||||
}
|
||||
catch (InvalidResourceException)
|
||||
{
|
||||
Logger.Warn("Resource is not an image");
|
||||
_stream.Seek(offset, SeekOrigin.Begin);
|
||||
var data = new byte[length];
|
||||
_stream.Read(data, 0, length);
|
||||
var blob = new Blob(data);
|
||||
resources.Add(blob);
|
||||
}
|
||||
Logger.Debug("Reading image {0}...", i);
|
||||
images.Add(new Image.Reader(_stream).Read());
|
||||
}
|
||||
return images;
|
||||
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Resources
|
||||
{
|
||||
interface IResource
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -35,11 +35,15 @@
|
|||
<HintPath>..\packages\Bumpkit.1.0.2\lib\BumpKit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.6\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
@ -47,17 +51,22 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Extractor.cs" />
|
||||
<Compile Include="ImageLoader.cs" />
|
||||
<Compile Include="InvalidResourceException.cs" />
|
||||
<Compile Include="Models\Blob.cs" />
|
||||
<Compile Include="Models\Image.cs" />
|
||||
<Compile Include="Models\IResource.cs" />
|
||||
<Compile Include="Models\FileDescriptor.cs" />
|
||||
<Compile Include="Models\Header.cs" />
|
||||
<Compile Include="Utils\BitReader.cs" />
|
||||
<Compile Include="Utils\BitWriter.cs" />
|
||||
<Compile Include="Image\ColorError.cs" />
|
||||
<Compile Include="Image\FloydSteinbergDitherer.cs" />
|
||||
<Compile Include="Image\Writer.cs" />
|
||||
<Compile Include="Writer.cs" />
|
||||
<Compile Include="Extractor.cs" />
|
||||
<Compile Include="FileWriter.cs" />
|
||||
<Compile Include="Models\Header.cs" />
|
||||
<Compile Include="Models\NewHeader.cs" />
|
||||
<Compile Include="Image\Reader.cs" />
|
||||
<Compile Include="FileReader.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp60</s:String></wpf:ResourceDictionary>
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using Resources.Models;
|
||||
|
||||
namespace Resources
|
||||
{
|
||||
|
@ -18,30 +19,30 @@ namespace Resources
|
|||
_stream = stream;
|
||||
}
|
||||
|
||||
public void Write(List<Bitmap> images)
|
||||
public void Write(List<IResource> resources)
|
||||
{
|
||||
var offsetsTable = new byte[images.Count * OffsetTableItemLength];
|
||||
var encodedImages = new MemoryStream[images.Count];
|
||||
var offsetsTable = new byte[resources.Count * OffsetTableItemLength];
|
||||
var encodedResources = new MemoryStream[resources.Count];
|
||||
|
||||
var offset = (uint) 0;
|
||||
for (var i = 0; i < images.Count; i++)
|
||||
for (var i = 0; i < resources.Count; i++)
|
||||
{
|
||||
Logger.Trace("Image {0} offset is {1}...", i, offset);
|
||||
Logger.Trace("Resource {0} offset is {1}...", i, offset);
|
||||
var offsetBytes = BitConverter.GetBytes(offset);
|
||||
offsetBytes.CopyTo(offsetsTable, i * OffsetTableItemLength);
|
||||
|
||||
var encodedImage = new MemoryStream();
|
||||
Logger.Debug("Encoding image {0}...", i);
|
||||
new Image.Writer(encodedImage).Write(images[i]);
|
||||
Logger.Debug("Encoding resource {0}...", i);
|
||||
resources[i].WriteTo(encodedImage);
|
||||
offset += (uint) encodedImage.Length;
|
||||
encodedImages[i] = encodedImage;
|
||||
encodedResources[i] = encodedImage;
|
||||
}
|
||||
|
||||
Logger.Trace("Writing images offsets table");
|
||||
Logger.Trace("Writing resources offsets table");
|
||||
_stream.Write(offsetsTable, 0, offsetsTable.Length);
|
||||
|
||||
Logger.Debug("Writing images");
|
||||
foreach (var encodedImage in encodedImages)
|
||||
Logger.Debug("Writing resources");
|
||||
foreach (var encodedImage in encodedResources)
|
||||
{
|
||||
encodedImage.Seek(0, SeekOrigin.Begin);
|
||||
encodedImage.CopyTo(_stream);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Bumpkit" version="1.0.2" targetFramework="net40-client" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40-client" />
|
||||
<package id="NLog" version="4.5.6" targetFramework="net40-client" />
|
||||
</packages>
|
|
@ -15,5 +15,8 @@ namespace WatchFace.Parser.Elements.ActivityElements
|
|||
[ParameterId(3)]
|
||||
[ParameterImageIndex]
|
||||
public long? DecimalPointImageIndex { get; set; }
|
||||
|
||||
[ParameterId(4)]
|
||||
public long? SuffixMilesImageIndex { get; set; }
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace WatchFace.Parser.Elements.AnalogDialFaceElements
|
|||
[ParameterId(1)]
|
||||
public bool OnlyBorder { get; set; }
|
||||
|
||||
[JsonConverter(typeof(HexStringJsonConverter))]
|
||||
[JsonConverter(typeof(ColorJsonConverter))]
|
||||
[ParameterId(2)]
|
||||
public long Color { get; set; }
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace WatchFace.Parser.Elements.BasicElements
|
|||
[ParameterId(7)]
|
||||
public long Width { get; set; }
|
||||
|
||||
[JsonConverter(typeof(HexStringJsonConverter))]
|
||||
[JsonConverter(typeof(ColorJsonConverter))]
|
||||
[ParameterId(8)]
|
||||
public long Color { get; set; }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Models;
|
||||
|
||||
namespace WatchFace.Parser.Elements.BasicElements
|
||||
{
|
||||
public class UnknownType
|
||||
{
|
||||
[ParameterId(1)]
|
||||
public long TopLeftX { get; set; }
|
||||
|
||||
[ParameterId(2)]
|
||||
public long TopLeftY { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
public long BottomRightX { get; set; }
|
||||
|
||||
[ParameterId(4)]
|
||||
public long BottomRightY { get; set; }
|
||||
|
||||
[ParameterId(5)]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public TextAlignment Alignment { get; set; }
|
||||
|
||||
[ParameterId(6)]
|
||||
public long Spacing { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Models;
|
||||
|
||||
namespace WatchFace.Parser.Elements.BasicElements
|
||||
{
|
||||
public class UnknownType14d6
|
||||
{
|
||||
[ParameterId(1)]
|
||||
public Coordinates Unknown1 { get; set; }
|
||||
|
||||
[ParameterId(2)]
|
||||
public Coordinates Unknown2 { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
public long? Unknown3 { get; set; }
|
||||
}
|
||||
}
|
|
@ -13,5 +13,11 @@ namespace WatchFace.Parser.Elements
|
|||
|
||||
[ParameterId(3)]
|
||||
public Scale Scale { get; set; }
|
||||
|
||||
[ParameterId(5)]
|
||||
public long? Unknown5 { get; set; }
|
||||
|
||||
[ParameterId(6)]
|
||||
public long? Unknown6 { get; set; }
|
||||
}
|
||||
}
|
|
@ -11,5 +11,11 @@ namespace WatchFace.Parser.Elements
|
|||
|
||||
[ParameterId(2)]
|
||||
public ImageSet WeekDay { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
public DateUnknown3 Unknown3 { get; set; }
|
||||
|
||||
[ParameterId(4)]
|
||||
public Coordinates Unknown4 { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Elements.BasicElements;
|
||||
|
||||
namespace WatchFace.Parser.Elements.DateElements
|
||||
{
|
||||
public class DateUnknown3
|
||||
{
|
||||
[ParameterId(2)]
|
||||
public UnknownType Unknown2 { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
using WatchFace.Parser.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Elements.TimeElements;
|
||||
using WatchFace.Parser.JsonConverters;
|
||||
|
||||
namespace WatchFace.Parser.Elements
|
||||
{
|
||||
|
@ -16,5 +18,12 @@ namespace WatchFace.Parser.Elements
|
|||
|
||||
[ParameterId(4)]
|
||||
public AmPm AmPm { get; set; }
|
||||
|
||||
[JsonConverter(typeof(DrawingOrderJsonConverter))]
|
||||
[ParameterId(5)]
|
||||
public long? DrawingOrder { get; set; }
|
||||
|
||||
[ParameterId(9)]
|
||||
public long? Unknown9 { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using Newtonsoft.Json;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Elements.BasicElements;
|
||||
using WatchFace.Parser.Elements.TimeElements;
|
||||
using WatchFace.Parser.JsonConverters;
|
||||
|
||||
namespace WatchFace.Parser.Elements
|
||||
{
|
||||
public class UnknownType14
|
||||
{
|
||||
[ParameterId(1)]
|
||||
public TwoDigits Unknown1 { get; set; }
|
||||
|
||||
[ParameterId(2)]
|
||||
public TwoDigits Unknown2 { get; set; }
|
||||
|
||||
[ParameterId(6)]
|
||||
public UnknownType14d6 Unknown6 { get; set; }
|
||||
|
||||
[ParameterId(7)]
|
||||
public UnknownType14d6 Unknown7 { get; set; }
|
||||
|
||||
[ParameterId(8)]
|
||||
public UnknownType14d6 Unknown8 { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,7 +5,8 @@ namespace WatchFace.Parser.Elements.WeatherElements
|
|||
{
|
||||
public class AirPollution
|
||||
{
|
||||
// TODO: Looks like here should be Id 1 also
|
||||
[ParameterId(1)]
|
||||
public Number Index { get; set; }
|
||||
|
||||
[ParameterId(2)]
|
||||
public ImageSet Icon { get; set; }
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
using WatchFace.Parser.Attributes;
|
||||
|
||||
namespace WatchFace.Parser.Elements.WeatherElements
|
||||
{
|
||||
public class CustomWeatherIcon
|
||||
{
|
||||
[ParameterId(1)]
|
||||
public long X { get; set; }
|
||||
|
||||
[ParameterId(2)]
|
||||
public long Y { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
[ParameterImageIndex]
|
||||
public long ImageIndex { get; set; }
|
||||
|
||||
[ParameterId(4)]
|
||||
[ParameterImagesCount]
|
||||
public long ImagesCount { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using WatchFace.Parser.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Elements.BasicElements;
|
||||
|
||||
namespace WatchFace.Parser.Elements.WeatherElements
|
||||
|
@ -12,9 +13,23 @@ namespace WatchFace.Parser.Elements.WeatherElements
|
|||
public TemperatureNumber Night { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
public Coordinates Unknown3 { get; set; }
|
||||
public Coordinates DayAlt { get; set; }
|
||||
|
||||
[ParameterId(4)]
|
||||
public Coordinates Unknown4 { get; set; }
|
||||
public Coordinates NightAlt { get; set; }
|
||||
|
||||
// For compatibility with "Unknown3" JSON attribute
|
||||
[JsonProperty("Unknown3")]
|
||||
private Coordinates Unknown3
|
||||
{
|
||||
set { DayAlt = value; }
|
||||
}
|
||||
|
||||
// For compatibility with "Unknown4" JSON attribute
|
||||
[JsonProperty("Unknown4")]
|
||||
private Coordinates Unknown4
|
||||
{
|
||||
set { NightAlt = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using WatchFace.Parser.Attributes;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using WatchFace.Parser.Elements.BasicElements;
|
||||
|
||||
namespace WatchFace.Parser.Elements.WeatherElements
|
||||
|
@ -8,12 +10,20 @@ namespace WatchFace.Parser.Elements.WeatherElements
|
|||
[ParameterId(1)]
|
||||
public Coordinates Coordinates { get; set; }
|
||||
|
||||
// TODO: Looks like here should be Id 2 also
|
||||
[ParameterId(2)]
|
||||
public CustomWeatherIcon CustomIcon { get; set; }
|
||||
|
||||
[ParameterId(3)]
|
||||
public Coordinates Unknown3 { get; set; }
|
||||
public Coordinates CoordinatesAlt { get; set; }
|
||||
|
||||
[ParameterId(4)]
|
||||
public Coordinates Unknown4 { get; set; }
|
||||
|
||||
// For compatibility with "Unknown3" JSON attribute
|
||||
[JsonProperty("Unknown3")]
|
||||
private Coordinates Unknown3
|
||||
{
|
||||
set { CoordinatesAlt = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace WatchFace.Parser.JsonConverters
|
||||
{
|
||||
public class HexStringJsonConverter : JsonConverter
|
||||
public class ColorJsonConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WatchFace.Parser.JsonConverters
|
||||
{
|
||||
public class DrawingOrderJsonConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteValue($"{value:X}");
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(uint) == objectType;
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
var str = (string) reader.Value;
|
||||
if (str == null)
|
||||
throw new JsonSerializationException();
|
||||
return Convert.ToInt64(str, 16);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
namespace WatchFace.Parser.Models
|
||||
{
|
||||
public enum DrawingOrderPosition
|
||||
{
|
||||
HourTens = 1,
|
||||
HourOnes = 2,
|
||||
MinuteTens = 3,
|
||||
MinuteOnes = 4
|
||||
}
|
||||
}
|
|
@ -22,6 +22,11 @@ namespace WatchFace.Parser.Models.Elements
|
|||
return new Rectangle((int) X, (int) Y, (int) (BottomRightX - X), (int) (BottomRightY - Y));
|
||||
}
|
||||
|
||||
public Rectangle GetAltBox(CoordinatesElement altCoordinates)
|
||||
{
|
||||
return new Rectangle((int) altCoordinates.X, (int) altCoordinates.Y, (int) (BottomRightX - X), (int) (BottomRightY - Y));
|
||||
}
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] images, int number, int minimumDigits = 1)
|
||||
{
|
||||
DrawerHelper.DrawImages(drawer, GetImagesForNumber(images, number, minimumDigits), (int) Spacing, Alignment, GetBox());
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
using System.Drawing;
|
||||
using WatchFace.Parser.Interfaces;
|
||||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class HoursElement : TwoDigitsElement, IDrawable
|
||||
{
|
||||
public HoursElement(Parameter parameter, Element parent, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
var timeElement = (TimeElement) _parent;
|
||||
var hours = timeElement.AmPm == null ? state.Time.Hour : state.Time.Hour % 12;
|
||||
Draw(drawer, resources, hours);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using System.Drawing;
|
||||
using WatchFace.Parser.Interfaces;
|
||||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class MinutesElement : TwoDigitsElement, IDrawable
|
||||
{
|
||||
public MinutesElement(Parameter parameter, Element parent, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
Draw(drawer, resources, state.Time.Minute);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using System.Drawing;
|
||||
using WatchFace.Parser.Interfaces;
|
||||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class SecondsElement : TwoDigitsElement, IDrawable
|
||||
{
|
||||
public SecondsElement(Parameter parameter, Element parent, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
Draw(drawer, resources, state.Time.Second);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +1,71 @@
|
|||
namespace WatchFace.Parser.Models.Elements
|
||||
using System.Drawing;
|
||||
using NLog;
|
||||
using WatchFace.Parser.Utils;
|
||||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class TimeElement : ContainerElement
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public TimeElement(Parameter parameter, Element parent = null, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public HoursElement Hours { get; set; }
|
||||
public MinutesElement Minutes { get; set; }
|
||||
public SecondsElement Seconds { get; set; }
|
||||
public TwoDigitsElement Hours { get; set; }
|
||||
public TwoDigitsElement Minutes { get; set; }
|
||||
public TwoDigitsElement Seconds { get; set; }
|
||||
public AmPmElement AmPm { get; set; }
|
||||
public long? DrawingOrder { get; set; }
|
||||
|
||||
public override void Draw(Graphics drawer, Bitmap[] images, WatchState state)
|
||||
{
|
||||
AmPm?.Draw(drawer, images, state);
|
||||
|
||||
var hours = AmPm == null ? state.Time.Hour : state.Time.Hour % 12;
|
||||
var drawingOrder = DrawingOrder ?? 0x1234;
|
||||
|
||||
foreach (var position in DrawingOrderIterator.Iterate(drawingOrder))
|
||||
switch (position)
|
||||
{
|
||||
case DrawingOrderPosition.HourTens:
|
||||
Hours?.Tens?.Draw(drawer, images, hours % 100 / 10);
|
||||
break;
|
||||
case DrawingOrderPosition.HourOnes:
|
||||
Hours?.Ones?.Draw(drawer, images, hours % 10);
|
||||
break;
|
||||
case DrawingOrderPosition.MinuteTens:
|
||||
Minutes?.Tens?.Draw(drawer, images, state.Time.Minute % 100 / 10);
|
||||
break;
|
||||
case DrawingOrderPosition.MinuteOnes:
|
||||
Minutes?.Ones?.Draw(drawer, images, state.Time.Minute % 10);
|
||||
break;
|
||||
default:
|
||||
Logger.Warn("Not supported element {0} in DrawingOrder value", position);
|
||||
break;
|
||||
}
|
||||
|
||||
Seconds?.Draw(drawer, images, state.Time.Second);
|
||||
}
|
||||
|
||||
protected override Element CreateChildForParameter(Parameter parameter)
|
||||
{
|
||||
switch (parameter.Id)
|
||||
{
|
||||
case 1:
|
||||
Hours = new HoursElement(parameter, this, nameof(Hours));
|
||||
Hours = new TwoDigitsElement(parameter, this, nameof(Hours));
|
||||
return Hours;
|
||||
case 2:
|
||||
Minutes = new MinutesElement(parameter, this, nameof(Minutes));
|
||||
Minutes = new TwoDigitsElement(parameter, this, nameof(Minutes));
|
||||
return Minutes;
|
||||
case 3:
|
||||
Seconds = new SecondsElement(parameter, this, nameof(Seconds));
|
||||
Seconds = new TwoDigitsElement(parameter, this, nameof(Seconds));
|
||||
return Seconds;
|
||||
case 4:
|
||||
AmPm = new AmPmElement(parameter, this, nameof(AmPm));
|
||||
return AmPm;
|
||||
case 5:
|
||||
DrawingOrder = parameter.Value;
|
||||
return new ValueElement(parameter, this, nameof(DrawingOrder));
|
||||
default:
|
||||
return base.CreateChildForParameter(parameter);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ namespace WatchFace.Parser.Models.Elements
|
|||
|
||||
public override void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
if (state.Air == AirCondition.Unknown) return;
|
||||
if (state.AirQuality == AirCondition.Unknown) return;
|
||||
|
||||
var imageIndex = (int) state.Air;
|
||||
var imageIndex = (int) state.AirQuality;
|
||||
Draw(drawer, resources, imageIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@ using WatchFace.Parser.Interfaces;
|
|||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class TodayNightTemperatureElement : TemperatureNumberElement, IDrawable
|
||||
public class AirQualityIndexNumberElement : NumberElement, IDrawable
|
||||
{
|
||||
public TodayNightTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
|
||||
public AirQualityIndexNumberElement(Parameter parameter, Element parent = null, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
if (state.NightTemperature != null)
|
||||
Draw(drawer, resources, state.NightTemperature.Value);
|
||||
if (state.AirQualityIndex != null)
|
||||
Draw(drawer, resources, state.AirQualityIndex.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,12 +5,16 @@
|
|||
public AirPollutionElement(Parameter parameter, Element parent = null, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public AirQualityIndexNumberElement Index { get; set; }
|
||||
public AirPollutionImageElement Current { get; set; }
|
||||
|
||||
protected override Element CreateChildForParameter(Parameter parameter)
|
||||
{
|
||||
switch (parameter.Id)
|
||||
{
|
||||
case 1:
|
||||
Index = new AirQualityIndexNumberElement(parameter, this);
|
||||
return Index;
|
||||
case 2:
|
||||
Current = new AirPollutionImageElement(parameter, this);
|
||||
return Current;
|
||||
|
|
|
@ -14,10 +14,11 @@ namespace WatchFace.Parser.Models.Elements
|
|||
public long MinusImageIndex { get; set; }
|
||||
public long? DegreesImageIndex { get; set; }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, int temperature)
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, int temperature, CoordinatesElement altCoordinates = null)
|
||||
{
|
||||
var drawingBox = altCoordinates == null ? Number.GetBox() : Number.GetAltBox(altCoordinates);
|
||||
var images = GetImagesForTemperature(resources, temperature);
|
||||
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, Number.GetBox());
|
||||
DrawerHelper.DrawImages(drawer, images, (int) Number.Spacing, Number.Alignment, drawingBox);
|
||||
}
|
||||
|
||||
public List<Bitmap> GetImagesForTemperature(Bitmap[] resources, int temperature)
|
||||
|
|
|
@ -1,31 +1,51 @@
|
|||
namespace WatchFace.Parser.Models.Elements
|
||||
using System.Drawing;
|
||||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class SeparateTemperatureElement : ContainerElement
|
||||
{
|
||||
public SeparateTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public TodayDayTemperatureElement Day { get; set; }
|
||||
public TodayNightTemperatureElement Night { get; set; }
|
||||
public CoordinatesElement Unknown3 { get; set; }
|
||||
public CoordinatesElement Unknown4 { get; set; }
|
||||
public TemperatureNumberElement Day { get; set; }
|
||||
public TemperatureNumberElement Night { get; set; }
|
||||
public CoordinatesElement DayAlt { get; set; }
|
||||
public CoordinatesElement NightAlt { get; set; }
|
||||
|
||||
public override void Draw(Graphics drawer, Bitmap[] images, WatchState state)
|
||||
{
|
||||
if (state.CurrentTemperature != null)
|
||||
{
|
||||
if (state.DayTemperature != null)
|
||||
Day?.Draw(drawer, images, state.DayTemperature.Value);
|
||||
if (state.NightTemperature != null)
|
||||
Night?.Draw(drawer, images, state.NightTemperature.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.DayTemperature != null)
|
||||
Day?.Draw(drawer, images, state.DayTemperature.Value, DayAlt);
|
||||
if (state.NightTemperature != null)
|
||||
Night?.Draw(drawer, images, state.NightTemperature.Value, NightAlt);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Element CreateChildForParameter(Parameter parameter)
|
||||
{
|
||||
switch (parameter.Id)
|
||||
{
|
||||
case 1:
|
||||
Day = new TodayDayTemperatureElement(parameter, this);
|
||||
Day = new TemperatureNumberElement(parameter, this);
|
||||
return Day;
|
||||
case 2:
|
||||
Night = new TodayNightTemperatureElement(parameter, this);
|
||||
Night = new TemperatureNumberElement(parameter, this);
|
||||
return Night;
|
||||
case 3:
|
||||
Unknown3 = new CoordinatesElement(parameter, this);
|
||||
return Unknown3;
|
||||
DayAlt = new CoordinatesElement(parameter, this);
|
||||
return DayAlt;
|
||||
case 4:
|
||||
Unknown4 = new CoordinatesElement(parameter, this);
|
||||
return Unknown4;
|
||||
NightAlt = new CoordinatesElement(parameter, this);
|
||||
return NightAlt;
|
||||
default:
|
||||
return base.CreateChildForParameter(parameter);
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
using System.Drawing;
|
||||
using WatchFace.Parser.Interfaces;
|
||||
|
||||
namespace WatchFace.Parser.Models.Elements
|
||||
{
|
||||
public class TodayDayTemperatureElement : TemperatureNumberElement, IDrawable
|
||||
{
|
||||
public TodayDayTemperatureElement(Parameter parameter, Element parent = null, string name = null) :
|
||||
base(parameter, parent, name) { }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
if (state.DayTemperature != null)
|
||||
Draw(drawer, resources, state.DayTemperature.Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,17 +10,23 @@ namespace WatchFace.Parser.Models.Elements
|
|||
base(parameter, parent, name) { }
|
||||
|
||||
public CoordinatesElement Current { get; set; }
|
||||
public CoordinatesElement Today { get; set; }
|
||||
public CoordinatesElement Tomorrow { get; set; }
|
||||
public ImageSetElement CustomIcon { get; set; }
|
||||
public CoordinatesElement CurrentAlt { get; set; }
|
||||
public CoordinatesElement Unknown4 { get; set; }
|
||||
|
||||
public void Draw(Graphics drawer, Bitmap[] resources, WatchState state)
|
||||
{
|
||||
if (state.CurrentWeather != WeatherCondition.Unknown && Current != null)
|
||||
drawer.DrawImage(LoadWeatherImage(state.CurrentWeather), Current.X, Current.Y);
|
||||
else if (state.TodayWeather != WeatherCondition.Unknown && Today != null)
|
||||
drawer.DrawImage(LoadWeatherImage(state.TodayWeather), Today.X, Today.Y);
|
||||
else if (state.TomorrowWeather != WeatherCondition.Unknown && Tomorrow != null)
|
||||
drawer.DrawImage(LoadWeatherImage(state.TomorrowWeather), Tomorrow.X, Tomorrow.Y);
|
||||
var useAltCoordinates = CurrentAlt != null && state.CurrentTemperature == null;
|
||||
var iconCoordinates = useAltCoordinates ? CurrentAlt : Current;
|
||||
|
||||
if (state.CurrentWeather > WeatherCondition.VeryHeavyDownpour ||
|
||||
state.CurrentWeather < WeatherCondition.Unknown) return;
|
||||
|
||||
if (iconCoordinates != null)
|
||||
drawer.DrawImage(LoadWeatherImage(state.CurrentWeather), iconCoordinates.X, iconCoordinates.Y);
|
||||
|
||||
if (CustomIcon != null)
|
||||
drawer.DrawImage(resources[CustomIcon.ImageIndex + (int)state.CurrentWeather], CustomIcon.X, CustomIcon.Y);
|
||||
}
|
||||
|
||||
private static Bitmap LoadWeatherImage(WeatherCondition weather)
|
||||
|
@ -37,12 +43,15 @@ namespace WatchFace.Parser.Models.Elements
|
|||
case 1:
|
||||
Current = new CoordinatesElement(parameter, this);
|
||||
return Current;
|
||||
case 2:
|
||||
CustomIcon = new ImageSetElement(parameter, this);
|
||||
return CustomIcon;
|
||||
case 3:
|
||||
Today = new CoordinatesElement(parameter, this);
|
||||
return Today;
|
||||
CurrentAlt = new CoordinatesElement(parameter, this);
|
||||
return CurrentAlt;
|
||||
case 4:
|
||||
Tomorrow = new CoordinatesElement(parameter, this);
|
||||
return Tomorrow;
|
||||
Unknown4 = new CoordinatesElement(parameter, this);
|
||||
return Unknown4;
|
||||
default:
|
||||
return base.CreateChildForParameter(parameter);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace WatchFace.Parser.Models
|
||||
{
|
||||
|
@ -12,15 +14,19 @@ namespace WatchFace.Parser.Models
|
|||
public int Distance { get; set; } = 2367;
|
||||
public int? Pulse { get; set; } = 62;
|
||||
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WeatherCondition CurrentWeather { get; set; } = WeatherCondition.PartlyCloudy;
|
||||
public int? CurrentTemperature { get; set; } = -10;
|
||||
public int? DayTemperature { get; set; } = -15;
|
||||
public int? NightTemperature { get; set; } = -24;
|
||||
public int? TomorrowDayTemperature { get; set; }
|
||||
public int? TomorrowNightTemperature { get; set; }
|
||||
public WeatherCondition CurrentWeather { get; set; } = WeatherCondition.Cloudy;
|
||||
public WeatherCondition TodayWeather { get; set; } = WeatherCondition.Unknown;
|
||||
public WeatherCondition TomorrowWeather { get; set; } = WeatherCondition.Unknown;
|
||||
public AirCondition Air { get; set; } = AirCondition.Excellent;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Air_quality_index#Mainland_China
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public AirCondition AirQuality { get; set; } = AirCondition.Excellent;
|
||||
public int? AirQualityIndex { get; set; } = 15;
|
||||
|
||||
public int BatteryLevel { get; set; } = 67;
|
||||
public bool Bluetooth { get; set; } = true;
|
||||
|
|
|
@ -2,7 +2,31 @@
|
|||
{
|
||||
public enum WeatherCondition
|
||||
{
|
||||
Unknown = 279,
|
||||
Cloudy = 247
|
||||
Unknown,
|
||||
PartlyCloudy,
|
||||
CloudyAndRain,
|
||||
CloudyAndSnow,
|
||||
Sunny,
|
||||
Cloudy,
|
||||
LightRain,
|
||||
LightSnow,
|
||||
Rain,
|
||||
Snow,
|
||||
HeavySnow,
|
||||
HeavyRain,
|
||||
SandStorm,
|
||||
SnowAndRain,
|
||||
Fog,
|
||||
Haze,
|
||||
Storm,
|
||||
VeryHeavySnow,
|
||||
FloatingDust,
|
||||
Downpour,
|
||||
Hail,
|
||||
HailStorm,
|
||||
HeavyDownpour,
|
||||
BlowingDust,
|
||||
Tornado,
|
||||
VeryHeavyDownpour
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using BumpKit;
|
||||
using WatchFace.Parser.Interfaces;
|
||||
using WatchFace.Parser.Models;
|
||||
|
||||
|
@ -10,46 +7,23 @@ namespace WatchFace.Parser
|
|||
{
|
||||
public class PreviewGenerator
|
||||
{
|
||||
public static void CreateGif(List<Parameter> descriptor, Bitmap[] images, Stream outputStream)
|
||||
public static IEnumerable<Image> CreateAnimation(List<Parameter> descriptor, Bitmap[] images,
|
||||
IEnumerable<WatchState> states)
|
||||
{
|
||||
var previewWatchFace = new Models.Elements.WatchFace(descriptor);
|
||||
var watchState = new WatchState();
|
||||
var time = watchState.Time;
|
||||
|
||||
using (var encoder = new GifEncoder(outputStream))
|
||||
foreach (var watchState in states)
|
||||
{
|
||||
for (var i = 0; i < 10; i++)
|
||||
using (var image = CreateFrame(previewWatchFace, images, watchState))
|
||||
{
|
||||
var num = i + 1;
|
||||
watchState.BatteryLevel = num * 10;
|
||||
|
||||
watchState.Pulse = 60 + num * 2;
|
||||
watchState.Steps = num * 1000;
|
||||
watchState.Calories = num * 75;
|
||||
watchState.Distance = num * 700;
|
||||
|
||||
watchState.Bluetooth = num > 1 && num < 6;
|
||||
watchState.Unlocked = num > 2 && num < 7;
|
||||
watchState.Alarm = num > 3 && num < 8;
|
||||
watchState.DoNotDisturb = num > 4 && num < 9;
|
||||
|
||||
watchState.DayTemperature += 2;
|
||||
watchState.NightTemperature += 4;
|
||||
watchState.CurrentTemperature += 3;
|
||||
|
||||
watchState.Time = new DateTime(time.Year, num, num * 2 + 5, i * 2, i * 6, i);
|
||||
using (var image = CreateFrame(previewWatchFace, images, watchState))
|
||||
{
|
||||
encoder.AddFrame(image, frameDelay: TimeSpan.FromSeconds(1));
|
||||
}
|
||||
yield return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Image CreateImage(IEnumerable<Parameter> descriptor, Bitmap[] resources)
|
||||
public static Image CreateImage(IEnumerable<Parameter> descriptor, Bitmap[] images, WatchState state)
|
||||
{
|
||||
var previewWatchFace = new Models.Elements.WatchFace(descriptor);
|
||||
return CreateFrame(previewWatchFace, resources, new WatchState());
|
||||
return CreateFrame(previewWatchFace, images, state);
|
||||
}
|
||||
|
||||
private static Image CreateFrame(IDrawable watchFace, Bitmap[] resources, WatchState state)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using Resources.Models;
|
||||
using WatchFace.Parser.Models;
|
||||
using Header = WatchFace.Parser.Models.Header;
|
||||
using Image = Resources.Models.Image;
|
||||
|
||||
namespace WatchFace.Parser
|
||||
{
|
||||
|
@ -18,7 +22,8 @@ namespace WatchFace.Parser
|
|||
}
|
||||
|
||||
public List<Parameter> Parameters { get; private set; }
|
||||
public List<Bitmap> Images { get; private set; }
|
||||
public List<IResource> Resources { get; private set; }
|
||||
public Bitmap[] Images => Resources.OfType<Image>().Select(i => i.Bitmap).ToArray();
|
||||
|
||||
public void Read()
|
||||
{
|
||||
|
@ -46,7 +51,7 @@ namespace WatchFace.Parser
|
|||
Logger.Trace("Watch face parameters locations were read:");
|
||||
|
||||
Parameters = ReadParameters(parametrsTableLength, parametersLocations);
|
||||
Images = new Resources.Reader(_stream).Read((uint) imagesCount);
|
||||
Resources = new Resources.Reader(_stream).Read((uint) imagesCount);
|
||||
}
|
||||
|
||||
private List<Parameter> ReadParameters(long coordinatesTableSize, ICollection<Parameter> parametersDescriptors)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using WatchFace.Parser.Models;
|
||||
|
||||
namespace WatchFace.Parser.Utils
|
||||
{
|
||||
public class DrawingOrderIterator
|
||||
{
|
||||
public static IEnumerable<DrawingOrderPosition> Iterate(long drawingOrder)
|
||||
{
|
||||
var order = drawingOrder;
|
||||
while (order != 0)
|
||||
{
|
||||
var position = (DrawingOrderPosition) ((order & 0xf000) >> 12);
|
||||
yield return position;
|
||||
order = (order << 4) & 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,8 +15,6 @@ namespace WatchFace.Parser.Utils
|
|||
var result = new List<Parameter>();
|
||||
var currentType = typeof(T);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
Logger.Trace("{0} '{1}'", path, currentType.Name);
|
||||
foreach (var kv in ElementsHelper.SortedProperties<T>())
|
||||
{
|
||||
var id = kv.Key;
|
||||
|
@ -52,11 +50,21 @@ namespace WatchFace.Parser.Utils
|
|||
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
foreach (var item in propertyValue)
|
||||
{
|
||||
Logger.Trace("{0} '{1}'", currentPath, propertyInfo.Name);
|
||||
result.Add(new Parameter(id, Build(item, currentPath)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(new Parameter(id, Build(propertyValue, currentPath)));
|
||||
var innerParameters = Build(propertyValue, currentPath);
|
||||
if (innerParameters.Count > 0)
|
||||
{
|
||||
Logger.Trace("{0} '{1}'", currentPath, propertyInfo.Name);
|
||||
result.Add(new Parameter(id, innerParameters));
|
||||
}
|
||||
else
|
||||
Logger.Trace("{0} '{1}': Skipped because of empty", currentPath, propertyInfo.Name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,8 +79,6 @@ namespace WatchFace.Parser.Utils
|
|||
|
||||
var thisMethod = typeof(ParametersConverter).GetMethod(nameof(Parse));
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
Logger.Trace("{0} '{1}'", path, currentType.Name);
|
||||
foreach (var parameter in descriptor)
|
||||
{
|
||||
var currentPath = string.IsNullOrEmpty(path)
|
||||
|
@ -107,6 +113,7 @@ namespace WatchFace.Parser.Utils
|
|||
}
|
||||
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
Logger.Trace("{0} '{1}'", currentPath, propertyInfo.Name);
|
||||
dynamic propertyValue = propertyInfo.GetValue(result, null);
|
||||
if (propertyValue == null)
|
||||
{
|
||||
|
@ -128,6 +135,7 @@ namespace WatchFace.Parser.Utils
|
|||
}
|
||||
else
|
||||
{
|
||||
Logger.Trace("{0} '{1}'", currentPath, propertyInfo.Name);
|
||||
dynamic propertyValue = propertyInfo.GetValue(result, null);
|
||||
if (propertyValue != null)
|
||||
throw new ArgumentException($"Parameter {parameter.Id} is already set for {currentType.Name}");
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
using System;
|
||||
using NLog;
|
||||
using Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using NLog;
|
||||
using Resources;
|
||||
using System.Linq;
|
||||
using Resources.Models;
|
||||
using WatchFace.Parser.Attributes;
|
||||
using Image = Resources.Models.Image;
|
||||
|
||||
namespace WatchFace.Parser.Utils
|
||||
{
|
||||
public class ImagesLoader
|
||||
public class ResourcesLoader
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private readonly string _imagesDirectory;
|
||||
|
||||
private readonly Dictionary<long, long> _mapping;
|
||||
|
||||
public ImagesLoader(string imagesDirectory)
|
||||
public ResourcesLoader(string imagesDirectory)
|
||||
{
|
||||
Images = new List<Bitmap>();
|
||||
Resources = new List<IResource>();
|
||||
_mapping = new Dictionary<long, long>();
|
||||
_imagesDirectory = imagesDirectory;
|
||||
}
|
||||
|
||||
public List<Bitmap> Images { get; }
|
||||
public List<IResource> Resources { get; }
|
||||
public Bitmap[] Images => Resources.OfType<Image>().Select(i => i.Bitmap).ToArray();
|
||||
|
||||
public void Process<T>(T serializable, string path = "")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(path)) Logger.Trace("Loading images for {0} '{1}'", path, typeof(T).Name);
|
||||
if (!string.IsNullOrEmpty(path)) Logger.Trace("Loading resources for {0} '{1}'", path, typeof(T).Name);
|
||||
|
||||
long? lastImageIndexValue = null;
|
||||
|
||||
|
@ -67,7 +71,7 @@ namespace WatchFace.Parser.Utils
|
|||
{
|
||||
if (lastImageIndexValue == null)
|
||||
throw new ArgumentException(
|
||||
$"Property {propertyInfo.Name} can't be processed becuase ImageIndex isn't present or it is zero"
|
||||
$"Property {propertyInfo.Name} can't be processed because ImageIndex isn't present or it is zero"
|
||||
);
|
||||
|
||||
var imagesCount = propertyType.IsGenericType
|
||||
|
@ -102,9 +106,10 @@ namespace WatchFace.Parser.Utils
|
|||
if (_mapping.ContainsKey(index))
|
||||
return _mapping[index];
|
||||
|
||||
var newImageIndex = Images.Count;
|
||||
var newImageIndex = Resources.Count;
|
||||
Logger.Trace("Loading image {0}...", newImageIndex);
|
||||
Images.Add(ImageLoader.LoadImageForNumber(_imagesDirectory, index));
|
||||
var resource = ImageLoader.LoadResourceForNumber(_imagesDirectory, index);
|
||||
Resources.Add(resource);
|
||||
_mapping[index] = newImageIndex;
|
||||
return newImageIndex;
|
||||
}
|
|
@ -31,19 +31,19 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BumpKit, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Bumpkit.1.0.2\lib\BumpKit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.6\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
|
@ -59,20 +59,25 @@
|
|||
<Compile Include="Elements\BasicElements\Coordinates.cs" />
|
||||
<Compile Include="Elements\BasicElements\Image.cs" />
|
||||
<Compile Include="Elements\BasicElements\ImageSet.cs" />
|
||||
<Compile Include="Elements\BasicElements\UnknownType14d6.cs" />
|
||||
<Compile Include="Elements\BasicElements\UnknownType.cs" />
|
||||
<Compile Include="Elements\BasicElements\Number.cs" />
|
||||
<Compile Include="Elements\BasicElements\Scale.cs" />
|
||||
<Compile Include="Elements\Battery.cs" />
|
||||
<Compile Include="Elements\Date.cs" />
|
||||
<Compile Include="Elements\DateElements\DateUnknown3.cs" />
|
||||
<Compile Include="Elements\DateElements\MonthAndDay.cs" />
|
||||
<Compile Include="Elements\DateElements\OneLineMonthAndDay.cs" />
|
||||
<Compile Include="Elements\DateElements\SeparateMonthAndDay.cs" />
|
||||
<Compile Include="Elements\StepsProgress.cs" />
|
||||
<Compile Include="Elements\Status.cs" />
|
||||
<Compile Include="Elements\StatusElements\Switch.cs" />
|
||||
<Compile Include="Elements\UnknownType14.cs" />
|
||||
<Compile Include="Elements\Time.cs" />
|
||||
<Compile Include="Elements\TimeElements\AmPm.cs" />
|
||||
<Compile Include="Elements\TimeElements\TwoDigits.cs" />
|
||||
<Compile Include="Elements\Weather.cs" />
|
||||
<Compile Include="Elements\WeatherElements\CustomWeatherIcon.cs" />
|
||||
<Compile Include="Elements\WeatherElements\AirPollution.cs" />
|
||||
<Compile Include="Elements\WeatherElements\OneLineTemperature.cs" />
|
||||
<Compile Include="Elements\WeatherElements\SeparateTemperature.cs" />
|
||||
|
@ -82,7 +87,9 @@
|
|||
<Compile Include="Elements\WeatherElements\WeatherIcon.cs" />
|
||||
<Compile Include="Helpers\DrawerHelper.cs" />
|
||||
<Compile Include="Interfaces\IDrawable.cs" />
|
||||
<Compile Include="JsonConverters\HexStringJsonConverter.cs" />
|
||||
<Compile Include="JsonConverters\DrawingOrderJsonConverter.cs" />
|
||||
<Compile Include="JsonConverters\ColorJsonConverter.cs" />
|
||||
<Compile Include="Models\DrawingOrderPosition.cs" />
|
||||
<Compile Include="Models\Elements\Activity\CaloriesElement.cs" />
|
||||
<Compile Include="Models\Elements\Activity\StepsElement.cs" />
|
||||
<Compile Include="Models\Elements\Activity\StepsGoalElement.cs" />
|
||||
|
@ -115,14 +122,10 @@
|
|||
<Compile Include="Models\Elements\Date\MonthAndDayElement.cs" />
|
||||
<Compile Include="Models\Elements\Date\WeekDayElement.cs" />
|
||||
<Compile Include="Models\Elements\TimeElement.cs" />
|
||||
<Compile Include="Models\Elements\Time\SecondsElement.cs" />
|
||||
<Compile Include="Models\Elements\Time\MinutesElement.cs" />
|
||||
<Compile Include="Models\Elements\Time\HoursElement.cs" />
|
||||
<Compile Include="Models\Elements\Common\TwoDigitsElement.cs" />
|
||||
<Compile Include="Models\Elements\WatchFace.cs" />
|
||||
<Compile Include="Models\Elements\Weather\AirPollution\AirQualityIndexNumberElement.cs" />
|
||||
<Compile Include="Models\Elements\Weather\Temperature\Today\OnelineTemperatureElement.cs" />
|
||||
<Compile Include="Models\Elements\Weather\Temperature\Today\TodayNightTemperatureElement.cs" />
|
||||
<Compile Include="Models\Elements\Weather\Temperature\Today\TodayDayTemperatureElement.cs" />
|
||||
<Compile Include="Models\Elements\Weather\Temperature\CurrentTemperatureElement.cs" />
|
||||
<Compile Include="Models\Elements\Weather\AirPollution\AirPollutionImageElement.cs" />
|
||||
<Compile Include="Models\Elements\Weather\AirPollutionElement.cs" />
|
||||
|
@ -155,8 +158,9 @@
|
|||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Reader.cs" />
|
||||
<Compile Include="Attributes\ParameterImagesCountAttribute.cs" />
|
||||
<Compile Include="Utils\DrawingOrderIterator.cs" />
|
||||
<Compile Include="Utils\ElementsHelper.cs" />
|
||||
<Compile Include="Utils\ImagesLoader.cs" />
|
||||
<Compile Include="Utils\ResourcesLoader.cs" />
|
||||
<Compile Include="Utils\ParametersConverter.cs" />
|
||||
<Compile Include="Attributes\ParameterImageIndexAttribute.cs" />
|
||||
<Compile Include="Attributes\ParameterIdAttribute.cs" />
|
||||
|
@ -173,13 +177,34 @@
|
|||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="WeatherIcons\247.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="WeatherIcons\279.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="WeatherIcons\1.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\0.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="WeatherIcons\10.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\11.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\12.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\13.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\14.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\15.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\16.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\17.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\18.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\19.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\2.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\20.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\21.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\22.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\23.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\24.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\25.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\3.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\4.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\5.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\6.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\7.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\8.png" />
|
||||
<EmbeddedResource Include="WeatherIcons\9.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp60</s:String></wpf:ResourceDictionary>
|
|
@ -31,5 +31,8 @@ namespace WatchFace.Parser
|
|||
|
||||
[ParameterId(10)]
|
||||
public AnalogDialFace AnalogDialFace { get; set; }
|
||||
|
||||
[ParameterId(14)]
|
||||
public UnknownType14 Unknown14 { get; set; }
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 352 B |
After Width: | Height: | Size: 334 B |
After Width: | Height: | Size: 325 B |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 334 B |
After Width: | Height: | Size: 236 B |
After Width: | Height: | Size: 302 B |
After Width: | Height: | Size: 365 B |
After Width: | Height: | Size: 340 B |
After Width: | Height: | Size: 229 B |
After Width: | Height: | Size: 319 B |
After Width: | Height: | Size: 372 B |
After Width: | Height: | Size: 335 B |
After Width: | Height: | Size: 367 B |
After Width: | Height: | Size: 316 B |
After Width: | Height: | Size: 270 B |
After Width: | Height: | Size: 278 B |
After Width: | Height: | Size: 305 B |
After Width: | Height: | Size: 374 B |
After Width: | Height: | Size: 326 B |
After Width: | Height: | Size: 297 B |
After Width: | Height: | Size: 314 B |
After Width: | Height: | Size: 316 B |
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 319 B |
|
@ -2,19 +2,20 @@
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
using Resources.Models;
|
||||
using WatchFace.Parser.Models;
|
||||
using WatchFace.Parser.Utils;
|
||||
using Header = WatchFace.Parser.Models.Header;
|
||||
|
||||
namespace WatchFace.Parser
|
||||
{
|
||||
public class Writer
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private readonly List<Bitmap> _images;
|
||||
private readonly List<IResource> _images;
|
||||
|
||||
private readonly Stream _stream;
|
||||
|
||||
public Writer(Stream stream, List<Bitmap> images)
|
||||
public Writer(Stream stream, List<IResource> images)
|
||||
{
|
||||
_stream = stream;
|
||||
_images = images;
|
||||
|
@ -26,6 +27,7 @@ namespace WatchFace.Parser
|
|||
var encodedParameters = new Dictionary<byte, MemoryStream>(descriptor.Count);
|
||||
foreach (var parameter in descriptor)
|
||||
{
|
||||
Logger.Trace("Parameter: {0}", parameter.Id);
|
||||
var memoryStream = new MemoryStream();
|
||||
foreach (var child in parameter.Children)
|
||||
child.Write(memoryStream);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Bumpkit" version="1.0.2" targetFramework="net40-client" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net40-client" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40-client" />
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net40-client" />
|
||||
<package id="NLog" version="4.5.6" targetFramework="net40-client" />
|
||||
</packages>
|
|
@ -3,6 +3,9 @@ using System.Collections.Generic;
|
|||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BumpKit;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
|
@ -10,7 +13,9 @@ using NLog.Targets;
|
|||
using Resources;
|
||||
using Resources.Models;
|
||||
using WatchFace.Parser;
|
||||
using WatchFace.Parser.Models;
|
||||
using WatchFace.Parser.Utils;
|
||||
using Image = System.Drawing.Image;
|
||||
using Reader = WatchFace.Parser.Reader;
|
||||
using Writer = WatchFace.Parser.Writer;
|
||||
|
||||
|
@ -20,6 +25,7 @@ namespace WatchFace
|
|||
{
|
||||
private const string AppName = "WatchFace";
|
||||
|
||||
private static readonly bool IsRunningOnMono = Type.GetType("Mono.Runtime") != null;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private static void Main(string[] args)
|
||||
|
@ -50,6 +56,7 @@ namespace WatchFace
|
|||
Console.WriteLine("File or directory '{0}' doesn't exists.", inputFileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
Console.WriteLine("Processing directory '{0}'", inputFileName);
|
||||
|
@ -61,6 +68,7 @@ namespace WatchFace
|
|||
{
|
||||
Logger.Fatal(e);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -99,13 +107,13 @@ namespace WatchFace
|
|||
var outputFileName = Path.Combine(outputDirectory, baseName + "_packed.bin");
|
||||
SetupLogger(Path.ChangeExtension(outputFileName, ".log"));
|
||||
|
||||
var watchFace = ReadConfig(inputFileName);
|
||||
var watchFace = ReadWatchFaceConfig(inputFileName);
|
||||
if (watchFace == null) return;
|
||||
|
||||
var imagesDirectory = Path.GetDirectoryName(inputFileName);
|
||||
try
|
||||
{
|
||||
WriteWatchFace(outputFileName, imagesDirectory, watchFace);
|
||||
WriteWatchFace(outputDirectory, outputFileName, imagesDirectory, watchFace);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -126,18 +134,12 @@ namespace WatchFace
|
|||
var watchFace = ParseResources(reader);
|
||||
if (watchFace == null) return;
|
||||
|
||||
Logger.Debug("Generating previews...");
|
||||
var preview = PreviewGenerator.CreateImage(reader.Parameters, reader.Images.ToArray());
|
||||
preview.Save(Path.Combine(outputDirectory, $"{baseName}_preview.png"), ImageFormat.Png);
|
||||
using (var gifOutput = File.OpenWrite(Path.Combine(outputDirectory, $"{baseName}_preview.gif")))
|
||||
{
|
||||
PreviewGenerator.CreateGif(reader.Parameters, reader.Images.ToArray(), gifOutput);
|
||||
}
|
||||
GeneratePreviews(reader.Parameters, reader.Images, outputDirectory, baseName);
|
||||
|
||||
Logger.Debug("Exporting resources to '{0}'", outputDirectory);
|
||||
var reDescriptor = new FileDescriptor {Images = reader.Images};
|
||||
var reDescriptor = new FileDescriptor {Resources = reader.Resources};
|
||||
new Extractor(reDescriptor).Extract(outputDirectory);
|
||||
ExportConfig(watchFace, Path.Combine(outputDirectory, $"{baseName}.json"));
|
||||
ExportWatchFaceConfig(watchFace, Path.Combine(outputDirectory, $"{baseName}.json"));
|
||||
}
|
||||
|
||||
private static void PackResources(string inputDirectory)
|
||||
|
@ -148,30 +150,52 @@ namespace WatchFace
|
|||
var logFileName = Path.Combine(outputDirectory, $"{baseName}_packed.log");
|
||||
SetupLogger(logFileName);
|
||||
|
||||
FileDescriptor resDescriptor;
|
||||
var headerFileName = Path.Combine(inputDirectory, "header.json");
|
||||
var versionFileName = Path.Combine(inputDirectory, "version");
|
||||
var resDescriptor = new FileDescriptor();
|
||||
using (var stream = File.OpenRead(versionFileName))
|
||||
using (var reader = new BinaryReader(stream))
|
||||
if (File.Exists(headerFileName))
|
||||
{
|
||||
resDescriptor.Version = reader.ReadByte();
|
||||
resDescriptor = ReadResConfig(headerFileName);
|
||||
}
|
||||
else if (File.Exists(versionFileName))
|
||||
{
|
||||
resDescriptor = new FileDescriptor();
|
||||
using (var stream = File.OpenRead(versionFileName))
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
resDescriptor.Version = reader.ReadByte();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"File 'header.json' or 'version' should exists in the folder with unpacked images. Res-file couldn't be created"
|
||||
);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var images = new List<Bitmap>();
|
||||
while (true)
|
||||
var images = new List<IResource>();
|
||||
while (resDescriptor.ResourcesCount == null || i < resDescriptor.ResourcesCount.Value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var image = ImageLoader.LoadImageForNumber(inputDirectory, i);
|
||||
images.Add(image);
|
||||
var resource = ImageLoader.LoadResourceForNumber(inputDirectory, i);
|
||||
images.Add(resource);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
Logger.Info("All images with sequenced names are loaded. Latest loaded image: {0}", i - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
resDescriptor.Images = images;
|
||||
|
||||
if (resDescriptor.ResourcesCount != null && resDescriptor.ResourcesCount.Value != images.Count)
|
||||
throw new ArgumentException(
|
||||
$"The .res-file should contain {resDescriptor.ResourcesCount.Value} images but was loaded {images.Count} images.");
|
||||
|
||||
resDescriptor.Resources = images;
|
||||
|
||||
using (var stream = File.OpenWrite(outputFileName))
|
||||
{
|
||||
|
@ -191,32 +215,28 @@ namespace WatchFace
|
|||
resDescriptor = FileReader.Read(stream);
|
||||
}
|
||||
|
||||
ExportResConfig(resDescriptor, Path.Combine(outputDirectory, "header.json"));
|
||||
new Extractor(resDescriptor).Extract(outputDirectory);
|
||||
}
|
||||
|
||||
private static void WriteWatchFace(string outputFileName, string imagesDirectory, Parser.WatchFace watchFace)
|
||||
private static void WriteWatchFace(string outputDirectory, string outputFileName, string imagesDirectory, Parser.WatchFace watchFace)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Debug("Reading referenced images from '{0}'", imagesDirectory);
|
||||
var imagesReader = new ImagesLoader(imagesDirectory);
|
||||
var imagesReader = new ResourcesLoader(imagesDirectory);
|
||||
imagesReader.Process(watchFace);
|
||||
|
||||
Logger.Trace("Building parameters for watch face...");
|
||||
var descriptor = ParametersConverter.Build(watchFace);
|
||||
|
||||
Logger.Debug("Generating preview...");
|
||||
var preview = PreviewGenerator.CreateImage(descriptor, imagesReader.Images.ToArray());
|
||||
preview.Save(Path.ChangeExtension(outputFileName, ".png"));
|
||||
using (var gifOutput = File.OpenWrite(Path.ChangeExtension(outputFileName, ".gif")))
|
||||
{
|
||||
PreviewGenerator.CreateGif(descriptor, imagesReader.Images.ToArray(), gifOutput);
|
||||
}
|
||||
var baseFilename = Path.GetFileNameWithoutExtension(outputFileName);
|
||||
GeneratePreviews(descriptor, imagesReader.Images, outputDirectory, baseFilename);
|
||||
|
||||
Logger.Debug("Writing watch face to '{0}'", outputFileName);
|
||||
using (var fileStream = File.OpenWrite(outputFileName))
|
||||
{
|
||||
var writer = new Writer(fileStream, imagesReader.Images);
|
||||
var writer = new Writer(fileStream, imagesReader.Resources);
|
||||
writer.Write(descriptor);
|
||||
fileStream.Flush();
|
||||
}
|
||||
|
@ -271,7 +291,7 @@ namespace WatchFace
|
|||
return unpackedPath;
|
||||
}
|
||||
|
||||
private static Parser.WatchFace ReadConfig(string jsonFileName)
|
||||
private static Parser.WatchFace ReadWatchFaceConfig(string jsonFileName)
|
||||
{
|
||||
Logger.Debug("Reading config...");
|
||||
try
|
||||
|
@ -294,7 +314,30 @@ namespace WatchFace
|
|||
}
|
||||
}
|
||||
|
||||
private static void ExportConfig(Parser.WatchFace watchFace, string jsonFileName)
|
||||
private static FileDescriptor ReadResConfig(string jsonFileName)
|
||||
{
|
||||
Logger.Debug("Reading resources config...");
|
||||
try
|
||||
{
|
||||
using (var fileStream = File.OpenRead(jsonFileName))
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
return JsonConvert.DeserializeObject<FileDescriptor>(reader.ReadToEnd(),
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
MissingMemberHandling = MissingMemberHandling.Error,
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Fatal(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExportWatchFaceConfig(Parser.WatchFace watchFace, string jsonFileName)
|
||||
{
|
||||
Logger.Debug("Exporting config...");
|
||||
try
|
||||
|
@ -313,6 +356,25 @@ namespace WatchFace
|
|||
}
|
||||
}
|
||||
|
||||
private static void ExportResConfig(FileDescriptor resDescriptor, string jsonFileName)
|
||||
{
|
||||
Logger.Debug("Exporting resources config...");
|
||||
try
|
||||
{
|
||||
using (var fileStream = File.OpenWrite(jsonFileName))
|
||||
using (var writer = new StreamWriter(fileStream))
|
||||
{
|
||||
writer.Write(JsonConvert.SerializeObject(resDescriptor, Formatting.Indented,
|
||||
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}));
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Fatal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupLogger(string logFileName)
|
||||
{
|
||||
var config = new LoggingConfiguration();
|
||||
|
@ -320,7 +382,10 @@ namespace WatchFace
|
|||
var fileTarget = new FileTarget
|
||||
{
|
||||
FileName = logFileName,
|
||||
Layout = "${level}|${message}"
|
||||
Layout = "${level}|${message}",
|
||||
KeepFileOpen = true,
|
||||
ConcurrentWrites = false,
|
||||
OpenFileCacheTimeout = 30
|
||||
};
|
||||
config.AddTarget("file", fileTarget);
|
||||
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, fileTarget));
|
||||
|
@ -331,5 +396,109 @@ namespace WatchFace
|
|||
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
|
||||
private static void GeneratePreviews(List<Parameter> parameters, Bitmap[] images, string outputDirectory, string baseName)
|
||||
{
|
||||
Logger.Debug("Generating previews...");
|
||||
|
||||
var states = GetPreviewStates();
|
||||
var staticPreview = PreviewGenerator.CreateImage(parameters, images, new WatchState());
|
||||
staticPreview.Save(Path.Combine(outputDirectory, $"{baseName}_static.png"), ImageFormat.Png);
|
||||
|
||||
var previewImages = PreviewGenerator.CreateAnimation(parameters, images, states);
|
||||
|
||||
if (IsRunningOnMono)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var previewImage in previewImages)
|
||||
{
|
||||
previewImage.Save(Path.Combine(outputDirectory, $"{baseName}_animated_{i}.png"), ImageFormat.Png);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var gifOutput = File.OpenWrite(Path.Combine(outputDirectory, $"{baseName}_animated.gif")))
|
||||
using (var encoder = new GifEncoder(gifOutput))
|
||||
{
|
||||
foreach (var previewImage in previewImages)
|
||||
encoder.AddFrame(previewImage, frameDelay: TimeSpan.FromSeconds(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<WatchState> GetPreviewStates()
|
||||
{
|
||||
var appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
var previewStatesPath = Path.Combine(appPath, "PreviewStates.json");
|
||||
|
||||
if (File.Exists(previewStatesPath))
|
||||
using (var stream = File.OpenRead(previewStatesPath))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var json = reader.ReadToEnd();
|
||||
return JsonConvert.DeserializeObject<List<WatchState>>(json);
|
||||
}
|
||||
|
||||
var previewStates = GenerateSampleStates();
|
||||
using (var stream = File.OpenWrite(previewStatesPath))
|
||||
using (var writer = new StreamWriter(stream))
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(previewStates, Formatting.Indented);
|
||||
writer.Write(json);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
return previewStates;
|
||||
}
|
||||
|
||||
private static IEnumerable<WatchState> GenerateSampleStates()
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
var states = new List<WatchState>();
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var num = i + 1;
|
||||
var watchState = new WatchState
|
||||
{
|
||||
BatteryLevel = 100 - i * 10,
|
||||
Pulse = 60 + num * 2,
|
||||
Steps = num * 1000,
|
||||
Calories = num * 75,
|
||||
Distance = num * 700,
|
||||
Bluetooth = num > 1 && num < 6,
|
||||
Unlocked = num > 2 && num < 7,
|
||||
Alarm = num > 3 && num < 8,
|
||||
DoNotDisturb = num > 4 && num < 9,
|
||||
|
||||
DayTemperature = -15 + 2 * i,
|
||||
NightTemperature = -24 + i * 4,
|
||||
};
|
||||
|
||||
if (num < 3)
|
||||
{
|
||||
watchState.AirQuality = AirCondition.Unknown;
|
||||
watchState.AirQualityIndex = null;
|
||||
|
||||
watchState.CurrentWeather = WeatherCondition.Unknown;
|
||||
watchState.CurrentTemperature = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = num - 2;
|
||||
watchState.AirQuality = (AirCondition) index;
|
||||
watchState.CurrentWeather = (WeatherCondition) index;
|
||||
|
||||
watchState.AirQualityIndex = index * 50 - 25;
|
||||
watchState.CurrentTemperature = -10 + i * 6;
|
||||
}
|
||||
|
||||
watchState.Time = new DateTime(time.Year, num, num * 2 + 5, i * 2, i * 6, i);
|
||||
states.Add(watchState);
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,16 +35,24 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="BumpKit, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Bumpkit.1.0.2\lib\BumpKit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.6\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp60</s:String></wpf:ResourceDictionary>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net40-client" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40-client" />
|
||||
<package id="Bumpkit" version="1.0.2" targetFramework="net40-client" />
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net40-client" />
|
||||
<package id="NLog" version="4.5.6" targetFramework="net40-client" />
|
||||
</packages>
|