Add font pack support

This commit is contained in:
Yukai Li 2021-02-21 01:56:23 -07:00
parent 8c7e99a2e8
commit 65dc96c4d6
2 changed files with 172 additions and 0 deletions

171
LibDgf/Font/FontPack.cs Normal file
View file

@ -0,0 +1,171 @@
using LibDgf.Dat;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace LibDgf.Font
{
public class FontPack
{
const int WIDTH = 24;
const int HEIGHT = 22;
const int PADDING = 16;
Dictionary<char, byte[]> characterMap = new Dictionary<char, byte[]>();
public int Count => characterMap.Count;
public IReadOnlyCollection<char> Characters => characterMap.Keys;
public byte[] this[char ch]
{
get
{
return characterMap[ch];
}
set
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (value.Length != (WIDTH * HEIGHT + PADDING) / 2)
throw new ArgumentException("Pixels has incorrect length.", nameof(value));
characterMap[ch] = value;
}
}
public bool Remove(char ch)
{
return characterMap.Remove(ch);
}
public void Clear()
{
characterMap.Clear();
}
public void Read(Stream stream)
{
Encoding shiftJisEncoding = Encoding.GetEncoding(932);
Clear();
DatReader dat = new DatReader(stream);
using (MemoryStream ms = new MemoryStream(dat.GetData(0)))
{
BinaryReader br = new BinaryReader(ms);
uint infoSize = br.ReadUInt32();
uint[] numChars = new uint[4];
// numChars[0] is dummy
for (int i = 1; i < numChars.Length; ++i)
{
numChars[i] = br.ReadUInt32();
}
byte[] chBytes = new byte[2];
for (int i = 1; i < numChars.Length; ++i)
{
using MemoryStream graphicsMs = new MemoryStream(dat.GetData(i));
BinaryReader graphicsBr = new BinaryReader(graphicsMs);
var numCharInSection = numChars[i];
for (int j = 0; j < numCharInSection; ++j)
{
// Characters are stored as 16-bit little-endian values
chBytes[1] = br.ReadByte();
chBytes[0] = br.ReadByte();
char ch = shiftJisEncoding.GetChars(chBytes)[0];
byte[] pixels = graphicsBr.ReadBytes((WIDTH * HEIGHT + PADDING) / 2); // 4bpp
characterMap.Add(ch, pixels);
}
}
}
}
public void Write(Stream stream)
{
Encoding shiftJisEncoding = Encoding.GetEncoding(932);
List<string> tempFiles = new List<string>();
try
{
ushort[] charRanges = new ushort[] { 0x0, 0x889f, 0x989f }; // General, Kanji 1, Kanji 2
uint[] charCounts = new uint[charRanges.Length];
// Build mapping from Unicode to Shift-JIS
List<Tuple<char, ushort>> encodedMapping = new List<Tuple<char, ushort>>();
char[] chArray = new char[1];
foreach (var ch in Characters)
{
chArray[0] = ch;
byte[] encodedBytes = shiftJisEncoding.GetBytes(chArray);
ushort encoded = (ushort)((encodedBytes[0] << 8) | encodedBytes[1]);
encodedMapping.Add(new Tuple<char, ushort>(ch, encoded));
}
encodedMapping.Sort((x, y) => x.Item2.CompareTo(y.Item2));
string infoFsPath = Path.GetTempFileName();
tempFiles.Add(infoFsPath);
int currentRange = -1;
FileStream currentRangeFs = null;
BinaryWriter currentRangeBw = null;
DatBuilder datBuilder = new DatBuilder();
datBuilder.ReplacementEntries.Add(new DatBuilder.ReplacementEntry
{
Index = 0,
SourceFile = infoFsPath
});
using (FileStream infoFs = File.Create(infoFsPath))
{
BinaryWriter infoBw = new BinaryWriter(infoFs);
infoBw.Write(new byte[4 + 4 * charCounts.Length]); // Dummy header
try
{
foreach (var pair in encodedMapping)
{
// Advance range if char matches next range's start
if (currentRange < charRanges.Length - 1 && pair.Item2 >= charRanges[currentRange + 1])
{
string path = Path.GetTempFileName();
tempFiles.Add(path);
if (currentRangeFs != null) currentRangeFs.Close();
currentRangeFs = File.Create(path);
currentRangeBw = new BinaryWriter(currentRangeFs);
++currentRange;
datBuilder.ReplacementEntries.Add(new DatBuilder.ReplacementEntry
{
Index = currentRange + 1,
SourceFile = path
});
}
infoBw.Write(pair.Item2);
currentRangeBw.Write(this[pair.Item1]);
++charCounts[currentRange];
}
}
finally
{
if (currentRangeFs != null) currentRangeFs.Close();
}
uint length = (uint)infoFs.Length;
// Pad to nearest 16 bytes
var paddedLength = (length + 15) & ~15;
infoBw.Write(new byte[paddedLength - length]);
infoFs.Seek(0, SeekOrigin.Begin);
infoBw.Write(length);
foreach (var count in charCounts)
{
infoBw.Write(count);
}
}
datBuilder.Build(stream);
}
finally
{
foreach (var file in tempFiles)
{
File.Delete(file);
}
}
}
}
}

View file

@ -8,6 +8,7 @@ Currently implemented
**Final**
- DAT: file packs
- font/*.pak: font pack
- Sound effects catalog (read support only)
- TXM: texture files