Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Concentus Oggfile v1.0.700
BepInEx/core/Concentus.Oggfile/netstandard2.0/Concentus.Oggfile.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Concentus.Structs; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTrademark("")] [assembly: NeutralResourcesLanguage("en")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Logan Stromberg")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Logan Stromberg, Andrew Ward")] [assembly: AssemblyDescription("This package implements file streams which can be used to extract or encode Opus packets in an Ogg-formatted audio file (usually .opus), giving developers a very simple API to perform the task of reading or writing audio files that can be played universally. The Concentus library is used to encode/decode the opus packets automatically. The codec can optionally be accelerated by also referencing the Concentus.Native package.")] [assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyInformationalVersion("1.0.7.0+27c3125205ddcd891822a398284b246636fafb94")] [assembly: AssemblyProduct("Concentus.Oggfile")] [assembly: AssemblyTitle("Concentus.Oggfile")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus.oggfile")] [assembly: AssemblyVersion("1.0.7.0")] namespace Concentus.Oggfile; internal static class BinaryHelpers { internal static void Int16ToByteArrayLittleEndian(short val, byte[] target, int targetOffset) { UInt16ToByteArrayLittleEndian((ushort)val, target, targetOffset); } internal static void UInt16ToByteArrayLittleEndian(ushort val, byte[] target, int targetOffset) { target[targetOffset + 1] = (byte)((uint)(val >> 8) & 0xFFu); target[targetOffset] = (byte)(val & 0xFFu); } internal static void Int16ToByteSpanLittleEndian(short val, ref Span<byte> target) { UInt16ToByteArraySpanEndian((ushort)val, ref target); } internal static void UInt16ToByteArraySpanEndian(ushort val, ref Span<byte> target) { target[1] = (byte)((uint)(val >> 8) & 0xFFu); target[0] = (byte)(val & 0xFFu); } internal static void Int16ToByteArrayBigEndian(short val, byte[] target, int targetOffset) { UInt16ToByteArrayBigEndian((ushort)val, target, targetOffset); } internal static void UInt16ToByteArrayBigEndian(ushort val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((uint)(val >> 8) & 0xFFu); target[targetOffset + 1] = (byte)(val & 0xFFu); } internal static void Int16ToByteSpanBigEndian(short val, ref Span<byte> target) { UInt16ToByteSpanBigEndian((ushort)val, ref target); } internal static void UInt16ToByteSpanBigEndian(ushort val, ref Span<byte> target) { target[0] = (byte)((uint)(val >> 8) & 0xFFu); target[1] = (byte)(val & 0xFFu); } internal static void Int32ToByteArrayLittleEndian(int val, byte[] target, int targetOffset) { UInt32ToByteArrayLittleEndian((uint)val, target, targetOffset); } internal static void UInt32ToByteArrayLittleEndian(uint val, byte[] target, int targetOffset) { target[targetOffset + 3] = (byte)((val >> 24) & 0xFFu); target[targetOffset + 2] = (byte)((val >> 16) & 0xFFu); target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu); target[targetOffset] = (byte)(val & 0xFFu); } internal static void Int32ToByteSpanLittleEndian(int val, ref Span<byte> target) { UInt32ToByteSpanLittleEndian((uint)val, ref target); } internal static void UInt32ToByteSpanLittleEndian(uint val, ref Span<byte> target) { target[3] = (byte)((val >> 24) & 0xFFu); target[2] = (byte)((val >> 16) & 0xFFu); target[1] = (byte)((val >> 8) & 0xFFu); target[0] = (byte)(val & 0xFFu); } internal static void UInt24ToByteArrayBigEndian(uint val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((val >> 16) & 0xFFu); target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu); target[targetOffset + 2] = (byte)(val & 0xFFu); } internal static void UInt24ToByteSpanBigEndian(uint val, ref Span<byte> target) { target[0] = (byte)((val >> 16) & 0xFFu); target[1] = (byte)((val >> 8) & 0xFFu); target[2] = (byte)(val & 0xFFu); } internal static void Int32ToByteArrayBigEndian(int val, byte[] target, int targetOffset) { UInt32ToByteArrayBigEndian((uint)val, target, targetOffset); } internal static void UInt32ToByteArrayBigEndian(uint val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((val >> 24) & 0xFFu); target[targetOffset + 1] = (byte)((val >> 16) & 0xFFu); target[targetOffset + 2] = (byte)((val >> 8) & 0xFFu); target[targetOffset + 3] = (byte)(val & 0xFFu); } internal static void Int32ToByteSpanBigEndian(int val, ref Span<byte> target) { UInt32ToByteSpanBigEndian((uint)val, ref target); } internal static void UInt32ToByteSpanBigEndian(uint val, ref Span<byte> target) { target[0] = (byte)((val >> 24) & 0xFFu); target[1] = (byte)((val >> 16) & 0xFFu); target[2] = (byte)((val >> 8) & 0xFFu); target[3] = (byte)(val & 0xFFu); } internal static void Int64ToByteArrayLittleEndian(long val, byte[] target, int targetOffset) { UInt64ToByteArrayLittleEndian((ulong)val, target, targetOffset); } internal static void UInt64ToByteArrayLittleEndian(ulong val, byte[] target, int targetOffset) { target[targetOffset + 7] = (byte)((val >> 56) & 0xFF); target[targetOffset + 6] = (byte)((val >> 48) & 0xFF); target[targetOffset + 5] = (byte)((val >> 40) & 0xFF); target[targetOffset + 4] = (byte)((val >> 32) & 0xFF); target[targetOffset + 3] = (byte)((val >> 24) & 0xFF); target[targetOffset + 2] = (byte)((val >> 16) & 0xFF); target[targetOffset + 1] = (byte)((val >> 8) & 0xFF); target[targetOffset] = (byte)(val & 0xFF); } internal static void Int64ToByteSpanLittleEndian(long val, ref Span<byte> target) { UInt64ToByteSpanLittleEndian((ulong)val, ref target); } internal static void UInt64ToByteSpanLittleEndian(ulong val, ref Span<byte> target) { target[7] = (byte)((val >> 56) & 0xFF); target[6] = (byte)((val >> 48) & 0xFF); target[5] = (byte)((val >> 40) & 0xFF); target[4] = (byte)((val >> 32) & 0xFF); target[3] = (byte)((val >> 24) & 0xFF); target[2] = (byte)((val >> 16) & 0xFF); target[1] = (byte)((val >> 8) & 0xFF); target[0] = (byte)(val & 0xFF); } internal static void Int64ToByteArrayBigEndian(long val, byte[] target, int targetOffset) { UInt64ToByteArrayBigEndian((ulong)val, target, targetOffset); } internal static void UInt64ToByteArrayBigEndian(ulong val, byte[] target, int targetOffset) { target[targetOffset] = (byte)((val >> 56) & 0xFF); target[targetOffset + 1] = (byte)((val >> 48) & 0xFF); target[targetOffset + 2] = (byte)((val >> 40) & 0xFF); target[targetOffset + 3] = (byte)((val >> 32) & 0xFF); target[targetOffset + 4] = (byte)((val >> 24) & 0xFF); target[targetOffset + 5] = (byte)((val >> 16) & 0xFF); target[targetOffset + 6] = (byte)((val >> 8) & 0xFF); target[targetOffset + 7] = (byte)(val & 0xFF); } internal static void Int64ToByteSpanBigEndian(long val, ref Span<byte> target) { UInt64ToByteSpanBigEndian((ulong)val, ref target); } internal static void UInt64ToByteSpanBigEndian(ulong val, ref Span<byte> target) { target[0] = (byte)((val >> 56) & 0xFF); target[1] = (byte)((val >> 48) & 0xFF); target[2] = (byte)((val >> 40) & 0xFF); target[3] = (byte)((val >> 32) & 0xFF); target[4] = (byte)((val >> 24) & 0xFF); target[5] = (byte)((val >> 16) & 0xFF); target[6] = (byte)((val >> 8) & 0xFF); target[7] = (byte)(val & 0xFF); } internal static short ByteArrayToInt16LittleEndian(byte[] source, int offset) { return (short)((short)(0 | (short)(source[offset + 1] << 8)) | (short)source[offset]); } internal static short ByteSpanToInt16LittleEndian(ref Span<byte> source) { return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]); } internal static short ByteSpanToInt16LittleEndian(ref ReadOnlySpan<byte> source) { return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]); } internal static ushort ByteArrayToUInt16LittleEndian(byte[] source, int offset) { return (ushort)((ushort)(0u | (ushort)(source[offset + 1] << 8)) | source[offset]); } internal static ushort ByteSpanToUInt16LittleEndian(ref Span<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]); } internal static ushort ByteSpanToUInt16LittleEndian(ref ReadOnlySpan<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]); } internal static short ByteArrayToInt16BigEndian(byte[] source, int offset) { return (short)((short)(0 | (short)(source[offset] << 8)) | (short)source[offset + 1]); } internal static short ByteSpanToInt16BigEndian(ref Span<byte> source) { return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]); } internal static short ByteSpanToInt16BigEndian(ref ReadOnlySpan<byte> source) { return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]); } internal static ushort ByteArrayToUInt16BigEndian(byte[] source, int offset) { return (ushort)((ushort)(0u | (ushort)(source[offset] << 8)) | source[offset + 1]); } internal static ushort ByteSpanToUInt16BigEndian(ref Span<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]); } internal static ushort ByteSpanToUInt16BigEndian(ref ReadOnlySpan<byte> source) { return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]); } internal static int ByteArrayToInt32LittleEndian(byte[] source, int offset) { return 0 | (source[offset + 3] << 24) | (source[offset + 2] << 16) | (source[offset + 1] << 8) | source[offset]; } internal static int ByteSpanToInt32LittleEndian(ref Span<byte> source) { return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0]; } internal static int ByteSpanToInt32LittleEndian(ref ReadOnlySpan<byte> source) { return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0]; } internal static uint ByteArrayToUInt32LittleEndian(byte[] source, int offset) { return 0u | (uint)(source[offset + 3] << 24) | (uint)(source[offset + 2] << 16) | (uint)(source[offset + 1] << 8) | source[offset]; } internal static uint ByteSpanToUInt32LittleEndian(ref Span<byte> source) { return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0]; } internal static uint ByteSpanToUInt32LittleEndian(ref ReadOnlySpan<byte> source) { return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0]; } internal static uint ByteArrayToUInt24BigEndian(byte[] source, int offset) { return 0u | (uint)(source[offset] << 16) | (uint)(source[offset + 1] << 8) | source[offset + 2]; } internal static uint ByteSpanToUInt24BigEndian(ref Span<byte> source) { return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2]; } internal static uint ByteSpanToUInt24BigEndian(ref ReadOnlySpan<byte> source) { return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2]; } internal static int ByteArrayToInt32BigEndian(byte[] source, int offset) { return 0 | (source[offset] << 24) | (source[offset + 1] << 16) | (source[offset + 2] << 8) | source[offset + 3]; } internal static int ByteSpanToInt32BigEndian(ref Span<byte> source) { return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3]; } internal static int ByteSpanToInt32BigEndian(ref ReadOnlySpan<byte> source) { return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3]; } internal static uint ByteArrayToUInt32BigEndian(byte[] source, int offset) { return 0u | (uint)(source[offset] << 24) | (uint)(source[offset + 1] << 16) | (uint)(source[offset + 2] << 8) | source[offset + 3]; } internal static uint ByteSpanToUInt32BigEndian(ref Span<byte> source) { return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3]; } internal static uint ByteSpanToUInt32BigEndian(ref ReadOnlySpan<byte> source) { return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3]; } internal static long ByteArrayToInt64LittleEndian(byte[] source, int offset) { return (long)(0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset]); } internal static long ByteSpanToInt64LittleEndian(ref Span<byte> source) { return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]); } internal static long ByteSpanToInt64LittleEndian(ref ReadOnlySpan<byte> source) { return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]); } internal static ulong ByteArrayToUInt64LittleEndian(byte[] source, int offset) { return 0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset]; } internal static ulong ByteSpanToUInt64LittleEndian(ref Span<byte> source) { return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]; } internal static ulong ByteSpanToUInt64LittleEndian(ref ReadOnlySpan<byte> source) { return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]; } internal static long ByteArrayToInt64BigEndian(byte[] source, int offset) { return (long)(0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7]); } internal static long ByteSpanToInt64BigEndian(ref Span<byte> source) { return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]); } internal static long ByteSpanToInt64BigEndian(ref ReadOnlySpan<byte> source) { return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]); } internal static ulong ByteArrayToUInt64BigEndian(byte[] source, int offset) { return 0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7]; } internal static ulong ByteSpanToUInt64BigEndian(ref Span<byte> source) { return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]; } internal static ulong ByteSpanToUInt64BigEndian(ref ReadOnlySpan<byte> source) { return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]; } } internal class BufferedReadStream : Stream { private const int DEFAULT_INITIAL_SIZE = 32768; private const int DEFAULT_MAX_SIZE = 262144; private Stream _baseStream; private StreamReadBuffer _buffer; private long _readPosition; public bool CloseBaseStream { get; set; } public bool MinimalRead { get { return _buffer.MinimalRead; } set { _buffer.MinimalRead = value; } } public int MaxBufferSize { get { return _buffer.MaxSize; } set { CheckLock(); _buffer.MaxSize = value; } } public long BufferBaseOffset => _buffer.BaseOffset; public int BufferBytesFilled => _buffer.BytesFilled; public override bool CanRead => true; public override bool CanSeek => true; public override bool CanWrite => false; public override long Length => _baseStream.Length; public override long Position { get { return _readPosition; } set { Seek(value, SeekOrigin.Begin); } } public BufferedReadStream(Stream baseStream) : this(baseStream, 32768, 262144, minimalRead: false) { } public BufferedReadStream(Stream baseStream, bool minimalRead) : this(baseStream, 32768, 262144, minimalRead) { } public BufferedReadStream(Stream baseStream, int initialSize, int maxSize) : this(baseStream, initialSize, maxSize, minimalRead: false) { } public BufferedReadStream(Stream baseStream, int initialSize, int maxBufferSize, bool minimalRead) { if (baseStream == null) { throw new ArgumentNullException("baseStream"); } if (!baseStream.CanRead) { throw new ArgumentException("baseStream"); } if (maxBufferSize < 1) { maxBufferSize = 1; } if (initialSize < 1) { initialSize = 1; } if (initialSize > maxBufferSize) { initialSize = maxBufferSize; } _baseStream = baseStream; _buffer = new StreamReadBuffer(baseStream, initialSize, maxBufferSize, minimalRead); _buffer.MaxSize = maxBufferSize; _buffer.MinimalRead = minimalRead; } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { if (_buffer != null) { _buffer.Dispose(); _buffer = null; } _ = CloseBaseStream; } } public void TakeLock() { } private void CheckLock() { } public void ReleaseLock() { } public void Discard(int bytes) { CheckLock(); _buffer.DiscardThrough(_buffer.BaseOffset + bytes); } public void DiscardThrough(long offset) { CheckLock(); _buffer.DiscardThrough(offset); } public override void Flush() { } public override int ReadByte() { CheckLock(); int num = _buffer.ReadByte(Position); if (num > -1) { Seek(1L, SeekOrigin.Current); } return num; } public override int Read(byte[] buffer, int offset, int count) { CheckLock(); int num = _buffer.Read(Position, buffer, offset, count); Seek(num, SeekOrigin.Current); return num; } public override long Seek(long offset, SeekOrigin origin) { CheckLock(); switch (origin) { case SeekOrigin.Current: offset += Position; break; case SeekOrigin.End: offset += _baseStream.Length; break; } if (!_baseStream.CanSeek) { if (offset < _buffer.BaseOffset) { throw new InvalidOperationException("Cannot seek to before the start of the buffer!"); } if (offset > _buffer.BufferEndOffset) { throw new InvalidOperationException("Cannot seek to beyond the end of the buffer! Discard some bytes."); } } return _readPosition = offset; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } internal abstract class DataPacket { [Flags] protected enum PacketFlags : byte { IsResync = 1, IsEndOfStream = 2, IsShort = 4, HasGranuleCount = 8, User1 = 0x10, User2 = 0x20, User3 = 0x40, User4 = 0x80 } private ulong _bitBucket; private int _bitCount; private int _readBits; private byte _overflowBits; private PacketFlags _packetFlags; private long _granulePosition; private long _pageGranulePosition; private int _length; private int _granuleCount; private int _pageSequenceNumber; public bool IsResync { get { return GetFlag(PacketFlags.IsResync); } internal set { SetFlag(PacketFlags.IsResync, value); } } public long GranulePosition { get { return _granulePosition; } set { _granulePosition = value; } } public long PageGranulePosition { get { return _pageGranulePosition; } internal set { _pageGranulePosition = value; } } public int Length { get { return _length; } protected set { _length = value; } } public bool IsEndOfStream { get { return GetFlag(PacketFlags.IsEndOfStream); } internal set { SetFlag(PacketFlags.IsEndOfStream, value); } } public long BitsRead => _readBits; public int? GranuleCount { get { if (GetFlag(PacketFlags.HasGranuleCount)) { return _granuleCount; } return null; } set { if (value.HasValue) { _granuleCount = value.Value; SetFlag(PacketFlags.HasGranuleCount, value: true); } else { SetFlag(PacketFlags.HasGranuleCount, value: false); } } } internal int PageSequenceNumber { get { return _pageSequenceNumber; } set { _pageSequenceNumber = value; } } internal bool IsShort { get { return GetFlag(PacketFlags.IsShort); } private set { SetFlag(PacketFlags.IsShort, value); } } protected bool GetFlag(PacketFlags flag) { return (_packetFlags & flag) == flag; } protected void SetFlag(PacketFlags flag, bool value) { if (value) { _packetFlags |= flag; } else { _packetFlags &= (PacketFlags)(byte)(~(int)flag); } } protected DataPacket(int length) { Length = length; } protected abstract int ReadNextByte(); public virtual void Done() { } public ulong TryPeekBits(int count, out int bitsRead) { ulong num = 0uL; switch (count) { default: throw new ArgumentOutOfRangeException("count"); case 0: bitsRead = 0; return 0uL; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: break; } while (_bitCount < count) { int num2 = ReadNextByte(); if (num2 == -1) { bitsRead = _bitCount; num = _bitBucket; _bitBucket = 0uL; _bitCount = 0; IsShort = true; return num; } _bitBucket = (ulong)((long)(num2 & 0xFF) << _bitCount) | _bitBucket; _bitCount += 8; if (_bitCount > 64) { _overflowBits = (byte)(num2 >> 72 - _bitCount); } } num = _bitBucket; if (count < 64) { num &= (ulong)((1L << count) - 1); } bitsRead = count; return num; } public void SkipBits(int count) { if (count == 0) { return; } if (_bitCount > count) { if (count > 63) { _bitBucket = 0uL; } else { _bitBucket >>= count; } if (_bitCount > 64) { int num = _bitCount - 64; _bitBucket |= (ulong)_overflowBits << _bitCount - count - num; if (num > count) { _overflowBits = (byte)(_overflowBits >> count); } } _bitCount -= count; _readBits += count; return; } if (_bitCount == count) { _bitBucket = 0uL; _bitCount = 0; _readBits += count; return; } count -= _bitCount; _readBits += _bitCount; _bitCount = 0; _bitBucket = 0uL; while (count > 8) { if (ReadNextByte() == -1) { count = 0; IsShort = true; break; } count -= 8; _readBits += 8; } if (count > 0) { int num2 = ReadNextByte(); if (num2 == -1) { IsShort = true; return; } _bitBucket = (ulong)(num2 >> count); _bitCount = 8 - count; _readBits += count; } } protected void ResetBitReader() { _bitBucket = 0uL; _bitCount = 0; _readBits = 0; IsShort = false; } public ulong ReadBits(int count) { if (count == 0) { return 0uL; } int bitsRead; ulong result = TryPeekBits(count, out bitsRead); SkipBits(count); return result; } public byte PeekByte() { int bitsRead; return (byte)TryPeekBits(8, out bitsRead); } public byte ReadByte() { return (byte)ReadBits(8); } public byte[] ReadBytes(int count) { List<byte> list = new List<byte>(count); while (list.Count < count) { list.Add(ReadByte()); } return list.ToArray(); } public int Read(byte[] buffer, int index, int count) { if (index < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("index"); } for (int i = 0; i < count; i++) { int bitsRead; byte b = (byte)TryPeekBits(8, out bitsRead); if (bitsRead == 0) { return i; } buffer[index++] = b; SkipBits(8); } return count; } public bool ReadBit() { return ReadBits(1) == 1; } public short ReadInt16() { return (short)ReadBits(16); } public int ReadInt32() { return (int)ReadBits(32); } public long ReadInt64() { return (long)ReadBits(64); } public ushort ReadUInt16() { return (ushort)ReadBits(16); } public uint ReadUInt32() { return (uint)ReadBits(32); } public ulong ReadUInt64() { return ReadBits(64); } public void SkipBytes(int count) { SkipBits(count * 8); } } internal interface IContainerReader : IDisposable { int[] StreamSerials { get; } bool CanSeek { get; } long WasteBits { get; } int PagesRead { get; } event EventHandler<NewStreamEventArgs> NewStream; bool Init(); bool FindNextStream(); int GetTotalPageCount(); } internal interface IPacketProvider : IDisposable { int StreamSerial { get; } bool CanSeek { get; } long ContainerBits { get; } event EventHandler<ParameterChangeEventArgs> ParameterChange; int GetTotalPageCount(); DataPacket GetNextPacket(); DataPacket PeekNextPacket(); DataPacket GetPacket(int packetIndex); long GetGranuleCount(); DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback); void SeekToPacket(DataPacket packet, int preRoll); } internal class NewStreamEventArgs : EventArgs { public IPacketProvider PacketProvider { get; private set; } public bool IgnoreStream { get; set; } public NewStreamEventArgs(IPacketProvider packetProvider) { if (packetProvider == null) { throw new ArgumentNullException("packetProvider"); } PacketProvider = packetProvider; } } internal class OggContainerReader : IContainerReader, IDisposable { private class PageHeader { public int StreamSerial { get; set; } public PageFlags Flags { get; set; } public long GranulePosition { get; set; } public int SequenceNumber { get; set; } public long DataOffset { get; set; } public int[] PacketSizes { get; set; } public bool LastPacketContinues { get; set; } public bool IsResync { get; set; } } private Crc _crc = new Crc(); private BufferedReadStream _stream; private Dictionary<int, PacketReader> _packetReaders; private List<int> _disposedStreamSerials; private long _nextPageOffset; private int _pageCount; private byte[] _readBuffer = new byte[65025]; private long _containerBits; private long _wasteBits; public int[] StreamSerials => _packetReaders.Keys.ToArray(); public int PagesRead => _pageCount; public bool CanSeek => _stream.CanSeek; public long WasteBits => _wasteBits; public event EventHandler<NewStreamEventArgs> NewStream; public OggContainerReader(Stream stream, bool closeOnDispose) { _packetReaders = new Dictionary<int, PacketReader>(); _disposedStreamSerials = new List<int>(); _stream = (stream as BufferedReadStream) ?? new BufferedReadStream(stream) { CloseBaseStream = closeOnDispose }; } public bool Init() { _stream.TakeLock(); try { return GatherNextPage() != -1; } finally { _stream.ReleaseLock(); } } public void Dispose() { int[] streamSerials = StreamSerials; foreach (int key in streamSerials) { _packetReaders[key].Dispose(); } _nextPageOffset = 0L; _containerBits = 0L; _wasteBits = 0L; _stream.Dispose(); } public IPacketProvider GetStream(int streamSerial) { if (!_packetReaders.TryGetValue(streamSerial, out var value)) { throw new ArgumentOutOfRangeException("streamSerial"); } return value; } public bool FindNextStream() { if (!CanSeek) { throw new InvalidOperationException(); } int count = _packetReaders.Count; while (_packetReaders.Count == count) { _stream.TakeLock(); try { if (GatherNextPage() == -1) { break; } } finally { _stream.ReleaseLock(); } } return count > _packetReaders.Count; } public int GetTotalPageCount() { if (!CanSeek) { throw new InvalidOperationException(); } while (true) { _stream.TakeLock(); try { if (GatherNextPage() == -1) { break; } } finally { _stream.ReleaseLock(); } } return _pageCount; } private PageHeader ReadPageHeader(long position) { _stream.Seek(position, SeekOrigin.Begin); if (_stream.Read(_readBuffer, 0, 27) != 27) { return null; } if (_readBuffer[0] != 79 || _readBuffer[1] != 103 || _readBuffer[2] != 103 || _readBuffer[3] != 83) { return null; } if (_readBuffer[4] != 0) { return null; } PageHeader pageHeader = new PageHeader(); pageHeader.Flags = (PageFlags)_readBuffer[5]; pageHeader.GranulePosition = BitConverter.ToInt64(_readBuffer, 6); pageHeader.StreamSerial = BitConverter.ToInt32(_readBuffer, 14); pageHeader.SequenceNumber = BitConverter.ToInt32(_readBuffer, 18); uint checkCrc = BitConverter.ToUInt32(_readBuffer, 22); _crc.Reset(); for (int i = 0; i < 22; i++) { _crc.Update(_readBuffer[i]); } _crc.Update(0); _crc.Update(0); _crc.Update(0); _crc.Update(0); _crc.Update(_readBuffer[26]); int num = _readBuffer[26]; if (_stream.Read(_readBuffer, 0, num) != num) { return null; } List<int> list = new List<int>(num); int num2 = 0; int num3 = 0; for (int j = 0; j < num; j++) { byte b = _readBuffer[j]; _crc.Update(b); if (num3 == list.Count) { list.Add(0); } list[num3] += b; if (b < byte.MaxValue) { num3++; pageHeader.LastPacketContinues = false; } else { pageHeader.LastPacketContinues = true; } num2 += b; } pageHeader.PacketSizes = list.ToArray(); pageHeader.DataOffset = position + 27 + num; if (_stream.Read(_readBuffer, 0, num2) != num2) { return null; } for (int k = 0; k < num2; k++) { _crc.Update(_readBuffer[k]); } if (_crc.Test(checkCrc)) { _containerBits += 8 * (27 + num); _pageCount++; return pageHeader; } return null; } private PageHeader FindNextPageHeader() { long num = _nextPageOffset; bool isResync = false; PageHeader pageHeader; while ((pageHeader = ReadPageHeader(num)) == null) { isResync = true; _wasteBits += 8L; num = (_stream.Position = num + 1); int num3 = 0; do { switch (_stream.ReadByte()) { case 79: if (_stream.ReadByte() == 103 && _stream.ReadByte() == 103 && _stream.ReadByte() == 83) { num += num3; goto end_IL_0032; } _stream.Seek(-3L, SeekOrigin.Current); break; case -1: return null; } _wasteBits += 8L; continue; end_IL_0032: break; } while (++num3 < 65536); if (num3 == 65536) { return null; } } pageHeader.IsResync = isResync; _nextPageOffset = pageHeader.DataOffset; for (int i = 0; i < pageHeader.PacketSizes.Length; i++) { _nextPageOffset += pageHeader.PacketSizes[i]; } return pageHeader; } private bool AddPage(PageHeader hdr) { if (!_packetReaders.TryGetValue(hdr.StreamSerial, out var value)) { value = new PacketReader(this, hdr.StreamSerial); } value.ContainerBits += _containerBits; _containerBits = 0L; bool isContinued = hdr.PacketSizes.Length == 1 && hdr.LastPacketContinues; bool isContinuation = (hdr.Flags & PageFlags.ContinuesPacket) == PageFlags.ContinuesPacket; bool isEndOfStream = false; bool isResync = hdr.IsResync; long num = hdr.DataOffset; int num2 = hdr.PacketSizes.Length; int[] packetSizes = hdr.PacketSizes; foreach (int num3 in packetSizes) { Packet packet = new Packet(this, num, num3) { PageGranulePosition = hdr.GranulePosition, IsEndOfStream = isEndOfStream, PageSequenceNumber = hdr.SequenceNumber, IsContinued = isContinued, IsContinuation = isContinuation, IsResync = isResync }; value.AddPacket(packet); num += num3; isContinuation = false; isResync = false; if (--num2 == 1) { isContinued = hdr.LastPacketContinues; isEndOfStream = (hdr.Flags & PageFlags.EndOfStream) == PageFlags.EndOfStream; } } if (!_packetReaders.ContainsKey(hdr.StreamSerial)) { _packetReaders.Add(hdr.StreamSerial, value); return true; } return false; } private int GatherNextPage() { PageHeader pageHeader; while (true) { pageHeader = FindNextPageHeader(); if (pageHeader == null) { return -1; } if (!_disposedStreamSerials.Contains(pageHeader.StreamSerial)) { if (!AddPage(pageHeader)) { break; } EventHandler<NewStreamEventArgs> newStream = this.NewStream; if (newStream == null) { break; } NewStreamEventArgs newStreamEventArgs = new NewStreamEventArgs(_packetReaders[pageHeader.StreamSerial]); newStream(this, newStreamEventArgs); if (!newStreamEventArgs.IgnoreStream) { break; } _packetReaders[pageHeader.StreamSerial].Dispose(); } } return pageHeader.StreamSerial; } internal void DisposePacketReader(PacketReader packetReader) { _disposedStreamSerials.Add(packetReader.StreamSerial); _packetReaders.Remove(packetReader.StreamSerial); } internal int PacketReadByte(long offset) { _stream.TakeLock(); try { _stream.Position = offset; return _stream.ReadByte(); } finally { _stream.ReleaseLock(); } } internal void PacketDiscardThrough(long offset) { _stream.TakeLock(); try { _stream.DiscardThrough(offset); } finally { _stream.ReleaseLock(); } } internal void GatherNextPage(int streamSerial) { if (!_packetReaders.ContainsKey(streamSerial)) { throw new ArgumentOutOfRangeException("streamSerial"); } int num; do { _stream.TakeLock(); try { if (_packetReaders[streamSerial].HasEndOfStream) { break; } num = GatherNextPage(); if (num != -1) { continue; } foreach (KeyValuePair<int, PacketReader> packetReader in _packetReaders) { if (!packetReader.Value.HasEndOfStream) { packetReader.Value.SetEndOfStream(); } } break; } finally { _stream.ReleaseLock(); } } while (num != streamSerial); } } internal class Crc { private const uint CRC32_POLY = 79764919u; private static uint[] crcTable; private uint _crc; public uint Value => _crc; static Crc() { crcTable = new uint[256]; for (uint num = 0u; num < 256; num++) { uint num2 = num << 24; for (int i = 0; i < 8; i++) { num2 = (num2 << 1) ^ ((num2 >= 2147483648u) ? 79764919u : 0u); } crcTable[num] = num2; } } public Crc() { Reset(); } public void Reset() { _crc = 0u; } public void Update(int nextVal) { _crc = (_crc << 8) ^ crcTable[nextVal ^ (_crc >> 24)]; } public bool Test(uint checkCrc) { return _crc == checkCrc; } } internal class Packet : DataPacket { private long _offset; private int _length; private int _curOfs; private Packet _mergedPacket; private Packet _next; private Packet _prev; private OggContainerReader _containerReader; internal Packet Next { get { return _next; } set { _next = value; } } internal Packet Prev { get { return _prev; } set { _prev = value; } } internal bool IsContinued { get { return GetFlag(PacketFlags.User1); } set { SetFlag(PacketFlags.User1, value); } } internal bool IsContinuation { get { return GetFlag(PacketFlags.User2); } set { SetFlag(PacketFlags.User2, value); } } internal Packet(OggContainerReader containerReader, long streamOffset, int length) : base(length) { _containerReader = containerReader; _offset = streamOffset; _length = length; _curOfs = 0; } internal void MergeWith(DataPacket continuation) { if (!(continuation is Packet mergedPacket)) { throw new ArgumentException("Incorrect packet type!"); } base.Length += continuation.Length; if (_mergedPacket == null) { _mergedPacket = mergedPacket; } else { _mergedPacket.MergeWith(continuation); } base.PageGranulePosition = continuation.PageGranulePosition; base.PageSequenceNumber = continuation.PageSequenceNumber; } internal void Reset() { _curOfs = 0; ResetBitReader(); if (_mergedPacket != null) { _mergedPacket.Reset(); } } protected override int ReadNextByte() { if (_curOfs == _length) { if (_mergedPacket == null) { return -1; } return _mergedPacket.ReadNextByte(); } int num = _containerReader.PacketReadByte(_offset + _curOfs); if (num != -1) { _curOfs++; } return num; } public override void Done() { if (_mergedPacket != null) { _mergedPacket.Done(); } else { _containerReader.PacketDiscardThrough(_offset + _length); } } } [DebuggerTypeProxy(typeof(DebugView))] internal class PacketReader : IPacketProvider, IDisposable { internal class DebugView { private PacketReader _reader; private Packet _last; private Packet _first; private Packet[] _packetList = new Packet[0]; public OggContainerReader Container => _reader._container; public int StreamSerial => _reader._streamSerial; public bool EndOfStreamFound => _reader._eosFound; public int CurrentPacketIndex { get { if (_reader._current == null) { return -1; } return Array.IndexOf(Packets, _reader._current); } } public Packet[] Packets { get { if (_reader._last == _last && _reader._first == _first) { return _packetList; } _last = _reader._last; _first = _reader._first; List<Packet> list = new List<Packet>(); for (Packet packet = _first; packet != null; packet = packet.Next) { list.Add(packet); } _packetList = list.ToArray(); return _packetList; } } public DebugView(PacketReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } _reader = reader; } } private OggContainerReader _container; private int _streamSerial; private bool _eosFound; private Packet _first; private Packet _current; private Packet _last; private object _packetLock = new object(); internal bool HasEndOfStream => _eosFound; public int StreamSerial => _streamSerial; public long ContainerBits { get; set; } public bool CanSeek => _container.CanSeek; public event EventHandler<ParameterChangeEventArgs> ParameterChange; internal PacketReader(OggContainerReader container, int streamSerial) { _container = container; _streamSerial = streamSerial; } public void Dispose() { _eosFound = true; _container.DisposePacketReader(this); _container = null; _current = null; if (_first != null) { Packet packet = _first; _first = null; while (packet.Next != null) { Packet next = packet.Next; packet.Next = null; packet = next; packet.Prev = null; } packet = null; } _last = null; } internal void AddPacket(Packet packet) { lock (_packetLock) { if (_eosFound) { return; } if (packet.IsResync) { packet.IsContinuation = false; if (_last != null) { _last.IsContinued = false; } } if (packet.IsContinuation) { if (_last == null) { throw new InvalidDataException(); } if (!_last.IsContinued) { throw new InvalidDataException(); } _last.MergeWith(packet); _last.IsContinued = packet.IsContinued; } else { if (packet == null) { throw new ArgumentException("Wrong packet datatype", "packet"); } if (_first == null) { _first = packet; _last = packet; } else { Packet packet2 = (packet.Prev = _last); Packet last2 = (packet2.Next = packet); _last = last2; } } if (packet.IsEndOfStream) { SetEndOfStream(); } } } internal void SetEndOfStream() { lock (_packetLock) { _eosFound = true; if (_last.IsContinued) { _last = _last.Prev; _last.Next.Prev = null; _last.Next = null; } } } public DataPacket GetNextPacket() { return _current = PeekNextPacketInternal(); } public DataPacket PeekNextPacket() { return PeekNextPacketInternal(); } private Packet PeekNextPacketInternal() { Packet packet; if (_current == null) { packet = _first; } else { while (true) { lock (_packetLock) { packet = _current.Next; if ((packet != null && !packet.IsContinued) || _eosFound) { break; } goto IL_004f; } IL_004f: _container.GatherNextPage(_streamSerial); } } if (packet != null) { if (packet.IsContinued) { throw new InvalidDataException("Packet is incomplete!"); } packet.Reset(); } return packet; } internal void ReadAllPages() { if (!CanSeek) { throw new InvalidOperationException(); } while (!_eosFound) { _container.GatherNextPage(_streamSerial); } } internal DataPacket GetLastPacket() { ReadAllPages(); return _last; } public int GetTotalPageCount() { ReadAllPages(); int num = 0; int num2 = 0; for (Packet packet = _first; packet != null; packet = packet.Next) { if (packet.PageSequenceNumber != num2) { num++; num2 = packet.PageSequenceNumber; } } return num; } public DataPacket GetPacket(int packetIndex) { if (!CanSeek) { throw new InvalidOperationException(); } if (packetIndex < 0) { throw new ArgumentOutOfRangeException("index"); } if (_first == null) { throw new InvalidOperationException("Packet reader has no packets!"); } Packet packet = _first; while (--packetIndex >= 0) { while (packet.Next == null) { if (_eosFound) { throw new ArgumentOutOfRangeException("index"); } _container.GatherNextPage(_streamSerial); } packet = packet.Next; } packet.Reset(); return packet; } private Packet GetLastPacketInPage(Packet packet) { if (packet != null) { int pageSequenceNumber = packet.PageSequenceNumber; while (packet.Next != null && packet.Next.PageSequenceNumber == pageSequenceNumber) { packet = packet.Next; } if (packet != null && packet.IsContinued) { packet = packet.Prev; } } return packet; } private Packet FindPacketInPage(Packet pagePacket, long targetGranulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback) { Packet lastPacketInPage = GetLastPacketInPage(pagePacket); if (lastPacketInPage == null) { return null; } Packet packet = lastPacketInPage; do { if (!packet.GranuleCount.HasValue) { if (packet == lastPacketInPage) { packet.GranulePosition = packet.PageGranulePosition; } else { packet.GranulePosition = packet.Next.GranulePosition - packet.Next.GranuleCount.Value; } if (packet == _last && _eosFound && packet.Prev.PageSequenceNumber < packet.PageSequenceNumber) { packet.GranuleCount = (int)(packet.GranulePosition - packet.Prev.PageGranulePosition); } else if (packet.Prev != null) { packet.Prev.Reset(); packet.Reset(); packet.GranuleCount = packetGranuleCountCallback(packet, packet.Prev); } else { if (packet.GranulePosition > packet.Next.GranulePosition - packet.Next.GranuleCount) { throw new InvalidOperationException("First data packet size mismatch"); } packet.GranuleCount = (int)packet.GranulePosition; } } if (targetGranulePos <= packet.GranulePosition && targetGranulePos > packet.GranulePosition - packet.GranuleCount) { if (packet.Prev != null && !packet.Prev.GranuleCount.HasValue) { packet.Prev.GranulePosition = packet.GranulePosition - packet.GranuleCount.Value; } return packet; } packet = packet.Prev; } while (packet != null && packet.PageSequenceNumber == lastPacketInPage.PageSequenceNumber); if (packet != null && packet.PageGranulePosition < targetGranulePos) { packet.GranulePosition = packet.PageGranulePosition; return packet.Next; } return null; } public DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback) { if (granulePos < 0) { throw new ArgumentOutOfRangeException("granulePos"); } Packet packet = null; Packet packet2 = _current ?? _first; if (granulePos > packet2.PageGranulePosition) { while (granulePos > packet2.PageGranulePosition) { if ((packet2.Next == null || packet2.IsContinued) && !_eosFound) { _container.GatherNextPage(_streamSerial); if (_eosFound) { packet2 = null; break; } } packet2 = packet2.Next; } return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback); } while (packet2.Prev != null && (granulePos <= packet2.Prev.PageGranulePosition || packet2.Prev.PageGranulePosition == -1)) { packet2 = packet2.Prev; } return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback); } public void SeekToPacket(DataPacket packet, int preRoll) { if (preRoll < 0) { throw new ArgumentOutOfRangeException("preRoll"); } if (packet == null) { throw new ArgumentNullException("granulePos"); } Packet packet2 = packet as Packet; if (packet2 == null) { throw new ArgumentException("Incorrect packet type!", "packet"); } while (--preRoll >= 0) { packet2 = packet2.Prev; if (packet2 == null) { throw new ArgumentOutOfRangeException("preRoll"); } } _current = packet2.Prev; } public long GetGranuleCount() { return GetLastPacket().PageGranulePosition; } } [Flags] internal enum PageFlags { None = 0, ContinuesPacket = 1, BeginningOfStream = 2, EndOfStream = 4 } internal class OpusHeader { private byte version; private byte channel_count; private ushort pre_skip; private uint input_sample_rate; private short output_gain; private byte mapping_family; private byte stream_count; private byte coupled_count; } public class OpusOggReadStream { private const double GranuleSampleRate = 48000.0; private readonly Stream _stream; private readonly IOpusDecoder _decoder; private byte[] _nextDataPacket; private IPacketProvider _packetProvider; private bool _endOfStream; private OggContainerReader _containerReader; public bool CanSeek => _stream.CanSeek; public OpusTags Tags { get; private set; } public bool HasNextPacket => !_endOfStream; public string LastError { get; private set; } public long PageGranulePosition { get; private set; } public TimeSpan CurrentTime => TimeSpan.FromSeconds((double)PageGranulePosition / 48000.0); public long GranuleCount { get; private set; } public TimeSpan TotalTime => TimeSpan.FromSeconds((double)GranuleCount / 48000.0); public long PagePosition { get; private set; } public long PageCount { get; private set; } public OpusOggReadStream(IOpusDecoder decoder, Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } _stream = stream; _decoder = decoder; _endOfStream = !Initialize(); } public short[] DecodeNextPacket() { //IL_0091: Expected O, but got Unknown if (_decoder == null) { throw new InvalidOperationException("Cannot decode opus packets as a decoder was never provided"); } if (_nextDataPacket == null || _nextDataPacket.Length == 0) { _endOfStream = true; return null; } try { int numSamples = OpusPacketInfo.GetNumSamples((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), _decoder.SampleRate); short[] array = new short[numSamples * _decoder.NumChannels]; _decoder.Decode((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), array.AsSpan(), numSamples, false); QueueNextPacket(); return array; } catch (OpusException val) { OpusException val2 = val; LastError = "Opus decoder threw exception: " + ((Exception)(object)val2).Message; return null; } } public byte[] ReadNextRawPacket() { if (_nextDataPacket == null || _nextDataPacket.Length == 0) { _endOfStream = true; return null; } byte[] nextDataPacket = _nextDataPacket; QueueNextPacket(); return nextDataPacket; } private bool Initialize() { try { OggContainerReader oggContainerReader = new OggContainerReader(_stream, closeOnDispose: true); if (!oggContainerReader.Init()) { LastError = "Could not initialize stream"; oggContainerReader.Dispose(); return false; } if (oggContainerReader.StreamSerials.Length == 0) { LastError = "Initialization failed: No elementary streams found in input file"; oggContainerReader.Dispose(); return false; } _containerReader = oggContainerReader; int streamSerial = oggContainerReader.StreamSerials[0]; _packetProvider = oggContainerReader.GetStream(streamSerial); if (CanSeek) { GranuleCount = _packetProvider.GetGranuleCount(); PageCount = _packetProvider.GetTotalPageCount(); } QueueNextPacket(); return true; } catch (Exception ex) { LastError = "Unknown initialization error: " + ex.Message; return false; } } public void SeekTo(TimeSpan playbackTime) { if (!CanSeek) { throw new InvalidOperationException("Stream is not seekable."); } if (playbackTime < TimeSpan.Zero || playbackTime > TotalTime) { throw new ArgumentOutOfRangeException("playbackTime"); } long granulePosition = Convert.ToInt64(playbackTime.TotalSeconds * 48000.0); SeekToGranulePosition(granulePosition); } private void SeekToGranulePosition(long granulePosition) { if (!CanSeek) { throw new InvalidOperationException("Stream is not seekable."); } if (granulePosition < 0 || granulePosition > GranuleCount) { throw new ArgumentOutOfRangeException("granulePosition"); } DataPacket dataPacket = _packetProvider.FindPacket(granulePosition, GetPacketLength); if (dataPacket == null || dataPacket.IsEndOfStream) { _endOfStream = true; _nextDataPacket = null; return; } _packetProvider.SeekToPacket(dataPacket, 1); PageGranulePosition = _packetProvider.PeekNextPacket().PageGranulePosition; if (_decoder != null) { _decoder.ResetState(); } } private int GetPacketLength(DataPacket curPacket, DataPacket lastPacket) { if (lastPacket == null || curPacket.IsResync) { return 0; } if (curPacket.ReadBit()) { return 0; } if (lastPacket.ReadBit()) { return 0; } return 1; } private void QueueNextPacket() { if (_endOfStream) { return; } DataPacket nextPacket = _packetProvider.GetNextPacket(); if (nextPacket == null) { _endOfStream = true; _nextDataPacket = null; return; } PageGranulePosition = nextPacket.PageGranulePosition; PagePosition = nextPacket.PageSequenceNumber; byte[] array = new byte[nextPacket.Length]; nextPacket.Read(array, 0, nextPacket.Length); nextPacket.Done(); if (array.Length > 8 && "OpusHead".Equals(Encoding.UTF8.GetString(array, 0, 8))) { QueueNextPacket(); } else if (array.Length > 8 && "OpusTags".Equals(Encoding.UTF8.GetString(array, 0, 8))) { Tags = OpusTags.ParsePacket(array, array.Length); QueueNextPacket(); } else { _nextDataPacket = array; } } public void Close() { _containerReader?.Dispose(); } } public class OpusOggWriteStream { private const int FRAME_SIZE_MS = 20; private const byte EARLY_FINALIZE_SEGMENT_LIMIT = 248; private IOpusEncoder _encoder; private Stream _outputStream; private Crc _crc; private int _inputChannels; private IResampler _resampler; private int _inputSampleRate; private readonly bool _leaveOpen; private int _encoderSampleRate; private short[] _opusFrame; private int _opusFrameSamples; private int _opusFrameIndex; private byte[] _currentHeader = new byte[400]; private byte[] _currentPayload = new byte[65536]; private int _headerIndex; private int _payloadIndex; private int _pageCounter; private int _logicalStreamId; private long _granulePosition; private byte _packetsInPage; private byte _lacingTableCount; private byte _maxPacketsPerPage = 248; private const int PAGE_FLAGS_POS = 5; private const int GRANULE_COUNT_POS = 6; private const int CHECKSUM_HEADER_POS = 22; private const int SEGMENT_COUNT_POS = 26; private bool _finalized; public TimeSpan MaxAudioLengthPerPage { get { return TimeSpan.FromMilliseconds(20 * _maxPacketsPerPage); } set { if (value <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException("MaxAudioLengthPerPage"); } _maxPacketsPerPage = (byte)Math.Min(248, Math.Max(1, (int)(value.TotalMilliseconds / 20.0))); } } public OpusOggWriteStream(IOpusEncoder encoder, Stream outputStream, OpusTags fileTags = null, int inputSampleRate = 0, int resamplerQuality = 5, bool leaveOpen = false) { _encoder = encoder; if (_encoder.UseDTX) { throw new ArgumentException("DTX is not currently supported in Ogg streams"); } _inputSampleRate = inputSampleRate; _leaveOpen = leaveOpen; if (_inputSampleRate == 0) { _inputSampleRate = _encoder.SampleRate; } _logicalStreamId = new Random().Next(); _encoderSampleRate = encoder.SampleRate; _inputChannels = encoder.NumChannels; _outputStream = outputStream; _opusFrameIndex = 0; _granulePosition = 0L; _opusFrameSamples = (int)((long)_encoderSampleRate * 20L / 1000); _opusFrame = new short[_opusFrameSamples * _inputChannels]; _crc = new Crc(); _resampler = ResamplerFactory.CreateResampler(_inputChannels, _inputSampleRate, _encoderSampleRate, resamplerQuality, (TextWriter)null); BeginNewPage(); WriteOpusHeadPage(); WriteOpusTagsPage(fileTags); } public void WriteSamples(short[] data, int offset, int count) { if (_finalized) { throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!"); } if (data.Length - offset < count) { throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + count + " samples, but it actually only has " + (data.Length - offset)); } int num = 0; for (int num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num); num2 > 0; num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num)) { if (_inputSampleRate != _encoderSampleRate) { int num3 = (count - num) / _inputChannels; int num4 = num2 / _inputChannels; _resampler.ProcessInterleaved(data.AsSpan(offset + num), ref num3, _opusFrame.AsSpan(_opusFrameIndex), ref num4); num += num3 * _inputChannels; _opusFrameIndex += num4 * _inputChannels; } else { data.AsSpan(offset + num, num2).CopyTo(_opusFrame.AsSpan(_opusFrameIndex, num2)); _opusFrameIndex += num2; num += num2; } if (_opusFrameIndex == _opusFrame.Length) { int num5 = _encoder.Encode((ReadOnlySpan<short>)_opusFrame.AsSpan(), _opusFrameSamples, _currentPayload.AsSpan(_payloadIndex), _currentPayload.Length - _payloadIndex); _payloadIndex += num5; _granulePosition += 960L; int num6 = num5; while (num6 >= 255) { num6 -= 255; _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num6; _lacingTableCount++; _packetsInPage++; if (_packetsInPage >= _maxPacketsPerPage || _lacingTableCount > 248) { FinalizePage(); } _opusFrameIndex = 0; } } } public void WriteSamples(float[] data, int offset, int count) { if (_finalized) { throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!"); } short[] array = new short[count]; for (int i = 0; i < count; i++) { array[i] = (short)(data[i + offset] * 32767f); } WriteSamples(array, 0, count); } public void Finish() { int num = _opusFrame.Length - _opusFrameIndex; short[] data = new short[num]; WriteSamples(data, 0, num); FinalizePage(); WriteStreamFinishedPage(); _outputStream.Flush(); if (!_leaveOpen) { _outputStream.Dispose(); } _finalized = true; } private void WriteStreamFinishedPage() { _currentHeader[_headerIndex++] = 0; _lacingTableCount++; _currentHeader[5] = 4; FinalizePage(); } private void WriteOpusHeadPage() { if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusHead on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 1; _currentPayload[_payloadIndex++] = (byte)_inputChannels; short val = 0; _payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex); _payloadIndex += WriteValueToByteBuffer(_encoderSampleRate, _currentPayload, _payloadIndex); short val2 = 0; _payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 0; _currentHeader[_headerIndex++] = (byte)_payloadIndex; _lacingTableCount++; _currentHeader[5] = 2; FinalizePage(); } private void WriteOpusTagsPage(OpusTags tags = null) { if (tags == null) { tags = new OpusTags(); } if (string.IsNullOrEmpty(tags.Comment)) { tags.Comment = _encoder.GetVersionString(); } if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusTags on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex); int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; int payloadIndex = _payloadIndex; _payloadIndex += 4; int num2 = 0; foreach (KeyValuePair<string, string> field in tags.Fields) { if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value)) { num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; num2++; } } WriteValueToByteBuffer(num2, _currentPayload, payloadIndex); int num3; for (num3 = _payloadIndex; num3 >= 255; num3 -= 255) { _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num3; _lacingTableCount++; FinalizePage(); } private void BeginNewPage() { _headerIndex = 0; _payloadIndex = 0; _packetsInPage = 0; _lacingTableCount = 0; _headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = _lacingTableCount; _pageCounter++; } private void FinalizePage() { if (_finalized) { throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!"); } if (_lacingTableCount != 0) { _currentHeader[26] = _lacingTableCount; WriteValueToByteBuffer(_granulePosition, _currentHeader, 6); _crc.Reset(); for (int i = 0; i < _headerIndex; i++) { _crc.Update(_currentHeader[i]); } for (int j = 0; j < _payloadIndex; j++) { _crc.Update(_currentPayload[j]); } WriteValueToByteBuffer(_crc.Value, _currentHeader, 22); _outputStream.Write(_currentHeader, 0, _headerIndex); _outputStream.Write(_currentPayload, 0, _payloadIndex); BeginNewPage(); } } private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset) { BinaryHelpers.Int32ToByteArrayLittleEndian(val, target, targetOffset); return 4; } private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset) { BinaryHelpers.Int64ToByteArrayLittleEndian(val, target, targetOffset); return 8; } private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset) { BinaryHelpers.UInt32ToByteArrayLittleEndian(val, target, targetOffset); return 4; } private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset) { BinaryHelpers.Int16ToByteArrayLittleEndian(val, target, targetOffset); return 2; } private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset) { if (string.IsNullOrEmpty(val)) { return 0; } return Encoding.UTF8.GetBytes(val, 0, val.Length, target, targetOffset); } } public class OpusRawWriteStream { private Stream _outputStream; private Crc _crc; private int _inputChannels; private readonly bool _leaveOpen; private int _inputSampleRate; private byte[] _currentHeader = new byte[400]; private byte[] _currentPayload = new byte[65536]; private int _headerIndex; private int _payloadIndex; private int _pageCounter; private int _logicalStreamId; private long _granulePosition; private byte _lacingTableCount; private const int PAGE_FLAGS_POS = 5; private const int GRANULE_COUNT_POS = 6; private const int CHECKSUM_HEADER_POS = 22; private const int SEGMENT_COUNT_POS = 26; private bool _finalized; public OpusRawWriteStream(Stream outputStream, OpusTags fileTags, int inputSampleRate, int inputNumChannels, bool leaveOpen = false) { _inputSampleRate = inputSampleRate; _logicalStreamId = new Random().Next(); _inputChannels = inputNumChannels; _leaveOpen = leaveOpen; _outputStream = outputStream; _granulePosition = 0L; _crc = new Crc(); BeginNewPage(); WriteOpusHeadPage(); WriteOpusTagsPage(fileTags); } public void WriteSinglePacket(byte[] opusPacket, int offset, int packetSize) { if (_finalized) { throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!"); } if (opusPacket.Length - offset < packetSize) { throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + packetSize + " samples, but it actually only has " + (opusPacket.Length - offset)); } Array.Copy(opusPacket, offset, _currentPayload, _payloadIndex, packetSize); _payloadIndex += packetSize; int numSamplesPerFrame = OpusPacketInfo.GetNumSamplesPerFrame((ReadOnlySpan<byte>)opusPacket.AsSpan(offset), 48000); _granulePosition += numSamplesPerFrame; int num = packetSize; while (num >= 255) { num -= 255; _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num; _lacingTableCount++; if (_lacingTableCount > 248) { FinalizePage(); } } public void Finish() { FinalizePage(); WriteStreamFinishedPage(); _outputStream.Flush(); if (!_leaveOpen) { _outputStream.Dispose(); } _finalized = true; } private void WriteStreamFinishedPage() { _currentHeader[_headerIndex++] = 0; _lacingTableCount++; _currentHeader[5] = 4; FinalizePage(); } private void WriteOpusHeadPage() { if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusHead on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 1; _currentPayload[_payloadIndex++] = (byte)_inputChannels; short val = 0; _payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex); _payloadIndex += WriteValueToByteBuffer(_inputSampleRate, _currentPayload, _payloadIndex); short val2 = 0; _payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex); _currentPayload[_payloadIndex++] = 0; _currentHeader[_headerIndex++] = (byte)_payloadIndex; _lacingTableCount++; _currentHeader[5] = 2; FinalizePage(); } private void WriteOpusTagsPage(OpusTags tags = null) { if (tags == null) { tags = new OpusTags(); } if (string.IsNullOrEmpty(tags.Comment)) { tags.Comment = "Concentus.OggFile"; } if (_payloadIndex != 0) { throw new InvalidOperationException("Must begin writing OpusTags on a new page!"); } _payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex); int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; int payloadIndex = _payloadIndex; _payloadIndex += 4; int num2 = 0; foreach (KeyValuePair<string, string> field in tags.Fields) { if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value)) { num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4); _payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex); _payloadIndex += num; num2++; } } WriteValueToByteBuffer(num2, _currentPayload, payloadIndex); int num3; for (num3 = _payloadIndex; num3 >= 255; num3 -= 255) { _currentHeader[_headerIndex++] = byte.MaxValue; _lacingTableCount++; } _currentHeader[_headerIndex++] = (byte)num3; _lacingTableCount++; FinalizePage(); } private void BeginNewPage() { _headerIndex = 0; _payloadIndex = 0; _lacingTableCount = 0; _headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex); _headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex); _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = 0; _currentHeader[_headerIndex++] = _lacingTableCount; _pageCounter++; } private void FinalizePage() { if (_finalized) { throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!"); } if (_lacingTableCount != 0) { _currentHeader[26] = _lacingTableCount; WriteValueToByteBuffer(_granulePosition, _currentHeader, 6); _crc.Reset(); for (int i = 0; i < _headerIndex; i++) { _crc.Update(_currentHeader[i]); } for (int j = 0; j < _payloadIndex; j++) { _crc.Update(_currentPayload[j]); } WriteValueToByteBuffer(_crc.Value, _currentHeader, 22); _outputStream.Write(_currentHeader, 0, _headerIndex); _outputStream.Write(_currentPayload, 0, _payloadIndex); BeginNewPage(); } } private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4); return 4; } private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 8); return 8; } private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4); return 4; } private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset) { Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 2); return 2; } private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset) { if (string.IsNullOrEmpty(val)) { return 0; } byte[] bytes = Encoding.UTF8.GetBytes(val); Array.Copy(bytes, 0, target, targetOffset, bytes.Length); return bytes.Length; } } public class OpusTagName { public const string Title = "title"; public const string Artist = "artist"; public const string Album = "album"; } public class OpusTags { private string _comment = string.Empty; private IDictionary<string, string> _fields = new Dictionary<string, string>(); public string Comment { get { return _comment; } set { _comment = value; } } public IDictionary<string, string> Fields => _fields; internal static OpusTags ParsePacket(byte[] packet, int packetLength) { if (packetLength < 8) { return null; } if (!"OpusTags".Equals(Encoding.UTF8.GetString(packet, 0, 8))) { return null; } OpusTags opusTags = new OpusTags(); int num = 8; int num2 = BitConverter.ToInt32(packet, num); num += 4; if (num2 > 0) { opusTags._comment = Encoding.UTF8.GetString(packet, num, num2); num += num2; } int num3 = BitConverter.ToInt32(packet, num); num += 4; for (int i = 0; i < num3; i++) { num2 = BitConverter.ToInt32(packet, num); num += 4; if (num2 > 0) { string @string = Encoding.UTF8.GetString(packet, num, num2); num += num2; int num4 = @string.IndexOf('='); if (num4 > 0) { string key = @string.Substring(0, num4); string value = @string.Substring(num4 + 1); opusTags._fields[key] = value; } } } return opusTags; } } internal class ParameterChangeEventArgs : EventArgs { public DataPacket FirstPacket { get; private set; } public ParameterChangeEventArgs(DataPacket firstPacket) { FirstPacket = firstPacket; } } internal class StreamReadBuffer : IDisposable { internal class StreamWrapper { internal Stream Source; internal object LockObject = new object(); internal long EofOffset = long.MaxValue; internal int RefCount = 1; } private class SavedBuffer { public byte[] Buffer; public long BaseOffset; public int End; public int DiscardCount; public long VersionSaved; } private static Dictionary<Stream, StreamWrapper> _lockObjects = new Dictionary<Stream, StreamWrapper>(); private StreamWrapper _wrapper; private int _maxSize; private byte[] _data; private long _baseOffset; private int _end; private int _discardCount; private bool _minimalRead; private long _versionCounter; private List<SavedBuffer> _savedBuffers; public bool MinimalRead { get { return _minimalRead; } set { _minimalRead = value; } } public int MaxSize { get { return _maxSize; } set { if (value < 1) { throw new ArgumentOutOfRangeException("Must be greater than zero."); } int num = 1 << (int)Math.Ceiling(Math.Log(value, 2.0)); if (num < _end) { if (num < _end - _discardCount) { throw new ArgumentOutOfRangeException("Must be greater than or equal to the number of bytes currently buffered."); } CommitDiscard(); byte[] array = new byte[num]; Buffer.BlockCopy(_data, 0, array, 0, _end); _data = array; } _maxSize = num; } } public long BaseOffset => _baseOffset + _discardCount; public int BytesFilled => _end - _discardCount; public int Length => _data.Length; internal long BufferEndOffset { get { if (_end - _discardCount > 0) { return _baseOffset + _discardCount + _maxSize; } if (_wrapper.Source.CanSeek) { return _wrapper.Source.Length; } return _baseOffset + Length; } } internal StreamReadBuffer(Stream source, int initialSize, int maxSize, bool minimalRead) { StreamWrapper value; lock (_lockObjects) { if (!_lockObjects.TryGetValue(source, out value)) { _lockObjects.Add(source, new StreamWrapper { Source = source }); value = _lockObjects[source]; if (source.CanSeek) { value.EofOffset = source.Length; } } else { value.RefCount++; } } initialSize = 2 << (int)Math.Log(initialSize - 1, 2.0); maxSize = 1 << (int)Math.Log(maxSize, 2.0); _wrapper = value; _data = new byte[initialSize]; _maxSize = maxSize; _minimalRead = minimalRead; _savedBuffers = new List<SavedBuffer>(); } public void Dispose() { lock (_lockObjects) { if (--_wrapper.RefCount == 0) { _lockObjects.Remove(_wrapper.Source); } } } public int Read(long offset, byte[] buffer, int index, int count) { if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (index < 0 || index + count > buffer.Length) { throw new ArgumentOutOfRangeException("index"); } if (count < 0) { throw new ArgumentOutOfRangeException("count"); } if (offset >= _wrapper.EofOffset) { return 0; } int srcOffset = EnsureAvailable(offset, ref count, isRecursion: false); Buffer.BlockCopy(_data, srcOffset, buffer, index, count); return count; } internal int ReadByte(long offset) { if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (offset >= _wrapper.EofOffset) { return -1; } int count = 1; int num = EnsureAvailable(offset, ref count, isRecursion: false); if (count == 1) { return _data[num]; } return -1; } private int EnsureAvailable(long offset, ref int count, bool isRecursion) { if (offset >= _baseOffset && offset + count < _baseOffset + _end) { return (int)(offset - _baseOffset); } if (count > _maxSize) { throw new InvalidOperationException("Not enough room in the buffer! Increase the maximum size and try again."); } _versionCounter++; if (!isRecursion) { for (int i = 0; i < _savedBuffers.Count; i++) { long num = _savedBuffers[i].BaseOffset - offset; if ((num < 0 && _savedBuffers[i].End + num > 0) || (num > 0 && count - num > 0)) { SwapBuffers(_savedBuffers[i]); return EnsureAvailable(offset, ref count, isRecursion: true); } } } while (_savedBuffers.Count > 0 && _savedBuffers[0].VersionSaved + 25 < _versionCounter) { _savedBuffers[0].Buffer = null; _savedBuffers.RemoveAt(0); } if (offset < _baseOffset && !_wrapper.Source.CanSeek) { throw new InvalidOperationException("Cannot seek before buffer on forward-only streams!"); } CalcBuffer(offset, count, out var readStart, out var readEnd); count = FillBuffer(offset, count, readStart, readEnd); return (int)(offset - _baseOffset); } private void SaveBuffer() { _savedBuffers.Add(new SavedBuffer { Buffer = _data, BaseOffset = _baseOffset, End = _end, DiscardCount = _discardCount, VersionSaved = _versionCounter }); _data = null; _end = 0; _discardCount = 0; } private void CreateNewBuffer(long offset, int count) { SaveBuffer(); _data = new byte[Math.Min(2 << (int)Math.Log(count - 1, 2.0), _maxSize)]; _baseOffset = offset; } private void SwapBuffers(SavedBuffer savedBuffer) { _savedBuffers.Remove(savedBuffer); SaveBuffer(); _data = savedBuffer.Buffer; _baseOffset = savedBuffer.BaseOffset; _end = savedBuffer.End; _discardCount = savedBuffer.DiscardCount; } private void CalcBuffer(long offset, int count, out int readStart, out int readEnd) { readStart = 0; readEnd = 0; if (offset < _baseOffset) { if (offset + _maxSize <= _baseOffset) { if (_baseOffset - (offset + _maxSize) > _maxSize) { CreateNewBuffer(offset, count); } else { EnsureBufferSize(count, copyContents: false, 0); } _baseOffset = offset; readEnd = count; } else { readEnd = (int)(offset - _baseOffset); EnsureBufferSize(Math.Min((int)(offset + _maxSize - _baseOffset), _end) - readEnd, copyContents: true, readEnd); readEnd = (int)(offset - _baseOffset) - readEnd; } } else if (offset >= _baseOffset + _maxSize) { if (offset - (_baseOffset + _maxSize) > _maxSize) { CreateNewBuffer(offset, count); } else { EnsureBufferSize(count, copyContents: false, 0); } _baseOffset = offset; readEnd = count; } else { readEnd = (int)(offset + count - _baseOffset); int num = Math.Max(readEnd - _maxSize, 0); EnsureBufferSize(readEnd - num, copyContents: true, num); readStart = _end; readEnd = (int)(offset + count - _baseOffset); } } private void EnsureBufferSize(int reqSize, bool copyContents, int copyOffset) { byte[] array = _data; if (reqSize > _data.Length) { if (reqSize > _maxSize) { if (!_wrapper.Source.CanSeek && reqSize - _discardCount > _maxSize) { throw new InvalidOperationException("Not enough room in the buffer! Increase the maximum size and try again."); } int num = reqSize - _maxSize; copyOffset += num; reqSize = _maxSize; } else { int num2; for (num2 = _data.Length; num2 < reqSize; num2 *= 2) { } reqSize = num2; } if (reqSize > _data.Length) { array = new byte[reqSize]; } } if (copyContents) { if ((copyOffset > 0 && copyOffset < _end) || (copyOffset == 0 && array != _data)) { Buffer.BlockCopy(_data, copyOffset, array, 0, _end - copyOffset); if ((_discardCount -= copyOffset) < 0) { _discardCount = 0; } } else if (copyOffset < 0 && -copyOffset < _end) { if (array != _data || _end <= -copyOffset) { Buffer.BlockCopy(_data, 0, array, -copyOffset, Math.Max(_end, Math.Min(_end, _data.Length + copyOffset))); } else { _end = copyOffset; } _discardCount = 0; } else { _end = copyOffset; _discardCount = 0; } _baseOffset += copyOffset; _end -= copyOffset; if (_end > array.Length) { _end = array.Length; } } else { _discardCount = 0; _end = 0; } _data = array; } private int FillBuffer(long offset, int count, int readStart, int readEnd) { long readOffset = _baseOffset + readStart; int readCount = readEnd - readStart; lock (_wrapper.LockObject) { readCount = PrepareStreamForRead(readCount, readOffset); ReadStream(readStart, readCount, readOffset); if (_end < readStart + readCount) { count = Math.Max(0, (int)(_baseOffset + _end - offset)); } else if (!_minimalRead && _end < _data.Length) { readCount = _data.Length - _end; readCount = PrepareStreamForRead(readCount, _baseOffset + _end); _end += _wrapper.Source.Read(_data, _end, readCount); } } return count; } private int PrepareStreamForRead(int readCount, long readOffset) { if (readCount > 0 && _wrapper.Source.Position != readOffset) { if (readOffset < _wrapper.EofOffset) { if (_wrapper.Source.CanSeek) { _wrapper.Source.Position = readOffset; } else { long num = readOffset - _wrapper.Source.Position; if (num < 0) { readCount = 0; } else { while (--num >= 0) { if (_wrapper.Source.ReadByte() == -1) { _wrapper.EofOffset = _wrapper.Source.Position; readCount = 0; break; } } } } } else { readCount = 0; } } return readCount; } private void ReadStream(int readStart, int readCount, long readOffset) { while (readCount > 0 && readOffset < _wrapper.EofOffset) { int num = _wrapper.Source.Read(_data, readStart, readCount); if (num == 0) { break; } readStart += num; readOffset += num; readCount -= num; } if (readStart > _end) { _end = readStart; } } public void DiscardThrough(long offset) { int val = (int)(offset - _baseOffset); _discardCount = Math.Max(val, _discardCount); if (_discardCount >= _data.Length) { CommitDiscard(); } } private void CommitDiscard() { if (_discardCount >= _data.Length || _discardCount >= _end) { _baseOffset += _discardCount; _end = 0; } else { Buffer.BlockCopy(_data, _discardCount, _data, 0, _end - _discardCount); _baseOffset += _discardCount; _end -= _discardCount; } _discardCount = 0; } }