diff --git a/SabreTools.IO/Extensions/BinaryReaderExtensions.cs b/SabreTools.IO/Extensions/BinaryReaderExtensions.cs index 53ed899..5a59f41 100644 --- a/SabreTools.IO/Extensions/BinaryReaderExtensions.cs +++ b/SabreTools.IO/Extensions/BinaryReaderExtensions.cs @@ -330,6 +330,8 @@ namespace SabreTools.IO.Extensions return reader.ReadNullTerminatedUTF8String(); else if (encoding.Equals(Encoding.Unicode)) return reader.ReadNullTerminatedUnicodeString(); + else if (encoding.Equals(Encoding.UTF32)) + return reader.ReadNullTerminatedUTF32String(); if (reader.BaseStream.Position >= reader.BaseStream.Length) return null; @@ -354,7 +356,7 @@ namespace SabreTools.IO.Extensions if (reader.BaseStream.Position >= reader.BaseStream.Length) return null; - byte[] buffer = ReadUntilNullNarrow(reader); + byte[] buffer = ReadUntilNull1Byte(reader); return Encoding.ASCII.GetString(buffer); } @@ -366,7 +368,7 @@ namespace SabreTools.IO.Extensions if (reader.BaseStream.Position >= reader.BaseStream.Length) return null; - byte[] buffer = ReadUntilNullNarrow(reader); + byte[] buffer = ReadUntilNull1Byte(reader); return Encoding.ASCII.GetString(buffer); } @@ -378,7 +380,19 @@ namespace SabreTools.IO.Extensions if (reader.BaseStream.Position >= reader.BaseStream.Length) return null; - byte[] buffer = ReadUntilNullWide(reader); + byte[] buffer = ReadUntilNull2Byte(reader); + return Encoding.Unicode.GetString(buffer); + } + + /// + /// Read a null-terminated UTF-32 string from the underlying stream + /// + public static string? ReadNullTerminatedUTF32String(this BinaryReader reader) + { + if (reader.BaseStream.Position >= reader.BaseStream.Length) + return null; + + byte[] buffer = ReadUntilNull4Byte(reader); return Encoding.Unicode.GetString(buffer); } @@ -648,7 +662,7 @@ namespace SabreTools.IO.Extensions /// /// Read bytes until a 1-byte null terminator is found /// - private static byte[] ReadUntilNullNarrow(BinaryReader reader) + private static byte[] ReadUntilNull1Byte(BinaryReader reader) { var bytes = new List(); while (reader.BaseStream.Position < reader.BaseStream.Length) @@ -666,7 +680,7 @@ namespace SabreTools.IO.Extensions /// /// Read bytes until a 2-byte null terminator is found /// - private static byte[] ReadUntilNullWide(BinaryReader reader) + private static byte[] ReadUntilNull2Byte(BinaryReader reader) { var bytes = new List(); while (reader.BaseStream.Position < reader.BaseStream.Length) @@ -680,5 +694,23 @@ namespace SabreTools.IO.Extensions return [.. bytes]; } + + /// + /// Read bytes until a 4-byte null terminator is found + /// + private static byte[] ReadUntilNull4Byte(BinaryReader reader) + { + var bytes = new List(); + while (reader.BaseStream.Position < reader.BaseStream.Length) + { + uint next = reader.ReadUInt32(); + if (next == 0x00000000) + break; + + bytes.AddRange(BitConverter.GetBytes(next)); + } + + return [.. bytes]; + } } } diff --git a/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs b/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs index fa1775e..d33ae38 100644 --- a/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs +++ b/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs @@ -458,6 +458,8 @@ namespace SabreTools.IO.Extensions return content.ReadNullTerminatedUTF8String(ref offset); else if (encoding.Equals(Encoding.Unicode)) return content.ReadNullTerminatedUnicodeString(ref offset); + else if (encoding.Equals(Encoding.UTF32)) + return content.ReadNullTerminatedUTF32String(ref offset); if (offset >= content.Length) return null; @@ -482,7 +484,7 @@ namespace SabreTools.IO.Extensions if (offset >= content.Length) return null; - byte[] buffer = ReadUntilNullNarrow(content, ref offset); + byte[] buffer = ReadUntilNull1Byte(content, ref offset); return Encoding.ASCII.GetString(buffer); } @@ -494,7 +496,7 @@ namespace SabreTools.IO.Extensions if (offset >= content.Length) return null; - byte[] buffer = ReadUntilNullNarrow(content, ref offset); + byte[] buffer = ReadUntilNull1Byte(content, ref offset); return Encoding.UTF8.GetString(buffer); } @@ -506,7 +508,19 @@ namespace SabreTools.IO.Extensions if (offset >= content.Length) return null; - byte[] buffer = ReadUntilNullWide(content, ref offset); + byte[] buffer = ReadUntilNull2Byte(content, ref offset); + return Encoding.Unicode.GetString(buffer); + } + + /// + /// Read a null-terminated UTF-32 string from the byte array + /// + public static string? ReadNullTerminatedUTF32String(this byte[] content, ref int offset) + { + if (offset >= content.Length) + return null; + + byte[] buffer = ReadUntilNull4Byte(content, ref offset); return Encoding.Unicode.GetString(buffer); } @@ -776,7 +790,7 @@ namespace SabreTools.IO.Extensions /// /// Read bytes until a 1-byte null terminator is found /// - private static byte[] ReadUntilNullNarrow(byte[] content, ref int offset) + private static byte[] ReadUntilNull1Byte(byte[] content, ref int offset) { var bytes = new List(); while (offset < content.Length) @@ -794,7 +808,7 @@ namespace SabreTools.IO.Extensions /// /// Read bytes until a 2-byte null terminator is found /// - private static byte[] ReadUntilNullWide(byte[] content, ref int offset) + private static byte[] ReadUntilNull2Byte(byte[] content, ref int offset) { var bytes = new List(); while (offset < content.Length) @@ -809,6 +823,24 @@ namespace SabreTools.IO.Extensions return [.. bytes]; } + /// + /// Read bytes until a 4-byte null terminator is found + /// + private static byte[] ReadUntilNull4Byte(byte[] content, ref int offset) + { + var bytes = new List(); + while (offset < content.Length) + { + uint next = content.ReadUInt32(ref offset); + if (next == 0x00000000) + break; + + bytes.AddRange(BitConverter.GetBytes(next)); + } + + return [.. bytes]; + } + /// /// Read a number of bytes from the byte array to a buffer /// diff --git a/SabreTools.IO/Extensions/StreamReaderExtensions.cs b/SabreTools.IO/Extensions/StreamReaderExtensions.cs index 6ce5ae0..8d23977 100644 --- a/SabreTools.IO/Extensions/StreamReaderExtensions.cs +++ b/SabreTools.IO/Extensions/StreamReaderExtensions.cs @@ -442,6 +442,8 @@ namespace SabreTools.IO.Extensions return stream.ReadNullTerminatedUTF8String(); else if (encoding.Equals(Encoding.Unicode)) return stream.ReadNullTerminatedUnicodeString(); + else if (encoding.Equals(Encoding.UTF32)) + return stream.ReadNullTerminatedUTF32String(); if (stream.Position >= stream.Length) return null; @@ -466,7 +468,7 @@ namespace SabreTools.IO.Extensions if (stream.Position >= stream.Length) return null; - byte[] buffer = ReadUntilNullNarrow(stream); + byte[] buffer = ReadUntilNull1Byte(stream); return Encoding.ASCII.GetString(buffer); } @@ -478,7 +480,7 @@ namespace SabreTools.IO.Extensions if (stream.Position >= stream.Length) return null; - byte[] buffer = ReadUntilNullNarrow(stream); + byte[] buffer = ReadUntilNull1Byte(stream); return Encoding.UTF8.GetString(buffer); } @@ -490,7 +492,19 @@ namespace SabreTools.IO.Extensions if (stream.Position >= stream.Length) return null; - byte[] buffer = ReadUntilNullWide(stream); + byte[] buffer = ReadUntilNull2Byte(stream); + return Encoding.Unicode.GetString(buffer); + } + + /// + /// Read a null-terminated UTF-32 string from the stream + /// + public static string? ReadNullTerminatedUTF32String(this Stream stream) + { + if (stream.Position >= stream.Length) + return null; + + byte[] buffer = ReadUntilNull4Byte(stream); return Encoding.Unicode.GetString(buffer); } @@ -760,7 +774,7 @@ namespace SabreTools.IO.Extensions /// /// Read bytes until a 1-byte null terminator is found /// - private static byte[] ReadUntilNullNarrow(Stream stream) + private static byte[] ReadUntilNull1Byte(Stream stream) { var bytes = new List(); while (stream.Position < stream.Length) @@ -778,7 +792,7 @@ namespace SabreTools.IO.Extensions /// /// Read bytes until a 2-byte null terminator is found /// - private static byte[] ReadUntilNullWide(Stream stream) + private static byte[] ReadUntilNull2Byte(Stream stream) { var bytes = new List(); while (stream.Position < stream.Length) @@ -814,5 +828,23 @@ namespace SabreTools.IO.Extensions return buffer; } + + /// + /// Read bytes until a 4-byte null terminator is found + /// + private static byte[] ReadUntilNull4Byte(Stream stream) + { + var bytes = new List(); + while (stream.Position < stream.Length) + { + uint next = stream.ReadUInt32(); + if (next == 0x00000000) + break; + + bytes.AddRange(BitConverter.GetBytes(next)); + } + + return [.. bytes]; + } } }