using System; using System.Collections.Generic; using System.IO; #if NET35_OR_GREATER || NETCOREAPP using System.Linq; #endif using System.Text; namespace SabreTools.IO { /// /// Extensions for Streams /// /// TODO: Add U/Int24 and U/Int48 methods public static class StreamExtensions { /// /// Read a UInt8 from the stream /// public static byte ReadByteValue(this Stream stream) { byte[] buffer = new byte[1]; stream.Read(buffer, 0, 1); return buffer[0]; } /// /// Read a UInt8[] from the stream /// public static byte[]? ReadBytes(this Stream stream, int count) { // If there's an invalid byte count, don't do anything if (count <= 0) return null; byte[] buffer = new byte[count]; stream.Read(buffer, 0, count); return buffer; } /// /// Read a Int8 from the stream /// public static sbyte ReadSByte(this Stream stream) { byte[] buffer = new byte[1]; stream.Read(buffer, 0, 1); return (sbyte)buffer[0]; } /// /// Read a Char from the stream /// public static char ReadChar(this Stream stream) { byte[] buffer = new byte[1]; stream.Read(buffer, 0, 1); return (char)buffer[0]; } /// /// Read a Int16 from the stream /// public static short ReadInt16(this Stream stream) { byte[] buffer = new byte[2]; stream.Read(buffer, 0, 2); return BitConverter.ToInt16(buffer, 0); } /// /// Read a Int16 from the stream in big-endian format /// public static short ReadInt16BigEndian(this Stream stream) { byte[] buffer = new byte[2]; stream.Read(buffer, 0, 2); Array.Reverse(buffer); return BitConverter.ToInt16(buffer, 0); } /// /// Read a UInt16 from the stream /// public static ushort ReadUInt16(this Stream stream) { byte[] buffer = new byte[2]; stream.Read(buffer, 0, 2); return BitConverter.ToUInt16(buffer, 0); } /// /// Read a UInt16 from the stream in big-endian format /// public static ushort ReadUInt16BigEndian(this Stream stream) { byte[] buffer = new byte[2]; stream.Read(buffer, 0, 2); Array.Reverse(buffer); return BitConverter.ToUInt16(buffer, 0); } /// /// Read an Int32 from the stream /// public static int ReadInt32(this Stream stream) { byte[] buffer = new byte[4]; stream.Read(buffer, 0, 4); return BitConverter.ToInt32(buffer, 0); } /// /// Read an Int32 from the stream in big-endian format /// public static int ReadInt32BigEndian(this Stream stream) { byte[] buffer = new byte[4]; stream.Read(buffer, 0, 4); Array.Reverse(buffer); return BitConverter.ToInt32(buffer, 0); } /// /// Read a UInt32 from the stream /// public static uint ReadUInt32(this Stream stream) { byte[] buffer = new byte[4]; stream.Read(buffer, 0, 4); return BitConverter.ToUInt32(buffer, 0); } /// /// Read a UInt32 from the stream in big-endian format /// public static uint ReadUInt32BigEndian(this Stream stream) { byte[] buffer = new byte[4]; stream.Read(buffer, 0, 4); Array.Reverse(buffer); return BitConverter.ToUInt32(buffer, 0); } /// /// Read a Int64 from the stream /// public static long ReadInt64(this Stream stream) { byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); return BitConverter.ToInt64(buffer, 0); } /// /// Read a Int64 from the stream in big-endian format /// public static long ReadInt64BigEndian(this Stream stream) { byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); Array.Reverse(buffer); return BitConverter.ToInt64(buffer, 0); } /// /// Read a UInt64 from the stream /// public static ulong ReadUInt64(this Stream stream) { byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); return BitConverter.ToUInt64(buffer, 0); } /// /// Read a UInt64 from the stream in big-endian format /// public static ulong ReadUInt64BigEndian(this Stream stream) { byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); Array.Reverse(buffer); return BitConverter.ToUInt64(buffer, 0); } /// /// Read a Guid from the stream /// public static Guid ReadGuid(this Stream stream) { byte[] buffer = new byte[16]; stream.Read(buffer, 0, 16); return new Guid(buffer); } /// /// Read a Guid from the stream in big-endian format /// public static Guid ReadGuidBigEndian(this Stream stream) { byte[] buffer = new byte[16]; stream.Read(buffer, 0, 16); Array.Reverse(buffer); return new Guid(buffer); } /// /// Read a null-terminated string from the stream /// public static string? ReadString(this Stream stream) => stream.ReadString(Encoding.Default); /// /// Read a null-terminated string from the stream /// public static string? ReadString(this Stream stream, Encoding encoding) { if (stream.Position >= stream.Length) return null; byte[] nullTerminator = encoding.GetBytes(new char[] { '\0' }); int charWidth = nullTerminator.Length; var tempBuffer = new List(); byte[] buffer = new byte[charWidth]; while (stream.Position < stream.Length && stream.Read(buffer, 0, charWidth) != 0 && !buffer.SequenceEqual(nullTerminator)) { tempBuffer.AddRange(buffer); } return encoding.GetString([.. tempBuffer]); } /// /// Seek to a specific point in the stream, if possible /// /// Input stream to try seeking on /// Optional offset to seek to public static long SeekIfPossible(this Stream input, long offset = 0) { // If the stream is null, don't even try if (input == null) return -1; // If the input is not seekable, just return the current position if (!input.CanSeek) { try { return input.Position; } catch { return -1; } } // Attempt to seek to the offset try { if (offset < 0) return input.Seek(offset, SeekOrigin.End); else if (offset >= 0) return input.Seek(offset, SeekOrigin.Begin); return input.Position; } catch { return -1; } } } }