Add font pack support
This commit is contained in:
parent
8c7e99a2e8
commit
65dc96c4d6
2 changed files with 172 additions and 0 deletions
171
LibDgf/Font/FontPack.cs
Normal file
171
LibDgf/Font/FontPack.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ Currently implemented
|
|||
|
||||
**Final**
|
||||
- DAT: file packs
|
||||
- font/*.pak: font pack
|
||||
- Sound effects catalog (read support only)
|
||||
- TXM: texture files
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue