using System; using System.Collections.Generic; using System.Text; namespace SabreTools.IO { /// /// Extensions for byte arrays /// /// TODO: Add U/Int24 and U/Int48 methods public static class ByteArrayExtensions { /// /// Read a UInt8 and increment the pointer to an array /// public static byte ReadByte(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 1); if (buffer == null) return default; return buffer[0]; } /// /// Read a UInt8[] and increment the pointer to an array /// public static byte[]? ReadBytes(this byte[]? content, ref int offset, int count) { // If the byte array is invalid, don't do anything if (content == null) return null; // If there's an invalid byte count, don't do anything if (count <= 0 || offset >= content.Length) return null; // Allocate enough space for the data requested byte[] buffer = new byte[count]; // If we have less data left than requested, only read until the end if (offset + count >= content.Length) count = content.Length - offset; // If we have a non-zero count, copy the data into the array if (count > 0) Array.Copy(content, offset, buffer, 0, Math.Min(count, content.Length - offset)); // Increment the offset and return offset += count; return buffer; } /// /// Read a Int8 and increment the pointer to an array /// public static sbyte ReadSByte(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 1); if (buffer == null) return default; return (sbyte)buffer[0]; } /// /// Read a Char and increment the pointer to an array /// public static char ReadChar(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 1); if (buffer == null) return default; return (char)buffer[0]; } /// /// Read a Int16 and increment the pointer to an array /// public static short ReadInt16(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 2); if (buffer == null) return default; return BitConverter.ToInt16(buffer, 0); } /// /// Read a Int16 in big-endian format and increment the pointer to an array /// public static short ReadInt16BigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 2); if (buffer == null) return default; Array.Reverse(buffer); return BitConverter.ToInt16(buffer, 0); } /// /// Read a UInt16 and increment the pointer to an array /// public static ushort ReadUInt16(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 2); if (buffer == null) return default; return BitConverter.ToUInt16(buffer, 0); } /// /// Read a UInt16 in big-endian format and increment the pointer to an array /// public static ushort ReadUInt16BigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 2); if (buffer == null) return default; Array.Reverse(buffer); return BitConverter.ToUInt16(buffer, 0); } /// /// Read a Int32 and increment the pointer to an array /// public static int ReadInt32(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 4); if (buffer == null) return default; return BitConverter.ToInt32(buffer, 0); } /// /// Read a Int32 in big-endian format and increment the pointer to an array /// public static int ReadInt32BigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 4); if (buffer == null) return default; Array.Reverse(buffer); return BitConverter.ToInt32(buffer, 0); } /// /// Read a UInt32 and increment the pointer to an array /// public static uint ReadUInt32(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 4); if (buffer == null) return default; return BitConverter.ToUInt32(buffer, 0); } /// /// Read a UInt32 in big-endian format and increment the pointer to an array /// public static uint ReadUInt32BigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 4); if (buffer == null) return default; Array.Reverse(buffer); return BitConverter.ToUInt32(buffer, 0); } /// /// Read a Int64 and increment the pointer to an array /// public static long ReadInt64(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 8); if (buffer == null) return default; return BitConverter.ToInt64(buffer, 0); } /// /// Read a Int64 in big-endian format and increment the pointer to an array /// public static long ReadInt64BigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 8); if (buffer == null) return default; Array.Reverse(buffer); return BitConverter.ToInt64(buffer, 0); } /// /// Read a UInt64 and increment the pointer to an array /// public static ulong ReadUInt64(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 8); if (buffer == null) return default; return BitConverter.ToUInt64(buffer, 0); } /// /// Read a UInt64 in big-endian format and increment the pointer to an array /// public static ulong ReadUInt64BigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 8); if (buffer == null) return default; Array.Reverse(buffer); return BitConverter.ToUInt64(buffer, 0); } /// /// Read a Guid and increment the pointer to an array /// public static Guid ReadGuid(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 16); if (buffer == null) return default; return new Guid(buffer); } /// /// Read a Guid in big-endian format and increment the pointer to an array /// public static Guid ReadGuidBigEndian(this byte[] content, ref int offset) { byte[]? buffer = content.ReadBytes(ref offset, 16); if (buffer == null) return default; Array.Reverse(buffer); return new Guid(buffer); } /// /// Read a null-terminated string from the stream /// public static string? ReadString(this byte[] content, ref int offset) => content.ReadString(ref offset, Encoding.Default); /// /// Read a null-terminated string from the stream /// public static string? ReadString(this byte[] content, ref int offset, Encoding encoding) { if (offset >= content.Length) return null; byte[] nullTerminator = encoding.GetBytes(new char[] { '\0' }); int charWidth = nullTerminator.Length; var 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]).TrimEnd('\0'); } } }