392 lines
12 KiB
C#
392 lines
12 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
|
|
namespace skyscraper5.Skyscraper.IO
|
|
{
|
|
public static class StreamExtensions
|
|
{
|
|
[DebuggerStepThrough]
|
|
public static ushort ReadUInt16BE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[2];
|
|
if (stream.Read(buffer, 0, 2) != 2)
|
|
throw new EndOfStreamException();
|
|
if (BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 2);
|
|
return BitConverter.ToUInt16(buffer, 0);
|
|
}
|
|
|
|
public static short ReadInt16LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[2];
|
|
if (stream.Read(buffer, 0, 2) != 2)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 2);
|
|
return BitConverter.ToInt16(buffer, 0);
|
|
}
|
|
|
|
public static ushort ReadUInt16LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[2];
|
|
if (stream.Read(buffer, 0, 2) != 2)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 2);
|
|
return BitConverter.ToUInt16(buffer, 0);
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static uint ReadUInt32BE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[4];
|
|
if (stream.Read(buffer, 0, 4) != 4)
|
|
throw new EndOfStreamException();
|
|
if (BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 4);
|
|
return BitConverter.ToUInt32(buffer, 0);
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static uint ReadUInt32LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[4];
|
|
if (stream.Read(buffer, 0, 4) != 4)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 4);
|
|
return BitConverter.ToUInt32(buffer, 0);
|
|
}
|
|
|
|
public static int ReadInt32LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[4];
|
|
if (stream.Read(buffer, 0, 4) != 4)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 4);
|
|
return BitConverter.ToInt32(buffer, 0);
|
|
}
|
|
|
|
public static ulong ReadUInt48BE(this Stream stream)
|
|
{
|
|
ulong result = 0;
|
|
result = ReadUInt16BE(stream);
|
|
result <<= 32;
|
|
result += ReadUInt32BE(stream);
|
|
return result;
|
|
}
|
|
|
|
public static long ReadInt64LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[8];
|
|
if (stream.Read(buffer, 0, 8) != 8)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 4);
|
|
return BitConverter.ToInt64(buffer, 0);
|
|
}
|
|
|
|
public static ulong ReadUInt64LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[8];
|
|
if (stream.Read(buffer, 0, 8) != 8)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 4);
|
|
return BitConverter.ToUInt64(buffer, 0);
|
|
}
|
|
|
|
public static ulong ReadUInt64BE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[8];
|
|
if (stream.Read(buffer, 0, 8) != 8)
|
|
throw new EndOfStreamException();
|
|
if (BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 4);
|
|
return BitConverter.ToUInt64(buffer, 0);
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static byte[] ReadBytes(this Stream stream, long length)
|
|
{
|
|
if (length > Int32.MaxValue)
|
|
throw new ArgumentOutOfRangeException();
|
|
if (length == 0)
|
|
return new byte[0];
|
|
if (length < 0)
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
int length32 = (int)length;
|
|
|
|
byte[] buffer = new byte[length32];
|
|
if (stream.Read(buffer, 0, length32) != length32)
|
|
throw new EndOfStreamException();
|
|
|
|
return buffer;
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static sbyte[] ReadSBytes(this Stream stream, long length)
|
|
{
|
|
if (length > Int32.MaxValue)
|
|
throw new ArgumentOutOfRangeException();
|
|
if (length == 0)
|
|
return new sbyte[0];
|
|
if (length < 0)
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
int length32 = (int)length;
|
|
|
|
sbyte[] buffer = new sbyte[length32];
|
|
for (int i = 0; i < length32; i++)
|
|
{
|
|
buffer[i] = (sbyte)stream.ReadUInt8();
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static byte ReadUInt8(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[1];
|
|
if (stream.Read(buffer, 0, 1) != 1)
|
|
throw new EndOfStreamException();
|
|
|
|
return buffer[0];
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static long GetAvailableBytes(this Stream stream)
|
|
{
|
|
if (stream is GZipStream)
|
|
{
|
|
GZipStream gzip = (GZipStream)stream;
|
|
long baseStreamPosition = gzip.BaseStream.Length - gzip.BaseStream.Position;
|
|
return baseStreamPosition;
|
|
}
|
|
return stream.Length - stream.Position;
|
|
}
|
|
|
|
public static void WriteUInt8Repeat(this Stream stream, byte uint8, int repetitions)
|
|
{
|
|
byte[] buffer = new byte[repetitions];
|
|
Array.Fill(buffer, uint8);
|
|
stream.Write(buffer, 0, repetitions);
|
|
}
|
|
|
|
public static uint ReadUInt24BE(this Stream stream)
|
|
{
|
|
uint result = stream.ReadUInt16BE();
|
|
result <<= 8;
|
|
result += stream.ReadUInt8();
|
|
return result;
|
|
}
|
|
|
|
public static double ReadDouble(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[8];
|
|
if (stream.Read(buffer, 0, 8) != 8)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer, 0, 8);
|
|
return BitConverter.ToDouble(buffer, 0);
|
|
}
|
|
|
|
public static void WriteUInt8(this Stream stream, byte value)
|
|
{
|
|
byte[] buffer = new byte[1];
|
|
buffer[0] = value;
|
|
stream.Write(buffer, 0, 1);
|
|
}
|
|
|
|
public static void WriteUInt16BE(this Stream stream, ushort value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1]) = (buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 2);
|
|
}
|
|
public static void WriteUInt16LE(this Stream stream, ushort value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (!BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1]) = (buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 2);
|
|
}
|
|
|
|
public static void WriteInt32BE(this Stream stream, int value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1], buffer[2], buffer[3]) = (buffer[3], buffer[2], buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 4);
|
|
}
|
|
|
|
public static void WriteUInt32BE(this Stream stream, uint value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1], buffer[2], buffer[3]) = (buffer[3], buffer[2], buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 4);
|
|
}
|
|
public static void WriteUInt32LE(this Stream stream, uint value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (!BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1], buffer[2], buffer[3]) = (buffer[3], buffer[2], buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 4);
|
|
}
|
|
|
|
public static void WriteInt32LE(this Stream stream, int value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (!BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1], buffer[2], buffer[3]) = (buffer[3], buffer[2], buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 4);
|
|
}
|
|
|
|
public static void WriteInt64LE(this Stream stream, long value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (!BitConverter.IsLittleEndian)
|
|
(buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]) = (buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2], buffer[1], buffer[0]);
|
|
stream.Write(buffer, 0, 8);
|
|
}
|
|
|
|
public static string ReadUTF8FixedLength(this Stream stream, int length)
|
|
{
|
|
byte[] buffer = new byte[length];
|
|
if (stream.Read(buffer, 0, length) != length)
|
|
throw new EndOfStreamException();
|
|
return Encoding.UTF8.GetString(buffer);
|
|
}
|
|
|
|
public static string ReadUTF16NullTerminated(this Stream stream)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
do
|
|
{
|
|
char c = (char)stream.ReadUInt16LE();
|
|
if (c == 0)
|
|
return sb.ToString();
|
|
sb.Append(c);
|
|
} while (stream.Position != stream.Length);
|
|
return sb.ToString();
|
|
}
|
|
|
|
[DebuggerStepThrough]
|
|
public static string ReadAsciiNullTerminated(this Stream stream)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
do
|
|
{
|
|
char c = (char)stream.ReadUInt8();
|
|
if (c == 0)
|
|
return sb.ToString();
|
|
sb.Append(c);
|
|
} while(stream.Position != stream.Length);
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a variable-length integer to the stream.
|
|
/// (code generated by ChatGPT, model GPT-4o)
|
|
/// </summary>
|
|
public static void WriteVarInt(this Stream stream, long value)
|
|
{
|
|
while ((value & ~0x7F) != 0) // More than 7 bits left?
|
|
{
|
|
stream.WriteByte((byte)((value & 0x7F) | 0x80)); // Write 7 bits + set highest bit
|
|
value >>= 7; // Shift right by 7 bits
|
|
}
|
|
stream.WriteByte((byte)value); // Write last byte (highest bit unset)
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a variable-length integer from the stream.
|
|
/// (code generated by ChatGPT, model GPT-4o)
|
|
/// </summary>
|
|
public static int ReadVarInt(this Stream stream)
|
|
{
|
|
int result = 0;
|
|
int shift = 0;
|
|
byte currentByte;
|
|
|
|
do
|
|
{
|
|
currentByte = (byte)stream.ReadByte();
|
|
result |= (currentByte & 0x7F) << shift; // Extract 7 bits and shift
|
|
shift += 7;
|
|
}
|
|
while ((currentByte & 0x80) != 0); // Continue while highest bit is set
|
|
|
|
return result;
|
|
}
|
|
|
|
public static double ReadFloat64LE(this Stream stream)
|
|
{
|
|
byte[] buffer = new byte[8];
|
|
if (stream.Read(buffer, 0, 8) != 8)
|
|
throw new EndOfStreamException();
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer);
|
|
return BitConverter.ToDouble(buffer, 0);
|
|
}
|
|
|
|
public static void WriteFloat64LE(this Stream stream, double value)
|
|
{
|
|
byte[] buffer = BitConverter.GetBytes(value);
|
|
if (!BitConverter.IsLittleEndian)
|
|
Array.Reverse(buffer);
|
|
stream.Write(buffer, 0, 8);
|
|
}
|
|
|
|
public static byte Peek(this Stream stream)
|
|
{
|
|
if (!stream.CanSeek)
|
|
throw new NotSupportedException("Stream is not peekable");
|
|
if (stream.Position == stream.Length)
|
|
throw new EndOfStreamException();
|
|
|
|
byte readUInt8 = stream.ReadUInt8();
|
|
stream.Position--;
|
|
return readUInt8;
|
|
}
|
|
|
|
public static void DumpToFile(this Stream stream, string filename)
|
|
{
|
|
FileStream fileStream = File.OpenWrite(filename);
|
|
stream.CopyTo(fileStream);
|
|
fileStream.Flush(true);
|
|
fileStream.Close();
|
|
fileStream.Dispose();
|
|
}
|
|
|
|
public static int TryReadExactly(this Stream stream, byte[] buffer, int offset, int count)
|
|
{
|
|
int totalRead = 0;
|
|
|
|
while (totalRead < count)
|
|
{
|
|
int bytesRead = stream.Read(buffer, offset + totalRead, count - totalRead);
|
|
if (bytesRead == 0)
|
|
{
|
|
// End of stream
|
|
break;
|
|
}
|
|
|
|
totalRead += bytesRead;
|
|
}
|
|
return totalRead;
|
|
}
|
|
}
|
|
}
|