/// <summary> /// Encodes a byte array /// </summary> /// <param name="data"></param> /// <param name="encodingMap"></param> /// <param name="rootName">Root filename</param> /// <returns></returns> public static CASRecord Encode(byte[] data, EMap encodingMap, string rootName = null) { using var bt = new BlockTableStreamWriter(encodingMap); bt.Write(data); var record = bt.Finalise(); record.FileName = rootName; return(record); }
/// <summary> /// Encodes a stream /// </summary> /// <param name="stream"></param> /// <param name="encodingMap"></param> /// <param name="rootName">Root filename</param> /// <returns></returns> public static CASRecord Encode(Stream stream, EMap encodingMap, string rootName = null) { using var bt = new BlockTableStreamWriter(encodingMap); stream.Position = 0; stream.CopyTo(bt); var record = bt.Finalise(); record.FileName = rootName; return(record); }
/// <summary> /// Adds a new block to the stream. This finalises the previous block preventing access /// </summary> /// <param name="blockencoding"></param> /// <param name="blockindex"></param> public void AddBlock(EMap blockencoding, int blockindex = -1) { // lock the previous substream if (_blocks.Count > 0) { _blocks[_curIndex].Lock(); } // compute the new index if (blockindex == -1) { blockindex = _blocks.Count; } _blocks.Add(blockindex, new BlockTableSubStream(memStream, blockencoding)); _curIndex = blockindex; }
/// <summary> /// Encodes a byte array and saves the result to disk /// </summary> /// <param name="data"></param> /// <param name="encodingMap"></param> /// <param name="directory"></param> /// <param name="rootName">Root filename</param> /// <returns></returns> public static CASRecord EncodeAndExport(byte[] data, EMap encodingMap, string directory, string rootName = null) { using var bt = new BlockTableStreamWriter(encodingMap); bt.Write(data); var record = bt.Finalise(); string saveLocation = Path.Combine(directory, record.EKey.ToString()); using (var fs = Helpers.Create(saveLocation)) { bt.WriteTo(fs); record.BLTEPath = saveLocation; record.FileName = rootName; } return(record); }
/// <summary> /// Encodes a stream using Blizzard-esque rules and saves the result to disk /// </summary> /// <param name="stream"></param> /// <param name="encodingMap"></param> /// <param name="directory"></param> /// <param name="rootName">Root filename</param> /// <returns></returns> public static CASRecord EncodeAndExport(Stream stream, EMap encodingMap, string directory, string rootName = null) { using (var bt = new BlockTableStreamWriter(encodingMap)) { stream.Position = 0; stream.CopyTo(bt); var record = bt.Finalise(); string saveLocation = Helpers.GetCDNPath(record.EKey.ToString(), "data", directory, true); using (var fs = Helpers.Create(saveLocation)) { bt.WriteTo(fs); record.BLTEPath = saveLocation; record.FileName = rootName; } return(record); } }
/// <summary> /// Finalises and encodes the stream's contents /// </summary> /// <returns></returns> public CASRecord Finalise() { if (Finalised) { return(Result); } // lock the final block _blocks[_blocks.Count - 1].Lock(); MD5Hash EKey, CKey = ComputeCKey(); EMap encoding = _blocks.Count == 1 ? _blocks[0].EncodingMap : new EMap(EType.ZLib, 9); uint decompressedSize = 0; string eSpec; using (var md5 = MD5.Create()) using (var ms = new MemoryStream((int)memStream.Length + 0x100)) using (var bw = new BinaryWriter(ms)) { // replace the stream contents with the BLTE structure uint headerSize = (uint)(_blocks.Count == 1 ? 0 : 0x18 * _blocks.Count + 0xC); // Header bw.Write(BlockTableStreamReader.BLTE_MAGIC); bw.WriteUInt32BE(headerSize); // Frame if (headerSize > 0) { bw.Write((byte)0xF); // flag bw.WriteUInt24BE((uint)_blocks.Count); // chunkCount // EBlock meta foreach (var block in _blocks.Values) { block.Finalise(); // apply encoding byte and any compression block.Position = 0; bw.WriteUInt32BE(block.CompressedSize); bw.WriteUInt32BE(block.DecompressedSize); bw.Write(md5.ComputeHash(block)); decompressedSize += block.DecompressedSize; } } else { var block = _blocks[0]; block.Finalise(); // apply encoding byte and any compression decompressedSize = block.DecompressedSize; } foreach (var block in _blocks.Values) { block.Position = 0; block.CopyTo(ms); } // calculate the EKey if (headerSize == 0) { ms.Position = 0; EKey = new MD5Hash(md5.ComputeHash(ms)); } else { EKey = ms.HashSlice(md5, 0, headerSize); } // set ESpec eSpec = string.Join(",", _blocks.Values); // merge the streams memStream.Position = 0; memStream.SetLength(ms.Length); memStream.Capacity = (int)ms.Length; ms.WriteTo(memStream); // cleanup _blocks.Clear(); Finalised = true; } // store for repeat finalisation return(Result = new CASRecord() { CKey = CKey, EBlock = new EBlock() { DecompressedSize = decompressedSize, CompressedSize = (uint)memStream.Length, EKey = EKey, EncodingMap = encoding }, ESpec = "b:{" + eSpec + "}" }); }
public BlockTableStreamWriter(EMap blockencoding, int blockindex = -1) { memStream = new MemoryStream(); _blocks = new SortedList <int, BlockTableSubStream>(); AddBlock(blockencoding, blockindex); }
public BlockTableSubStream(MemoryStream inner, EMap map) { _innerStream = inner; _startPos = inner.Position; EncodingMap = map; }
public EBlock() { EncodingMap = default; }