public void RebuildPkg(string inputpkg) { UInt16 blockheader = 8; List <FontHeaderInfo> FontInfo = new List <FontHeaderInfo>(); List <BlockRange> BlockInfo = new List <BlockRange>(); List <charDatum> blockchars = new List <charDatum>(); List <int> blockcharsizes = new List <int>(); MemoryStream outBlocks = new MemoryStream(); MemoryStream chartable = new MemoryStream(); MemoryStream datatable = new MemoryStream(); BinaryWriter ow = new BinaryWriter(outBlocks); BinaryWriter ctw = new BinaryWriter(chartable); BinaryWriter dw = new BinaryWriter(datatable); UInt16 tempChar; UInt16 lastChar = 0; int blockindex = 0; int blockoffset = 0; Int16 charcount = 0; bool freshblock = true; int chartablesize = 0; BlockRange blockk = new BlockRange(); for (int i = 0; i < Fonts.Count; i++) { FontHeaderInfo fontt = new FontHeaderInfo(); for (int j = 0; j < Fonts[i].Characters.Count; j++) { if (j == 0) { fontt.startBlock = (short)blockindex; } //check if we can fit this character into the current block var tempSize = 20 + Fonts[i].Characters[j].Data.dataSize; if (chartablesize + datatable.Length + tempSize > ChunkSize - 8) { //nope can't fit //build block character table var chartableoffset = 8; for (int k = 0; k < blockchars.Count; k++) { ctw.Write(blockchars[k].characterCode); ctw.Write(blockchars[k].fontIndex); ctw.Write(chartablesize + chartableoffset); chartableoffset += blockcharsizes[k]; } blockchars.Clear(); blockcharsizes.Clear(); //calculate block padding var blockdiff = (ChunkSize - 8) - (chartablesize + datatable.Length); byte[] diffBuffer = new byte[blockdiff]; //write block ow.Write(blockheader); ow.Write(charcount); ow.Write((Int16)(ctw.BaseStream.Length + 8)); ow.Write((Int16)(dw.BaseStream.Length)); ow.Write(chartable.ToArray()); ow.Write(datatable.ToArray()); ow.Write(diffBuffer); //note the last character in the block for header blockk.endIndex = new charDatum() { characterCode = lastChar, fontIndex = (short)i };; BlockInfo.Add(blockk); //clean up and reset stuff blockk = new BlockRange(); freshblock = true; blockindex += 1; charcount = 0; blockoffset = 0; chartablesize = 0; ctw.Close(); dw.Close(); chartable = new MemoryStream(); datatable = new MemoryStream(); ctw = new BinaryWriter(chartable); dw = new BinaryWriter(datatable); } tempChar = Fonts[i].Characters[j].CharCode; //note the first character in the block if (freshblock) { blockk.startIndex = new charDatum() { characterCode = tempChar, fontIndex = (short)i }; freshblock = false; } //add character in temp character table blockchars.Add(new charDatum() { characterCode = tempChar, fontIndex = (short)i }); //add character data to temp stream dw.Write(Fonts[i].Characters[j].Data.dispWidth); dw.Write(Fonts[i].Characters[j].Data.dataSize); dw.Write(Fonts[i].Characters[j].Data.width); dw.Write(Fonts[i].Characters[j].Data.height); dw.Write(Fonts[i].Characters[j].Data.leftpad); dw.Write(Fonts[i].Characters[j].Data.dispHeight); dw.Write(Fonts[i].Characters[j].Data.compressedData); //calculate character padding var dataalign = 12 + Fonts[i].Characters[j].Data.dataSize; byte[] alignhelp = new byte[0]; if (dataalign % 8 != 0) { var dataalign2 = dataalign / 8; alignhelp = new byte[((dataalign2 * 8) + 8) - dataalign]; dw.Write(alignhelp); } //note the size with padding for the character table blockcharsizes.Add(Fonts[i].Characters[j].Data.dataSize + 12 + alignhelp.Length); //clean up and reset stuff blockoffset += Fonts[i].Characters[j].Data.dataSize + 12 + alignhelp.Length; charcount += 1; chartablesize += 8; lastChar = tempChar; } fontt.blockCount = (short)(blockindex - fontt.startBlock + 1); FontInfo.Add(fontt); } //out of fonts and characters so write the final block var chartableoffsetx = 8; for (int k = 0; k < blockchars.Count; k++) { ctw.Write(blockchars[k].characterCode); ctw.Write(blockchars[k].fontIndex); ctw.Write(chartablesize + chartableoffsetx); chartableoffsetx += blockcharsizes[k]; } var blockdiffx = (ChunkSize - 8) - (chartablesize + datatable.Length); byte[] diffBufferx = new byte[blockdiffx]; ow.Write(blockheader); ow.Write(charcount); ow.Write((Int16)(ctw.BaseStream.Length + 8)); ow.Write((Int16)(dw.BaseStream.Length)); ow.Write(chartable.ToArray()); ow.Write(datatable.ToArray()); ow.Write(diffBufferx); blockk.endIndex = new charDatum() { characterCode = lastChar, fontIndex = (short)(Fonts.Count - 1) };; BlockInfo.Add(blockk); ow.Close(); ctw.Close(); dw.Close(); chartable.Close(); datatable.Close(); //begin writing new stuff to the font package FileStream fs = new FileStream(inputpkg, FileMode.Open, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs); //update ordering bw.BaseStream.Position = 8 + (maxFontCount * 0xC); for (int i = 0; i < OrderList.Count; i++) { bw.Write(OrderList[i]); } //update the block info for each font bw.BaseStream.Position = 0x8; for (int i = 0; i < FontInfo.Count; i++) { bw.BaseStream.Position += 8; bw.Write((short)FontInfo[i].startBlock); bw.Write((short)FontInfo[i].blockCount); } //update the character info for each block bw.BaseStream.Position = BlockRangesOffset; for (int i = 0; i < BlockInfo.Count; i++) { bw.Write(BlockInfo[i].startIndex.characterCode); bw.Write(BlockInfo[i].startIndex.fontIndex); bw.Write(BlockInfo[i].endIndex.characterCode); bw.Write(BlockInfo[i].endIndex.fontIndex); } //clean up any nonexistant block info for (int i = 0; i < (maxFontCount - Fonts.Count); i++) { bw.Write((Int64)0); } //update block count bw.BaseStream.Position = (8 + (maxFontCount * 12) + (maxFontCount * 4) + 12); //0x114; bw.Write((int)BlockInfo.Count); //update each font header with new info for (int i = 0; i < Fonts.Count; i++) { bw.BaseStream.Position = FontEntries[i].offset + 0x24; bw.Write(Fonts[i].LineHeight); bw.Write(Fonts[i].LineTopPad); bw.Write(Fonts[i].LineBottomPad); bw.Write(Fonts[i].LineIndent); var datasize = 0; for (int j = 0; j < Fonts[i].Characters.Count; j++) { datasize += Fonts[i].Characters[j].Data.dataSize; } bw.BaseStream.Position = FontEntries[i].offset + 0x13C; bw.Write((int)Fonts[i].Characters.Count); bw.BaseStream.Position += 0x14; bw.Write(datasize); } //finally write the new blocks bw.BaseStream.SetLength(ChunkSize); bw.BaseStream.Position = ChunkSize; bw.Write(outBlocks.ToArray()); bw.Close(); fs.Close(); }
/// <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); }