// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Tools.DotNETCommon { /// /// Reads data from a binary output stream. Similar to the NET Framework BinaryReader class, but supports fast serialization of object graphs and container types, and supports nullable objects. /// Significantly faster than BinaryReader due to the expectation that the whole stream is in memory before deserialization. /// public class BinaryArchiveReader : IDisposable { /// /// The input stream. /// Stream Stream; /// /// The input buffer /// byte[] Buffer; /// /// Current position within the buffer /// int BufferPos; /// /// List of previously serialized objects /// List Objects = new List(); /// /// Constructor /// /// The buffer to read from public BinaryArchiveReader(byte[] Buffer) { this.Buffer = Buffer; } /// /// Constructor /// /// File to read from public BinaryArchiveReader(FileReference FileName) { this.Buffer = FileReference.ReadAllBytes(FileName); } /// /// Constructor /// /// Stream to read from public BinaryArchiveReader(Stream Stream) { Buffer = new byte[Stream.Length]; Stream.Read(Buffer, 0, Buffer.Length); } /// /// Dispose of the stream owned by this reader /// public void Dispose() { if(Stream != null) { Stream.Dispose(); Stream = null; } Buffer = null; } /// /// Reads a bool from the stream /// /// The value that was read public bool ReadBool() { return ReadByte() != 0; } /// /// Reads a single byte from the stream /// /// The value that was read public byte ReadByte() { byte Value = Buffer[BufferPos]; BufferPos++; return Value; } /// /// Reads a single signed byte from the stream /// /// The value that was read public sbyte ReadSignedByte() { return (sbyte)ReadByte(); } /// /// Reads a single short from the stream /// /// The value that was read public short ReadShort() { return (short)ReadUnsignedShort(); } /// /// Reads a single unsigned short from the stream /// /// The value that was read public ushort ReadUnsignedShort() { ushort Value = (ushort)(Buffer[BufferPos + 0] | (Buffer[BufferPos + 1] << 8)); BufferPos += 2; return Value; } /// /// Reads a single int from the stream /// /// The value that was read public int ReadInt() { return (int)ReadUnsignedInt(); } /// /// Reads a single unsigned int from the stream /// /// The value that was read public uint ReadUnsignedInt() { uint Value = (uint)(Buffer[BufferPos + 0] | (Buffer[BufferPos + 1] << 8) | (Buffer[BufferPos + 2] << 16) | (Buffer[BufferPos + 3] << 24)); BufferPos += 4; return Value; } /// /// Reads a single long from the stream /// /// The value that was read public long ReadLong() { return (long)ReadUnsignedLong(); } /// /// Reads a single unsigned long from the stream /// /// The value that was read public ulong ReadUnsignedLong() { ulong Value = (ulong)ReadUnsignedInt(); Value |= (ulong)ReadUnsignedInt() << 32; return Value; } /// /// Reads a string from the stream /// /// The value that was read public string ReadString() { byte[] Bytes = ReadByteArray(); if(Bytes == null) { return null; } else { return Encoding.UTF8.GetString(Bytes); } } /// /// Reads a byte array from the stream /// /// The data that was read public byte[] ReadByteArray() { return ReadPrimitiveArray(sizeof(byte)); } /// /// Reads a short array from the stream /// /// The data that was read public short[] ReadShortArray() { return ReadPrimitiveArray(sizeof(short)); } /// /// Reads an int array from the stream /// /// The data that was read public int[] ReadIntArray() { return ReadPrimitiveArray(sizeof(int)); } /// /// Reads an array of primitive types from the stream /// /// Size of a single element /// The data that was read private T[] ReadPrimitiveArray(int ElementSize) where T : struct { int Length = ReadInt(); if(Length < 0) { return null; } else { T[] Result = new T[Length]; ReadBulkData(Result, Length * ElementSize); return Result; } } /// /// Reads a byte array from the stream /// /// Length of the array to read /// The data that was read public byte[] ReadFixedSizeByteArray(int Length) { return ReadFixedSizePrimitiveArray(sizeof(byte), Length); } /// /// Reads a short array from the stream /// /// Length of the array to read /// The data that was read public short[] ReadFixedSizeShortArray(int Length) { return ReadFixedSizePrimitiveArray(sizeof(short), Length); } /// /// Reads an int array from the stream /// /// Length of the array to read /// The data that was read public int[] ReadFixedSizeIntArray(int Length) { return ReadFixedSizePrimitiveArray(sizeof(int), Length); } /// /// Reads an array of primitive types from the stream /// /// Size of a single element /// Number of elements to read /// The data that was read private T[] ReadFixedSizePrimitiveArray(int ElementSize, int ElementCount) where T : struct { T[] Result = new T[ElementCount]; ReadBulkData(Result, ElementSize * ElementCount); return Result; } /// /// Reads bulk data from the stream into the given buffer /// /// Array which receives the data that was read /// Size of data to read private void ReadBulkData(Array Data, int Size) { System.Buffer.BlockCopy(Buffer, BufferPos, Data, 0, Size); BufferPos += Size; } /// /// Reads an array of items /// /// New array public T[] ReadArray(Func ReadElement) { int Count = ReadInt(); if(Count < 0) { return null; } else { T[] Result = new T[Count]; for(int Idx = 0; Idx < Count; Idx++) { Result[Idx] = ReadElement(); } return Result; } } /// /// Reads a list of items /// /// The element type for the list /// Delegate used to read a single element /// List of items public List ReadList(Func ReadElement) { int Count = ReadInt(); if(Count < 0) { return null; } else { List Result = new List(Count); for(int Idx = 0; Idx < Count; Idx++) { Result.Add(ReadElement()); } return Result; } } /// /// Reads a hashset of items /// /// The element type for the set /// Delegate used to read a single element /// Set of items public HashSet ReadHashSet(Func ReadElement) { int Count = ReadInt(); if(Count < 0) { return null; } else { HashSet Result = new HashSet(); for(int Idx = 0; Idx < Count; Idx++) { Result.Add(ReadElement()); } return Result; } } /// /// Reads a hashset of items /// /// The element type for the set /// Delegate used to read a single element /// Comparison function for the set /// Set of items public HashSet ReadHashSet(Func ReadElement, IEqualityComparer Comparer) { int Count = ReadInt(); if(Count < 0) { return null; } else { HashSet Result = new HashSet(Comparer); for(int Idx = 0; Idx < Count; Idx++) { Result.Add(ReadElement()); } return Result; } } /// /// Reads a dictionary of items /// /// Type of the dictionary key /// Type of the dictionary value /// Delegate used to read a single key /// Delegate used to read a single value /// New dictionary instance public Dictionary ReadDictionary(Func ReadKey, Func ReadValue) { int Count = ReadInt(); if(Count < 0) { return null; } else { Dictionary Result = new Dictionary(Count); for(int Idx = 0; Idx < Count; Idx++) { Result.Add(ReadKey(), ReadValue()); } return Result; } } /// /// Reads a dictionary of items /// /// Type of the dictionary key /// Type of the dictionary value /// Delegate used to read a single key /// Delegate used to read a single value /// Comparison function for keys in the dictionary /// New dictionary instance public Dictionary ReadDictionary(Func ReadKey, Func ReadValue, IEqualityComparer Comparer) { int Count = ReadInt(); if(Count < 0) { return null; } else { Dictionary Result = new Dictionary(Count, Comparer); for(int Idx = 0; Idx < Count; Idx++) { Result.Add(ReadKey(), ReadValue()); } return Result; } } /// /// Reads a nullable object from the archive /// /// The nullable type /// Delegate used to read a value public Nullable ReadNullable(Func ReadValue) where T : struct { if(ReadBool()) { return new Nullable(ReadValue()); } else { return null; } } /// /// Reads an object, which may be null, from the archive. Does not handle de-duplicating object references. /// /// Type of the object to read /// Delegate used to read the object /// The object instance public T ReadOptionalObject(Func Read) where T : class { if(ReadBool()) { return Read(); } else { return null; } } /// /// Reads an object reference from the stream. Each referenced object will only be serialized once using the supplied delegates. Reading an object instance is /// done in two phases; first the object is created and its reference stored in the unique object list, then the object contents are read. This allows the object to /// serialize a reference to itself. /// /// Type of the object to read. /// Delegate used to create an object instance /// Delegate used to read an object instance /// Object instance public T ReadObjectReference(Func CreateObject, Action ReadObject) where T : class { int Index = ReadInt(); if(Index < 0) { return null; } else { if(Index == Objects.Count) { T Object = CreateObject(); Objects.Add(Object); ReadObject(Object); } return (T)Objects[Index]; } } /// /// Reads an object reference from the stream. Each object will only be serialized once using the supplied delegate; subsequent reads reference the original. /// Since the reader only receives the object reference when the CreateObject delegate returns, it is not possible for the object to serialize a reference to itself. /// /// Type of the object to read. /// Delegate used to create an object instance. The object may not reference itself recursively. /// Object instance public T ReadObjectReference(Func ReadObject) where T : class { int Index = ReadInt(); if(Index < 0) { return null; } else { // Temporarily add the reader to the object list, so we can detect invalid recursive references. if(Index == Objects.Count) { Objects.Add(null); Objects[Index] = ReadObject(); } if(Objects[Index] == null) { throw new InvalidOperationException("Attempt to serialize reference to object recursively."); } return (T)Objects[Index]; } } } }