Created
July 27, 2022 03:11
-
-
Save donnaken15/275977617e3cdfa87fc80703f94d449f to your computer and use it in GitHub Desktop.
BMFont to GH3 Font converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Drawing; | |
using System.IO; | |
// BRO JUST USE QUICKBMS | |
class BMF { | |
public class INF { | |
public ushort fontSize; | |
public byte flags; | |
public byte charSet; | |
public ushort stretchH; | |
public byte aa; | |
public byte[] padding; // [4] | |
public byte[] spacing; // [2] | |
public byte outline; | |
public string fontName; | |
public INF() { | |
padding = new byte[4]; | |
spacing = new byte[2]; | |
} | |
} | |
public class COMN { | |
public ushort lineHeight; | |
public ushort _base; | |
public ushort scaleW; | |
public ushort scaleH; | |
public ushort pages; | |
public byte flags; | |
public byte[] channels; // [4] | |
public COMN() { | |
channels = new byte[4]; | |
} | |
} | |
public struct CHAR { | |
public uint id; | |
public ushort x,y,w,h; | |
public ushort xoff,yoff; | |
public ushort xadv; | |
public byte page; | |
public byte chnl; | |
} | |
string sig; | |
byte ver; | |
public INF info; | |
public COMN common; | |
public CHAR[] chars; | |
public uint charCount; | |
public string imgpath; | |
public Image img; | |
public BMF(string filename) { | |
info = new INF(); | |
common = new COMN(); | |
if (!File.Exists(filename)) | |
throw new Exception("File does not exist."); | |
BinaryReader bmfile = new BinaryReader(File.Open(filename,FileMode.Open)); | |
imgpath = Path.GetDirectoryName(filename) + Path.GetFileNameWithoutExtension(filename) + "_0.png"; // games don't use multiple pages | |
sig = System.Text.Encoding.ASCII.GetString(bmfile.ReadBytes(3)); | |
ver = bmfile.ReadByte(); | |
bmfile.BaseStream.Position++; // 0x01 | |
uint infoSize = bmfile.ReadUInt32(); | |
info.fontSize = bmfile.ReadUInt16(); | |
info.flags = bmfile.ReadByte(); | |
info.charSet = bmfile.ReadByte(); | |
info.stretchH = bmfile.ReadUInt16(); | |
info.aa = bmfile.ReadByte(); | |
for (int i = 0; i < 4; i++) | |
info.padding[i] = bmfile.ReadByte(); | |
for (int i = 0; i < 2; i++) | |
info.spacing[i] = bmfile.ReadByte(); | |
info.outline = bmfile.ReadByte(); | |
info.fontName = System.Text.Encoding.ASCII.GetString(bmfile.ReadBytes((int)(infoSize)-0xE)); | |
//bmfile.BaseStream.Position++; // 0x02 | |
//uint commonSize = bmfile.ReadUInt32(); | |
bmfile.BaseStream.Position += 5; | |
common.lineHeight = bmfile.ReadUInt16(); | |
common._base = bmfile.ReadUInt16(); | |
common.scaleW = bmfile.ReadUInt16(); | |
common.scaleH = bmfile.ReadUInt16(); | |
common.pages = bmfile.ReadUInt16(); | |
common.flags = bmfile.ReadByte(); | |
for (int i = 0; i < 4; i++) | |
common.channels[i] = bmfile.ReadByte(); | |
bmfile.BaseStream.Position++; // 0x03 | |
uint pageSize = bmfile.ReadUInt32(); | |
bmfile.BaseStream.Position += pageSize; | |
bmfile.BaseStream.Position++; // 0x04 | |
uint charsSize = bmfile.ReadUInt32(); | |
charCount = charsSize / 0x14; | |
chars = new CHAR[charCount]; | |
for (int i = 0; i < charCount; i++) | |
{ | |
chars[i].id = bmfile.ReadUInt32(); // why int for a char | |
chars[i].x = bmfile.ReadUInt16(); // also why even all this | |
chars[i].y = bmfile.ReadUInt16(); // c# bad and cringpiled, no fread to struct[] | |
chars[i].w = bmfile.ReadUInt16(); // without unsafe probably | |
chars[i].h = bmfile.ReadUInt16(); | |
chars[i].xoff = bmfile.ReadUInt16(); | |
chars[i].yoff = bmfile.ReadUInt16(); | |
chars[i].xadv = bmfile.ReadUInt16(); | |
chars[i].page = bmfile.ReadByte(); | |
chars[i].chnl = bmfile.ReadByte(); | |
/*Console.WriteLine("TEST: "+ | |
chars[i].id.ToString("X2")+", x: "+ | |
chars[i].x.ToString()+", y: "+ | |
chars[i].y.ToString()+", w: "+ | |
chars[i].w.ToString()+", h: "+ | |
chars[i].h.ToString()+", xoff: "+ | |
chars[i].xoff.ToString()+", yoff: "+ | |
chars[i].yoff.ToString()+", xadv: "+ | |
chars[i].xadv.ToString());/**/ | |
} | |
bmfile.Close(); | |
img = Image.FromFile(imgpath); | |
} | |
} | |
struct FNT { // ty Zedek | |
public struct glyph { | |
public float x,y,x2,y2,w,h,vShift,hShift,unkD; | |
} | |
public struct imghed { | |
public ushort magic; | |
public ushort unk1; | |
public uint key; // not using | |
public ushort width; | |
public ushort height; | |
public ushort a1; | |
public ushort width2; | |
public ushort height2; | |
public ushort b1; | |
public byte mips; // not using // 1 | |
public byte bpp; // didnt effect anything for me // 8 | |
public byte cmp; // // 5 | |
public byte pad0; | |
public uint pad1; | |
public uint ptr; // 0x28 | |
public uint size; | |
public uint pad2; | |
} | |
public int unk; | |
public int shifter; | |
public int unk_b; | |
public float height; | |
public ushort constA; | |
public ushort constB; | |
public ushort[] glyph_ptrs; // 0x10000 | |
public ushort a1; | |
public float spaceWidth; | |
public uint imageStart; | |
public glyph[] glyphs; | |
public const uint glyphcount = 65535; // because why | |
public imghed imghead; | |
// space doesn't have it's own glyph? | |
// that'd probably make sense in sone way | |
public static FNT BMF2FNT(BMF bmf) | |
{ | |
FNT fnt = new FNT(); | |
fnt.unk = 24; | |
fnt.shifter = -2; | |
fnt.unk_b = -2; | |
fnt.height = 63; | |
fnt.constA = 2; | |
fnt.constB = 164; | |
fnt.glyph_ptrs = new ushort[glyphcount]; | |
fnt.a1 = 1; | |
fnt.spaceWidth = 15; // TODO: get space glyph's width on its own | |
fnt.imageStart = 0x64 + ((glyphcount+33) << 1) + ((bmf.charCount) * 0x24); | |
fnt.glyphs = new glyph[glyphcount]; | |
for (ushort i = 0; i < bmf.charCount; i++) | |
{ | |
fnt.glyph_ptrs[bmf.chars[i].id] = i; | |
fnt.glyphs[bmf.chars[i].id].x = (float)bmf.chars[i].x / (float)bmf.img.Width; | |
fnt.glyphs[bmf.chars[i].id].y = (float)bmf.chars[i].y / (float)bmf.img.Height; | |
fnt.glyphs[bmf.chars[i].id].x2 = (float)bmf.chars[i].w / (float)bmf.img.Width; | |
fnt.glyphs[bmf.chars[i].id].y2 = (float)bmf.chars[i].h / (float)bmf.img.Height; | |
fnt.glyphs[bmf.chars[i].id].x2 += fnt.glyphs[bmf.chars[i].id].x; | |
fnt.glyphs[bmf.chars[i].id].y2 += fnt.glyphs[bmf.chars[i].id].y; | |
fnt.glyphs[bmf.chars[i].id].w = bmf.chars[i].w; | |
fnt.glyphs[bmf.chars[i].id].h = bmf.chars[i].h; | |
fnt.glyphs[bmf.chars[i].id].vShift = 0;//bmf.chars[i].xoff; | |
fnt.glyphs[bmf.chars[i].id].hShift = 0;//bmf.chars[i].yoff; doesnt work | |
} | |
fnt.imghead.magic = 0x0A28; | |
fnt.imghead.unk1 = 0x1102; | |
// expecting all these images to be power of two, hoping that doesn't break it | |
// it works for img but not sure how it would work out with fnts | |
fnt.imghead.width = (ushort)bmf.img.Width; | |
fnt.imghead.height = (ushort)bmf.img.Height; | |
fnt.imghead.width2 = (ushort)bmf.img.Width; | |
fnt.imghead.height2 = (ushort)bmf.img.Height; | |
fnt.imghead.a1 = 1; | |
fnt.imghead.b1 = 1; | |
fnt.imghead.mips = 1; | |
fnt.imghead.bpp = 8; | |
fnt.imghead.cmp = 5; | |
fnt.imghead.ptr = 0x28; | |
fnt.imghead.size = (uint)File.ReadAllBytes(bmf.imgpath).Length; | |
return fnt; | |
} | |
} | |
class Program | |
{ | |
static uint Eswap(uint value) | |
{ | |
return ((value & 0xFF) << 24) | | |
((value & 0xFF00) << 8) | | |
((value & 0xFF0000) >> 8) | | |
((value & 0xFF000000) >> 24); | |
} | |
static uint Eswap(int value) | |
{ | |
return Eswap((uint)value); | |
} | |
static ushort Eswap(ushort value) | |
{ | |
return (ushort)((value << 8) | (value >> 8)); | |
} | |
static BMF bmf; | |
public static void Main(string[] args) | |
{ | |
if (args.Length == 0) | |
return; | |
bmf = new BMF(args[0]); | |
FNT newfnt = FNT.BMF2FNT(bmf); | |
BinaryWriter fnt = new BinaryWriter(File.Create("BMF_TEST.fnt")); | |
fnt.Write(Eswap(newfnt.unk)); | |
fnt.Write(Eswap(newfnt.shifter)); | |
fnt.Write(Eswap(newfnt.unk_b)); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(newfnt.height),0))); | |
fnt.Write(Eswap(newfnt.constA)); | |
fnt.Write(Eswap(newfnt.constB)); | |
//newfnt.glyph_ptrs[0xFFFF] = 1234; | |
for (ushort i = 0; i < FNT.glyphcount; i++) | |
fnt.Write(Eswap(newfnt.glyph_ptrs[i])); | |
for (byte i = 0; i < 33; i++) // kms plz | |
fnt.Write(Eswap((ushort)0)); // kms plz// kms plz// kms plz// kms plz// kms plz// kms plz// kms plz | |
fnt.Write(Eswap(newfnt.a1)); | |
fnt.Write((ushort)0xADDE); | |
fnt.Write(0xFFFFFFFF); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(newfnt.spaceWidth),0))); | |
for (ushort i = 0; i < 16; i++) | |
fnt.Write(0); | |
fnt.Write(Eswap(newfnt.imageStart)); | |
for (int i = 0; i < bmf.charCount; i++) | |
{ | |
FNT.glyph glyph = newfnt.glyphs[bmf.chars[i].id]; | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.x),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.y),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.x2),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.y2),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.w),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.h),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.vShift),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.hShift),0))); | |
fnt.Write(Eswap(BitConverter.ToUInt32(BitConverter.GetBytes(glyph.unkD),0))); | |
} | |
fnt.Write(Eswap(newfnt.imghead.magic)); | |
fnt.Write(Eswap(newfnt.imghead.unk1)); | |
fnt.Write(Eswap(newfnt.imghead.key)); | |
fnt.Write(Eswap(newfnt.imghead.width)); | |
fnt.Write(Eswap(newfnt.imghead.height)); | |
fnt.Write(Eswap(newfnt.imghead.a1)); | |
fnt.Write(Eswap(newfnt.imghead.width2)); | |
fnt.Write(Eswap(newfnt.imghead.height2)); | |
fnt.Write(Eswap(newfnt.imghead.b1)); | |
fnt.Write(newfnt.imghead.mips); | |
fnt.Write(newfnt.imghead.bpp); | |
fnt.Write(newfnt.imghead.cmp); | |
fnt.Write(newfnt.imghead.pad0); | |
fnt.Write(Eswap(newfnt.imghead.pad1)); | |
fnt.Write(Eswap(newfnt.imghead.ptr)); | |
fnt.Write(Eswap(newfnt.imghead.size)); | |
fnt.Write(Eswap(newfnt.imghead.pad2)); | |
fnt.Write(File.ReadAllBytes(bmf.imgpath)); | |
fnt.Close(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
compiled with mono C# compiler