From 64beee1574ae09425451a9b1c1c0a7d5cbd8a3ad Mon Sep 17 00:00:00 2001 From: Valeriy Mironov Date: Sun, 11 Feb 2018 13:49:17 +0200 Subject: [PATCH] Added extracting font images from 0.1.0.86 firmware --- FwFonts/Constants.cs | 7 +++ FwFonts/FileReader.cs | 74 +++++++++++++++++++++++++++ FwFonts/FwFonts.csproj | 18 +++++++ FwFonts/GlyphReader.cs | 41 +++++++++++++++ FwFonts/Models/BlockDescriptor.cs | 27 ++++++++++ FwFonts/Models/FirmweareDescriptor.cs | 10 ++++ FwFonts/Models/FontDescriptor.cs | 11 ++++ FwFonts/Models/GlyphDescriptor.cs | 35 +++++++++++++ FwFonts/Reader.cs | 44 ++++++++++++++++ FwFonts/packages.config | 5 ++ WatchFace/Program.cs | 28 ++++++++++ WatchFace/WatchFace.csproj | 4 ++ 12 files changed, 304 insertions(+) create mode 100644 FwFonts/Constants.cs create mode 100644 FwFonts/FileReader.cs create mode 100644 FwFonts/GlyphReader.cs create mode 100644 FwFonts/Models/BlockDescriptor.cs create mode 100644 FwFonts/Models/FirmweareDescriptor.cs create mode 100644 FwFonts/Models/FontDescriptor.cs create mode 100644 FwFonts/Models/GlyphDescriptor.cs create mode 100644 FwFonts/Reader.cs create mode 100644 FwFonts/packages.config diff --git a/FwFonts/Constants.cs b/FwFonts/Constants.cs new file mode 100644 index 0000000..c7b0d72 --- /dev/null +++ b/FwFonts/Constants.cs @@ -0,0 +1,7 @@ +namespace FwFonts +{ + public class Constants + { + public const uint FirmwareBase = 0x8008000; + } +} \ No newline at end of file diff --git a/FwFonts/FileReader.cs b/FwFonts/FileReader.cs new file mode 100644 index 0000000..29a66ea --- /dev/null +++ b/FwFonts/FileReader.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; +using FwFonts.Models; +using NLog; + +namespace FwFonts +{ + public class FileReader + { + private const uint VersionOffset = 0x803BC80; + + private static readonly Dictionary FontOffsets = new Dictionary + { + ["wf1"] = 0x806B848, + ["wf5"] = 0x806BB70, + ["font3"] = 0x806BF84, + ["font2"] = 0x806C388, + ["font1"] = 0x806C9F4, + ["wf7_time"] = 0x806CFDC, + ["wf8_time"] = 0x806D4EC, + ["wf6"] = 0x806D674, + ["font4"] = 0x806D7C4, + ["wf7_wf8_small"] = 0x806DA90, + ["wf8_big"] = 0x806DD2C + }; + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public static FirmweareDescriptor Read(Stream stream) + { + var binaryReader = new BinaryReader(stream); + + stream.Seek(VersionOffset - Constants.FirmwareBase, SeekOrigin.Begin); + + var version = Encoding.ASCII.GetString(binaryReader.ReadBytes(8)); + Logger.Debug("Firmware version was read:"); + Logger.Debug("Version: {0}", version); + + var result = new FirmweareDescriptor {Fonts = new Dictionary()}; + + foreach (var fontOffset in FontOffsets) + { + var font = new FontDescriptor + { + Images = new Dictionary(), + Blocks = new List() + }; + + var offset = fontOffset.Value - Constants.FirmwareBase; + var name = fontOffset.Key; + Logger.Debug("Reading font '{0}' from offset 0x{1}", name, offset); + BlockDescriptor block; + do + { + stream.Seek(offset, SeekOrigin.Begin); + block = BlockDescriptor.ReadFrom(binaryReader); + var blockImages = new Reader(stream).ReadBlock(block); + + foreach (var blockImage in blockImages) + font.Images.Add(blockImage.Key, blockImage.Value); + font.Blocks.Add(block); + + offset = block.NextBlockFileOffset; + } while (block.NextBlockOffset > 0); + + result.Fonts[name] = font; + } + + return result; + } + } +} \ No newline at end of file diff --git a/FwFonts/FwFonts.csproj b/FwFonts/FwFonts.csproj index 109ca46..b588046 100644 --- a/FwFonts/FwFonts.csproj +++ b/FwFonts/FwFonts.csproj @@ -30,10 +30,25 @@ 4 + + ..\packages\Bumpkit.1.0.2\lib\BumpKit.dll + + + ..\packages\NLog.4.4.12\lib\net40\NLog.dll + + + + + + + + + + @@ -41,5 +56,8 @@ Utils + + + \ No newline at end of file diff --git a/FwFonts/GlyphReader.cs b/FwFonts/GlyphReader.cs new file mode 100644 index 0000000..470db01 --- /dev/null +++ b/FwFonts/GlyphReader.cs @@ -0,0 +1,41 @@ +using System.Drawing; +using System.IO; +using BumpKit; +using FwFonts.Models; +using Utils; + +namespace FwFonts +{ + public class GlyphReader + { + private readonly Stream _stream; + private readonly BinaryReader _reader; + + public GlyphReader(Stream stream) + { + _stream = stream; + _reader = new BinaryReader(stream); + } + + public Bitmap Read(GlyphDescriptor glyph) + { + _stream.Seek(glyph.DataFileOffset, SeekOrigin.Begin); + var image = new Bitmap(glyph.Width + glyph.OffsetX, glyph.Height + glyph.OffsetY); + using (var context = image.CreateUnsafeContext()) + { + for (var y = glyph.OffsetY; y < image.Height; y++) + { + var rowBytes = _reader.ReadBytes(glyph.RowLength); + var bitReader = new BitReader(rowBytes); + for (var x = glyph.OffsetX; x < image.Width; x++) + { + var pixelValue = bitReader.ReadBits(1); + var color = pixelValue == 1 ? Color.Black : Color.FromArgb(0, Color.White); + context.SetPixel(x, y, color); + } + } + } + return image; + } + } +} \ No newline at end of file diff --git a/FwFonts/Models/BlockDescriptor.cs b/FwFonts/Models/BlockDescriptor.cs new file mode 100644 index 0000000..236ba4c --- /dev/null +++ b/FwFonts/Models/BlockDescriptor.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace FwFonts.Models +{ + public class BlockDescriptor + { + public char StartSymbol { get; set; } + public char EndSymbol { get; set; } + public uint GlyphsOffset { get; set; } + public uint NextBlockOffset { get; set; } + + public bool HasNextBlock => NextBlockOffset > 0; + public uint NextBlockFileOffset => NextBlockOffset - Constants.FirmwareBase; + public uint GlyphsFileOffset => GlyphsOffset - Constants.FirmwareBase; + + public static BlockDescriptor ReadFrom(BinaryReader reader) + { + return new BlockDescriptor + { + StartSymbol = (char) reader.ReadUInt16(), + EndSymbol = (char) reader.ReadUInt16(), + GlyphsOffset = reader.ReadUInt32(), + NextBlockOffset = reader.ReadUInt32() + }; + } + } +} \ No newline at end of file diff --git a/FwFonts/Models/FirmweareDescriptor.cs b/FwFonts/Models/FirmweareDescriptor.cs new file mode 100644 index 0000000..8247e52 --- /dev/null +++ b/FwFonts/Models/FirmweareDescriptor.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace FwFonts.Models +{ + public class FirmweareDescriptor + { + public string Version { get; set; } + public Dictionary Fonts { get; set; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/FwFonts/Models/FontDescriptor.cs b/FwFonts/Models/FontDescriptor.cs new file mode 100644 index 0000000..55f00bf --- /dev/null +++ b/FwFonts/Models/FontDescriptor.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Drawing; + +namespace FwFonts.Models +{ + public class FontDescriptor + { + public List Blocks { get; set; } = new List(); + public Dictionary Images { get; set; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/FwFonts/Models/GlyphDescriptor.cs b/FwFonts/Models/GlyphDescriptor.cs new file mode 100644 index 0000000..9f5d3da --- /dev/null +++ b/FwFonts/Models/GlyphDescriptor.cs @@ -0,0 +1,35 @@ +using System.IO; + +namespace FwFonts.Models +{ + public class GlyphDescriptor + { + public byte Width { get; set; } + public byte Height { get; set; } + public byte OffsetX { get; set; } + public byte OffsetY { get; set; } + public ushort DrawWidth { get; set; } + public ushort Unknown6 { get; set; } + public uint DataOffset { get; set; } + + public char Symbol { get; set; } + + public int RowLength => Width % 8 == 0 ? Width / 8 : Width / 8 + 1; + public uint DataFileOffset => DataOffset - Constants.FirmwareBase; + + public static GlyphDescriptor ReadFrom(BinaryReader reader, char symbol) + { + return new GlyphDescriptor + { + Width = reader.ReadByte(), + Height = reader.ReadByte(), + OffsetX = reader.ReadByte(), + OffsetY = reader.ReadByte(), + DrawWidth = reader.ReadUInt16(), + Unknown6 = reader.ReadUInt16(), + DataOffset = reader.ReadUInt32(), + Symbol = symbol + }; + } + } +} \ No newline at end of file diff --git a/FwFonts/Reader.cs b/FwFonts/Reader.cs new file mode 100644 index 0000000..4702bbe --- /dev/null +++ b/FwFonts/Reader.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using FwFonts.Models; +using NLog; + +namespace FwFonts +{ + public class Reader + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly BinaryReader _binaryReader; + private readonly Stream _stream; + + public Reader(Stream stream) + { + _stream = stream; + _binaryReader = new BinaryReader(_stream); + } + + public Dictionary ReadBlock(BlockDescriptor block) + { + _stream.Seek(block.GlyphsFileOffset, SeekOrigin.Begin); + + Logger.Trace("Reading block of symbols from '{0}' to '{1}' images...", block.StartSymbol, block.EndSymbol); + var glyphs = new List(block.EndSymbol - block.StartSymbol); + for (var currentChar = block.StartSymbol; currentChar <= block.EndSymbol; currentChar++) + { + Logger.Trace("Reading descriptor of symbol '{0}'...", currentChar); + glyphs.Add(GlyphDescriptor.ReadFrom(_binaryReader, currentChar)); + } + + var images = new Dictionary(glyphs.Count); + foreach (var glyphDescriptor in glyphs) + { + Logger.Debug("Reading image of symbol '{0}'...", glyphDescriptor.Symbol); + images[glyphDescriptor.Symbol] = new GlyphReader(_stream).Read(glyphDescriptor); + } + + return images; + } + } +} \ No newline at end of file diff --git a/FwFonts/packages.config b/FwFonts/packages.config new file mode 100644 index 0000000..7a78cd9 --- /dev/null +++ b/FwFonts/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/WatchFace/Program.cs b/WatchFace/Program.cs index 0c84f90..5308335 100644 --- a/WatchFace/Program.cs +++ b/WatchFace/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; +using FwFonts.Models; using Newtonsoft.Json; using NLog; using NLog.Config; @@ -85,6 +86,9 @@ namespace WatchFace case ".latin": UnpackFont(inputFileName); break; + case ".fw": + UnpackFwFont(inputFileName); + break; default: Console.WriteLine("The app doesn't support files with extension {0}.", inputFileExtension); Console.WriteLine("Only 'bin', 'res' and 'json' files are supported at this time."); @@ -206,6 +210,30 @@ namespace WatchFace } } + private static void UnpackFwFont(string inputFileName) + { + var outputDirectory = CreateOutputDirectory(inputFileName); + var baseName = Path.GetFileNameWithoutExtension(inputFileName); + SetupLogger(Path.Combine(outputDirectory, $"{baseName}.log")); + + FwFonts.Models.FirmweareDescriptor descriptor; + using (var stream = File.OpenRead(inputFileName)) + { + descriptor = FwFonts.FileReader.Read(stream); + } + + foreach (var font in descriptor.Fonts) + { + var fontDirectory = Path.Combine(outputDirectory, font.Key); + if (!Directory.Exists(fontDirectory)) Directory.CreateDirectory(fontDirectory); + + foreach (var image in font.Value.Images) + { + image.Value.Save(Path.Combine(fontDirectory, $"0x{(ushort)image.Key:x4}.png"), ImageFormat.Png); + } + } + } + private static void UnpackFont(string inputFileName) { var outputDirectory = CreateOutputDirectory(inputFileName); diff --git a/WatchFace/WatchFace.csproj b/WatchFace/WatchFace.csproj index a516e21..daa2e0f 100644 --- a/WatchFace/WatchFace.csproj +++ b/WatchFace/WatchFace.csproj @@ -62,6 +62,10 @@ {390eaf2d-9fa8-4dc1-8d4f-ea565ad3c682} Fonts + + {e50e8437-0511-4a94-808a-ad5f3105d060} + FwFonts + {edd55d5d-9e80-451b-ac8a-0746ba6dc6e9} Resources