Added extracting font images from 0.1.0.86 firmware

fonts_experiment
Valeriy Mironov 2018-02-11 13:49:17 +02:00
parent ac785b0c6a
commit 64beee1574
12 changed files with 304 additions and 0 deletions

7
FwFonts/Constants.cs Normal file
View File

@ -0,0 +1,7 @@
namespace FwFonts
{
public class Constants
{
public const uint FirmwareBase = 0x8008000;
}
}

74
FwFonts/FileReader.cs Normal file
View File

@ -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<string, uint> FontOffsets = new Dictionary<string, uint>
{
["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<string, FontDescriptor>()};
foreach (var fontOffset in FontOffsets)
{
var font = new FontDescriptor
{
Images = new Dictionary<char, Bitmap>(),
Blocks = new List<BlockDescriptor>()
};
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;
}
}
}

View File

@ -30,10 +30,25 @@
<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="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="GlyphReader.cs" />
<Compile Include="Models\FirmweareDescriptor.cs" />
<Compile Include="Models\GlyphDescriptor.cs" />
<Compile Include="Models\FontDescriptor.cs" />
<Compile Include="FileReader.cs" />
<Compile Include="Models\BlockDescriptor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reader.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Utils\Utils.csproj">
@ -41,5 +56,8 @@
<Name>Utils</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

41
FwFonts/GlyphReader.cs Normal file
View File

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

View File

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

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace FwFonts.Models
{
public class FirmweareDescriptor
{
public string Version { get; set; }
public Dictionary<string, FontDescriptor> Fonts { get; set; } = new Dictionary<string, FontDescriptor>();
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Drawing;
namespace FwFonts.Models
{
public class FontDescriptor
{
public List<BlockDescriptor> Blocks { get; set; } = new List<BlockDescriptor>();
public Dictionary<char, Bitmap> Images { get; set; } = new Dictionary<char, Bitmap>();
}
}

View File

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

44
FwFonts/Reader.cs Normal file
View File

@ -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<char, Bitmap> 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<GlyphDescriptor>(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<char, Bitmap>(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;
}
}
}

5
FwFonts/packages.config Normal file
View File

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

View File

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

View File

@ -62,6 +62,10 @@
<Project>{390eaf2d-9fa8-4dc1-8d4f-ea565ad3c682}</Project>
<Name>Fonts</Name>
</ProjectReference>
<ProjectReference Include="..\FwFonts\FwFonts.csproj">
<Project>{e50e8437-0511-4a94-808a-ad5f3105d060}</Project>
<Name>FwFonts</Name>
</ProjectReference>
<ProjectReference Include="..\Resources\Resources.csproj">
<Project>{edd55d5d-9e80-451b-ac8a-0746ba6dc6e9}</Project>
<Name>Resources</Name>