using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using Oni.Imaging; namespace Oni { internal class BinaryWriter : System.IO.BinaryWriter { private static readonly byte[] padding = new byte[32]; private static readonly Encoding encoding = Encoding.UTF8; private readonly Stack positionStack = new Stack(); public BinaryWriter(Stream stream) : base(stream, encoding) { } public void WriteInstanceId(int index) { Write(InstanceFileWriter.MakeInstanceId(index)); } public void Write(IEnumerable descriptors) { foreach (var descriptor in descriptors) Write(descriptor); } public void Write(ImporterDescriptor descriptor) { if (descriptor == null) Write(0); else Write(InstanceFileWriter.MakeInstanceId(descriptor.Index)); } public void Write(Color c) { Write(c.ToBgra32()); } public void Write(Vector2 v) { Write(v.X); Write(v.Y); } public void Write(Vector3 v) { Write(v.X); Write(v.Y); Write(v.Z); } public void Write(Vector4 v) { Write(v.X); Write(v.Y); Write(v.Z); Write(v.W); } public void Write(Quaternion q) { Write(q.X); Write(q.Y); Write(q.Z); Write(-q.W); } public void Write(Plane p) { Write(p.Normal); Write(p.D); } public void Write(BoundingBox bbox) { Write(bbox.Min); Write(bbox.Max); } public void Write(BoundingSphere bsphere) { Write(bsphere.Center); Write(bsphere.Radius); } public void WriteMatrix4x3(Matrix m) { Write(m.M11); Write(m.M12); Write(m.M13); Write(m.M21); Write(m.M22); Write(m.M23); Write(m.M31); Write(m.M32); Write(m.M33); Write(m.M41); Write(m.M42); Write(m.M43); } public void Write(short[] a) { foreach (short v in a) Write(v); } public void Write(ushort[] a) { foreach (ushort v in a) Write(v); } public void Write(int[] a) { foreach (int v in a) Write(v); } public void Write(int[] v, int startIndex, int length) { for (int i = startIndex; i < startIndex + length; i++) Write(v[i]); } public void Write(IEnumerable a) { foreach (float v in a) Write(v); } public void Write(IEnumerable a) { foreach (int i in a) Write(i); } public void Write(Color[] a) { foreach (Color v in a) Write(v); } public void Write(IEnumerable a) { foreach (Vector2 v in a) Write(v); } public void Write(IEnumerable a) { foreach (Vector3 v in a) Write(v); } public void Write(IEnumerable a) { foreach (Plane v in a) Write(v); } public void Write(string s, int maxLength) { if (s == null) { Skip(maxLength); return; } if (encoding.GetByteCount(s) > maxLength) throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "The string '{0}' is too long (max length is {1})", s, maxLength)); byte[] data = new byte[maxLength]; encoding.GetBytes(s, 0, s.Length, data, 0); Write(data); } public void WriteByte(int value) { if (value < byte.MinValue || byte.MaxValue < value) throw new ArgumentOutOfRangeException("Value too large for Byte", "value"); Write((byte)value); } public void WriteInt16(int value) { if (value < short.MinValue || short.MaxValue < value) throw new ArgumentOutOfRangeException("Value too large for Int16", "value"); Write((short)value); } public void WriteUInt16(int value) { if (value < 0 || value > UInt16.MaxValue) throw new ArgumentOutOfRangeException("Value too large for UInt16", "value"); Write((ushort)value); } public void Write(byte value, int count) { if (value == 0 && Position == OutStream.Length) { Seek(count, SeekOrigin.Current); } else { for (int i = 0; i < count; i++) Write(value); } } public void Skip(int length) { Position += length; } public override Stream BaseStream { get { // // Note: return base OutStream directly instead of BaseStream to avoid // flushing the stream. // return base.OutStream; } } public void PushPosition(int newPosition) { positionStack.Push(Position); Position = newPosition; } public void PopPosition() { Position = positionStack.Pop(); } public int Position { get { return (int)BaseStream.Position; } set { int currentPosition = (int)OutStream.Position; int delta = value - currentPosition; if (delta == 0) return; // // Prevent changing the output stream position for small changes of position. // This avoids flushing the write cache of the stream which results in poor perf. // if (0 < delta && delta <= 32 && Position == OutStream.Length) OutStream.Write(padding, 0, delta); else OutStream.Position = value; } } public void WriteAt(int position, int value) { PushPosition(position); Write(value); PopPosition(); } public void WriteAt(int position, short value) { PushPosition(position); Write(value); PopPosition(); } public int Align32() { int position = Utils.Align32(Position); Position = position; return position; } } }