private void AddLayoutEntry(CASResult blte) { if (EKeys.ContainsKey(blte.EKey)) { EKeys.Remove(blte.EKey); } // generate ESpecString string ESpecString; uint size = blte.CompressedSize - 30; // the below suffices and is technically correct // however this could be more compliant https://wowdev.wiki/CASC#Encoding_Specification_.28ESpec.29 if (blte.CEKey == CASContainer.BuildConfig.GetKey("root")) // root is always z { ESpecString = "z"; } else if (size >= 1024 * 256) // 256K* seems to be the max { ESpecString = "b:{256K*=z}"; } else if (size > 1024) { ESpecString = "b:{" + (int)Math.Floor(size / 1024d) + "K*=z}"; // closest floored KB } else { ESpecString = "b:{" + size + "*=z}"; // actual B size } // string index int stridx = ESpecStringTable.IndexOf(ESpecString); if (stridx == -1) { stridx = ESpecStringTable.Count - 2; // ignore the 0 byte ESpecStringTable.Insert(stridx, ESpecString); } // create the entry var entry = new EncodingEKeyPageTable() { FileSize = size, EKey = blte.EKey, ESpecStringIndex = (uint)stridx }; EKeys.Add(entry.EKey, entry); }
public EncodingHandler(BLTEStream blte) { if (blte.Length != long.Parse(CASContainer.BuildConfig["encoding-size"][0])) { CASContainer.Settings?.Logger.LogAndThrow(Logging.LogType.Critical, "Encoding File is corrupt."); } BinaryReader stream = new BinaryReader(blte); Header = new EncodingHeader() { Magic = stream.ReadBytes(2), Version = stream.ReadByte(), ChecksumSizeC = stream.ReadByte(), ChecksumSizeE = stream.ReadByte(), PageSizeCEKey = stream.ReadUInt16(), PageSizeEKey = stream.ReadUInt16(), PageCountCEKey = stream.ReadUInt32BE(), PageCountEKey = stream.ReadUInt32BE(), Unknown_x11 = stream.ReadByte(), ESpecBlockSize = stream.ReadUInt32BE() }; // ESpec string table ESpecStringTable.AddRange(Encoding.ASCII.GetString(stream.ReadBytes((int)Header.ESpecBlockSize)).Split('\0')); // skip CE page table lookup stream.ReadBytes((int)Header.PageCountCEKey * 32); // read CE page table data for (int i = 0; i < Header.PageCountCEKey; i++) { long start = stream.BaseStream.Position; ushort keysCount; while ((keysCount = stream.ReadUInt16()) != 0) { var entry = new EncodingCEKeyPageTable() { DecompressedSize = stream.ReadUInt32BE(), CKey = new MD5Hash(stream) }; for (int ki = 0; ki < keysCount; ki++) { entry.EKeys.Add(new MD5Hash(stream)); } CEKeys.Add(entry.CKey, entry); } if (stream.BaseStream.Position % CHUNK_SIZE != 0) { stream.BaseStream.Position += CHUNK_SIZE - ((stream.BaseStream.Position - start) % CHUNK_SIZE); } } // skip EKey page table lookup stream.ReadBytes((int)Header.PageCountEKey * 32); // read EKey page table data for (int i = 0; i < Header.PageCountEKey; i++) { long start = stream.BaseStream.Position; MD5Hash hash; while (!(hash = new MD5Hash(stream)).IsEmpty) { var entry = new EncodingEKeyPageTable() { EKey = hash, ESpecStringIndex = stream.ReadUInt32BE(), FileSize = stream.ReadUInt40BE() }; EKeys.Add(entry.EKey, entry); } if (stream.BaseStream.Position % CHUNK_SIZE != 0) { stream.BaseStream.Position += CHUNK_SIZE - ((stream.BaseStream.Position - start) % CHUNK_SIZE); } } // Encoding file ESpecStringTable stream.ReadBytes((int)(stream.BaseStream.Length - stream.BaseStream.Position)); EncodingMap = blte.EncodingMap.ToArray(); blte?.Dispose(); stream?.Dispose(); }