mirror of
https://github.com/SabreTools/SabreTools.IO.git
synced 2026-02-09 05:35:35 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8df3fe2473 | ||
|
|
a2bb83ab9a | ||
|
|
bcb77f2de6 | ||
|
|
35d4c22a20 | ||
|
|
893cb73b0d | ||
|
|
607a0375c7 | ||
|
|
87d1dfe266 | ||
|
|
d8f16b12b5 | ||
|
|
cf08658b1e | ||
|
|
9547e1a355 | ||
|
|
20cdb5b65e | ||
|
|
fc10565186 | ||
|
|
c824db6b18 | ||
|
|
a3c26fed38 | ||
|
|
2dc259d978 | ||
|
|
2d950ddc54 | ||
|
|
294c5c26df | ||
|
|
dc356767ab | ||
|
|
0b6c7e9885 | ||
|
|
339da9fc16 | ||
|
|
7bc18c6952 | ||
|
|
ec3afeed73 | ||
|
|
658ceb5d0e | ||
|
|
e9a905d4a3 | ||
|
|
1127e96f26 | ||
|
|
89a8ad3703 | ||
|
|
ff7f7c0b8c |
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class BinaryReaderExtensionsTests
|
||||
{
|
||||
@@ -12,6 +14,15 @@ namespace SabreTools.IO.Test
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
short read = br.ReadInt16();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16BigEndianTest()
|
||||
{
|
||||
@@ -21,6 +32,15 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ushort read = br.ReadUInt16();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16BigEndianTest()
|
||||
{
|
||||
@@ -30,6 +50,15 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadInt32();
|
||||
Assert.Equal(0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32BigEndianTest()
|
||||
{
|
||||
@@ -39,6 +68,15 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadUInt32();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32BigEndianTest()
|
||||
{
|
||||
@@ -48,6 +86,35 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
float expected = BitConverter.Int32BitsToSingle(0x03020100);
|
||||
float read = br.ReadSingle();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
float expected = BitConverter.Int32BitsToSingle(0x00010203);
|
||||
float read = br.ReadSingleBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
long read = br.ReadInt64();
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64BigEndianTest()
|
||||
{
|
||||
@@ -57,6 +124,15 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadUInt64();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64BigEndianTest()
|
||||
{
|
||||
@@ -66,8 +142,130 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
|
||||
double read = br.ReadDouble();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
|
||||
double read = br.ReadDoubleBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new Guid(_bytes);
|
||||
Guid read = br.ReadGuid();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
Guid read = br.ReadGuidBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new Int128(BitConverter.ToUInt64(_bytes, 0), BitConverter.ToUInt64(_bytes, 8));
|
||||
Int128 read = br.ReadInt128();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = new Int128(BitConverter.ToUInt64(reversed, 0), BitConverter.ToUInt64(reversed, 8));
|
||||
Int128 read = br.ReadInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new UInt128(BitConverter.ToUInt64(_bytes, 0), BitConverter.ToUInt64(_bytes, 8));
|
||||
UInt128 read = br.ReadUInt128();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = new UInt128(BitConverter.ToUInt64(reversed, 0), BitConverter.ToUInt64(reversed, 8));
|
||||
UInt128 read = br.ReadUInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeExplicitTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new TestStructExplicit
|
||||
{
|
||||
FirstValue = 0x03020100,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0504,
|
||||
FourthValue = 0x0706,
|
||||
};
|
||||
var read = br.ReadType<TestStructExplicit>();
|
||||
Assert.Equal(expected.FirstValue, read.FirstValue);
|
||||
Assert.Equal(expected.SecondValue, read.SecondValue);
|
||||
Assert.Equal(expected.ThirdValue, read.ThirdValue);
|
||||
Assert.Equal(expected.FourthValue, read.FourthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeSequentialTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new TestStructSequential
|
||||
{
|
||||
FirstValue = 0x03020100,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
};
|
||||
var read = br.ReadType<TestStructSequential>();
|
||||
Assert.Equal(expected.FirstValue, read.FirstValue);
|
||||
Assert.Equal(expected.SecondValue, read.SecondValue);
|
||||
Assert.Equal(expected.ThirdValue, read.ThirdValue);
|
||||
Assert.Equal(expected.FourthValue, read.FourthValue);
|
||||
}
|
||||
|
||||
// TODO: Add byte[], char[] tests
|
||||
// TODO: Add float, double tests
|
||||
// TODO: Add decimal tests
|
||||
// TODO: Add string reading tests
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Linq;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class ByteArrayExtensionsTests
|
||||
{
|
||||
@@ -196,7 +196,7 @@ namespace SabreTools.IO.Test
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndian()
|
||||
public void ReadGuidBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
@@ -244,6 +244,43 @@ namespace SabreTools.IO.Test
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeExplicitTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new TestStructExplicit
|
||||
{
|
||||
FirstValue = 0x03020100,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0504,
|
||||
FourthValue = 0x0706,
|
||||
};
|
||||
var read = _bytes.ReadType<TestStructExplicit>(ref offset);
|
||||
Assert.Equal(expected.FirstValue, read.FirstValue);
|
||||
Assert.Equal(expected.SecondValue, read.SecondValue);
|
||||
Assert.Equal(expected.ThirdValue, read.ThirdValue);
|
||||
Assert.Equal(expected.FourthValue, read.FourthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeSequentialTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new TestStructSequential
|
||||
{
|
||||
FirstValue = 0x03020100,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
};
|
||||
var read = _bytes.ReadType<TestStructSequential>(ref offset);
|
||||
Assert.Equal(expected.FirstValue, read.FirstValue);
|
||||
Assert.Equal(expected.SecondValue, read.SecondValue);
|
||||
Assert.Equal(expected.ThirdValue, read.ThirdValue);
|
||||
Assert.Equal(expected.FourthValue, read.FourthValue);
|
||||
}
|
||||
|
||||
// TODO: Add decimal tests
|
||||
// TODO: Add string reading tests
|
||||
}
|
||||
}
|
||||
138
SabreTools.IO.Test/Extensions/EnumerableExtensionsTests.cs
Normal file
138
SabreTools.IO.Test/Extensions/EnumerableExtensionsTests.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class EnumerableExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void SafeEnumerateEmptyTest()
|
||||
{
|
||||
var source = Enumerable.Empty<string>();
|
||||
var safe = source.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Empty(list);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateNoErrorTest()
|
||||
{
|
||||
var source = new List<string> { "a", "ab", "abc" };
|
||||
var safe = source.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Equal(3, list.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateErrorMidTest()
|
||||
{
|
||||
var source = new List<string> { "a", "ab", "abc" };
|
||||
var wrapper = new ErrorEnumerable(source);
|
||||
|
||||
var safe = wrapper.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateErrorLastTest()
|
||||
{
|
||||
var source = new List<string> { "a", "ab", "abc", "abcd" };
|
||||
var wrapper = new ErrorEnumerable(source);
|
||||
|
||||
var safe = wrapper.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fake enumerable that uses <see cref="ErrorEnumerator"/>
|
||||
/// </summary>
|
||||
private class ErrorEnumerable : IEnumerable<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerator to use during enumeration
|
||||
/// </summary>
|
||||
private readonly ErrorEnumerator _enumerator;
|
||||
|
||||
public ErrorEnumerable(IEnumerable<string> source)
|
||||
{
|
||||
_enumerator = new ErrorEnumerator(source);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<string> GetEnumerator() => _enumerator;
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => _enumerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fake enumerator that throws an exception every other item while moving to the next item
|
||||
/// </summary>
|
||||
private class ErrorEnumerator : IEnumerator<string>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_index == -1)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
return _enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerator from the source enumerable
|
||||
/// </summary>
|
||||
private readonly IEnumerator<string> _enumerator;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerators start before the data
|
||||
/// </summary>
|
||||
private int _index = -1;
|
||||
|
||||
public ErrorEnumerator(IEnumerable<string> source)
|
||||
{
|
||||
_enumerator = source.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose() { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool MoveNext()
|
||||
{
|
||||
// Move to the next item, if possible
|
||||
bool moved = _enumerator.MoveNext();
|
||||
if (!moved)
|
||||
return false;
|
||||
|
||||
// Get the next real item
|
||||
_index++;
|
||||
|
||||
// Every other move, throw an exception
|
||||
if (_index % 2 == 1)
|
||||
throw new Exception("Access issue for this item in the enumerable");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Reset()
|
||||
{
|
||||
_enumerator.Reset();
|
||||
_index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class StreamExtensionsTests
|
||||
{
|
||||
@@ -190,7 +190,7 @@ namespace SabreTools.IO.Test
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndian()
|
||||
public void ReadGuidBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
@@ -238,6 +238,43 @@ namespace SabreTools.IO.Test
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeExplicitTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new TestStructExplicit
|
||||
{
|
||||
FirstValue = 0x03020100,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0504,
|
||||
FourthValue = 0x0706,
|
||||
};
|
||||
var read = stream.ReadType<TestStructExplicit>();
|
||||
Assert.Equal(expected.FirstValue, read.FirstValue);
|
||||
Assert.Equal(expected.SecondValue, read.SecondValue);
|
||||
Assert.Equal(expected.ThirdValue, read.ThirdValue);
|
||||
Assert.Equal(expected.FourthValue, read.FourthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeSequentialTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new TestStructSequential
|
||||
{
|
||||
FirstValue = 0x03020100,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
};
|
||||
var read = stream.ReadType<TestStructSequential>();
|
||||
Assert.Equal(expected.FirstValue, read.FirstValue);
|
||||
Assert.Equal(expected.SecondValue, read.SecondValue);
|
||||
Assert.Equal(expected.ThirdValue, read.ThirdValue);
|
||||
Assert.Equal(expected.FourthValue, read.FourthValue);
|
||||
}
|
||||
|
||||
// TODO: Add decimal tests
|
||||
// TODO: Add string reading tests
|
||||
}
|
||||
}
|
||||
20
SabreTools.IO.Test/Extensions/TestStructExplicit.cs
Normal file
20
SabreTools.IO.Test/Extensions/TestStructExplicit.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct TestStructExplicit
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int FirstValue;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public int SecondValue;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public ushort ThirdValue;
|
||||
|
||||
[FieldOffset(6)]
|
||||
public short FourthValue;
|
||||
}
|
||||
}
|
||||
16
SabreTools.IO.Test/Extensions/TestStructSequential.cs
Normal file
16
SabreTools.IO.Test/Extensions/TestStructSequential.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TestStructSequential
|
||||
{
|
||||
public int FirstValue;
|
||||
|
||||
public int SecondValue;
|
||||
|
||||
public ushort ThirdValue;
|
||||
|
||||
public short FourthValue;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Big endian reading overloads for BinaryReader
|
||||
/// Extensions for BinaryReader
|
||||
/// </summary>
|
||||
/// <remarks>TODO: Add U/Int24 and U/Int48 methods</remarks>
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
/// <inheritdoc cref="BinaryReader.Read(byte[], int, int)"/>
|
||||
@@ -30,104 +34,319 @@ namespace SabreTools.IO.Extensions
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static byte[] ReadBytesBigEndian(this BinaryReader reader, int count)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(count);
|
||||
Array.Reverse(retval);
|
||||
return retval;
|
||||
byte[] buffer = reader.ReadBytes(count);
|
||||
Array.Reverse(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadChars(int)"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static char[] ReadCharsBigEndian(this BinaryReader reader, int count)
|
||||
{
|
||||
char[] retval = reader.ReadChars(count);
|
||||
Array.Reverse(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadDecimal"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static decimal ReadDecimalBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(16);
|
||||
Array.Reverse(retval);
|
||||
|
||||
int i1 = BitConverter.ToInt32(retval, 0);
|
||||
int i2 = BitConverter.ToInt32(retval, 4);
|
||||
int i3 = BitConverter.ToInt32(retval, 8);
|
||||
int i4 = BitConverter.ToInt32(retval, 12);
|
||||
|
||||
return new decimal([i1, i2, i3, i4]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadDouble"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static double ReadDoubleBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(8);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToDouble(retval, 0);
|
||||
char[] buffer = reader.ReadChars(count);
|
||||
Array.Reverse(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt16"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static short ReadInt16BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(2);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToInt16(retval, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt32"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt32BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(4);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToInt32(retval, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt64"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static long ReadInt64BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(8);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToInt64(retval, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadSingle"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static float ReadSingleBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(4);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToSingle(retval, 0);
|
||||
byte[] buffer = reader.ReadBytes(2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadUInt16"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ushort ReadUInt16BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(2);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToUInt16(retval, 0);
|
||||
byte[] buffer = reader.ReadBytes(2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt32"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt32BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static uint ReadUInt32BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(4);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToUInt32(retval, 0);
|
||||
byte[] buffer = reader.ReadBytes(4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadSingle"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static float ReadSingleBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt64"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static long ReadInt64BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadUInt64"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ulong ReadUInt64BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] retval = reader.ReadBytes(8);
|
||||
Array.Reverse(retval);
|
||||
return BitConverter.ToUInt64(retval, 0);
|
||||
byte[] buffer = reader.ReadBytes(8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadDouble"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static double ReadDoubleBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadDecimal"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static decimal ReadDecimalBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
int i1 = BitConverter.ToInt32(buffer, 0);
|
||||
int i2 = BitConverter.ToInt32(buffer, 4);
|
||||
int i3 = BitConverter.ToInt32(buffer, 8);
|
||||
int i4 = BitConverter.ToInt32(buffer, 12);
|
||||
|
||||
return new decimal([i1, i2, i3, i4]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid from the underlying stream
|
||||
/// </summary>
|
||||
public static Guid ReadGuid(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
return new Guid(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid from the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Guid ReadGuidBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
Array.Reverse(buffer);
|
||||
return new Guid(buffer);
|
||||
}
|
||||
|
||||
// TODO: Determine if the reverse reads are doing what are expected
|
||||
#if NET7_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Read an Int128 from the underlying stream
|
||||
/// </summary>
|
||||
public static Int128 ReadInt128(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
return new Int128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int128 from the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Int128 ReadInt128BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
Array.Reverse(buffer);
|
||||
return new Int128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 from the underlying stream
|
||||
/// </summary>
|
||||
public static UInt128 ReadUInt128(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 from the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static UInt128 ReadUInt128BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
Array.Reverse(buffer);
|
||||
return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedString(this BinaryReader reader, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.Equals(Encoding.ASCII))
|
||||
return reader.ReadNullTerminatedAnsiString();
|
||||
else if (encoding.Equals(Encoding.Unicode))
|
||||
return reader.ReadNullTerminatedUnicodeString();
|
||||
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte ch = reader.ReadByte();
|
||||
buffer.Add(ch);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated ASCII string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedAnsiString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte ch = reader.ReadByte();
|
||||
buffer.Add(ch);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated Unicode string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte[] ch = reader.ReadBytes(2);
|
||||
buffer.AddRange(ch);
|
||||
if (ch[0] == '\0' && ch[1] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return Encoding.Unicode.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte size = reader.ReadByte();
|
||||
if (reader.BaseStream.Position + size >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = reader.ReadUInt16();
|
||||
if (reader.BaseStream.Position + size >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a string that is terminated by a newline but contains a quoted portion that
|
||||
/// may also contain a newline from the stream
|
||||
/// </summary>
|
||||
public static string? ReadQuotedString(this BinaryReader reader)
|
||||
=> reader.ReadQuotedString(Encoding.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Read a string that is terminated by a newline but contains a quoted portion that
|
||||
/// may also contain a newline from the stream
|
||||
/// </summary>
|
||||
public static string? ReadQuotedString(this BinaryReader reader, Encoding encoding)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
var bytes = new List<byte>();
|
||||
bool openQuote = false;
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
// Read the byte value
|
||||
byte b = reader.ReadByte();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> from the underlying stream
|
||||
/// </summary>
|
||||
public static T? ReadType<T>(this BinaryReader reader)
|
||||
{
|
||||
int typeSize = Marshal.SizeOf(typeof(T));
|
||||
byte[] buffer = reader.ReadBytes(typeSize);
|
||||
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
var data = (T?)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||
handle.Free();
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
@@ -31,6 +32,17 @@ namespace SabreTools.IO.Extensions
|
||||
public static byte[] ReadBytes(this byte[] content, ref int offset, int count)
|
||||
=> ReadToBuffer(content, ref offset, count);
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt8[] and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static byte[] ReadBytesBigEndian(this byte[] content, ref int offset, int count)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, count);
|
||||
Array.Reverse(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int8 and increment the pointer to an array
|
||||
/// </summary>
|
||||
@@ -59,8 +71,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int16 in big-endian format and increment the pointer to an array
|
||||
/// Read an Int16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static short ReadInt16BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 2);
|
||||
@@ -78,8 +91,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt16 in big-endian format and increment the pointer to an array
|
||||
/// Read a UInt16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ushort ReadUInt16BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 2);
|
||||
@@ -97,8 +111,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int32 in big-endian format and increment the pointer to an array
|
||||
/// Read an Int32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt32BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
@@ -116,8 +131,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt32 in big-endian format and increment the pointer to an array
|
||||
/// Read a UInt32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static uint ReadUInt32BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
@@ -135,8 +151,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Single in big-endian format and increment the pointer to an array
|
||||
/// Read a Single and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static float ReadSingleBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
@@ -154,8 +171,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int64 in big-endian format and increment the pointer to an array
|
||||
/// Read an Int64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static long ReadInt64BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
@@ -173,8 +191,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt64 in big-endian format and increment the pointer to an array
|
||||
/// Read a UInt64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ulong ReadUInt64BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
@@ -192,8 +211,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Double in big-endian format and increment the pointer to an array
|
||||
/// Read a Double and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static double ReadDoubleBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
@@ -201,6 +221,38 @@ namespace SabreTools.IO.Extensions
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Decimal and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static decimal ReadDecimal(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
|
||||
int i1 = BitConverter.ToInt32(buffer, 0);
|
||||
int i2 = BitConverter.ToInt32(buffer, 4);
|
||||
int i3 = BitConverter.ToInt32(buffer, 8);
|
||||
int i4 = BitConverter.ToInt32(buffer, 12);
|
||||
|
||||
return new decimal([i1, i2, i3, i4]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Decimal and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static decimal ReadDecimalBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
int i1 = BitConverter.ToInt32(buffer, 0);
|
||||
int i2 = BitConverter.ToInt32(buffer, 4);
|
||||
int i3 = BitConverter.ToInt32(buffer, 8);
|
||||
int i4 = BitConverter.ToInt32(buffer, 12);
|
||||
|
||||
return new decimal([i1, i2, i3, i4]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid and increment the pointer to an array
|
||||
/// </summary>
|
||||
@@ -211,8 +263,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid in big-endian format and increment the pointer to an array
|
||||
/// Read a Guid and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Guid ReadGuidBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
@@ -232,8 +285,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int128 in big-endian format and increment the pointer to an array
|
||||
/// Read an Int128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Int128 ReadInt128BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
@@ -251,8 +305,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 in big-endian format and increment the pointer to an array
|
||||
/// Read a UInt128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static UInt128 ReadUInt128BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
@@ -262,34 +317,101 @@ namespace SabreTools.IO.Extensions
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the byte array
|
||||
/// Read a null-terminated string from the array
|
||||
/// </summary>
|
||||
public static string? ReadString(this byte[] content, ref int offset)
|
||||
=> content.ReadString(ref offset, Encoding.Default);
|
||||
public static string? ReadNullTerminatedString(this byte[] content, ref int offset, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.Equals(Encoding.ASCII))
|
||||
return content.ReadNullTerminatedAnsiString(ref offset);
|
||||
else if (encoding.Equals(Encoding.Unicode))
|
||||
return content.ReadNullTerminatedUnicodeString(ref offset);
|
||||
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (offset < content.Length)
|
||||
{
|
||||
byte ch = content.ReadByteValue(ref offset);
|
||||
buffer.Add(ch);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the byte array
|
||||
/// Read a null-terminated ASCII string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadString(this byte[] content, ref int offset, Encoding encoding)
|
||||
public static string? ReadNullTerminatedAnsiString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] nullTerminator = encoding.GetBytes("\0");
|
||||
int charWidth = nullTerminator.Length;
|
||||
|
||||
var keyChars = new List<char>();
|
||||
List<byte> buffer = [];
|
||||
while (offset < content.Length)
|
||||
{
|
||||
char c = encoding.GetChars(content, offset, charWidth)[0];
|
||||
keyChars.Add(c);
|
||||
offset += charWidth;
|
||||
|
||||
if (c == '\0')
|
||||
byte ch = content.ReadByteValue(ref offset);
|
||||
buffer.Add(ch);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return new string([.. keyChars]).TrimEnd('\0');
|
||||
return Encoding.ASCII.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated Unicode string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (offset < content.Length)
|
||||
{
|
||||
byte[] ch = content.ReadBytes(ref offset, 2);
|
||||
buffer.AddRange(ch);
|
||||
if (ch[0] == '\0' && ch[1] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return Encoding.Unicode.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
byte size = content.ReadByteValue(ref offset);
|
||||
if (offset + size >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this byte[] content, ref int offset)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
return null;
|
||||
|
||||
ushort size = content.ReadUInt16(ref offset);
|
||||
if (offset + size >= content.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = content.ReadBytes(ref offset, size);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -331,6 +453,21 @@ namespace SabreTools.IO.Extensions
|
||||
return new string([.. keyChars]).TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> from the underlying stream
|
||||
/// </summary>
|
||||
public static T? ReadType<T>(this byte[] content, ref int offset)
|
||||
{
|
||||
int typeSize = Marshal.SizeOf(typeof(T));
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, typeSize);
|
||||
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
var data = (T?)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||
handle.Free();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a number of bytes from the current byte array to a buffer
|
||||
/// </summary>
|
||||
|
||||
38
SabreTools.IO/Extensions/EnumerableExtensions.cs
Normal file
38
SabreTools.IO/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Safely iterate through an enumerable, skipping any errors
|
||||
/// </summary>
|
||||
public static IEnumerable<T> SafeEnumerate<T>(this IEnumerable<T> enumerable)
|
||||
{
|
||||
// Get the enumerator for the enumerable
|
||||
var enumerator = enumerable.GetEnumerator();
|
||||
|
||||
// Iterate through and absorb any errors
|
||||
while (true)
|
||||
{
|
||||
// Attempt to move to the next item
|
||||
bool moved;
|
||||
try
|
||||
{
|
||||
moved = enumerator.MoveNext();
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the end of the enumeration is reached
|
||||
if (!moved)
|
||||
yield break;
|
||||
|
||||
// Return the next value from the enumeration
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,23 +118,262 @@ namespace SabreTools.IO.Extensions
|
||||
return null;
|
||||
|
||||
// If it does and it is empty, return a blank enumerable
|
||||
#if NET20 || NET35
|
||||
if (!Directory.GetFiles(root, "*", SearchOption.AllDirectories).Any())
|
||||
#else
|
||||
if (!Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Any())
|
||||
#endif
|
||||
if (!root!.SafeEnumerateFileSystemEntries("*", SearchOption.AllDirectories).Any())
|
||||
return [];
|
||||
|
||||
// Otherwise, get the complete list
|
||||
#if NET20 || NET35
|
||||
return Directory.GetDirectories(root, "*", SearchOption.AllDirectories)
|
||||
.Where(dir => !Directory.GetFiles(dir, "*", SearchOption.AllDirectories).Any())
|
||||
return root!.SafeEnumerateDirectories("*", SearchOption.AllDirectories)
|
||||
.Where(dir => !dir.SafeEnumerateFileSystemEntries("*", SearchOption.AllDirectories).Any())
|
||||
.ToList();
|
||||
#else
|
||||
return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories)
|
||||
.Where(dir => !Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Any())
|
||||
.ToList();
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Safe Directory Enumeration
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetDirectories(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetDirectories(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetDirectories(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetDirectories(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string, SearchOption)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetDirectories(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetDirectories(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetFiles(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetFiles(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string, string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetFiles(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetFiles(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string, string, SearchOption)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetFiles(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetFiles(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFileSystemEntries(string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetFileSystemEntries(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetFileSystemEntries(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetFileSystemEntries(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetFileSystemEntries(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string, SearchOption)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static IEnumerable<string> SafeGetFileSystemEntries(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.GetFileSystemEntries(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetDirectories(string)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path)
|
||||
=> path.SafeGetDirectories();
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetDirectories(string, string)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern)
|
||||
=> path.SafeGetDirectories(searchPattern);
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string, SearchOption)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetDirectories(string, string, SearchOption)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern, SearchOption searchOption)
|
||||
=> path.SafeGetDirectories(searchPattern, searchOption);
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetFiles(string)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path)
|
||||
=> path.SafeGetFiles();
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string, string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetFiles(string, string)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern)
|
||||
=> path.SafeGetFiles(searchPattern);
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string, string, SearchOption)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetFiles(string, string, SearchOption)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern, SearchOption searchOption)
|
||||
=> path.SafeGetFiles(searchPattern, searchOption);
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFileSystemEntries(string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetFileSystemEntries(string)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path)
|
||||
=> path.SafeGetFileSystemEntries();
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFileSystemEntries(string, string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetFileSystemEntries(string, string)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern)
|
||||
=> path.SafeGetFileSystemEntries(searchPattern);
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFileSystemEntries(string, string)"/>
|
||||
/// <remarks>Calls <see cref="SafeGetFileSystemEntries(string, string, SearchOption)"/> implementation</remarks>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern, SearchOption searchOption)
|
||||
=> path.SafeGetFileSystemEntries(searchPattern, searchOption);
|
||||
#else
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path)
|
||||
{
|
||||
var enumerable = Directory.EnumerateDirectories(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern)
|
||||
{
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path)
|
||||
{
|
||||
var enumerable = Directory.EnumerateFiles(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern)
|
||||
{
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path)
|
||||
{
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern)
|
||||
{
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
@@ -55,8 +55,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int16 from the stream in big-endian format
|
||||
/// Read an Int16 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static short ReadInt16BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 2);
|
||||
@@ -74,8 +75,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt16 from the stream in big-endian format
|
||||
/// Read a UInt16 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ushort ReadUInt16BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 2);
|
||||
@@ -93,8 +95,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int32 from the stream in big-endian format
|
||||
/// Read an Int32 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt32BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
@@ -112,8 +115,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt32 from the stream in big-endian format
|
||||
/// Read a UInt32 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static uint ReadUInt32BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
@@ -131,8 +135,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Single from the stream in big-endian format
|
||||
/// Read a Single from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static float ReadSingleBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
@@ -150,8 +155,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int64 from the stream in big-endian format
|
||||
/// Read an Int64 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static long ReadInt64BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
@@ -169,8 +175,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt64 from the stream in big-endian format
|
||||
/// Read a UInt64 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ulong ReadUInt64BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
@@ -188,8 +195,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Double from the stream in big-endian format
|
||||
/// Read a Double from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static double ReadDoubleBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
@@ -197,6 +205,38 @@ namespace SabreTools.IO.Extensions
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Decimal from the stream
|
||||
/// </summary>
|
||||
public static decimal ReadDecimal(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
|
||||
int i1 = BitConverter.ToInt32(buffer, 0);
|
||||
int i2 = BitConverter.ToInt32(buffer, 4);
|
||||
int i3 = BitConverter.ToInt32(buffer, 8);
|
||||
int i4 = BitConverter.ToInt32(buffer, 12);
|
||||
|
||||
return new decimal([i1, i2, i3, i4]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Decimal from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static decimal ReadDecimalBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
int i1 = BitConverter.ToInt32(buffer, 0);
|
||||
int i2 = BitConverter.ToInt32(buffer, 4);
|
||||
int i3 = BitConverter.ToInt32(buffer, 8);
|
||||
int i4 = BitConverter.ToInt32(buffer, 12);
|
||||
|
||||
return new decimal([i1, i2, i3, i4]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid from the stream
|
||||
/// </summary>
|
||||
@@ -207,8 +247,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid from the stream in big-endian format
|
||||
/// Read a Guid from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Guid ReadGuidBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
@@ -228,8 +269,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int128 from the stream in big-endian format
|
||||
/// Read an Int128 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Int128 ReadInt128BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
@@ -247,8 +289,9 @@ namespace SabreTools.IO.Extensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 from the stream in big-endian format
|
||||
/// Read a UInt128 from the stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static UInt128 ReadUInt128BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
@@ -260,29 +303,99 @@ namespace SabreTools.IO.Extensions
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadString(this Stream stream)
|
||||
=> stream.ReadString(Encoding.Default);
|
||||
public static string? ReadNullTerminatedString(this Stream stream, Encoding encoding)
|
||||
{
|
||||
// Short-circuit to explicit implementations
|
||||
if (encoding.Equals(Encoding.ASCII))
|
||||
return stream.ReadNullTerminatedAnsiString();
|
||||
else if (encoding.Equals(Encoding.Unicode))
|
||||
return stream.ReadNullTerminatedUnicodeString();
|
||||
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
byte ch = stream.ReadByteValue();
|
||||
buffer.Add(ch);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return encoding.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the stream
|
||||
/// Read a null-terminated ASCII string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadString(this Stream stream, Encoding encoding)
|
||||
public static string? ReadNullTerminatedAnsiString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] nullTerminator = encoding.GetBytes("\0");
|
||||
int charWidth = nullTerminator.Length;
|
||||
|
||||
var tempBuffer = new List<byte>();
|
||||
|
||||
byte[] buffer = new byte[charWidth];
|
||||
while (stream.Position < stream.Length && stream.Read(buffer, 0, charWidth) != 0 && !buffer.SequenceEqual(nullTerminator))
|
||||
List<byte> buffer = [];
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
tempBuffer.AddRange(buffer);
|
||||
byte ch = stream.ReadByteValue();
|
||||
buffer.Add(ch);
|
||||
if (ch == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return encoding.GetString([.. tempBuffer]);
|
||||
return Encoding.ASCII.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated Unicode string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
byte[] ch = stream.ReadBytes(2);
|
||||
buffer.AddRange(ch);
|
||||
if (ch[0] == '\0' && ch[1] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return Encoding.Unicode.GetString([.. buffer]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte-prefixed ASCII string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedAnsiString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte size = stream.ReadByteValue();
|
||||
if (stream.Position + size >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a ushort-prefixed Unicode string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadPrefixedUnicodeString(this Stream stream)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
ushort size = stream.ReadUInt16();
|
||||
if (stream.Position + size >= stream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = stream.ReadBytes(size);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -324,6 +437,21 @@ namespace SabreTools.IO.Extensions
|
||||
return line.TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> from the stream
|
||||
/// </summary>
|
||||
public static T? ReadType<T>(this Stream stream)
|
||||
{
|
||||
int typeSize = Marshal.SizeOf(typeof(T));
|
||||
byte[] buffer = ReadToBuffer(stream, typeSize);
|
||||
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
var data = (T?)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
|
||||
handle.Free();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek to a specific point in the stream, if possible
|
||||
/// </summary>
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace SabreTools.IO
|
||||
string filename = Path.GetFileName(CurrentPath);
|
||||
|
||||
// If we have a true ParentPath, remove it from CurrentPath and return the remainder
|
||||
if (!string.IsNullOrEmpty(ParentPath) && !PathsEqual(CurrentPath, ParentPath))
|
||||
if (!string.IsNullOrEmpty(ParentPath) && !PathsEqual(CurrentPath, ParentPath))
|
||||
filename = CurrentPath.Remove(0, ParentPath!.Length + 1);
|
||||
|
||||
// If we're sanitizing the path after, do so
|
||||
@@ -57,34 +57,31 @@ namespace SabreTools.IO
|
||||
/// <returns>Complete output path</returns>
|
||||
public string? GetOutputPath(string? outDir, bool inplace)
|
||||
{
|
||||
// If the current path is empty, we can't do anything
|
||||
// If the current path is empty
|
||||
if (string.IsNullOrEmpty(CurrentPath))
|
||||
return null;
|
||||
|
||||
// If the output dir is empty (and we're not inplace), we can't do anything
|
||||
outDir = outDir?.Trim();
|
||||
if (string.IsNullOrEmpty(outDir) && !inplace)
|
||||
return null;
|
||||
|
||||
// Check if we have a split path or not
|
||||
bool splitpath = !string.IsNullOrEmpty(ParentPath);
|
||||
|
||||
// If we have an inplace output, use the directory name from the input path
|
||||
// If we have an inplace output
|
||||
if (inplace)
|
||||
return Path.GetDirectoryName(CurrentPath);
|
||||
|
||||
// If the current and parent paths are the same, just use the output directory
|
||||
if (!splitpath || CurrentPath.Length == (ParentPath?.Length ?? 0))
|
||||
// If the output dir is empty after trimming
|
||||
outDir = outDir?.Trim();
|
||||
if (string.IsNullOrEmpty(outDir))
|
||||
return null;
|
||||
|
||||
// If the parent path is empty or the paths are equal
|
||||
if (string.IsNullOrEmpty(ParentPath) || PathsEqual(CurrentPath, ParentPath))
|
||||
return outDir;
|
||||
|
||||
// By default, the working parent directory is the parent path
|
||||
string workingParent = ParentPath ?? string.Empty;
|
||||
string workingParent = ParentPath!;
|
||||
|
||||
// TODO: Should this be the default? Always create a subfolder if a folder is found?
|
||||
// If we are processing a path that is coming from a directory and we are outputting to the current directory, we want to get the subfolder to write to
|
||||
if (outDir == Environment.CurrentDirectory)
|
||||
workingParent = Path.GetDirectoryName(ParentPath ?? string.Empty) ?? string.Empty;
|
||||
|
||||
workingParent = Path.GetDirectoryName(ParentPath) ?? string.Empty;
|
||||
|
||||
// Handle bizarre Windows-like paths on Linux
|
||||
if (workingParent.EndsWith(":") && Path.DirectorySeparatorChar == '/')
|
||||
workingParent += '/';
|
||||
@@ -98,7 +95,7 @@ namespace SabreTools.IO
|
||||
string combinedPath = Path.Combine(outDir!, strippedPath);
|
||||
return Path.GetDirectoryName(combinedPath);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determine if two paths are equal or not
|
||||
/// </summary>
|
||||
@@ -129,8 +126,8 @@ namespace SabreTools.IO
|
||||
if (input == null)
|
||||
return null;
|
||||
|
||||
// Replace alternate directory separators with the correct one
|
||||
return input.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
// Replace '\' with '/'
|
||||
return input.Replace('\\', '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace SabreTools.IO
|
||||
@@ -70,7 +71,7 @@ namespace SabreTools.IO
|
||||
private static List<string> GetDirectoriesOrderedHelper(string dir, List<string> infiles, string pattern)
|
||||
{
|
||||
// Take care of the files in the top directory
|
||||
List<string> toadd = [.. Directory.GetDirectories(dir, pattern, SearchOption.TopDirectoryOnly)];
|
||||
List<string> toadd = [.. dir.SafeEnumerateDirectories(pattern, SearchOption.TopDirectoryOnly)];
|
||||
toadd.Sort(new NaturalComparer());
|
||||
infiles.AddRange(toadd);
|
||||
|
||||
@@ -149,12 +150,12 @@ namespace SabreTools.IO
|
||||
private static List<string> GetFilesOrderedHelper(string dir, List<string> infiles, string pattern)
|
||||
{
|
||||
// Take care of the files in the top directory
|
||||
List<string> toadd = [.. Directory.GetFiles(dir, pattern, SearchOption.TopDirectoryOnly)];
|
||||
List<string> toadd = [.. dir.SafeEnumerateFiles(pattern, SearchOption.TopDirectoryOnly)];
|
||||
toadd.Sort(new NaturalComparer());
|
||||
infiles.AddRange(toadd);
|
||||
|
||||
// Then recurse through and add from the directories
|
||||
List<string> subDirs = [.. Directory.GetDirectories(dir, pattern, SearchOption.TopDirectoryOnly)];
|
||||
List<string> subDirs = [.. dir.SafeEnumerateDirectories(pattern, SearchOption.TopDirectoryOnly)];
|
||||
subDirs.Sort(new NaturalComparer());
|
||||
foreach (string subdir in subDirs)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.3.7</Version>
|
||||
<Version>1.4.0</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
|
||||
36
publish-nix.sh
Executable file
36
publish-nix.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#! /bin/bash
|
||||
|
||||
# This batch file assumes the following:
|
||||
# - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
#
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# Optional parameters
|
||||
NO_BUILD=false
|
||||
while getopts "uba" OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
b)
|
||||
NO_BUILD=true
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option provided"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set the current directory as a variable
|
||||
BUILD_FOLDER=$PWD
|
||||
|
||||
# Only build if requested
|
||||
if [ $NO_BUILD = false ]
|
||||
then
|
||||
# Restore Nuget packages for all builds
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Create Nuget Package
|
||||
dotnet pack SabreTools.IO/SabreTools.IO.csproj --output $BUILD_FOLDER
|
||||
fi
|
||||
26
publish-win.ps1
Normal file
26
publish-win.ps1
Normal file
@@ -0,0 +1,26 @@
|
||||
# This batch file assumes the following:
|
||||
# - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
#
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# Optional parameters
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("NoBuild")]
|
||||
[switch]$NO_BUILD
|
||||
)
|
||||
|
||||
# Set the current directory as a variable
|
||||
$BUILD_FOLDER = $PSScriptRoot
|
||||
|
||||
# Only build if requested
|
||||
if (!$NO_BUILD.IsPresent)
|
||||
{
|
||||
# Restore Nuget packages for all builds
|
||||
Write-Host "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Create Nuget Package
|
||||
dotnet pack SabreTools.IO\SabreTools.IO.csproj --output $BUILD_FOLDER
|
||||
}
|
||||
Reference in New Issue
Block a user