context: using System;
  using System.IO;
  using System.IO.Compression;
  using System.Text;
  
  namespace Cyotek.Data.Nbt.Serialization
  {
    public class BinaryTagReader : TagReader
    {
      #region Constants
  
      private readonly Stream _originalStream;
  
      private readonly TagState _state;
  
      private readonly Stream _stream;
  
      #endregion
  
      #region Constructors
  
      public BinaryTagReader(Stream stream)
        : this(stream, true)
      { }
  
      public BinaryTagReader(Stream stream, bool autoDetectCompression)
      {
        if (stream.CanSeek && autoDetectCompression)
        {
          if (stream.IsGzipCompressed())
          {
            _originalStream = stream;
            _stream = new GZipStream(_originalStream, CompressionMode.Decompress);
          }
          else if (stream.IsDeflateCompressed())
          {
            _originalStream = stream;
            _stream = new DeflateStream(_originalStream, CompressionMode.Decompress);
          }
          else
          {
            _stream = stream;
          }
        }
        else
        {
          _stream = stream;
        }
  
        _state = new TagState(FileAccess.Read);
        _state.Start();
      }
  
      #endregion
  
      #region Static Methods
  
      private static bool FirstDecompressedByteIsTagCompound(Stream stream)
      {
        bool result;
  
        try
        {
          using (Stream decompressionStream = new GZipStream(stream, CompressionMode.Decompress, true))
          {
            result = decompressionStream.ReadByte() == (int)TagType.Compound;
          }
        }
        catch (InvalidDataException)
        {
          result = false;
        }
  
        return result;
      }
  
      private static bool FirstDeflatedByteIsTagCompound(Stream stream)
      {
        bool result;
  
        try
        {
          using (Stream decompressionStream = new DeflateStream(stream, CompressionMode.Decompress, true))
          {
            result = decompressionStream.ReadByte() == (int)TagType.Compound;
          }
        }
        catch (InvalidDataException)
        {
          result = false;
        }
  
        return result;
      }
  
      #endregion
  
      #region Methods
  
      public override bool IsNbtDocument()
      {
        bool result;
        Stream stream;
        long position;
  
        stream = _originalStream ?? _stream;
  
        if (stream.CanSeek)
        {
          position = stream.Position;
        }
        else
        {
          position = -1;
        }
  
        if (stream.IsGzipCompressed())
        {
          result = FirstDecompressedByteIsTagCompound(stream);
        }
        else if (stream.IsDeflateCompressed())
        {
          result = FirstDeflatedByteIsTagCompound(stream);
        }
        else if (stream.ReadByte() == (int)TagType.Compound)
        {
          result = true;
        }
        else
        {
          result = false;
        }
  
        if (stream.CanSeek)
        {
          stream.Position = position;
        }
  
        return result;
      }
  
      public override byte ReadByte()
      {
        int data;
  
        data = _stream.ReadByte();
  
        if (data != (data & 0xFF))
        {
          throw new InvalidDataException();
        }
  
        return (byte)data;
      }
  
      public override byte[] ReadByteArray()
      {
        int length;
        byte[] data;
  
        length = this.ReadInt();
        data = new byte[length];
  
        if (length != _stream.Read(data, 0, length))
        {
          throw new InvalidDataException();
        }
  
        return data;
      }
  
      public override TagDictionary ReadCompound()
      {
        TagDictionary results;
        Tag tag;
  
        results = new TagDictionary();
  
        tag = this.ReadTag();
  
        while (tag.Type != TagType.End)
        {
          results.Add(tag);
          tag = this.ReadTag();
        }
  
        return results;
      }
  
      public override double ReadDouble()
      {
        byte[] data;
  
        data = new byte[BitHelper.DoubleSize];
  
        if (BitHelper.DoubleSize != _stream.Read(data, 0, BitHelper.DoubleSize))
        {
          throw new InvalidDataException();
        }
  
        if (TagWriter.IsLittleEndian)
        {
          BitHelper.SwapBytes(data, 0, BitHelper.DoubleSize);
        }
  
        return BitConverter.ToDouble(data, 0);
      }
  
      public override float ReadFloat()
      {
        byte[] data;
  
        data = new byte[BitHelper.FloatSize];
  
        if (BitHelper.FloatSize != _stream.Read(data, 0, BitHelper.FloatSize))
        {
          throw new InvalidDataException();
        }
  
        if (TagWriter.IsLittleEndian)
        {
          BitHelper.SwapBytes(data, 0, BitHelper.FloatSize);
        }
  
        return BitConverter.ToSingle(data, 0);
      }
  
      public override int ReadInt()
      {
        byte[] data;
  
        data = new byte[BitHelper.IntSize];
  
        if (BitHelper.IntSize != _stream.Read(data, 0, BitHelper.IntSize))
        {
          throw new InvalidDataException();
        }
  
        return BinaryTagReader.ReadInt(data,0);
      }
  
      private static int ReadInt(byte[] data, int offset)
      {
        if (TagWriter.IsLittleEndian)
        {
          BitHelper.SwapBytes(data, offset, BitHelper.IntSize);
        }
  
        return BitConverter.ToInt32(data, offset);
      }
  
      public override int[] ReadIntArray()
      {
        int length;
        int bufferLength;
        byte[] buffer;
        int[] values;
        bool isLittleEndian;
  
        isLittleEndian = TagWriter.IsLittleEndian;
        length = this.ReadInt();
        bufferLength = length * BitHelper.IntSize;
        buffer = new byte[bufferLength];
  
        if (bufferLength != _stream.Read(buffer, 0, bufferLength))
        {
          throw new InvalidDataException();
        }
  
        values = new int[length];
  
        for (int i = 0; i < length; i++)
        {
          values[i] = BinaryTagReader.ReadInt(buffer, i * BitHelper.IntSize);
        }
  
        return values;
      }
      public override long[] ReadLongArray()
      {
        int length;
        int bufferLength;
        byte[] buffer;
        long[] values;
        bool isLittleEndian;
  
        isLittleEndian = TagWriter.IsLittleEndian;
        length = this.ReadInt();
        bufferLength = length * BitHelper.LongSize;
        buffer = new byte[bufferLength];
  
        if (bufferLength != _stream.Read(buffer, 0, bufferLength))
        {
          throw new InvalidDataException();
        }
  
        values = new long[length];
  
  
gt:       for (int i = 0; i < length; i++)