Decompiled source of Concentus Oggfile v1.0.600

BepInEx/core/Concentus.Oggfile/netstandard2.0/Concentus.Oggfile.dll

Decompiled 2 months ago
using 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.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6.0+dee65d9de0ef009658c2d60be6fd9b29d362a482")]
[assembly: AssemblyProduct("Concentus.Oggfile")]
[assembly: AssemblyTitle("Concentus.Oggfile")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus.oggfile")]
[assembly: AssemblyVersion("1.0.6.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;

	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";
				return false;
			}
			if (oggContainerReader.StreamSerials.Length == 0)
			{
				LastError = "Initialization failed: No elementary streams found in input file";
				return false;
			}
			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 class OpusOggWriteStream
{
	private const int FRAME_SIZE_MS = 20;

	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 _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 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++;
				if (_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;
		_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;
	}
}