static List <UInt16> ReadFFNT(BinaryReader br) { UInt16 BOM = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); List <UInt16> charList = new List <UInt16>(); Encoding encoding = Encoding.Default; if (BOM == 0xFEFF) { //Big endian Console.WriteLine("Parsing Big Endian FFNT..."); br.BaseStream.Position = 0x6; br.BaseStream.Position = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()) + 0x13; switch (br.ReadByte()) { case 0: encoding = Encoding.UTF8; break; case 1: encoding = Encoding.Unicode; break; case 2: encoding = Encoding.GetEncoding(932); //Because Microsoft couldn't add an easier way to pick ShiftJIS break; case 3: encoding = Encoding.GetEncoding(1252); //Again, same as above just for CP1252 break; default: throw new FormatException("Wrong encoding!!!"); } br.BaseStream.Position += 0x9; br.BaseStream.Position = ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()) - 0x8; String MapSignature = new string(br.ReadChars(4)); if (MapSignature != "CMAP") { throw new FormatException("Invalid CMAP section!!!"); } bool HasNext = true; UInt32 NextPtr = (uint)br.BaseStream.Position + 0x4; List <CMAP> cmap_Header = new List <CMAP>(); List <CMAP_Item> cmap = new List <CMAP_Item>(); while (HasNext) { br.BaseStream.Position = NextPtr - 0x8; cmap_Header.Add(new CMAP(ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()), ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()))); if (cmap_Header[cmap_Header.Count - 1].Signature != 0x434D4150) { throw new FormatException("Invalid CMAP section!!!"); } while (br.BaseStream.Position != cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8) { switch (cmap_Header[cmap_Header.Count - 1].MappingMethod) //This is very stupid, but it works { case 0: var IndexOffset = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i <= cmap_Header[cmap_Header.Count - 1].CodeEnd; i++) { cmap.Add(new CMAP_Item(i, IndexOffset)); IndexOffset++; } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 1: for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i < cmap_Header[cmap_Header.Count - 1].CodeEnd + 1; i++) { var index = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); if (index != 0xFFFF) { cmap.Add(new CMAP_Item(i, index)); } } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 2: var Num = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); for (var i = 0; i < Num; i++) { cmap.Add(new CMAP_Item(ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()))); } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; } } NextPtr = cmap_Header[cmap_Header.Count - 1].PtrNext; if (NextPtr == 0x0) { HasNext = false; } } foreach (CMAP_Item item in cmap) { charList.Add(BitConverter.ToUInt16(Encoding.Convert(encoding, Encoding.Unicode, BitConverter.GetBytes(item.Code)), 0)); } charList.Sort(); } else if (BOM == 0xFFFE) { //Little endian Console.WriteLine("Parsing Little Endian FFNT..."); br.BaseStream.Position = 0x6; br.BaseStream.Position = br.ReadUInt16() + 0x13; switch (br.ReadByte()) { case 0: encoding = Encoding.UTF8; break; case 1: encoding = Encoding.Unicode; break; case 2: encoding = Encoding.GetEncoding(932); //Because Microsoft couldn't add an easier way to pick ShiftJIS break; case 3: encoding = Encoding.GetEncoding(1252); //Again, same as above just for CP1252 break; default: throw new FormatException("Wrong encoding!!!"); } br.BaseStream.Position += 0x9; br.BaseStream.Position = br.ReadUInt32() - 0x8; String MapSignature = new string(br.ReadChars(4)); if (MapSignature != "CMAP") { throw new FormatException("Invalid CMAP section!!!"); } bool HasNext = true; UInt32 NextPtr = (uint)br.BaseStream.Position + 0x4; List <CMAP> cmap_Header = new List <CMAP>(); List <CMAP_Item> cmap = new List <CMAP_Item>(); while (HasNext) { br.BaseStream.Position = NextPtr - 0x8; cmap_Header.Add(new CMAP(br.ReadUInt32(), br.ReadUInt32(), br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt32())); if (cmap_Header[cmap_Header.Count - 1].Signature != 0x50414D43) { throw new FormatException("Invalid CMAP section!!!"); } while (br.BaseStream.Position != cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8) { switch (cmap_Header[cmap_Header.Count - 1].MappingMethod) //This is very stupid, but it works { case 0: var IndexOffset = br.ReadUInt16(); for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i <= cmap_Header[cmap_Header.Count - 1].CodeEnd; i++) { cmap.Add(new CMAP_Item(i, IndexOffset)); IndexOffset++; } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 1: for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i < cmap_Header[cmap_Header.Count - 1].CodeEnd + 1; i++) { var index = br.ReadUInt16(); if (index != 0xFFFF) { cmap.Add(new CMAP_Item(i, index)); } } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 2: var Num = br.ReadUInt16(); for (var i = 0; i < Num; i++) { cmap.Add(new CMAP_Item(br.ReadUInt16(), br.ReadUInt16())); } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; } } NextPtr = cmap_Header[cmap_Header.Count - 1].PtrNext; if (NextPtr == 0x0) { HasNext = false; } } foreach (CMAP_Item item in cmap) { charList.Add(BitConverter.ToUInt16(Encoding.Convert(encoding, Encoding.Unicode, BitConverter.GetBytes(item.Code)), 0)); } charList.Sort(); } else { throw new FormatException("Wrong BOM!!!"); } Console.WriteLine("Parsed " + charList.Count + " charcters from FFNT"); return(charList); }
static List <UInt16> ReadRFNT(BinaryReader br) { //Sidenote: only Big Endian variant of this code has a kinda in-depth explanation of how the code works, I was just lazy enogh to copy-paste that into other code sections) //Read Byte Order Mark to determine how to further process the file UInt16 BOM = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); //Initialize some variables List <UInt16> charList = new List <UInt16>(); Encoding encoding = Encoding.Default; //Do the thing, split by endianess if (BOM == 0xFEFF) { //Big endian Console.WriteLine("Parsing Big Endian RFNT..."); br.BaseStream.Position = 0xC; //Goto position of FINF offset br.BaseStream.Position = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()) + 0xf; //Go to character encoding offset //Read character encoding switch (br.ReadByte()) { case 0: encoding = Encoding.UTF8; break; case 1: encoding = Encoding.Unicode; break; case 2: encoding = Encoding.GetEncoding(932); //Because Microsoft couldn't add an easier way to pick ShiftJIS break; case 3: encoding = Encoding.GetEncoding(1252); //Again, same as above just for CP1252 break; default: throw new FormatException("Wrong encoding!!!"); } br.BaseStream.Position += 0x8; //Go to CMAP pointer position br.BaseStream.Position = ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()) - 0x8; //Go to first CMAP section //Read first CMAP header String MapSignature = new string(br.ReadChars(4)); if (MapSignature != "CMAP") { throw new FormatException("Invalid CMAP section!!!"); } bool HasNext = true; UInt32 NextPtr = (uint)br.BaseStream.Position + 0x4; List <CMAP> cmap_Header = new List <CMAP>(); List <CMAP_Item> cmap = new List <CMAP_Item>(); //Parse all CMAP data while (HasNext) { br.BaseStream.Position = NextPtr - 0x8; //Go to offset of next CMAP section cmap_Header.Add(new CMAP(ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()), ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt32_ReverseEndianness(br.ReadUInt32()))); //Read the header if (cmap_Header[cmap_Header.Count - 1].Signature != 0x434D4150) { throw new FormatException("Invalid CMAP section!!!"); //Validate CMAP by checking it's signaure } //And here goes the main CMAP parsing thing which I won't explain, since it should be quite obvious while (br.BaseStream.Position != cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8) { switch (cmap_Header[cmap_Header.Count - 1].MappingMethod) { case 0: //Direct mapping method parser var IndexOffset = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i <= cmap_Header[cmap_Header.Count - 1].CodeEnd; i++) { cmap.Add(new CMAP_Item(i, IndexOffset)); IndexOffset++; } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 1: //Table mapping method for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i < cmap_Header[cmap_Header.Count - 1].CodeEnd + 1; i++) { var index = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); if (index != 0xFFFF) { cmap.Add(new CMAP_Item(i, index)); } } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 2: //Scan mapping method var Num = ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()); for (var i = 0; i < Num; i++) { cmap.Add(new CMAP_Item(ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()), ReverseEndianness.UInt16_ReverseEndianness(br.ReadUInt16()))); } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; } } NextPtr = cmap_Header[cmap_Header.Count - 1].PtrNext; //Save pointer to the next CMAP section if (NextPtr == 0x0) { HasNext = false; //If NextPtr == 0x0 then we should stop parsing } } foreach (CMAP_Item item in cmap) { charList.Add(BitConverter.ToUInt16(Encoding.Convert(encoding, Encoding.Unicode, BitConverter.GetBytes(item.Code)), 0)); } charList.Sort(); } else if (BOM == 0xFFFE) { //Little endian Console.WriteLine("Parsing Little Endian RFNT..."); br.BaseStream.Position = 0xC; br.BaseStream.Position = br.ReadUInt16() + 0xf; switch (br.ReadByte()) { case 0: encoding = Encoding.UTF8; break; case 1: encoding = Encoding.Unicode; break; case 2: encoding = Encoding.GetEncoding(932); //Because Microsoft couldn't add an easier way to pick ShiftJIS break; case 3: encoding = Encoding.GetEncoding(1252); //Again, same as above just for CP1252 break; default: throw new FormatException("Wrong encoding!!!"); } br.BaseStream.Position += 0x8; br.BaseStream.Position = br.ReadUInt32() - 0x8; String MapSignature = new string(br.ReadChars(4)); if (MapSignature != "CMAP") { throw new FormatException("Invalid CMAP section!!!"); } bool HasNext = true; UInt32 NextPtr = (uint)br.BaseStream.Position + 0x4; List <CMAP> cmap_Header = new List <CMAP>(); List <CMAP_Item> cmap = new List <CMAP_Item>(); while (HasNext) { br.BaseStream.Position = NextPtr - 0x8; cmap_Header.Add(new CMAP(br.ReadUInt32(), br.ReadUInt32(), br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt16(), br.ReadUInt32())); if (cmap_Header[cmap_Header.Count - 1].Signature != 0x50414D43) { throw new FormatException("Invalid CMAP section!!!"); } while (br.BaseStream.Position != cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8) { switch (cmap_Header[cmap_Header.Count - 1].MappingMethod) { case 0: var IndexOffset = br.ReadUInt16(); for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i <= cmap_Header[cmap_Header.Count - 1].CodeEnd; i++) { cmap.Add(new CMAP_Item(i, IndexOffset)); IndexOffset++; } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 1: for (var i = cmap_Header[cmap_Header.Count - 1].CodeBegin; i < cmap_Header[cmap_Header.Count - 1].CodeEnd + 1; i++) { var index = br.ReadUInt16(); if (index != 0xFFFF) { cmap.Add(new CMAP_Item(i, index)); } } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; case 2: var Num = br.ReadUInt16(); for (var i = 0; i < Num; i++) { cmap.Add(new CMAP_Item(br.ReadUInt16(), br.ReadUInt16())); } br.BaseStream.Position = cmap_Header[cmap_Header.Count - 1].PtrNext - 0x8; break; } } NextPtr = cmap_Header[cmap_Header.Count - 1].PtrNext; if (NextPtr == 0x0) { HasNext = false; } } foreach (CMAP_Item item in cmap) { charList.Add(BitConverter.ToUInt16(Encoding.Convert(encoding, Encoding.Unicode, BitConverter.GetBytes(item.Code)), 0)); } charList.Sort(); } else { throw new FormatException("Wrong BOM!!!"); } Console.WriteLine("Parsed " + charList.Count + " charcters from RFNT"); return(charList); }