diff --git a/SabreTools.IO/StreamExtensions.cs b/SabreTools.IO/StreamExtensions.cs
index a32617c..a08e622 100644
--- a/SabreTools.IO/StreamExtensions.cs
+++ b/SabreTools.IO/StreamExtensions.cs
@@ -230,6 +230,44 @@ namespace SabreTools.IO
return encoding.GetString([.. tempBuffer]);
}
+ ///
+ /// Read a string that is terminated by a newline but contains a quoted portion that
+ /// may also contain a newline from the stream
+ ///
+ public static string? ReadQuotedString(this Stream stream) => stream.ReadQuotedString(Encoding.Default);
+
+ ///
+ /// Read a string that is terminated by a newline but contains a quoted portion that
+ /// may also contain a newline from the stream
+ ///
+ public static string? ReadQuotedString(this Stream stream, Encoding encoding)
+ {
+ if (stream.Position >= stream.Length)
+ return null;
+
+ var bytes = new List();
+ bool openQuote = false;
+ while (stream.Position < stream.Length)
+ {
+ // Read the byte value
+ byte b = stream.ReadByteValue();
+
+ // If we have a quote, flip the flag
+ if (b == (byte)'"')
+ openQuote = !openQuote;
+
+ // If we have a newline not in a quoted string, exit the loop
+ else if (b == (byte)'\n' && !openQuote)
+ break;
+
+ // Add the byte to the set
+ bytes.Add(b);
+ }
+
+ var line = encoding.GetString([.. bytes]);
+ return line.TrimEnd();
+ }
+
///
/// Seek to a specific point in the stream, if possible
///