using System; using System.Collections.Generic; using System.Text; using System.IO; namespace FSO.Files.Utils { /// /// The order to read bytes in. /// public enum ByteOrder { BIG_ENDIAN, LITTLE_ENDIAN } /// /// IOBuffer is a very basic wrapper over System.BinaryReader that inherits from IDisposable. /// public class IoBuffer : IDisposable, BCFReadProxy { private Stream Stream; private BinaryReader Reader; public ByteOrder ByteOrder = ByteOrder.BIG_ENDIAN; /// /// Creates a new IOBuffer instance from a stream. /// /// public IoBuffer(Stream stream) { this.Stream = stream; this.Reader = new BinaryReader(stream); } /// /// More to read in this stream? /// public bool HasMore { get { return Stream.Position < Stream.Length - 1; } } /// /// Skips a number of bytes in the current stream, starting from the current position. /// /// Number of bytes to skip. public void Skip(long numBytes) { Reader.BaseStream.Seek(numBytes, SeekOrigin.Current); } /// /// Seeks in the current stream. /// /// Where to start from. /// The offset to seek to. public void Seek(SeekOrigin origin, long offset) { Reader.BaseStream.Seek(offset, origin); } public long Position => Stream.Position; /// /// Reads a variable length unsigned integer from the current stream. /// /// A uint. public uint ReadVarLen() { uint result = 0; int shift = 0; byte read = 0x80; while ((read&0x80) > 0) { read = ReadByte(); result |= (uint)((read & 0x7F) << shift); shift += 7; } return result; } /// /// Reads an unsigned 16bit integer from the current stream. /// /// A ushort. public ushort ReadUInt16() { var value = Reader.ReadUInt16(); if (ByteOrder == ByteOrder.BIG_ENDIAN) { value = Endian.SwapUInt16(value); } return value; } /// /// Reads a 16bit integer from the current stream. /// /// A short. public short ReadInt16() { var value = Reader.ReadInt16(); if (ByteOrder == ByteOrder.BIG_ENDIAN) { value = Endian.SwapInt16(value); } return value; } /// /// Reads a 32bit integer from the current stream. /// /// An int. public int ReadInt32() { var value = Reader.ReadInt32(); if (ByteOrder == ByteOrder.BIG_ENDIAN) { value = Endian.SwapInt32(value); } return value; } /// /// Reads a 64bit integer from the current stream. /// /// An int. public long ReadInt64() { var value = Reader.ReadInt64(); if (ByteOrder == ByteOrder.BIG_ENDIAN) { value = Endian.SwapInt64(value); } return value; } /// /// Reads an unsigned 32bit integer from the current stream. /// /// A uint. public uint ReadUInt32() { var value = Reader.ReadUInt32(); if (ByteOrder == ByteOrder.BIG_ENDIAN) { value = Endian.SwapUInt32(value); } return value; } /// /// Reads a number of ASCII characters from the current stream. /// /// The number of characters to read. /// A string, INCLUDING the trailing 0. public string ReadCString(int num) { return ReadCString(num, false); } /// /// Reads a number of ASCII characters from the current stream. /// /// The number of characters to read. /// Trim the trailing 0? /// A string, with or without the trailing 0. public string ReadCString(int num, bool trimNull) { var result = ASCIIEncoding.ASCII.GetString(Reader.ReadBytes(num)); if (trimNull) { /** Trim on \0 **/ var io = result.IndexOf('\0'); if (io != -1) { result = result.Substring(0, io); } } return result; } /// /// Reads a byte from the current stream. /// /// A byte. public byte ReadByte() { return Reader.ReadByte(); } /// /// Reads a number of bytes from the current stream. /// /// Number of bytes to read. /// An byte array. public byte[] ReadBytes(uint num) { return Reader.ReadBytes((int)num); } /// /// Reads a number of bytes from the current stream. /// /// Number of bytes to read. /// An byte array. public byte[] ReadBytes(int num) { return Reader.ReadBytes(num); } /// /// Reads a pascal string from the current stream, which is prefixed by a 16bit short. /// /// A string. public string ReadLongPascalString() { var length = ReadInt16(); return Encoding.ASCII.GetString(Reader.ReadBytes(length)); } /// /// Reads a C string from the current stream. /// /// A string. public string ReadNullTerminatedString() { var sb = new StringBuilder(); while (true){ char ch = (char)Reader.ReadByte(); if (ch == '\0'){ break; } sb.Append(ch); } return sb.ToString(); } public string ReadNullTerminatedUTF8() { var sb = new List(); while (true) { var b = Reader.ReadByte(); if (b == 0) break; sb.Add(b); } return Encoding.UTF8.GetString(sb.ToArray()); } /// /// Reads a pascal string from the current stream. /// /// A string. public string ReadVariableLengthPascalString() { return Reader.ReadString(); } /// /// Reads a pascal string from the current stream, prefixed by a byte. /// /// A string. public string ReadPascalString() { var length = ReadByte(); return Encoding.ASCII.GetString(Reader.ReadBytes(length)); } /// /// Reads a float from the current stream. /// /// A float. [System.Security.SecuritySafeCritical] // auto-generated public virtual unsafe float ReadFloat() { return Reader.ReadSingle(); } /// /// Sets a mark at the current position in the stream. /// private long _Mark; public void Mark() { _Mark = Reader.BaseStream.Position; } /// /// Seeks in the current stream from the current mark plus the number of bytes. /// /// The number of bytes to add to the offset (mark). public void SeekFromMark(long numBytes) { Reader.BaseStream.Seek(_Mark + numBytes, SeekOrigin.Begin); } #region IDisposable Members public void Dispose() { } #endregion /// /// Creates a new IOBuffer instance from a stream. /// /// A stream. /// A new IOBuffer instance. public static IoBuffer FromStream(Stream stream) { return new IoBuffer(stream); } /// /// Creates a new IOBuffer instance from a stream, using a specified byte order. /// /// A stream. /// Byte order to use. /// A new IOBuffer instance. public static IoBuffer FromStream(Stream stream, ByteOrder order) { var item = FromStream(stream); item.ByteOrder = order; return item; } /// /// Creates a new IOBuffer instance from a byte array. /// /// The byte array to use. /// A new IOBuffer instance. public static IoBuffer FromBytes(byte[] bytes) { return FromStream(new MemoryStream(bytes)); } /// /// Creates a new IOBuffer instance from a byte array, using a specified byte order. /// /// The byte array to use. /// Byte order to use. /// A new IOBuffer instance. public static IoBuffer FromBytes(byte[] bytes, ByteOrder order) { return FromStream(new MemoryStream(bytes), order); } } }