using System; using System.Collections.Generic; using System.IO; using System.Text; using Oni.Imaging; namespace Oni { internal sealed class BinaryReader : IDisposable { #region Private data private static readonly byte[] seekBuffer = new byte[512]; private static readonly Encoding encoding = Encoding.UTF8; private const float rotationAngleScale = MathHelper.Pi / 32767.5f; private FileStream stream; private byte[] buffer; private bool bigEndian; private InstanceFile instanceFile; #endregion public BinaryReader(string filePath) { this.buffer = new byte[8]; this.stream = File.OpenRead(filePath); } public BinaryReader(string filePath, bool bigEndian) : this(filePath) { this.bigEndian = bigEndian; } public BinaryReader(string filePath, InstanceFile instanceFile) : this(filePath) { this.instanceFile = instanceFile; } public void Dispose() { if (stream != null) stream.Dispose(); stream = null; buffer = null; } public string Name => stream.Name; public int Length => (int)stream.Length; public int Position { get { return (int)stream.Position; } set { int currentPosition = (int)stream.Position; int delta = value - currentPosition; if (delta == 0) return; if (delta > 0 && delta <= seekBuffer.Length) stream.Read(seekBuffer, 0, delta); else stream.Position = value; } } public void Skip(int length) { Position += length; } public void SkipCString() { for (int b = 1; b != 0 && b != -1; b = stream.ReadByte()) ; } public int Read(byte[] buffer, int offset, int length) { return stream.Read(buffer, offset, length); } public byte[] ReadBytes(int length) { var buffer = new byte[length]; int offset = 0; while (length > 0) { int read = stream.Read(buffer, offset, length); if (read == 0) break; offset += read; length -= read; } if (offset != buffer.Length) { var result = new byte[offset]; Buffer.BlockCopy(buffer, 0, result, 0, offset); buffer = result; } return buffer; } public byte ReadByte() { int value = stream.ReadByte(); if (value == -1) throw new EndOfStreamException(); return (byte)value; } public bool ReadBoolean() { return (ReadByte() != 0); } public ushort ReadUInt16() { FillBuffer(2); if (bigEndian) { return (ushort)(buffer[1] | buffer[0] << 8); } return (ushort)(buffer[0] | buffer[1] << 8); } public ushort[] ReadUInt16Array(int length) { var array = new ushort[length]; for (int i = 0; i < array.Length; i++) array[i] = ReadUInt16(); return array; } public uint ReadUInt32() { FillBuffer(4); if (bigEndian) { return (uint)(buffer[3] | buffer[2] << 8 | buffer[1] << 16 | buffer[0] << 24); } return (uint)(buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24); } public ulong ReadUInt64() { FillBuffer(8); ulong lo, hi; if (bigEndian) { hi = (uint)(buffer[3] | buffer[2] << 8 | buffer[1] << 16 | buffer[0] << 24); lo = (uint)(buffer[7] | buffer[6] << 8 | buffer[5] << 16 | buffer[4] << 24); } else { lo = (uint)(buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24); hi = (uint)(buffer[4] | buffer[5] << 8 | buffer[6] << 16 | buffer[7] << 24); } return (hi << 32) | lo; } public short ReadInt16() { return (short)ReadUInt16(); } public short[] ReadInt16Array(int length) { var array = new short[length]; for (int i = 0; i < array.Length; i++) array[i] = ReadInt16(); return array; } public int ReadInt32() { return (int)ReadUInt32(); } public int[] ReadInt32VarArray() { return ReadInt32Array(ReadInt32()); } public int[] ReadInt32Array(int length) { var array = new int[length]; for (int i = 0; i < array.Length; i++) array[i] = ReadInt32(); return array; } public long ReadInt64() { return (long)ReadUInt64(); } public unsafe float ReadSingle() { uint value = ReadUInt32(); return *((float*)&value); } public float[] ReadSingleArray(int length) { var data = new float[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadSingle(); return data; } public unsafe double ReadDouble() { ulong value = ReadUInt64(); return *((double*)&value); } public Vector2 ReadVector2() { return new Vector2(ReadSingle(), ReadSingle()); } public Vector2[] ReadVector2VarArray() { return ReadVector2Array(ReadInt32()); } public Vector2[] ReadVector2Array(int length) { var data = new Vector2[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadVector2(); return data; } public Vector3 ReadVector3() { return new Vector3(ReadSingle(), ReadSingle(), ReadSingle()); } public Vector3[] ReadVector3VarArray() { return ReadVector3Array(ReadInt32()); } public Vector3[] ReadVector3Array(int length) { var data = new Vector3[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadVector3(); return data; } public Plane ReadPlane() { return new Plane(ReadVector3(), ReadSingle()); } public Plane[] ReadPlaneVarArray() { return ReadPlaneArray(ReadInt32()); } public Plane[] ReadPlaneArray(int length) { var data = new Plane[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadPlane(); return data; } public Quaternion ReadQuaternion() { return new Quaternion(ReadSingle(), ReadSingle(), ReadSingle(), -ReadSingle()); } public Quaternion ReadCompressedQuaternion() { return Quaternion.CreateFromAxisAngle(Vector3.UnitX, ReadInt16() * rotationAngleScale) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, ReadInt16() * rotationAngleScale) * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, ReadInt16() * rotationAngleScale); } public BoundingBox ReadBoundingBox() { return new BoundingBox(ReadVector3(), ReadVector3()); } public Matrix ReadMatrix4x3() { Matrix m; m.M11 = ReadSingle(); m.M12 = ReadSingle(); m.M13 = ReadSingle(); m.M14 = 0.0f; m.M21 = ReadSingle(); m.M22 = ReadSingle(); m.M23 = ReadSingle(); m.M24 = 0.0f; m.M31 = ReadSingle(); m.M32 = ReadSingle(); m.M33 = ReadSingle(); m.M34 = 0.0f; m.M41 = ReadSingle(); m.M42 = ReadSingle(); m.M43 = ReadSingle(); m.M44 = 1.0f; return m; } public Color ReadColor() { uint color = ReadUInt32(); var r = (byte)((color >> 16) & 0xff); var g = (byte)((color >> 08) & 0xff); var b = (byte)((color >> 00) & 0xff); var a = (byte)((color >> 24) & 0xff); return new Color(r, g, b, a); } public Color[] ReadColorArray(int length) { var data = new Color[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadColor(); return data; } public string ReadString(int maxLength) { var bytes = ReadBytes(maxLength); for (int i = 0; i < bytes.Length; i++) { if (bytes[i] == 0) return encoding.GetString(bytes, 0, i); } return encoding.GetString(bytes); } public string ReadCString() { var buffer = new List(64); byte b; while ((b = ReadByte()) != 0) buffer.Add(b); return encoding.GetString(buffer.ToArray()); } public InstanceDescriptor ReadInstance() { return instanceFile.ResolveLink(ReadInt32()); } public InstanceDescriptor[] ReadInstanceArray(int length) { var data = new InstanceDescriptor[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadInstance(); return data; } public InstanceDescriptor ReadLink() { return instanceFile.GetDescriptor(ReadInt32()); } public InstanceDescriptor[] ReadLinkArray(int length) { var data = new InstanceDescriptor[length]; for (int i = 0; i < data.Length; i++) data[i] = ReadLink(); return data; } private void FillBuffer(int count) { int offset = 0; while (count > 0) { int read = stream.Read(buffer, offset, count); if (read == 0) throw new EndOfStreamException(); offset += read; count -= read; } } } }