/// <summary> /// Loads a font package from the given file. /// </summary> /// <returns>0 = success, 1 = bad header version, 2 = font count is zero</returns> public int Load(string filename) { FileStream fs = new FileStream(filename, FileMode.Open); BinaryReader br = new BinaryReader(fs); ms = new MemoryStream(br.ReadBytes((int)br.BaseStream.Length)); fs.Close(); br = new BinaryReader(ms); br.BaseStream.Position = 0; //Make sure this is a font package var fileMagic = br.ReadUInt32(); if (fileMagic != 0xC0000003) { ms.Close(); br.Close(); return(1); } int fontCount = br.ReadInt32(); if (fontCount <= 0) { ms.Close(); br.Close(); return(2); } maxFontCount = 16; //check to see if package belongs to Halo 4 var maxcounthack = br.ReadInt32(); if (maxcounthack == 1048) { maxFontCount = 64; } //get the font headers info br.BaseStream.Position -= 4; for (int i = 0; i < maxFontCount; i++) { FontEntries.Add(new FontHeaderInfo() { offset = br.ReadInt32(), size = br.ReadInt32(), startBlock = br.ReadInt16(), blockCount = br.ReadInt16() }); } //read engine ordering for (int i = 0; i < maxFontCount; i++) { OrderList.Add(br.ReadInt32()); } int headerTableOffset = br.ReadInt32(); int headerTableSize = br.ReadInt32(); BlockRangesOffset = br.ReadInt32(); int blockCount = br.ReadInt32(); br.BaseStream.Position = BlockRangesOffset; for (int i = 0; i < blockCount; i++) { var startchar = br.ReadUInt16(); var startfont = br.ReadInt16(); var endchar = br.ReadUInt16(); var endfont = br.ReadInt16(); charDatum start = new charDatum() { characterCode = startchar, fontIndex = startfont }; charDatum end = new charDatum() { characterCode = endchar, fontIndex = endfont }; BlockRange range = new BlockRange() { startIndex = start, endIndex = end }; BlockRanges.Add(range); } //read data blocks //set position for hax to check for h4b br.BaseStream.Position = 0x8000; ChunkSize = 0x8000; if (maxFontCount == 64 & br.ReadInt16() != 8) //blocks seem to have a short value of 8 as some header or version { ChunkSize = 0xC000; } for (int i = 0; i < blockCount; i++) { Block dataBlock = new Block(); dataBlock.Read(br, ChunkSize + (i * ChunkSize)); Blocks.Add(dataBlock); } //read font headers and find its characters for (int i = 0; i < fontCount; i++) { var tempfont = new PackageFont(); br.BaseStream.Position = FontEntries[i].offset; tempfont.ReadHeader(br); for (int bl = FontEntries[i].startBlock; bl < (FontEntries[i].startBlock + FontEntries[i].blockCount); bl++) { for (int ch = 0; ch < Blocks[bl].charCount; ch++) { if (Blocks[bl].charTable[ch].index.fontIndex == i) { var tempchar = new FontCharacter(Blocks[bl].charTable[ch].index.characterCode, Blocks[bl].charData[ch]); tempfont.Characters.Add(tempchar); } } } Fonts.Add(tempfont); } br.Close(); ms.Close(); return(0); }
/// <summary> /// Converts the given System.Drawing.Image and adds or replaces to the given font index/character. Left/Right padding is removed and set as the display width. /// </summary> /// <param name="charcode"></param> /// <param name="fontindex"></param> /// <param name="image"></param> /// <returns></returns> public void AddCustomCharacter(UInt16 charcode, int fontindex, Image image, CharTint tint, bool cropme, int dwidth = -1) { int origwidth = image.Width; if (cropme) { image = CropWidth((Bitmap)image); } Bitmap bm = (Bitmap)image; BitmapData bd = bm.LockBits( new Rectangle(new Point(0, 0), bm.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat); int bytes = Math.Abs(bd.Stride) * bm.Height; byte[] bmBytes = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(bd.Scan0, bmBytes, 0, bytes); bm.UnlockBits(bd); //h2 code I don't wanna mess with to convert to 32 bit if (image.PixelFormat != PixelFormat.Format32bppArgb) { int sBpp = (Image.GetPixelFormatSize(bm.PixelFormat) / 8); int stride = Math.Abs(bd.Stride); byte[] bmBytes2 = new byte[bytes * 4 / sBpp]; for (int i = 0; i < bd.Height; i++) { for (int ii = 0; ii < bd.Width; ii++) { switch (image.PixelFormat) { case PixelFormat.Format8bppIndexed: int colorIndex = bmBytes[i * stride + ii * sBpp]; bmBytes2[(i * bm.Width * 4) + ii * 4 + 0] = bm.Palette.Entries[colorIndex].B; // b bmBytes2[(i * bm.Width * 4) + ii * 4 + 1] = bm.Palette.Entries[colorIndex].G; // g bmBytes2[(i * bm.Width * 4) + ii * 4 + 2] = bm.Palette.Entries[colorIndex].R; // r bmBytes2[(i * bm.Width * 4) + ii * 4 + 3] = bm.Palette.Entries[colorIndex].A; // a break; case PixelFormat.Format24bppRgb: bmBytes2[(i * bm.Width * 4) + ii * 4 + 0] = bmBytes[i * stride + ii * sBpp + 0]; // b bmBytes2[(i * bm.Width * 4) + ii * 4 + 1] = bmBytes[i * stride + ii * sBpp + 1]; // g bmBytes2[(i * bm.Width * 4) + ii * 4 + 2] = bmBytes[i * stride + ii * sBpp + 2]; // r bmBytes2[(i * bm.Width * 4) + ii * 4 + 3] = 0xff; // a break; default: throw new System.NotSupportedException(image.PixelFormat.ToString() + " format not supported!"); } } } bmBytes = bmBytes2; bytes = bm.Width * 4 * bm.Height; } List <ushort> pixelList = new List <ushort>(); //convert pixels to 16 bit for (int i = 0; i < bytes / 4; i++) { byte blue = bmBytes[i * 4 + 0]; byte green = bmBytes[i * 4 + 1]; byte red = bmBytes[i * 4 + 2]; byte alpha = bmBytes[i * 4 + 3]; byte AR = (byte)(((alpha & 0xF0)) | ((red >> 4))); byte GB = (byte)(((green & 0xF0)) | ((blue >> 4))); ushort pixel = (ushort)((AR << 8) | GB); //adjust whites/blacks if requested switch (tint) { case CharTint.Cool: if ((pixel & 0xFFF) == 0x000) { pixel += 1; } else if (((pixel & 0xFFF) % 0x111) == 0) { pixel -= 0x100; } break; case CharTint.Warm: if ((pixel & 0xFFF) == 0x000) { pixel += 0x100; } else if (((pixel & 0xFFF) % 0x111) == 0) { pixel -= 1; } break; } pixelList.Add(pixel); } List <byte> data = new List <byte>(); ushort basePixel = 0xFFF; //start compressing pixels like the game do if (image.Width > 1) { for (int i = 0; i < pixelList.Count; i++) { byte run = 0; ushort pixel = pixelList[i]; ushort nextpixel = 0; bool dontChange = false; //grab the next pixel, or prevent writing if (i + 1 >= pixelList.Count) { dontChange = true; } else { nextpixel = pixelList[i + 1]; } //get the alpha for the first pixel var alpha = (pixel & 0xF000) >> 12; //extra checking to see if we are updating the base if ((pixel & 0xFFF) == (basePixel & 0xFFF) && (pixel & 0xFFF) == (nextpixel & 0xFFF)) { dontChange = true; } //do we write base change bytes if ((pixel != basePixel) && ((pixel & 0xFFF) != (basePixel & 0xFFF)) || (!dontChange)) { data.Add(0); data.Add((byte)((pixel & 0xFF00) >> 8)); data.Add((byte)pixel); basePixel = (ushort)(pixel); continue; } //check for easy long-run 0/F alpha pixels if (alpha == 0 || alpha == 0xF) { while ((i + run) < pixelList.Count && pixelList[i + run] == pixel && run < 0x3F) { run++; } if (run > 1) { data.Add((byte)(((alpha & 4) << 4) | run)); i += run - 1; continue; } } //if the alpha wasn't 0/f or only lasts 1 pixel, gotta do all this run = 0; byte codeL = 0; codeL = (byte)(alpha & 0xE); codeL <<= 2; codeL |= 0x80; byte codeR = 0; //get alpha for the next pixel alpha = (nextpixel & 0xF000) >> 12; //find run and limit if alpha is unsupported for a longer run while ((i + 1 + run) < pixelList.Count && (pixelList[i + 1 + run]) == (nextpixel) && run < 5) { run++; } if (run > 1 && alpha != 0 && alpha != 0xF) { run = 1; } //start writing if (run == 0) { data.Add((byte)(codeL)); continue; } else if (run == 1) { codeR = (byte)(alpha >> 1); data.Add((byte)((codeL | codeR) | 0xC0)); i += run; continue; } else if (run == 2) //these may still have a trick instead of using hardcoded codeRs but idk { codeR = (alpha == 0xF) ? (byte)3 : (byte)7; } else if (run == 3) { codeR = (alpha == 0xF) ? (byte)2 : (byte)6; } else if (run == 4) { codeR = (alpha == 0xF) ? (byte)1 : (byte)5; } else if (run == 5) { if (alpha == 0xF) { run--; //run becomes 4; codeR = 1; } else { codeR = 4; } } data.Add((byte)(codeL | codeR)); i += run; } } else { data.Add(0x15); } // Check if compressed size is too large to be imported if (data.Count > short.MaxValue) { throw new System.OverflowException("Compressed image size must not be larger than " + short.MaxValue.ToString("n0") + " bytes\nCurrent image compresses to " + data.Count.ToString("n0") + " bytes"); } //see if we are adding or replacing var existingindex = Fonts[fontindex].FindCharacter(charcode); CharacterData ci = new CharacterData(); if (existingindex != -1) { ci.leftpad = Fonts[fontindex].Characters[existingindex].Data.leftpad; ci.dispHeight = Fonts[fontindex].Characters[existingindex].Data.dispHeight; } else { ci.leftpad = 0; ci.dispHeight = (ushort)Fonts[fontindex].LineHeight; } ci.width = (UInt16)image.Width; ci.height = (UInt16)image.Height; if (dwidth == -1) { ci.dispWidth = (UInt16)origwidth; } else { ci.dispWidth = (ushort)dwidth; } ci.compressedData = data.ToArray(); ci.dataSize = (UInt16)ci.compressedData.Length; FontCharacter ife = new FontCharacter(charcode, ci); ife.isdupe = false; if (existingindex != -1) { Fonts[fontindex].Characters[existingindex] = ife; } else { Fonts[fontindex].Characters.Add(ife); Fonts[fontindex].SortCharacters(); } }
private int LoadFile(string filename) { List <int> CharacterPointers = new List <int>(); List <CharacterData> CharacterEntries = new List <CharacterData>(); List <int> kernlist = new List <int>(); FileStream fs = new FileStream(filename, FileMode.Open); BinaryReader br = new BinaryReader(fs); ms = new MemoryStream(br.ReadBytes((int)br.BaseStream.Length)); fs.Close(); br = new BinaryReader(ms); br.BaseStream.Position = 0x200; //Make sure this is a font file var fileMagic = br.ReadUInt32(); if (fileMagic != 0xF0000001) { ms.Close(); br.Close(); return(1); } PackageFont tempfont = new PackageFont(); tempfont.Name = filename.Substring(filename.LastIndexOf("\\") + 1); tempfont.HeaderVersion = (int)fileMagic; tempfont.ReadH2Header(br); br.BaseStream.Position = 0x3AC; int unk7 = br.ReadInt32(); int unk8 = br.ReadInt32(); int characterdataoffset = 0x40400 + (tempfont.CharacterCount * 0x10); //read characters for (int i = 0; i < tempfont.CharacterCount; i++) { CharacterData tempchar = new CharacterData(); tempchar.Read(br, 0x40400 + (i * 0x10), true); br.BaseStream.Position = tempchar.OffsetH2; tempchar.compressedData = br.ReadBytes(tempchar.dataSize); CharacterEntries.Add(tempchar); } //read character indexes and match things up for (int i = 0; i < 65536; i++) { br.BaseStream.Position = 0x400 + (i * 4); int charindex = br.ReadInt32(); FontCharacter tempfchar = new FontCharacter((ushort)i, CharacterEntries[charindex]); if (CharacterPointers.Contains(charindex)) { tempfchar.isdupe = true; } else { CharacterPointers.Add(charindex); } tempfont.Characters.Add(tempfchar); tempfchar = null; } tempfont.H2File = filename; Fonts.Add(tempfont); br.Close(); ms.Close(); return(0); }