/// <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);
        }