diff --git a/ByteArrayExtensions.cs b/ByteArrayExtensions.cs new file mode 100644 index 0000000..8941e50 --- /dev/null +++ b/ByteArrayExtensions.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SabreTools.IO +{ + /// + /// Extensions for byte arrays + /// + /// TODO: Add U/Int24 and U/Int48 methods + /// TODO: Add big-endian methods + public static class ByteArrayExtensions + { + /// + /// Read a byte and increment the pointer to an array + /// + public static byte ReadByte(this byte[] content, ref int offset) + { + return content[offset++]; + } + + /// + /// Read a byte array and increment the pointer to an array + /// +#if NET48 + public static byte[] ReadBytes(this byte[] content, ref int offset, int count) +#else + public static byte[]? ReadBytes(this byte[] content, ref int offset, int count) +#endif + { + // If there's an invalid byte count, don't do anything + if (count <= 0) + return null; + + byte[] buffer = new byte[count]; + Array.Copy(content, offset, buffer, 0, Math.Min(count, content.Length - offset)); + offset += count; + return buffer; + } + + /// + /// Read an sbyte and increment the pointer to an array + /// + public static sbyte ReadSByte(this byte[] content, ref int offset) + { + return (sbyte)content[offset++]; + } + + /// + /// Read a char and increment the pointer to an array + /// + public static char ReadChar(this byte[] content, ref int offset) + { + return (char)content[offset++]; + } + + /// + /// Read a short and increment the pointer to an array + /// + public static short ReadInt16(this byte[] content, ref int offset) + { + short value = BitConverter.ToInt16(content, offset); + offset += 2; + return value; + } + + /// + /// Read a ushort and increment the pointer to an array + /// + public static ushort ReadUInt16(this byte[] content, ref int offset) + { + ushort value = BitConverter.ToUInt16(content, offset); + offset += 2; + return value; + } + + /// + /// Read a int and increment the pointer to an array + /// + public static int ReadInt32(this byte[] content, ref int offset) + { + int value = BitConverter.ToInt32(content, offset); + offset += 4; + return value; + } + + /// + /// Read a uint and increment the pointer to an array + /// + public static uint ReadUInt32(this byte[] content, ref int offset) + { + uint value = BitConverter.ToUInt32(content, offset); + offset += 4; + return value; + } + + /// + /// Read a long and increment the pointer to an array + /// + public static long ReadInt64(this byte[] content, ref int offset) + { + long value = BitConverter.ToInt64(content, offset); + offset += 8; + return value; + } + + /// + /// Read a ulong and increment the pointer to an array + /// + public static ulong ReadUInt64(this byte[] content, ref int offset) + { + ulong value = BitConverter.ToUInt64(content, offset); + offset += 8; + return value; + } + + /// + /// Read a Guid from the stream + /// + public static Guid ReadGuid(this byte[] content, ref int offset) + { + byte[] buffer = new byte[16]; + Array.Copy(content, offset, buffer, 0, 16); + offset += 16; + return new Guid(buffer); + } + + /// + /// Read a null-terminated string from the stream + /// +#if NET48 + public static string ReadString(this byte[] content, ref int offset) => content.ReadString(ref offset, Encoding.Default); +#else + public static string? ReadString(this byte[] content, ref int offset) => content.ReadString(ref offset, Encoding.Default); +#endif + + /// + /// Read a null-terminated string from the stream + /// +#if NET48 + public static string ReadString(this byte[] content, ref int offset, Encoding encoding) +#else + public static string? ReadString(this byte[] content, ref int offset, Encoding encoding) +#endif + { + if (offset >= content.Length) + return null; + + byte[] nullTerminator = encoding.GetBytes(new char[] { '\0' }); + int charWidth = nullTerminator.Length; + + List keyChars = new List(); + while (offset < content.Length) + { + char c = encoding.GetChars(content, offset, charWidth)[0]; + keyChars.Add(c); + offset += charWidth; + + if (c == '\0') + break; + } + + return new string(keyChars.ToArray()).TrimEnd('\0'); + } + } +} \ No newline at end of file diff --git a/StreamExtensions.cs b/StreamExtensions.cs index c6286be..d8f400b 100644 --- a/StreamExtensions.cs +++ b/StreamExtensions.cs @@ -1,12 +1,247 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; namespace SabreTools.IO { /// - /// Extensions to Stream functionality + /// Extensions for Streams /// + /// TODO: Add U/Int24 and U/Int48 methods public static class StreamExtensions { + /// + /// Read a byte 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 byte array from the stream + /// +#if NET48 + public static byte[] ReadBytes(this Stream stream, int count) +#else + public static byte[]? ReadBytes(this Stream stream, int count) +#endif + { + // 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 an sbyte 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 character 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 short 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 short 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 ushort 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 ushort 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 int 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 int 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 uint 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 uint 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 long 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 long 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 ulong 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 ulong 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 + /// +#if NET48 + public static string ReadString(this Stream stream) => stream.ReadString(Encoding.Default); +#else + public static string? ReadString(this Stream stream) => stream.ReadString(Encoding.Default); +#endif + + /// + /// Read a null-terminated string from the stream + /// +#if NET48 + public static string ReadString(this Stream stream, Encoding encoding) +#else + public static string? ReadString(this Stream stream, Encoding encoding) +#endif + { + if (stream.Position >= stream.Length) + return null; + + byte[] nullTerminator = encoding.GetBytes(new char[] { '\0' }); + int charWidth = nullTerminator.Length; + + List 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.ToArray()); + } + /// /// Seek to a specific point in the stream, if possible ///