Skip to content

Instantly share code, notes, and snippets.

@donnaken15
Created July 27, 2022 03:11
Show Gist options
  • Save donnaken15/275977617e3cdfa87fc80703f94d449f to your computer and use it in GitHub Desktop.
Save donnaken15/275977617e3cdfa87fc80703f94d449f to your computer and use it in GitHub Desktop.
BMFont to GH3 Font converter
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();
}
}
@donnaken15
Copy link
Author

compiled with mono C# compiler

@donnaken15
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment