mirror of
https://github.com/SabreTools/SabreTools.IO.git
synced 2026-02-08 13:49:55 +00:00
Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f3a9d0ede | ||
|
|
4400f0bb87 | ||
|
|
9f12ee642d | ||
|
|
b084b13ec3 | ||
|
|
93b3accbfd | ||
|
|
e96c3114d9 | ||
|
|
04f095018c | ||
|
|
ff18049832 | ||
|
|
5e7d2901c8 | ||
|
|
add7bc8ea5 | ||
|
|
f95853cc87 | ||
|
|
3f146d45a8 | ||
|
|
f3689087e6 | ||
|
|
d2d191d86f | ||
|
|
d6cc7faea8 | ||
|
|
3b56150cc9 | ||
|
|
a7b50dfdf2 | ||
|
|
0b7ab5b932 | ||
|
|
e43560cbbd | ||
|
|
861bfdc4f4 | ||
|
|
933dd70654 | ||
|
|
0d2a2a3b7d | ||
|
|
6ad4872bd4 | ||
|
|
8c0f54c059 | ||
|
|
ab1b0646c4 | ||
|
|
450d8aab11 | ||
|
|
ec8908aec0 | ||
|
|
fb7ca7cde0 | ||
|
|
67ca20f71b | ||
|
|
ae3a27eee1 | ||
|
|
3b7f910a98 | ||
|
|
244edc132f | ||
|
|
cec495d55a | ||
|
|
7656734bb2 | ||
|
|
fa9310de39 | ||
|
|
fe466dfe25 | ||
|
|
d2c59c565f | ||
|
|
4e221f33d5 | ||
|
|
c5c8ce67ba | ||
|
|
8e3d204329 | ||
|
|
200d947f30 | ||
|
|
dfccdcfb05 | ||
|
|
72ff3ead48 | ||
|
|
fb15aecb87 | ||
|
|
897e54ca61 | ||
|
|
3af6bc8365 | ||
|
|
de05bae3f8 | ||
|
|
97d603abb7 | ||
|
|
ed32302447 | ||
|
|
c125dc4ec0 | ||
|
|
f154ae47c0 | ||
|
|
0d0e960b98 | ||
|
|
4a9f84ab66 | ||
|
|
39277ee443 | ||
|
|
ed367ace6d | ||
|
|
80e72832a4 | ||
|
|
8924a50432 | ||
|
|
97f00a2565 | ||
|
|
f35231d95b | ||
|
|
96c6bba93e | ||
|
|
b0d81f225b | ||
|
|
ef699ee1fb | ||
|
|
0910b716ba | ||
|
|
584feb33e6 | ||
|
|
b99c80390e | ||
|
|
3a79646650 | ||
|
|
f16f05beb9 | ||
|
|
e901b52143 | ||
|
|
a638b146b4 | ||
|
|
6bfc961a87 | ||
|
|
a825bae039 | ||
|
|
e0eba8e5bb | ||
|
|
fb4b533dfb | ||
|
|
6162af2216 | ||
|
|
3b5fd128f0 | ||
|
|
9beb2177aa | ||
|
|
f0033af712 | ||
|
|
3de5b2378d | ||
|
|
ee7ce59627 | ||
|
|
39bf9c19ad | ||
|
|
32cab49bae | ||
|
|
5d71957841 | ||
|
|
7ea182c7d8 | ||
|
|
b97ec13661 | ||
|
|
8caeea053f | ||
|
|
a7476b6ac9 | ||
|
|
f0095f9e41 | ||
|
|
a0b5ea1368 | ||
|
|
a94d2c8c64 | ||
|
|
8c19ad712a | ||
|
|
0317f751b9 | ||
|
|
b8d431b06b | ||
|
|
3fcf10e2f7 | ||
|
|
40e439b18c | ||
|
|
bf707b1c11 | ||
|
|
d074a6a7ee | ||
|
|
0c736c2491 | ||
|
|
964506057d | ||
|
|
cd08925411 | ||
|
|
6ea8aab7c7 | ||
|
|
561dbdcc9a | ||
|
|
b4bad28823 | ||
|
|
ec9db7e732 | ||
|
|
3c7401fefc | ||
|
|
09e66c9ec3 | ||
|
|
8d1bc3957c | ||
|
|
245ca9010a | ||
|
|
88207100f1 | ||
|
|
3befd9255a | ||
|
|
37f2848bb2 | ||
|
|
c5dca60d28 | ||
|
|
351e46534d | ||
|
|
d39324c887 | ||
|
|
163f49281d | ||
|
|
03d0f7dd18 | ||
|
|
fbbe77f5f2 | ||
|
|
4bffd9d31c | ||
|
|
904aed1c44 | ||
|
|
69a41b2487 | ||
|
|
f326c921e6 | ||
|
|
73c4e8dd50 | ||
|
|
e4c8bbc3f9 | ||
|
|
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,4 +1,4 @@
|
||||
name: Nuget Pack
|
||||
name: Build and Test
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -16,31 +16,22 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build library
|
||||
run: dotnet build
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: 'SabreTools.IO/bin/Release/*.nupkg'
|
||||
- name: Run publish script
|
||||
run: ./publish-nix.sh
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'SabreTools.IO/bin/Release/*.nupkg'
|
||||
artifacts: "*.nupkg,*.snupkg"
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
7
.github/workflows/check_pr.yml
vendored
7
.github/workflows/check_pr.yml
vendored
@@ -11,10 +11,13 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
run: dotnet test
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# SabreTools.IO
|
||||
|
||||
[](https://github.com/SabreTools/SabreTools.IO/actions/workflows/build_and_test.yml)
|
||||
|
||||
This library comprises I/O functionality for the following file types:
|
||||
|
||||
- ClrMamePro-derived Metadata files
|
||||
@@ -9,3 +11,9 @@ This library comprises I/O functionality for the following file types:
|
||||
There are also some extensions that are useful for wrapping common functionality required by SabreTools.
|
||||
|
||||
Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTools.IO).
|
||||
|
||||
## Releases
|
||||
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.IO/releases)
|
||||
|
||||
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/SabreTools.IO/releases/rolling)
|
||||
|
||||
@@ -1,17 +1,159 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class BinaryReaderExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test pattern from 0x00-0x0F
|
||||
/// </summary>
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the decimal value 0.0123456789
|
||||
/// </summary>
|
||||
private static readonly byte[] _decimalBytes =
|
||||
[
|
||||
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadByteArrayTest()
|
||||
{
|
||||
byte[] arr = new byte[4];
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.Read(arr, 0, 4);
|
||||
Assert.Equal(4, read);
|
||||
Assert.True(arr.SequenceEqual(_bytes.Take(4)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadByteArrayBigEndianTest()
|
||||
{
|
||||
byte[] arr = new byte[4];
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadBigEndian(arr, 0, 4);
|
||||
Assert.Equal(4, read);
|
||||
Assert.True(arr.SequenceEqual(_bytes.Take(4).Reverse()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharArrayTest()
|
||||
{
|
||||
char[] arr = new char[4];
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.Read(arr, 0, 4);
|
||||
Assert.Equal(4, read);
|
||||
Assert.True(arr.SequenceEqual(_bytes.Take(4).Select(b => (char)b)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharArrayBigEndianTest()
|
||||
{
|
||||
char[] arr = new char[4];
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadBigEndian(arr, 0, 4);
|
||||
Assert.Equal(4, read);
|
||||
Assert.True(arr.SequenceEqual(_bytes.Take(4).Select(b => (char)b).Reverse()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadByteTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
byte read = br.ReadByte();
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int length = 4;
|
||||
byte[] read = br.ReadBytes(length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int length = 4;
|
||||
byte[] read = br.ReadBytesBigEndian(length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length).Reverse()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharsTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int length = 4;
|
||||
char[] read = br.ReadChars(length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length).Select(b => (char)b)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharsBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int length = 4;
|
||||
char[] read = br.ReadCharsBigEndian(length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length).Select(b => (char)b).Reverse()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSByteTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
sbyte read = br.ReadSByte();
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
char read = br.ReadChar();
|
||||
Assert.Equal('\0', read);
|
||||
}
|
||||
|
||||
[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 +163,24 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
short read = br.ReadInt16LittleEndian();
|
||||
Assert.Equal(0x0100, 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 +190,127 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ushort read = br.ReadUInt16LittleEndian();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ushort read = br.ReadWORD();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ushort read = br.ReadWORDBigEndian();
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDLittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ushort read = br.ReadWORDLittleEndian();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadHalfTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
Half expected = BitConverter.Int16BitsToHalf(0x0100);
|
||||
Half read = br.ReadHalf();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadHalfBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
Half expected = BitConverter.Int16BitsToHalf(0x0001);
|
||||
Half read = br.ReadHalfBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadInt24();
|
||||
Assert.Equal(0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadInt24BigEndian();
|
||||
Assert.Equal(0x000102, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadInt24LittleEndian();
|
||||
Assert.Equal(0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadUInt24();
|
||||
Assert.Equal((uint)0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadUInt24BigEndian();
|
||||
Assert.Equal((uint)0x000102, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadUInt24LittleEndian();
|
||||
Assert.Equal((uint)0x020100, 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 +320,24 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
int read = br.ReadInt32LittleEndian();
|
||||
Assert.Equal(0x03020100, 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 +347,125 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadUInt32LittleEndian();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadDWORD();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadDWORDBigEndian();
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDLittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
uint read = br.ReadDWORDLittleEndian();
|
||||
Assert.Equal((uint)0x03020100, 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 ReadInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
long read = br.ReadInt48();
|
||||
Assert.Equal(0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
long read = br.ReadInt48BigEndian();
|
||||
Assert.Equal(0x000102030405, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
long read = br.ReadInt48LittleEndian();
|
||||
Assert.Equal(0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadUInt48();
|
||||
Assert.Equal((ulong)0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadUInt48BigEndian();
|
||||
Assert.Equal((ulong)0x000102030405, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadUInt48LittleEndian();
|
||||
Assert.Equal((ulong)0x050403020100, 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 +475,24 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal(0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
long read = br.ReadInt64LittleEndian();
|
||||
Assert.Equal(0x0706050403020100, 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 +502,430 @@ namespace SabreTools.IO.Test
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
// TODO: Add byte[], char[] tests
|
||||
// TODO: Add float, double tests
|
||||
// TODO: Add string reading tests
|
||||
[Fact]
|
||||
public void ReadUInt64LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadUInt64LittleEndian();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadQWORD();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadQWORDBigEndian();
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDLittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
ulong read = br.ReadQWORDLittleEndian();
|
||||
Assert.Equal((ulong)0x0706050403020100, 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 ReadDecimalTest()
|
||||
{
|
||||
var stream = new MemoryStream(_decimalBytes);
|
||||
var br = new BinaryReader(stream);
|
||||
decimal expected = 0.0123456789M;
|
||||
decimal read = br.ReadDecimal();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDecimalBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_decimalBytes.Reverse().ToArray());
|
||||
var br = new BinaryReader(stream);
|
||||
decimal expected = 0.0123456789M;
|
||||
decimal read = br.ReadDecimalBigEndian();
|
||||
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 = (Int128)new BigInteger(_bytes);
|
||||
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 = (Int128)new BigInteger(reversed);
|
||||
Int128 read = br.ReadInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = (UInt128)new BigInteger(_bytes);
|
||||
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 = (UInt128)new BigInteger(reversed);
|
||||
UInt128 read = br.ReadUInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedStringTest()
|
||||
{
|
||||
// Encoding.ASCII
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
string? actual = br.ReadNullTerminatedString(Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF8
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Unicode
|
||||
bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF32
|
||||
bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
br = new BinaryReader(stream);
|
||||
actual = br.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeTest()
|
||||
{
|
||||
// Guid
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expectedGuid = new Guid(_bytes);
|
||||
Guid actualGuid = br.ReadType<Guid>();
|
||||
Assert.Equal(expectedGuid, actualGuid);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
stream = new MemoryStream(_bytes);
|
||||
br = new BinaryReader(stream);
|
||||
Half expectedHalf = BitConverter.Int16BitsToHalf(0x0100);
|
||||
Half actualHalf = br.ReadType<Half>();
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
stream = new MemoryStream(_bytes);
|
||||
br = new BinaryReader(stream);
|
||||
Int128 expectedInt128 = (Int128)new BigInteger(_bytes);
|
||||
Int128 actualInt128 = br.ReadType<Int128>();
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
|
||||
// UInt128
|
||||
stream = new MemoryStream(_bytes);
|
||||
br = new BinaryReader(stream);
|
||||
UInt128 expectedUInt128 = (UInt128)new BigInteger(_bytes);
|
||||
UInt128 actualUInt128 = br.ReadType<UInt128>();
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
stream = new MemoryStream(_bytes);
|
||||
br = new BinaryReader(stream);
|
||||
TestEnum expectedTestEnum = (TestEnum)0x03020100;
|
||||
TestEnum actualTestEnum = br.ReadType<TestEnum>();
|
||||
Assert.Equal(expectedTestEnum, actualTestEnum);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeExplicitTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(bytesWithString);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new TestStructExplicit
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0504,
|
||||
FourthValue = 0x0706,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
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);
|
||||
Assert.Equal(expected.FifthValue, read.FifthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeSequentialTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(bytesWithString);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new TestStructSequential
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
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);
|
||||
Assert.Equal(expected.FifthValue, read.FifthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeStringsTest()
|
||||
{
|
||||
byte[] structBytes =
|
||||
[
|
||||
0x03, 0x41, 0x42, 0x43, // AnsiBStr
|
||||
0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, // BStr
|
||||
0x41, 0x42, 0x43, // ByValTStr
|
||||
0x41, 0x42, 0x43, 0x00, // LPStr
|
||||
0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, // LPWStr
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(structBytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new TestStructStrings
|
||||
{
|
||||
AnsiBStr = "ABC",
|
||||
BStr = "ABC",
|
||||
ByValTStr = "ABC",
|
||||
LPStr = "ABC",
|
||||
LPWStr = "ABC",
|
||||
};
|
||||
var read = br.ReadType<TestStructStrings>();
|
||||
Assert.Equal(expected.AnsiBStr, read.AnsiBStr);
|
||||
Assert.Equal(expected.BStr, read.BStr);
|
||||
Assert.Equal(expected.ByValTStr, read.ByValTStr);
|
||||
Assert.Equal(expected.LPStr, read.LPStr);
|
||||
Assert.Equal(expected.LPWStr, read.LPWStr);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeArraysTest()
|
||||
{
|
||||
byte[] structBytes =
|
||||
[
|
||||
// Byte Array
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
|
||||
// Int Array
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
0x04, 0x03, 0x02, 0x01,
|
||||
0x05, 0x04, 0x03, 0x02,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
|
||||
// Enum Array
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
0x04, 0x03, 0x02, 0x01,
|
||||
0x05, 0x04, 0x03, 0x02,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
|
||||
// Struct Array (X, Y)
|
||||
0xFF, 0x00, 0x00, 0xFF,
|
||||
0x00, 0xFF, 0xFF, 0x00,
|
||||
0xAA, 0x55, 0x55, 0xAA,
|
||||
0x55, 0xAA, 0xAA, 0x55,
|
||||
|
||||
// LPArray
|
||||
0x04, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(structBytes);
|
||||
var br = new BinaryReader(stream);
|
||||
var expected = new TestStructArrays
|
||||
{
|
||||
ByteArray = [0x00, 0x01, 0x02, 0x03],
|
||||
IntArray = [0x00010203, 0x01020304, 0x02030405, 0x03040506],
|
||||
EnumArray =
|
||||
[
|
||||
(TestEnum)0x00010203,
|
||||
(TestEnum)0x01020304,
|
||||
(TestEnum)0x02030405,
|
||||
(TestEnum)0x03040506,
|
||||
],
|
||||
StructArray =
|
||||
[
|
||||
new TestStructPoint { X = 0x00FF, Y = 0xFF00 },
|
||||
new TestStructPoint { X = 0xFF00, Y = 0x00FF },
|
||||
new TestStructPoint { X = 0x55AA, Y = 0xAA55 },
|
||||
new TestStructPoint { X = 0xAA55, Y = 0x55AA },
|
||||
],
|
||||
LPByteArrayLength = 0x0004,
|
||||
LPByteArray = [0x00, 0x01, 0x02, 0x03],
|
||||
};
|
||||
var read = br.ReadType<TestStructArrays>();
|
||||
Assert.NotNull(read.ByteArray);
|
||||
Assert.True(expected.ByteArray.SequenceEqual(read.ByteArray));
|
||||
Assert.NotNull(read.IntArray);
|
||||
Assert.True(expected.IntArray.SequenceEqual(read.IntArray));
|
||||
Assert.NotNull(read.EnumArray);
|
||||
Assert.True(expected.EnumArray.SequenceEqual(read.EnumArray));
|
||||
Assert.NotNull(read.StructArray);
|
||||
Assert.True(expected.StructArray.SequenceEqual(read.StructArray));
|
||||
Assert.Equal(expected.LPByteArrayLength, read.LPByteArrayLength);
|
||||
Assert.NotNull(read.LPByteArray);
|
||||
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeInheritanceTest()
|
||||
{
|
||||
byte[] structBytes1 =
|
||||
[
|
||||
0x41, 0x42, 0x43, 0x44, // Signature
|
||||
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
|
||||
0xAA, 0x55, 0xAA, 0x55, // FieldA
|
||||
0x55, 0xAA, 0x55, 0xAA, // FieldB
|
||||
];
|
||||
|
||||
var stream1 = new MemoryStream(structBytes1);
|
||||
var br1 = new BinaryReader(stream1);
|
||||
var expected1 = new TestStructInheritanceChild1
|
||||
{
|
||||
Signature = [0x41, 0x42, 0x43, 0x44],
|
||||
IdentifierType = 0xFF00FF00,
|
||||
FieldA = 0x55AA55AA,
|
||||
FieldB = 0xAA55AA55,
|
||||
};
|
||||
var read1 = br1.ReadType<TestStructInheritanceChild1>();
|
||||
Assert.NotNull(read1?.Signature);
|
||||
Assert.Equal(expected1.Signature, read1.Signature);
|
||||
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
|
||||
Assert.Equal(expected1.FieldA, read1.FieldA);
|
||||
Assert.Equal(expected1.FieldB, read1.FieldB);
|
||||
|
||||
byte[] structBytes2 =
|
||||
[
|
||||
0x41, 0x42, 0x43, 0x44, // Signature
|
||||
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
|
||||
0xAA, 0x55, // FieldA
|
||||
0x55, 0xAA, // FieldB
|
||||
];
|
||||
|
||||
var stream2 = new MemoryStream(structBytes2);
|
||||
var br2 = new BinaryReader(stream2);
|
||||
var expected2 = new TestStructInheritanceChild2
|
||||
{
|
||||
Signature = [0x41, 0x42, 0x43, 0x44],
|
||||
IdentifierType = 0xFF00FF00,
|
||||
FieldA = 0x55AA,
|
||||
FieldB = 0xAA55,
|
||||
};
|
||||
var read2 = br2.ReadType<TestStructInheritanceChild2>();
|
||||
Assert.NotNull(read2?.Signature);
|
||||
Assert.Equal(expected2.Signature, read2.Signature);
|
||||
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
|
||||
Assert.Equal(expected2.FieldA, read2.FieldA);
|
||||
Assert.Equal(expected2.FieldB, read2.FieldB);
|
||||
}
|
||||
}
|
||||
}
|
||||
631
SabreTools.IO.Test/Extensions/BinaryWriterExtensionsTests.cs
Normal file
631
SabreTools.IO.Test/Extensions/BinaryWriterExtensionsTests.cs
Normal file
@@ -0,0 +1,631 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class BinaryWriterExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test pattern from 0x00-0x0F
|
||||
/// </summary>
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the decimal value 0.0123456789
|
||||
/// </summary>
|
||||
private static readonly byte[] _decimalBytes =
|
||||
[
|
||||
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void WriteByteValueTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bw.Write((byte)0x00);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bw.Write([0x00, 0x01, 0x02, 0x03]);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bw.WriteBigEndian([0x03, 0x02, 0x01, 0x00]);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSByteTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bw.Write((sbyte)0x00);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bw.Write('\0');
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharEncodingTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x00, 0x00];
|
||||
bw.Write('\0', Encoding.Unicode);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bw.Write((short)0x0100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = bw.WriteBigEndian((short)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bw.Write((ushort)0x0100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = bw.WriteBigEndian((ushort)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteHalfTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bw.Write(BitConverter.Int16BitsToHalf(0x0100));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHalfBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = bw.WriteBigEndian(BitConverter.Int16BitsToHalf(0x0001));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bw.WriteAsInt24(0x020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = bw.WriteAsInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bw.WriteAsUInt24(0x020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = bw.WriteAsUInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bw.Write(0x03020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = bw.WriteBigEndian(0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bw.Write((uint)0x03020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = bw.WriteBigEndian((uint)0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bw.Write(BitConverter.Int32BitsToSingle(0x03020100));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = bw.WriteBigEndian(BitConverter.Int32BitsToSingle(0x00010203));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bw.WriteAsInt48(0x050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = bw.WriteAsInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bw.WriteAsUInt48(0x050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = bw.WriteAsUInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bw.Write(0x0706050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = bw.WriteBigEndian(0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bw.Write((ulong)0x0706050403020100);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = bw.WriteBigEndian((ulong)0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bw.Write(BitConverter.Int64BitsToDouble(0x0706050403020100));
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = bw.WriteBigEndian(BitConverter.Int64BitsToDouble(0x0001020304050607));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDecimalTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _decimalBytes.Take(16).ToArray();
|
||||
bw.Write(0.0123456789M);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDecimalBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _decimalBytes.Take(16).Reverse().ToArray();
|
||||
bool write = bw.WriteBigEndian(0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteGuidTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.Write(new Guid(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteGuidBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.WriteBigEndian(new Guid(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.Write((Int128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.WriteBigEndian((Int128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.Write((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = bw.WriteBigEndian((UInt128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUTF8String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = bw.WriteNullTerminatedUTF32String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = bw.WritePrefixedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = bw.WritePrefixedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeTest()
|
||||
{
|
||||
// Guid
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
bool actual = bw.WriteType<Guid>(new Guid(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
stream = new MemoryStream(new byte[2], 0, 2, true, true);
|
||||
bw = new BinaryWriter(stream);
|
||||
actual = bw.WriteType<Half>(BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(2)], stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
bw = new BinaryWriter(stream);
|
||||
actual = bw.WriteType<Int128>((Int128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
|
||||
// UInt128
|
||||
stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
bw = new BinaryWriter(stream);
|
||||
actual = bw.WriteType<UInt128>((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
bw = new BinaryWriter(stream);
|
||||
actual = bw.WriteType<TestEnum>((TestEnum)0x03020100);
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(4)], stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeExplicitTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
var obj = new TestStructExplicit
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(12).ToArray();
|
||||
bool write = bw.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeSequentialTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(new byte[24], 0, count: 24, true, true);
|
||||
var bw = new BinaryWriter(stream);
|
||||
var obj = new TestStructSequential
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(16).ToArray();
|
||||
bool write = bw.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that a set of actual bytes matches the expected bytes
|
||||
/// </summary>
|
||||
private static void ValidateBytes(byte[] expected, byte[] actual)
|
||||
{
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,247 +3,90 @@ using System.Linq;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class ByteArrayExtensionsTests
|
||||
{
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
#region Is Null or Empty
|
||||
|
||||
[Fact]
|
||||
public void ReadByteTest()
|
||||
public void IsNullOrEmpty_Null_True()
|
||||
{
|
||||
int offset = 0;
|
||||
byte read = _bytes.ReadByte(ref offset);
|
||||
Assert.Equal(0x00, read);
|
||||
byte[]? arr = null;
|
||||
bool actual = arr.IsNullOrEmpty();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadByteValueTest()
|
||||
public void IsNullOrEmpty_Empty_True()
|
||||
{
|
||||
int offset = 0;
|
||||
byte read = _bytes.ReadByteValue(ref offset);
|
||||
Assert.Equal(0x00, read);
|
||||
byte[]? arr = [];
|
||||
bool actual = arr.IsNullOrEmpty();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesTest()
|
||||
public void IsNullOrEmpty_NonEmpty_False()
|
||||
{
|
||||
int offset = 0, length = 4;
|
||||
byte[] read = _bytes.ReadBytes(ref offset, length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length)));
|
||||
byte[]? arr = [0x01];
|
||||
bool actual = arr.IsNullOrEmpty();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region To Hex String
|
||||
|
||||
[Fact]
|
||||
public void ToHexString_Null()
|
||||
{
|
||||
byte[]? arr = null;
|
||||
string? actual = arr.ToHexString();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSByteTest()
|
||||
public void ToHexString_Valid()
|
||||
{
|
||||
int offset = 0;
|
||||
sbyte read = _bytes.ReadSByte(ref offset);
|
||||
Assert.Equal(0x00, read);
|
||||
byte[]? arr = [0x01, 0x02, 0x03, 0x04];
|
||||
string expected = "01020304";
|
||||
|
||||
string? actual = arr.ToHexString();
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region From Hex String
|
||||
|
||||
[Fact]
|
||||
public void FromHexString_Null()
|
||||
{
|
||||
string? str = null;
|
||||
byte[]? actual = str.FromHexString();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharTest()
|
||||
public void FromHexString_Valid()
|
||||
{
|
||||
int offset = 0;
|
||||
char read = _bytes.ReadChar(ref offset);
|
||||
Assert.Equal('\0', read);
|
||||
string str = "01020304";
|
||||
byte[]? expected = [0x01, 0x02, 0x03, 0x04];
|
||||
|
||||
byte[]? actual = str.FromHexString();
|
||||
Assert.NotNull(actual);
|
||||
Assert.True(expected.SequenceEqual(actual));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16Test()
|
||||
public void FromHexString_Invalid()
|
||||
{
|
||||
int offset = 0;
|
||||
short read = _bytes.ReadInt16(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
string str = "0102030G";
|
||||
byte[]? actual = str.FromHexString();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
short read = _bytes.ReadInt16BigEndian(ref offset);
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16Test()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadUInt16(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadUInt16BigEndian(ref offset);
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32Test()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt32(ref offset);
|
||||
Assert.Equal(0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt32BigEndian(ref offset);
|
||||
Assert.Equal(0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32Test()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt32(ref offset);
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt32BigEndian(ref offset);
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleTest()
|
||||
{
|
||||
int offset = 0;
|
||||
float expected = BitConverter.Int32BitsToSingle(0x03020100);
|
||||
float read = _bytes.ReadSingle(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
float expected = BitConverter.Int32BitsToSingle(0x00010203);
|
||||
float read = _bytes.ReadSingleBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64Test()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt64(ref offset);
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt64BigEndian(ref offset);
|
||||
Assert.Equal(0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64Test()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt64(ref offset);
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt64BigEndian(ref offset);
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleTest()
|
||||
{
|
||||
int offset = 0;
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
|
||||
double read = _bytes.ReadDouble(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
|
||||
double read = _bytes.ReadDoubleBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new Guid(_bytes);
|
||||
Guid read = _bytes.ReadGuid(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndian()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
Guid read = _bytes.ReadGuidBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadInt128Test()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new Int128(BitConverter.ToUInt64(_bytes, 0), BitConverter.ToUInt64(_bytes, 8));
|
||||
Int128 read = _bytes.ReadInt128(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt128BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = new Int128(BitConverter.ToUInt64(reversed, 0), BitConverter.ToUInt64(reversed, 8));
|
||||
Int128 read = _bytes.ReadInt128BigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128Test()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new UInt128(BitConverter.ToUInt64(_bytes, 0), BitConverter.ToUInt64(_bytes, 8));
|
||||
UInt128 read = _bytes.ReadUInt128(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = new UInt128(BitConverter.ToUInt64(reversed, 0), BitConverter.ToUInt64(reversed, 8));
|
||||
UInt128 read = _bytes.ReadUInt128BigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: Add string reading tests
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
786
SabreTools.IO.Test/Extensions/ByteArrayReaderExtensionsTests.cs
Normal file
786
SabreTools.IO.Test/Extensions/ByteArrayReaderExtensionsTests.cs
Normal file
@@ -0,0 +1,786 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class ByteArrayReaderExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test pattern from 0x00-0x0F
|
||||
/// </summary>
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the decimal value 0.0123456789
|
||||
/// </summary>
|
||||
private static readonly byte[] _decimalBytes =
|
||||
[
|
||||
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadByteTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte read = _bytes.ReadByte(ref offset);
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadByteValueTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte read = _bytes.ReadByteValue(ref offset);
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesTest()
|
||||
{
|
||||
int offset = 0, length = 4;
|
||||
byte[] read = _bytes.ReadBytes(ref offset, length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSByteTest()
|
||||
{
|
||||
int offset = 0;
|
||||
sbyte read = _bytes.ReadSByte(ref offset);
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharTest()
|
||||
{
|
||||
int offset = 0;
|
||||
char read = _bytes.ReadChar(ref offset);
|
||||
Assert.Equal('\0', read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16Test()
|
||||
{
|
||||
int offset = 0;
|
||||
short read = _bytes.ReadInt16(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
short read = _bytes.ReadInt16BigEndian(ref offset);
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
short read = _bytes.ReadInt16LittleEndian(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16Test()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadUInt16(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadUInt16BigEndian(ref offset);
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadUInt16LittleEndian(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadWORD(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadWORDBigEndian(ref offset);
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDLittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ushort read = _bytes.ReadWORDLittleEndian(ref offset);
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadHalfTest()
|
||||
{
|
||||
int offset = 0;
|
||||
Half expected = BitConverter.Int16BitsToHalf(0x0100);
|
||||
Half read = _bytes.ReadHalf(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadHalfBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
Half expected = BitConverter.Int16BitsToHalf(0x0001);
|
||||
Half read = _bytes.ReadHalfBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24Test()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt24(ref offset);
|
||||
Assert.Equal(0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt24BigEndian(ref offset);
|
||||
Assert.Equal(0x000102, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt24LittleEndian(ref offset);
|
||||
Assert.Equal(0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24Test()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt24(ref offset);
|
||||
Assert.Equal((uint)0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt24BigEndian(ref offset);
|
||||
Assert.Equal((uint)0x000102, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt24LittleEndian(ref offset);
|
||||
Assert.Equal((uint)0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32Test()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt32(ref offset);
|
||||
Assert.Equal(0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt32BigEndian(ref offset);
|
||||
Assert.Equal(0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
int read = _bytes.ReadInt32LittleEndian(ref offset);
|
||||
Assert.Equal(0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32Test()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt32(ref offset);
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt32BigEndian(ref offset);
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadUInt32LittleEndian(ref offset);
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadDWORD(ref offset);
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadDWORDBigEndian(ref offset);
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDLittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
uint read = _bytes.ReadDWORDLittleEndian(ref offset);
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleTest()
|
||||
{
|
||||
int offset = 0;
|
||||
float expected = BitConverter.Int32BitsToSingle(0x03020100);
|
||||
float read = _bytes.ReadSingle(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
float expected = BitConverter.Int32BitsToSingle(0x00010203);
|
||||
float read = _bytes.ReadSingleBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48Test()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt48(ref offset);
|
||||
Assert.Equal(0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt48BigEndian(ref offset);
|
||||
Assert.Equal(0x000102030405, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt48LittleEndian(ref offset);
|
||||
Assert.Equal(0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48Test()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt48(ref offset);
|
||||
Assert.Equal((ulong)0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt48BigEndian(ref offset);
|
||||
Assert.Equal((ulong)0x000102030405, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt48LittleEndian(ref offset);
|
||||
Assert.Equal((ulong)0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64Test()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt64(ref offset);
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt64BigEndian(ref offset);
|
||||
Assert.Equal(0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
long read = _bytes.ReadInt64LittleEndian(ref offset);
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64Test()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt64(ref offset);
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt64BigEndian(ref offset);
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64LittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadUInt64LittleEndian(ref offset);
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadQWORD(ref offset);
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadQWORDBigEndian(ref offset);
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDLittleEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
ulong read = _bytes.ReadQWORDLittleEndian(ref offset);
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleTest()
|
||||
{
|
||||
int offset = 0;
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
|
||||
double read = _bytes.ReadDouble(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
|
||||
double read = _bytes.ReadDoubleBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDecimalTest()
|
||||
{
|
||||
int offset = 0;
|
||||
decimal expected = 0.0123456789M;
|
||||
decimal read = _decimalBytes.ReadDecimal(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDecimalBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
decimal expected = 0.0123456789M;
|
||||
decimal read = _decimalBytes.Reverse().ToArray().ReadDecimalBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new Guid(_bytes);
|
||||
Guid read = _bytes.ReadGuid(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
Guid read = _bytes.ReadGuidBigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadInt128Test()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = (Int128)new BigInteger(_bytes);
|
||||
Int128 read = _bytes.ReadInt128(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt128BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = (Int128)new BigInteger(reversed);
|
||||
Int128 read = _bytes.ReadInt128BigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128Test()
|
||||
{
|
||||
int offset = 0;
|
||||
var expected = (UInt128)new BigInteger(_bytes);
|
||||
UInt128 read = _bytes.ReadUInt128(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128BigEndianTest()
|
||||
{
|
||||
int offset = 0;
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = (UInt128)new BigInteger(reversed);
|
||||
UInt128 read = _bytes.ReadUInt128BigEndian(ref offset);
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedStringTest()
|
||||
{
|
||||
// Encoding.ASCII
|
||||
int offset = 0;
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
string? actual = bytes.ReadNullTerminatedString(ref offset, Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF8
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Unicode
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF32
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
offset = 0;
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
actual = bytes.ReadNullTerminatedString(ref offset, Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeTest()
|
||||
{
|
||||
// Guid
|
||||
int offset = 0;
|
||||
var expectedGuid = new Guid(_bytes);
|
||||
Guid actualGuid = _bytes.ReadType<Guid>(ref offset);
|
||||
Assert.Equal(expectedGuid, actualGuid);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
offset = 0;
|
||||
Half expectedHalf = BitConverter.Int16BitsToHalf(0x0100);
|
||||
Half actualHalf = _bytes.ReadType<Half>(ref offset);
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
offset = 0;
|
||||
Int128 expectedInt128 = (Int128)new BigInteger(_bytes);
|
||||
Int128 actualInt128 = _bytes.ReadType<Int128>(ref offset);
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
|
||||
// UInt128
|
||||
offset = 0;
|
||||
UInt128 expectedUInt128 = (UInt128)new BigInteger(_bytes);
|
||||
UInt128 actualUInt128 = _bytes.ReadType<UInt128>(ref offset);
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
offset = 0;
|
||||
TestEnum expectedTestEnum = (TestEnum)0x03020100;
|
||||
TestEnum actualTestEnum = _bytes.ReadType<TestEnum>(ref offset);
|
||||
Assert.Equal(expectedTestEnum, actualTestEnum);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeExplicitTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
int offset = 0;
|
||||
var expected = new TestStructExplicit
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0504,
|
||||
FourthValue = 0x0706,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
var read = bytesWithString.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()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
int offset = 0;
|
||||
var expected = new TestStructSequential
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
var read = bytesWithString.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);
|
||||
Assert.Equal(expected.FifthValue, read.FifthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeStringsTest()
|
||||
{
|
||||
byte[] structBytes =
|
||||
[
|
||||
0x03, 0x41, 0x42, 0x43, // AnsiBStr
|
||||
0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, // BStr
|
||||
0x41, 0x42, 0x43, // ByValTStr
|
||||
0x41, 0x42, 0x43, 0x00, // LPStr
|
||||
0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, // LPWStr
|
||||
];
|
||||
|
||||
int offset = 0;
|
||||
var expected = new TestStructStrings
|
||||
{
|
||||
AnsiBStr = "ABC",
|
||||
BStr = "ABC",
|
||||
ByValTStr = "ABC",
|
||||
LPStr = "ABC",
|
||||
LPWStr = "ABC",
|
||||
};
|
||||
var read = structBytes.ReadType<TestStructStrings>(ref offset);
|
||||
Assert.Equal(expected.AnsiBStr, read.AnsiBStr);
|
||||
Assert.Equal(expected.BStr, read.BStr);
|
||||
Assert.Equal(expected.ByValTStr, read.ByValTStr);
|
||||
Assert.Equal(expected.LPStr, read.LPStr);
|
||||
Assert.Equal(expected.LPWStr, read.LPWStr);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeArraysTest()
|
||||
{
|
||||
byte[] structBytes =
|
||||
[
|
||||
// Byte Array
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
|
||||
// Int Array
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
0x04, 0x03, 0x02, 0x01,
|
||||
0x05, 0x04, 0x03, 0x02,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
|
||||
// Enum Array
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
0x04, 0x03, 0x02, 0x01,
|
||||
0x05, 0x04, 0x03, 0x02,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
|
||||
// Struct Array (X, Y)
|
||||
0xFF, 0x00, 0x00, 0xFF,
|
||||
0x00, 0xFF, 0xFF, 0x00,
|
||||
0xAA, 0x55, 0x55, 0xAA,
|
||||
0x55, 0xAA, 0xAA, 0x55,
|
||||
|
||||
// LPArray
|
||||
0x04, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
];
|
||||
|
||||
int offset = 0;
|
||||
var expected = new TestStructArrays
|
||||
{
|
||||
ByteArray = [0x00, 0x01, 0x02, 0x03],
|
||||
IntArray = [0x00010203, 0x01020304, 0x02030405, 0x03040506],
|
||||
EnumArray =
|
||||
[
|
||||
(TestEnum)0x00010203,
|
||||
(TestEnum)0x01020304,
|
||||
(TestEnum)0x02030405,
|
||||
(TestEnum)0x03040506,
|
||||
],
|
||||
StructArray =
|
||||
[
|
||||
new TestStructPoint { X = 0x00FF, Y = 0xFF00 },
|
||||
new TestStructPoint { X = 0xFF00, Y = 0x00FF },
|
||||
new TestStructPoint { X = 0x55AA, Y = 0xAA55 },
|
||||
new TestStructPoint { X = 0xAA55, Y = 0x55AA },
|
||||
],
|
||||
LPByteArrayLength = 0x0004,
|
||||
LPByteArray = [0x00, 0x01, 0x02, 0x03],
|
||||
};
|
||||
var read = structBytes.ReadType<TestStructArrays>(ref offset);
|
||||
Assert.NotNull(read.ByteArray);
|
||||
Assert.True(expected.ByteArray.SequenceEqual(read.ByteArray));
|
||||
Assert.NotNull(read.IntArray);
|
||||
Assert.True(expected.IntArray.SequenceEqual(read.IntArray));
|
||||
Assert.NotNull(read.EnumArray);
|
||||
Assert.True(expected.EnumArray.SequenceEqual(read.EnumArray));
|
||||
Assert.NotNull(read.StructArray);
|
||||
Assert.True(expected.StructArray.SequenceEqual(read.StructArray));
|
||||
Assert.Equal(expected.LPByteArrayLength, read.LPByteArrayLength);
|
||||
Assert.NotNull(read.LPByteArray);
|
||||
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeInheritanceTest()
|
||||
{
|
||||
byte[] structBytes1 =
|
||||
[
|
||||
0x41, 0x42, 0x43, 0x44, // Signature
|
||||
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
|
||||
0xAA, 0x55, 0xAA, 0x55, // FieldA
|
||||
0x55, 0xAA, 0x55, 0xAA, // FieldB
|
||||
];
|
||||
|
||||
int offset1 = 0;
|
||||
var expected1 = new TestStructInheritanceChild1
|
||||
{
|
||||
Signature = [0x41, 0x42, 0x43, 0x44],
|
||||
IdentifierType = 0xFF00FF00,
|
||||
FieldA = 0x55AA55AA,
|
||||
FieldB = 0xAA55AA55,
|
||||
};
|
||||
var read1 = structBytes1.ReadType<TestStructInheritanceChild1>(ref offset1);
|
||||
Assert.NotNull(read1?.Signature);
|
||||
Assert.Equal(expected1.Signature, read1.Signature);
|
||||
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
|
||||
Assert.Equal(expected1.FieldA, read1.FieldA);
|
||||
Assert.Equal(expected1.FieldB, read1.FieldB);
|
||||
|
||||
byte[] structBytes2 =
|
||||
[
|
||||
0x41, 0x42, 0x43, 0x44, // Signature
|
||||
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
|
||||
0xAA, 0x55, // FieldA
|
||||
0x55, 0xAA, // FieldB
|
||||
];
|
||||
|
||||
int offset2 = 0;
|
||||
var expected2 = new TestStructInheritanceChild2
|
||||
{
|
||||
Signature = [0x41, 0x42, 0x43, 0x44],
|
||||
IdentifierType = 0xFF00FF00,
|
||||
FieldA = 0x55AA,
|
||||
FieldB = 0xAA55,
|
||||
};
|
||||
var read2 = structBytes2.ReadType<TestStructInheritanceChild2>(ref offset2);
|
||||
Assert.NotNull(read2?.Signature);
|
||||
Assert.Equal(expected2.Signature, read2.Signature);
|
||||
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
|
||||
Assert.Equal(expected2.FieldA, read2.FieldA);
|
||||
Assert.Equal(expected2.FieldB, read2.FieldB);
|
||||
}
|
||||
}
|
||||
}
|
||||
650
SabreTools.IO.Test/Extensions/ByteArrayWriterExtensionsTests.cs
Normal file
650
SabreTools.IO.Test/Extensions/ByteArrayWriterExtensionsTests.cs
Normal file
@@ -0,0 +1,650 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class ByteArrayWriterExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test pattern from 0x00-0x0F
|
||||
/// </summary>
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the decimal value 0.0123456789
|
||||
/// </summary>
|
||||
private static readonly byte[] _decimalBytes =
|
||||
[
|
||||
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void WriteByteTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bool write = buffer.Write(ref offset, (byte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.Write(ref offset, [0x00, 0x01, 0x02, 0x03]);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesBigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, [0x03, 0x02, 0x01, 0x00]);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSByteTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bool write = buffer.Write(ref offset, (sbyte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bool write = buffer.Write(ref offset, '\0');
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharEncodingTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = [0x00, 0x00];
|
||||
bool write = buffer.Write(ref offset, '\0', Encoding.Unicode);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = buffer.Write(ref offset, (short)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (short)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = buffer.Write(ref offset, (ushort)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (ushort)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteHalfTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = buffer.Write(ref offset, BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHalfBigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int16BitsToHalf(0x0001));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = buffer.WriteAsInt24(ref offset, 0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = buffer.WriteAsInt24BigEndian(ref offset, 0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt24Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = buffer.WriteAsUInt24(ref offset, 0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt24BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = buffer.WriteAsUInt24BigEndian(ref offset, 0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.Write(ref offset, 0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, 0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.Write(ref offset, (uint)0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (uint)0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.Write(ref offset, BitConverter.Int32BitsToSingle(0x03020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleBigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int32BitsToSingle(0x00010203));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt48Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = buffer.WriteAsInt48(ref offset, 0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt48BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = buffer.WriteAsInt48BigEndian(ref offset, 0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt48Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = buffer.WriteAsUInt48(ref offset, 0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt48BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = buffer.WriteAsUInt48BigEndian(ref offset, 0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = buffer.Write(ref offset, 0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, 0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = buffer.Write(ref offset, (ulong)0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (ulong)0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = buffer.Write(ref offset, BitConverter.Int64BitsToDouble(0x0706050403020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleBigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, BitConverter.Int64BitsToDouble(0x0001020304050607));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDecimalTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _decimalBytes.Take(16).ToArray();
|
||||
bool write = buffer.Write(ref offset, 0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDecimalBigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _decimalBytes.Take(16).Reverse().ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, 0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteGuidTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.Write(ref offset, new Guid(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteGuidBigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, new Guid(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteInt128Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.Write(ref offset, (Int128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt128BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (Int128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt128Test()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.Write(ref offset, (UInt128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt128BigEndianTest()
|
||||
{
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = buffer.WriteBigEndian(ref offset, (UInt128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedAnsiString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUTF8String(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[8];
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUnicodeString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[16];
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = buffer.WriteNullTerminatedUTF32String(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[4];
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = buffer.WritePrefixedAnsiString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[8];
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = buffer.WritePrefixedUnicodeString(ref offset, "ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeTest()
|
||||
{
|
||||
// Guid
|
||||
int offset = 0;
|
||||
byte[] buffer = new byte[16];
|
||||
bool actual = buffer.WriteType<Guid>(ref offset, new Guid(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, buffer);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
offset = 0;
|
||||
buffer = new byte[2];
|
||||
actual = buffer.WriteType<Half>(ref offset, BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(2)], buffer);
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
offset = 0;
|
||||
buffer = new byte[16];
|
||||
actual = buffer.WriteType<Int128>(ref offset, (Int128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, buffer);
|
||||
|
||||
// UInt128
|
||||
offset = 0;
|
||||
buffer = new byte[16];
|
||||
actual = buffer.WriteType<UInt128>(ref offset, (UInt128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, buffer);
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
offset = 0;
|
||||
buffer = new byte[4];
|
||||
actual = buffer.WriteType<TestEnum>(ref offset, (TestEnum)0x03020100);
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(4)], buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeExplicitTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
byte[] buffer = new byte[16];
|
||||
int offset = 0;
|
||||
var obj = new TestStructExplicit
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(12).ToArray();
|
||||
bool write = buffer.WriteType(ref offset, obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeSequentialTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
byte[] buffer = new byte[24];
|
||||
int offset = 0;
|
||||
var obj = new TestStructSequential
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(16).ToArray();
|
||||
bool write = buffer.WriteType(ref offset, obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that a set of actual bytes matches the expected bytes
|
||||
/// </summary>
|
||||
private static void ValidateBytes(byte[] expected, byte[] actual)
|
||||
{
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
SabreTools.IO.Test/Extensions/EnumerableExtensionsTests.cs
Normal file
163
SabreTools.IO.Test/Extensions/EnumerableExtensionsTests.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
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 SafeEnumerate_Empty()
|
||||
{
|
||||
var source = Enumerable.Empty<string>();
|
||||
var safe = source.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Empty(list);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerate_Throws()
|
||||
{
|
||||
var source = new List<string> { "a", "ab", "abc" };
|
||||
var wrapper = new ThrowsEnumerable(source);
|
||||
|
||||
var safe = wrapper.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Empty(list);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerate_NoError()
|
||||
{
|
||||
var source = new List<string> { "a", "ab", "abc" };
|
||||
var safe = source.SafeEnumerate();
|
||||
var list = safe.ToList();
|
||||
Assert.Equal(3, list.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerate_ErrorMid()
|
||||
{
|
||||
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 SafeEnumerate_ErrorLast()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fake enumerable that throws an exception for the enumerator
|
||||
/// </summary>
|
||||
private class ThrowsEnumerable : IEnumerable<string>
|
||||
{
|
||||
public ThrowsEnumerable(IEnumerable<string> source) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<string> GetEnumerator() => throw new Exception();
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
@@ -5,6 +8,116 @@ namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class IOExtensionsTests
|
||||
{
|
||||
#region Ensure
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData("", null)]
|
||||
[InlineData(" ", " ")] // TODO: This is a bad result
|
||||
[InlineData("dirname", "dirname")]
|
||||
[InlineData("\"dirname\"", "dirname")]
|
||||
public void EnsureTest(string? dir, string? expected)
|
||||
{
|
||||
// Handle test setup
|
||||
expected ??= PathTool.GetRuntimeDirectory();
|
||||
if (expected != null)
|
||||
expected = Path.GetFullPath(expected);
|
||||
|
||||
string actual = dir.Ensure(create: false);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get Encoding
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_EmptyPath()
|
||||
{
|
||||
string path = "";
|
||||
Encoding expected = Encoding.Default;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_InvalidPath()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "INVALID");
|
||||
Encoding expected = Encoding.Default;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
// Disable warning about UTF7 usage
|
||||
#pragma warning disable SYSLIB0001
|
||||
[Fact]
|
||||
public void GetEncoding_UTF7()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf7bom.txt");
|
||||
Encoding expected = Encoding.UTF7;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
#pragma warning restore SYSLIB0001
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_UTF8()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf8bom.txt");
|
||||
Encoding expected = Encoding.UTF8;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_Unicode()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf16lebom.txt");
|
||||
Encoding expected = Encoding.Unicode;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_BigEndianUnicode()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf16bebom.txt");
|
||||
Encoding expected = Encoding.BigEndianUnicode;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_UTF32()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "utf32bom.txt");
|
||||
Encoding expected = Encoding.UTF32;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetEncoding_ASCII()
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, "TestData", "ascii.txt");
|
||||
Encoding expected = Encoding.Default;
|
||||
|
||||
var actual = path.GetEncoding();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get Normalized Extension
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData("", null)]
|
||||
@@ -15,10 +128,185 @@ namespace SabreTools.IO.Test.Extensions
|
||||
[InlineData("NO-EXTENSION.", null)]
|
||||
[InlineData("filename.ext", "ext")]
|
||||
[InlineData("FILENAME.EXT", "ext")]
|
||||
public void NormalizedExtensionTest(string? path, string? expected)
|
||||
public void GetNormalizedExtensionTest(string? path, string? expected)
|
||||
{
|
||||
string? actual = path.GetNormalizedExtension();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Path
|
||||
|
||||
[Fact]
|
||||
public void ListEmpty_NullDirectory()
|
||||
{
|
||||
string? dir = null;
|
||||
var empty = dir.ListEmpty();
|
||||
Assert.Null(empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListEmpty_InvalidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData", "INVALID");
|
||||
var empty = dir.ListEmpty();
|
||||
Assert.Null(empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListEmpty_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var empty = dir.ListEmpty();
|
||||
Assert.NotNull(empty);
|
||||
Assert.Empty(empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetDirectories_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var dirs = dir.SafeGetDirectories();
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetDirectories_ValidDirectory_Pattern()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var dirs = dir.SafeGetDirectories("*");
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetDirectories_ValidDirectory_PatternOption()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var dirs = dir.SafeGetDirectories("*", SearchOption.AllDirectories);
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFiles_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var files = dir.SafeGetFiles();
|
||||
Assert.NotEmpty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFiles_ValidDirectory_Pattern()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var files = dir.SafeGetFiles("*");
|
||||
Assert.NotEmpty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFiles_ValidDirectory_PatternOption()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var files = dir.SafeGetFiles("*", SearchOption.AllDirectories);
|
||||
Assert.NotEmpty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFileSystemEntries_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var entries = dir.SafeGetFileSystemEntries();
|
||||
Assert.NotEmpty(entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFileSystemEntries_ValidDirectory_Pattern()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var entries = dir.SafeGetFileSystemEntries("*");
|
||||
Assert.NotEmpty(entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeGetFileSystemEntries_ValidDirectory_PatternOption()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var entries = dir.SafeGetFileSystemEntries("*", SearchOption.AllDirectories);
|
||||
Assert.NotEmpty(entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateDirectories_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var dirs = dir.SafeEnumerateDirectories();
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateDirectories_ValidDirectory_Pattern()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var dirs = dir.SafeEnumerateDirectories("*");
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateDirectories_ValidDirectory_PatternOption()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var dirs = dir.SafeEnumerateDirectories("*", SearchOption.AllDirectories);
|
||||
Assert.Single(dirs);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFiles_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var files = dir.SafeEnumerateFiles();
|
||||
Assert.NotEmpty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFiles_ValidDirectory_Pattern()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var files = dir.SafeEnumerateFiles("*");
|
||||
Assert.NotEmpty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFiles_ValidDirectory_PatternOption()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var files = dir.SafeEnumerateFiles("*", SearchOption.AllDirectories);
|
||||
Assert.NotEmpty(files);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFileSystemEntries_ValidDirectory()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var entries = dir.SafeEnumerateFileSystemEntries();
|
||||
Assert.NotEmpty(entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFileSystemEntries_ValidDirectory_Pattern()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var entries = dir.SafeEnumerateFileSystemEntries("*");
|
||||
Assert.NotEmpty(entries);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SafeEnumerateFileSystemEntries_ValidDirectory_PatternOption()
|
||||
{
|
||||
string dir = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
var entries = dir.SafeEnumerateFileSystemEntries("*", SearchOption.AllDirectories);
|
||||
Assert.NotEmpty(entries);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,243 +1,232 @@
|
||||
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 StreamExtensionsTests
|
||||
{
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
#region Align to Boundary
|
||||
|
||||
[Fact]
|
||||
public void ReadByteValueTest()
|
||||
public void AlignToBoundary_Null_False()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
byte read = stream.ReadByteValue();
|
||||
Assert.Equal(0x00, read);
|
||||
Stream? stream = null;
|
||||
byte alignment = 4;
|
||||
bool actual = stream.AlignToBoundary(alignment);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesTest()
|
||||
public void AlignToBoundary_Empty_False()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int length = 4;
|
||||
byte[] read = stream.ReadBytes(length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length)));
|
||||
Stream? stream = new MemoryStream([]);
|
||||
byte alignment = 4;
|
||||
bool actual = stream.AlignToBoundary(alignment);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSByteTest()
|
||||
public void AlignToBoundary_EOF_False()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
sbyte read = stream.ReadSByte();
|
||||
Assert.Equal(0x00, read);
|
||||
Stream? stream = new MemoryStream([0x01, 0x02]);
|
||||
byte alignment = 4;
|
||||
|
||||
stream.Position = 1;
|
||||
bool actual = stream.AlignToBoundary(alignment);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharTest()
|
||||
public void AlignToBoundary_TooShort_False()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
char read = stream.ReadChar();
|
||||
Assert.Equal('\0', read);
|
||||
Stream? stream = new MemoryStream([0x01, 0x02]);
|
||||
byte alignment = 4;
|
||||
|
||||
stream.Position = 1;
|
||||
bool actual = stream.AlignToBoundary(alignment);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16Test()
|
||||
public void AlignToBoundary_CanAlign_True()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
short read = stream.ReadInt16();
|
||||
Assert.Equal(0x0100, read);
|
||||
Stream? stream = new MemoryStream([0x01, 0x02, 0x03, 0x04, 0x05]);
|
||||
byte alignment = 4;
|
||||
|
||||
stream.Position = 1;
|
||||
bool actual = stream.AlignToBoundary(alignment);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Seek If Possible
|
||||
|
||||
[Fact]
|
||||
public void SeekIfPossible_NonSeekable_CurrentPosition()
|
||||
{
|
||||
var stream = new NonSeekableStream();
|
||||
long actual = stream.SeekIfPossible(0);
|
||||
Assert.Equal(8, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16BigEndianTest()
|
||||
public void SeekIfPossible_NonPositionable_InvalidPosition()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
short read = stream.ReadInt16BigEndian();
|
||||
Assert.Equal(0x0001, read);
|
||||
var stream = new NonPositionableStream();
|
||||
long actual = stream.SeekIfPossible(0);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16Test()
|
||||
public void SeekIfPossible_HiddenNonSeekable_InvalidPosition()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadUInt16();
|
||||
Assert.Equal(0x0100, read);
|
||||
var stream = new HiddenNonSeekableStream();
|
||||
long actual = stream.SeekIfPossible(0);
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16BigEndianTest()
|
||||
public void SeekIfPossible_NonNegative_ValidPosition()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadUInt16BigEndian();
|
||||
Assert.Equal(0x0001, read);
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, false, true);
|
||||
long actual = stream.SeekIfPossible(5);
|
||||
Assert.Equal(5, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32Test()
|
||||
public void SeekIfPossible_Negative_ValidPosition()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt32();
|
||||
Assert.Equal(0x03020100, read);
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, false, true);
|
||||
long actual = stream.SeekIfPossible(-3);
|
||||
Assert.Equal(13, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32BigEndianTest()
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Represents a hidden non-seekable stream
|
||||
/// </summary>
|
||||
private class HiddenNonSeekableStream : Stream
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt32BigEndian();
|
||||
Assert.Equal(0x00010203, read);
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => 16;
|
||||
|
||||
public override long Position { get => 8; set => throw new NotSupportedException(); }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32Test()
|
||||
/// <summary>
|
||||
/// Represents a non-seekable stream
|
||||
/// </summary>
|
||||
private class NonSeekableStream : Stream
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt32();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => 16;
|
||||
|
||||
public override long Position { get => 8; set => throw new NotSupportedException(); }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32BigEndianTest()
|
||||
/// <summary>
|
||||
/// Represents a non-seekable, non-positionable stream
|
||||
/// </summary>
|
||||
private class NonPositionableStream : Stream
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt32BigEndian();
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
public override bool CanRead => true;
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
float expected = BitConverter.Int32BitsToSingle(0x03020100);
|
||||
float read = stream.ReadSingle();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
public override bool CanSeek => false;
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
float expected = BitConverter.Int32BitsToSingle(0x00010203);
|
||||
float read = stream.ReadSingleBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
public override bool CanWrite => true;
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt64();
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
public override long Length => 16;
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt64BigEndian();
|
||||
Assert.Equal(0x0001020304050607, read);
|
||||
}
|
||||
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt64();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt64BigEndian();
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
|
||||
double read = stream.ReadDouble();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
|
||||
double read = stream.ReadDoubleBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new Guid(_bytes);
|
||||
Guid read = stream.ReadGuid();
|
||||
Assert.Equal(expected, read);
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndian()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
Guid read = stream.ReadGuidBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new Int128(BitConverter.ToUInt64(_bytes, 0), BitConverter.ToUInt64(_bytes, 8));
|
||||
Int128 read = stream.ReadInt128();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = new Int128(BitConverter.ToUInt64(reversed, 0), BitConverter.ToUInt64(reversed, 8));
|
||||
Int128 read = stream.ReadInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new UInt128(BitConverter.ToUInt64(_bytes, 0), BitConverter.ToUInt64(_bytes, 8));
|
||||
UInt128 read = stream.ReadUInt128();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = new UInt128(BitConverter.ToUInt64(reversed, 0), BitConverter.ToUInt64(reversed, 8));
|
||||
UInt128 read = stream.ReadUInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: Add string reading tests
|
||||
}
|
||||
}
|
||||
790
SabreTools.IO.Test/Extensions/StreamReaderExtensionsTests.cs
Normal file
790
SabreTools.IO.Test/Extensions/StreamReaderExtensionsTests.cs
Normal file
@@ -0,0 +1,790 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class StreamReaderExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test pattern from 0x00-0x0F
|
||||
/// </summary>
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the decimal value 0.0123456789
|
||||
/// </summary>
|
||||
private static readonly byte[] _decimalBytes =
|
||||
[
|
||||
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void ReadByteArrayTest()
|
||||
{
|
||||
byte[] arr = new byte[4];
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.Read(arr, 0, 4);
|
||||
Assert.Equal(4, read);
|
||||
Assert.True(arr.SequenceEqual(_bytes.Take(4)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadByteValueTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
byte read = stream.ReadByteValue();
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int length = 4;
|
||||
byte[] read = stream.ReadBytes(length);
|
||||
Assert.Equal(length, read.Length);
|
||||
Assert.True(read.SequenceEqual(_bytes.Take(length)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSByteTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
sbyte read = stream.ReadSByte();
|
||||
Assert.Equal(0x00, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCharTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
char read = stream.ReadChar();
|
||||
Assert.Equal('\0', read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
short read = stream.ReadInt16();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
short read = stream.ReadInt16BigEndian();
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt16LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
short read = stream.ReadInt16LittleEndian();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadUInt16();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadUInt16BigEndian();
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadUInt16LittleEndian();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadWORD();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadWORDBigEndian();
|
||||
Assert.Equal(0x0001, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadWORDLittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ushort read = stream.ReadWORDLittleEndian();
|
||||
Assert.Equal(0x0100, read);
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadHalfTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
Half expected = BitConverter.Int16BitsToHalf(0x0100);
|
||||
Half read = stream.ReadHalf();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadHalfBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
Half expected = BitConverter.Int16BitsToHalf(0x0001);
|
||||
Half read = stream.ReadHalfBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt24();
|
||||
Assert.Equal(0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt24BigEndian();
|
||||
Assert.Equal(0x000102, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt24LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt24LittleEndian();
|
||||
Assert.Equal(0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt24();
|
||||
Assert.Equal((uint)0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt24BigEndian();
|
||||
Assert.Equal((uint)0x000102, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt24LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt24LittleEndian();
|
||||
Assert.Equal((uint)0x020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt32();
|
||||
Assert.Equal(0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt32BigEndian();
|
||||
Assert.Equal(0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt32LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
int read = stream.ReadInt32LittleEndian();
|
||||
Assert.Equal(0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt32();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt32BigEndian();
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadUInt32LittleEndian();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadDWORD();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadDWORDBigEndian();
|
||||
Assert.Equal((uint)0x00010203, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDWORDLittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
uint read = stream.ReadDWORDLittleEndian();
|
||||
Assert.Equal((uint)0x03020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
float expected = BitConverter.Int32BitsToSingle(0x03020100);
|
||||
float read = stream.ReadSingle();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSingleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
float expected = BitConverter.Int32BitsToSingle(0x00010203);
|
||||
float read = stream.ReadSingleBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt48();
|
||||
Assert.Equal(0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt48BigEndian();
|
||||
Assert.Equal(0x000102030405, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt48LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt48LittleEndian();
|
||||
Assert.Equal(0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt48();
|
||||
Assert.Equal((ulong)0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt48BigEndian();
|
||||
Assert.Equal((ulong)0x000102030405, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt48LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt48LittleEndian();
|
||||
Assert.Equal((ulong)0x050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt64();
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt64BigEndian();
|
||||
Assert.Equal(0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt64LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
long read = stream.ReadInt64LittleEndian();
|
||||
Assert.Equal(0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt64();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt64BigEndian();
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64LittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadUInt64LittleEndian();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadQWORD();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadQWORDBigEndian();
|
||||
Assert.Equal((ulong)0x0001020304050607, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadQWORDLittleEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
ulong read = stream.ReadQWORDLittleEndian();
|
||||
Assert.Equal((ulong)0x0706050403020100, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0706050403020100);
|
||||
double read = stream.ReadDouble();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDoubleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
double expected = BitConverter.Int64BitsToDouble(0x0001020304050607);
|
||||
double read = stream.ReadDoubleBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDecimalTest()
|
||||
{
|
||||
var stream = new MemoryStream(_decimalBytes);
|
||||
decimal expected = 0.0123456789M;
|
||||
decimal read = stream.ReadDecimal();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadDecimalBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_decimalBytes.Reverse().ToArray());
|
||||
decimal expected = 0.0123456789M;
|
||||
decimal read = stream.ReadDecimalBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new Guid(_bytes);
|
||||
Guid read = stream.ReadGuid();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGuidBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = new Guid(_bytes.Reverse().ToArray());
|
||||
Guid read = stream.ReadGuidBigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void ReadInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = (Int128)new BigInteger(_bytes);
|
||||
Int128 read = stream.ReadInt128();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = (Int128)new BigInteger(reversed);
|
||||
Int128 read = stream.ReadInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expected = (UInt128)new BigInteger(_bytes);
|
||||
UInt128 read = stream.ReadUInt128();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var reversed = _bytes.Reverse().ToArray();
|
||||
var expected = (UInt128)new BigInteger(reversed);
|
||||
UInt128 read = stream.ReadUInt128BigEndian();
|
||||
Assert.Equal(expected, read);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void ReadNullTerminatedStringTest()
|
||||
{
|
||||
// Encoding.ASCII
|
||||
byte[] bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
var stream = new MemoryStream(bytes);
|
||||
string? actual = stream.ReadNullTerminatedString(Encoding.ASCII);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF8
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.UTF8);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Unicode
|
||||
bytes = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.Unicode);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.UTF32
|
||||
bytes = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.UTF32);
|
||||
Assert.Equal("ABC", actual);
|
||||
|
||||
// Encoding.Latin1
|
||||
bytes = [0x41, 0x42, 0x43, 0x00];
|
||||
stream = new MemoryStream(bytes);
|
||||
actual = stream.ReadNullTerminatedString(Encoding.Latin1);
|
||||
Assert.Equal("ABC", actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeTest()
|
||||
{
|
||||
// Guid
|
||||
var stream = new MemoryStream(_bytes);
|
||||
var expectedGuid = new Guid(_bytes);
|
||||
Guid actualGuid = stream.ReadType<Guid>();
|
||||
Assert.Equal(expectedGuid, actualGuid);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
stream = new MemoryStream(_bytes);
|
||||
Half expectedHalf = BitConverter.Int16BitsToHalf(0x0100);
|
||||
Half actualHalf = stream.ReadType<Half>();
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
stream = new MemoryStream(_bytes);
|
||||
Int128 expectedInt128 = (Int128)new BigInteger(_bytes);
|
||||
Int128 actualInt128 = stream.ReadType<Int128>();
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
|
||||
// UInt128
|
||||
stream = new MemoryStream(_bytes);
|
||||
UInt128 expectedUInt128 = (UInt128)new BigInteger(_bytes);
|
||||
UInt128 actualUInt128 = stream.ReadType<UInt128>();
|
||||
Assert.Equal(expectedHalf, actualHalf);
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
stream = new MemoryStream(_bytes);
|
||||
TestEnum expectedTestEnum = (TestEnum)0x03020100;
|
||||
TestEnum actualTestEnum = stream.ReadType<TestEnum>();
|
||||
Assert.Equal(expectedTestEnum, actualTestEnum);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeExplicitTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(bytesWithString);
|
||||
var expected = new TestStructExplicit
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0504,
|
||||
FourthValue = 0x0706,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
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()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(bytesWithString);
|
||||
var expected = new TestStructSequential
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
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);
|
||||
Assert.Equal(expected.FifthValue, read.FifthValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeStringsTest()
|
||||
{
|
||||
byte[] structBytes =
|
||||
[
|
||||
0x03, 0x41, 0x42, 0x43, // AnsiBStr
|
||||
0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, // BStr
|
||||
0x41, 0x42, 0x43, // ByValTStr
|
||||
0x41, 0x42, 0x43, 0x00, // LPStr
|
||||
0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, // LPWStr
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(structBytes);
|
||||
var expected = new TestStructStrings
|
||||
{
|
||||
AnsiBStr = "ABC",
|
||||
BStr = "ABC",
|
||||
ByValTStr = "ABC",
|
||||
LPStr = "ABC",
|
||||
LPWStr = "ABC",
|
||||
};
|
||||
var read = stream.ReadType<TestStructStrings>();
|
||||
Assert.Equal(expected.AnsiBStr, read.AnsiBStr);
|
||||
Assert.Equal(expected.BStr, read.BStr);
|
||||
Assert.Equal(expected.ByValTStr, read.ByValTStr);
|
||||
Assert.Equal(expected.LPStr, read.LPStr);
|
||||
Assert.Equal(expected.LPWStr, read.LPWStr);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeArraysTest()
|
||||
{
|
||||
byte[] structBytes =
|
||||
[
|
||||
// Byte Array
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
|
||||
// Int Array
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
0x04, 0x03, 0x02, 0x01,
|
||||
0x05, 0x04, 0x03, 0x02,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
|
||||
// Enum Array
|
||||
0x03, 0x02, 0x01, 0x00,
|
||||
0x04, 0x03, 0x02, 0x01,
|
||||
0x05, 0x04, 0x03, 0x02,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
|
||||
// Struct Array (X, Y)
|
||||
0xFF, 0x00, 0x00, 0xFF,
|
||||
0x00, 0xFF, 0xFF, 0x00,
|
||||
0xAA, 0x55, 0x55, 0xAA,
|
||||
0x55, 0xAA, 0xAA, 0x55,
|
||||
|
||||
// LPArray
|
||||
0x04, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(structBytes);
|
||||
var expected = new TestStructArrays
|
||||
{
|
||||
ByteArray = [0x00, 0x01, 0x02, 0x03],
|
||||
IntArray = [0x00010203, 0x01020304, 0x02030405, 0x03040506],
|
||||
EnumArray =
|
||||
[
|
||||
(TestEnum)0x00010203,
|
||||
(TestEnum)0x01020304,
|
||||
(TestEnum)0x02030405,
|
||||
(TestEnum)0x03040506,
|
||||
],
|
||||
StructArray =
|
||||
[
|
||||
new TestStructPoint { X = 0x00FF, Y = 0xFF00 },
|
||||
new TestStructPoint { X = 0xFF00, Y = 0x00FF },
|
||||
new TestStructPoint { X = 0x55AA, Y = 0xAA55 },
|
||||
new TestStructPoint { X = 0xAA55, Y = 0x55AA },
|
||||
],
|
||||
LPByteArrayLength = 0x0004,
|
||||
LPByteArray = [0x00, 0x01, 0x02, 0x03],
|
||||
};
|
||||
var read = stream.ReadType<TestStructArrays>();
|
||||
Assert.NotNull(read.ByteArray);
|
||||
Assert.True(expected.ByteArray.SequenceEqual(read.ByteArray));
|
||||
Assert.NotNull(read.IntArray);
|
||||
Assert.True(expected.IntArray.SequenceEqual(read.IntArray));
|
||||
Assert.NotNull(read.EnumArray);
|
||||
Assert.True(expected.EnumArray.SequenceEqual(read.EnumArray));
|
||||
Assert.NotNull(read.StructArray);
|
||||
Assert.True(expected.StructArray.SequenceEqual(read.StructArray));
|
||||
Assert.Equal(expected.LPByteArrayLength, read.LPByteArrayLength);
|
||||
Assert.NotNull(read.LPByteArray);
|
||||
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadTypeInheritanceTest()
|
||||
{
|
||||
byte[] structBytes1 =
|
||||
[
|
||||
0x41, 0x42, 0x43, 0x44, // Signature
|
||||
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
|
||||
0xAA, 0x55, 0xAA, 0x55, // FieldA
|
||||
0x55, 0xAA, 0x55, 0xAA, // FieldB
|
||||
];
|
||||
|
||||
var stream1 = new MemoryStream(structBytes1);
|
||||
var expected1 = new TestStructInheritanceChild1
|
||||
{
|
||||
Signature = [0x41, 0x42, 0x43, 0x44],
|
||||
IdentifierType = 0xFF00FF00,
|
||||
FieldA = 0x55AA55AA,
|
||||
FieldB = 0xAA55AA55,
|
||||
};
|
||||
var read1 = stream1.ReadType<TestStructInheritanceChild1>();
|
||||
Assert.NotNull(read1?.Signature);
|
||||
Assert.Equal(expected1.Signature, read1.Signature);
|
||||
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
|
||||
Assert.Equal(expected1.FieldA, read1.FieldA);
|
||||
Assert.Equal(expected1.FieldB, read1.FieldB);
|
||||
|
||||
byte[] structBytes2 =
|
||||
[
|
||||
0x41, 0x42, 0x43, 0x44, // Signature
|
||||
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
|
||||
0xAA, 0x55, // FieldA
|
||||
0x55, 0xAA, // FieldB
|
||||
];
|
||||
|
||||
var stream2 = new MemoryStream(structBytes2);
|
||||
var expected2 = new TestStructInheritanceChild2
|
||||
{
|
||||
Signature = [0x41, 0x42, 0x43, 0x44],
|
||||
IdentifierType = 0xFF00FF00,
|
||||
FieldA = 0x55AA,
|
||||
FieldB = 0xAA55,
|
||||
};
|
||||
var read2 = stream2.ReadType<TestStructInheritanceChild2>();
|
||||
Assert.NotNull(read2?.Signature);
|
||||
Assert.Equal(expected2.Signature, read2.Signature);
|
||||
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
|
||||
Assert.Equal(expected2.FieldA, read2.FieldA);
|
||||
Assert.Equal(expected2.FieldB, read2.FieldB);
|
||||
}
|
||||
}
|
||||
}
|
||||
596
SabreTools.IO.Test/Extensions/StreamWriterExtensionsTests.cs
Normal file
596
SabreTools.IO.Test/Extensions/StreamWriterExtensionsTests.cs
Normal file
@@ -0,0 +1,596 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class StreamWriterExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test pattern from 0x00-0x0F
|
||||
/// </summary>
|
||||
private static readonly byte[] _bytes =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Represents the decimal value 0.0123456789
|
||||
/// </summary>
|
||||
private static readonly byte[] _decimalBytes =
|
||||
[
|
||||
0x15, 0xCD, 0x5B, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void WriteByteValueTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bool write = stream.Write((byte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = StreamWriterExtensions.Write(stream, [0x00, 0x01, 0x02, 0x03]);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteBytesBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
stream.WriteBigEndian([0x03, 0x02, 0x01, 0x00]);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSByteTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bool write = stream.Write((sbyte)0x00);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(1).ToArray();
|
||||
bool write = stream.Write('\0');
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteCharEncodingTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [0x00, 0x00];
|
||||
stream.Write('\0', Encoding.Unicode);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = stream.Write((short)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = stream.WriteBigEndian((short)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = stream.Write((ushort)0x0100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt16BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = stream.WriteBigEndian((ushort)0x0001);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteHalfTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = stream.Write(BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHalfBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(2).ToArray();
|
||||
bool write = stream.WriteBigEndian(BitConverter.Int16BitsToHalf(0x0001));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = stream.WriteAsInt24(0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = stream.WriteAsInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt24Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = stream.WriteAsUInt24(0x020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt24BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(3).ToArray();
|
||||
bool write = stream.WriteAsUInt24BigEndian(0x000102);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = stream.Write(0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = stream.WriteBigEndian(0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = stream.Write((uint)0x03020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt32BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = stream.WriteBigEndian((uint)0x00010203);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = stream.Write(BitConverter.Int32BitsToSingle(0x03020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSingleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(4).ToArray();
|
||||
bool write = stream.WriteBigEndian(BitConverter.Int32BitsToSingle(0x00010203));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = stream.WriteAsInt48(0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = stream.WriteAsInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt48Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = stream.WriteAsUInt48(0x050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt48BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(6).ToArray();
|
||||
bool write = stream.WriteAsUInt48BigEndian(0x000102030405);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = stream.Write(0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = stream.WriteBigEndian(0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = stream.Write((ulong)0x0706050403020100);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt64BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = stream.WriteBigEndian((ulong)0x0001020304050607);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = stream.Write(BitConverter.Int64BitsToDouble(0x0706050403020100));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDoubleBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(8).ToArray();
|
||||
bool write = stream.WriteBigEndian(BitConverter.Int64BitsToDouble(0x0001020304050607));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDecimalTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _decimalBytes.Take(16).ToArray();
|
||||
bool write = stream.Write(0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDecimalBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _decimalBytes.Take(16).Reverse().ToArray();
|
||||
bool write = stream.WriteBigEndian(0.0123456789M);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteGuidTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.Write(new Guid(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteGuidBigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.WriteBigEndian(new Guid(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[Fact]
|
||||
public void WriteInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.Write((Int128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.WriteBigEndian((Int128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt128Test()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.Write((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteUInt128BigEndianTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = _bytes.Take(16).ToArray();
|
||||
bool write = stream.WriteBigEndian((UInt128)new BigInteger(_bytes.Reverse().ToArray()));
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF8StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x41, 0x42, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUTF8String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
byte[] expected = [0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteNullTerminatedUTF32StringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
byte[] expected = [0x41, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
|
||||
bool write = stream.WriteNullTerminatedUTF32String("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedAnsiStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
byte[] expected = [0x03, 0x41, 0x42, 0x43];
|
||||
|
||||
bool write = stream.WritePrefixedAnsiString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritePrefixedUnicodeStringTest()
|
||||
{
|
||||
var stream = new MemoryStream(new byte[8], 0, 8, true, true);
|
||||
byte[] expected = [0x03, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00];
|
||||
|
||||
bool write = stream.WritePrefixedUnicodeString("ABC");
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeTest()
|
||||
{
|
||||
// Guid
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
bool actual = stream.WriteType<Guid>(new Guid(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
// Half
|
||||
stream = new MemoryStream(new byte[2], 0, 2, true, true);
|
||||
actual = stream.WriteType<Half>(BitConverter.Int16BitsToHalf(0x0100));
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(2)], stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
// Int128
|
||||
stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
actual = stream.WriteType<Int128>((Int128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
|
||||
// UInt128
|
||||
stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
actual = stream.WriteType<UInt128>((UInt128)new BigInteger(_bytes));
|
||||
Assert.True(actual);
|
||||
ValidateBytes(_bytes, stream.GetBuffer());
|
||||
#endif
|
||||
|
||||
// Enum
|
||||
stream = new MemoryStream(new byte[4], 0, 4, true, true);
|
||||
actual = stream.WriteType<TestEnum>((TestEnum)0x03020100);
|
||||
Assert.True(actual);
|
||||
ValidateBytes([.. _bytes.Take(4)], stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeExplicitTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(new byte[16], 0, 16, true, true);
|
||||
var obj = new TestStructExplicit
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(12).ToArray();
|
||||
bool write = stream.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTypeSequentialTest()
|
||||
{
|
||||
byte[] bytesWithString =
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x41, 0x42, 0x43, 0x00,
|
||||
];
|
||||
|
||||
var stream = new MemoryStream(new byte[24], 0, 24, true, true);
|
||||
var obj = new TestStructSequential
|
||||
{
|
||||
FirstValue = TestEnum.RecognizedTestValue,
|
||||
SecondValue = 0x07060504,
|
||||
ThirdValue = 0x0908,
|
||||
FourthValue = 0x0B0A,
|
||||
FifthValue = "ABC",
|
||||
};
|
||||
byte[] expected = bytesWithString.Take(16).ToArray();
|
||||
bool write = stream.WriteType(obj);
|
||||
Assert.True(write);
|
||||
ValidateBytes(expected, stream.GetBuffer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that a set of actual bytes matches the expected bytes
|
||||
/// </summary>
|
||||
private static void ValidateBytes(byte[] expected, byte[] actual)
|
||||
{
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
Assert.Equal(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SabreTools.IO.Test/Extensions/TestEnum.cs
Normal file
9
SabreTools.IO.Test/Extensions/TestEnum.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
internal enum TestEnum : uint
|
||||
{
|
||||
None = 0x00000000,
|
||||
RecognizedTestValue = 0x03020100,
|
||||
UpperBoundaryValue = 0xFFFFFFFF,
|
||||
}
|
||||
}
|
||||
59
SabreTools.IO.Test/Extensions/TestStructArrays.cs
Normal file
59
SabreTools.IO.Test/Extensions/TestStructArrays.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal struct TestStructArrays
|
||||
{
|
||||
/// <summary>
|
||||
/// 4 entry byte array
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[]? ByteArray;
|
||||
|
||||
/// <summary>
|
||||
/// 4 entry int array
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public int[]? IntArray;
|
||||
|
||||
/// <summary>
|
||||
/// 4 entry int array
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public TestEnum[]? EnumArray;
|
||||
|
||||
/// <summary>
|
||||
/// 4 entry struct array
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public TestStructPoint[]? StructArray;
|
||||
|
||||
/// <summary>
|
||||
/// Length of <see cref="LPByteArray"/>
|
||||
/// </summary>
|
||||
public ushort LPByteArrayLength;
|
||||
|
||||
/// <summary>
|
||||
/// 4 entry byte array whose length is defined by <see cref="LPByteArrayLength"/>
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)]
|
||||
public byte[]? LPByteArray;
|
||||
|
||||
// /// <summary>
|
||||
// /// 4 entry nested byte array
|
||||
// /// </summary>
|
||||
// /// <remarks>This will likely fail</remarks>
|
||||
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
// public byte[][]? NestedByteArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Struct for nested tests
|
||||
/// </summary>
|
||||
internal struct TestStructPoint
|
||||
{
|
||||
public ushort X;
|
||||
public ushort Y;
|
||||
}
|
||||
}
|
||||
23
SabreTools.IO.Test/Extensions/TestStructExplicit.cs
Normal file
23
SabreTools.IO.Test/Extensions/TestStructExplicit.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct TestStructExplicit
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public TestEnum FirstValue;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public int SecondValue;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public ushort ThirdValue;
|
||||
|
||||
[FieldOffset(6)]
|
||||
public short FourthValue;
|
||||
|
||||
[FieldOffset(8), MarshalAs(UnmanagedType.LPStr)]
|
||||
public string? FifthValue;
|
||||
}
|
||||
}
|
||||
29
SabreTools.IO.Test/Extensions/TestStructInheritance.cs
Normal file
29
SabreTools.IO.Test/Extensions/TestStructInheritance.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal class TestStructInheritanceParent
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||
public byte[]? Signature;
|
||||
|
||||
public uint IdentifierType;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal class TestStructInheritanceChild1 : TestStructInheritanceParent
|
||||
{
|
||||
public uint FieldA;
|
||||
|
||||
public uint FieldB;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal class TestStructInheritanceChild2 : TestStructInheritanceParent
|
||||
{
|
||||
public ushort FieldA;
|
||||
|
||||
public ushort FieldB;
|
||||
}
|
||||
}
|
||||
19
SabreTools.IO.Test/Extensions/TestStructSequential.cs
Normal file
19
SabreTools.IO.Test/Extensions/TestStructSequential.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TestStructSequential
|
||||
{
|
||||
public TestEnum FirstValue;
|
||||
|
||||
public int SecondValue;
|
||||
|
||||
public ushort ThirdValue;
|
||||
|
||||
public short FourthValue;
|
||||
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string? FifthValue;
|
||||
}
|
||||
}
|
||||
39
SabreTools.IO.Test/Extensions/TestStructStrings.cs
Normal file
39
SabreTools.IO.Test/Extensions/TestStructStrings.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#pragma warning disable CS0618 // Obsolete unmanaged types
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal struct TestStructStrings
|
||||
{
|
||||
/// <summary>
|
||||
/// ASCII-encoded, byte-length-prefixed string
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.AnsiBStr)]
|
||||
public string? AnsiBStr;
|
||||
|
||||
/// <summary>
|
||||
/// Unicode-encoded, WORD-length-prefixed string
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.BStr)]
|
||||
public string? BStr;
|
||||
|
||||
/// <summary>
|
||||
/// Fixed length string
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 3)]
|
||||
public string? ByValTStr;
|
||||
|
||||
/// <summary>
|
||||
/// ASCII-encoded, null-terminated string
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string? LPStr;
|
||||
|
||||
/// <summary>
|
||||
/// Unicode-encoded, null-terminated string
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string? LPWStr;
|
||||
}
|
||||
}
|
||||
242
SabreTools.IO.Test/Extensions/XmlTextWriterExtensionsTests.cs
Normal file
242
SabreTools.IO.Test/Extensions/XmlTextWriterExtensionsTests.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using SabreTools.IO.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Extensions
|
||||
{
|
||||
public class XmlTextWriterExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void WriteRequiredAttributeString_NullInputThrow_Throws()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element />";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("element");
|
||||
Assert.Throws<ArgumentNullException>(()
|
||||
=> writer.WriteRequiredAttributeString("attr", null, throwOnError: true));
|
||||
writer.WriteEndElement();
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(52, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRequiredAttributeString_NullInputNoThrow_Writes()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"\" />";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("element");
|
||||
writer.WriteRequiredAttributeString("attr", null, throwOnError: false);
|
||||
writer.WriteEndElement();
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(60, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRequiredAttributeString_ValidInput_Writes()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"val\" />";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("element");
|
||||
writer.WriteRequiredAttributeString("attr", "val", throwOnError: false);
|
||||
writer.WriteEndElement();
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(63, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRequiredElementString_NullInputThrow_Throws()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
Assert.Throws<ArgumentNullException>(()
|
||||
=> writer.WriteRequiredElementString("element", null, throwOnError: true));
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(41, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRequiredElementString_NullInputNoThrow_Writes()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element></element>";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteRequiredElementString("element", null, throwOnError: false);
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(60, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteRequiredElementString_ValidInput_Writes()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element>val</element>";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteRequiredElementString("element", "val", throwOnError: false);
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(63, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalAttributeString_NullInput_NoWrite()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element />";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("element");
|
||||
writer.WriteOptionalAttributeString("attr", null);
|
||||
writer.WriteEndElement();
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(52, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalAttributeString_EmptyInput_NoWrite()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element />";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("element");
|
||||
writer.WriteOptionalAttributeString("attr", string.Empty);
|
||||
writer.WriteEndElement();
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(52, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalAttributeString_ValidInput_Writes()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"val\" />";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("element");
|
||||
writer.WriteOptionalAttributeString("attr", "val");
|
||||
writer.WriteEndElement();
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(63, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalElementString_NullInput_NoWrite()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteOptionalElementString("element", null);
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(41, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalElementString_EmptyInput_NoWrite()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteOptionalElementString("element", string.Empty);
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(41, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteOptionalElementString_ValidInput_Writes()
|
||||
{
|
||||
string expected = "<?xml version=\"1.0\" encoding=\"utf-8\"?><element>val</element>";
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new XmlTextWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteOptionalElementString("element", "val");
|
||||
writer.Flush();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(63, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
SabreTools.IO.Test/IniFileTests.cs
Normal file
67
SabreTools.IO.Test/IniFileTests.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
{
|
||||
public class IniFileTests
|
||||
{
|
||||
[Fact]
|
||||
public void EndToEndTest()
|
||||
{
|
||||
string expected = "[section1]\nkey1=value1\nkey2=value2\n";
|
||||
|
||||
// Build the INI
|
||||
var iniFile = new IniFile();
|
||||
iniFile.AddOrUpdate("section1.key1", "value1");
|
||||
iniFile["section1.key2"] = "value2";
|
||||
iniFile["section2.key3"] = "REMOVEME";
|
||||
bool removed = iniFile.Remove("section2.key3");
|
||||
|
||||
Assert.True(removed);
|
||||
Assert.Equal("value1", iniFile["section1.key1"]);
|
||||
Assert.Equal("value2", iniFile["section1.key2"]);
|
||||
|
||||
// Write the INI
|
||||
var stream = new MemoryStream();
|
||||
bool write = iniFile.Write(stream);
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.True(write);
|
||||
Assert.Equal(38, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
// Parse the INI
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var secondIni = new IniFile(stream);
|
||||
Assert.Equal("value1", secondIni["section1.key1"]);
|
||||
Assert.Equal("value2", secondIni["section1.key2"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveInvalidKeyTest()
|
||||
{
|
||||
var iniFile = new IniFile();
|
||||
bool removed = iniFile.Remove("invalid.key");
|
||||
Assert.False(removed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEmptyStreamTest()
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var iniFile = new IniFile(stream);
|
||||
Assert.Empty(iniFile);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteEmptyIniFileTest()
|
||||
{
|
||||
var iniFile = new IniFile();
|
||||
var stream = new MemoryStream();
|
||||
bool write = iniFile.Write(stream);
|
||||
Assert.False(write);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
SabreTools.IO.Test/Logging/ConvertersTests.cs
Normal file
38
SabreTools.IO.Test/Logging/ConvertersTests.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using SabreTools.IO.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Logging
|
||||
{
|
||||
public class ConvertersTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, LogLevel.VERBOSE)]
|
||||
[InlineData("", LogLevel.VERBOSE)]
|
||||
[InlineData("INVALID", LogLevel.VERBOSE)]
|
||||
[InlineData("verbose", LogLevel.VERBOSE)]
|
||||
[InlineData("VERBOSE", LogLevel.VERBOSE)]
|
||||
[InlineData("user", LogLevel.USER)]
|
||||
[InlineData("USER", LogLevel.USER)]
|
||||
[InlineData("warning", LogLevel.WARNING)]
|
||||
[InlineData("WARNING", LogLevel.WARNING)]
|
||||
[InlineData("error", LogLevel.ERROR)]
|
||||
[InlineData("ERROR", LogLevel.ERROR)]
|
||||
public void AsLogLevelTest(string? level, LogLevel expected)
|
||||
{
|
||||
LogLevel actual = level.AsLogLevel();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(LogLevel.VERBOSE, "VERBOSE")]
|
||||
[InlineData(LogLevel.USER, "USER")]
|
||||
[InlineData(LogLevel.WARNING, "WARNING")]
|
||||
[InlineData(LogLevel.ERROR, "ERROR")]
|
||||
[InlineData((LogLevel)99, null)]
|
||||
public void FromLogLevelTest(LogLevel level, string? expected)
|
||||
{
|
||||
string? actual = level.FromLogLevel();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
SabreTools.IO.Test/Logging/InternalStopwatchTests.cs
Normal file
40
SabreTools.IO.Test/Logging/InternalStopwatchTests.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using SabreTools.IO.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Logging
|
||||
{
|
||||
public class InternalStopwatchTests
|
||||
{
|
||||
[Fact]
|
||||
public void Stopwatch_NoSubject_StartNoSubject()
|
||||
{
|
||||
var stopwatch = new InternalStopwatch();
|
||||
stopwatch.Start();
|
||||
stopwatch.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stopwatch_NoSubject_StartSubject()
|
||||
{
|
||||
var stopwatch = new InternalStopwatch();
|
||||
stopwatch.Start("start");
|
||||
stopwatch.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stopwatch_Subject_StartNoSubject()
|
||||
{
|
||||
var stopwatch = new InternalStopwatch("init");
|
||||
stopwatch.Start();
|
||||
stopwatch.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stopwatch_Subject_StartSubject()
|
||||
{
|
||||
var stopwatch = new InternalStopwatch("init");
|
||||
stopwatch.Start("start");
|
||||
stopwatch.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
54
SabreTools.IO.Test/Logging/LoggerTests.cs
Normal file
54
SabreTools.IO.Test/Logging/LoggerTests.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using SabreTools.IO.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.Logging
|
||||
{
|
||||
public class LoggerTests
|
||||
{
|
||||
[Fact]
|
||||
public void EndToEnd()
|
||||
{
|
||||
Assert.Null(LoggerImpl.Filename);
|
||||
Assert.False(LoggerImpl.LogToFile);
|
||||
Assert.Null(LoggerImpl.LogDirectory);
|
||||
Assert.True(LoggerImpl.AppendPrefix);
|
||||
Assert.False(LoggerImpl.ThrowOnError);
|
||||
|
||||
LoggerImpl.Start();
|
||||
|
||||
var logger = new Logger();
|
||||
|
||||
logger.Verbose("verbose");
|
||||
logger.Verbose(new Exception());
|
||||
logger.Verbose(new Exception(), "verbose");
|
||||
logger.Verbose(1, 1, "verbose");
|
||||
|
||||
logger.User("user");
|
||||
logger.User(new Exception());
|
||||
logger.User(new Exception(), "user");
|
||||
logger.User(1, 1, "user");
|
||||
|
||||
logger.Warning("warning");
|
||||
logger.Warning(new Exception());
|
||||
logger.Warning(new Exception(), "warning");
|
||||
logger.Warning(1, 1, "warning");
|
||||
|
||||
logger.Error("error");
|
||||
logger.Error(new Exception());
|
||||
logger.Error(new Exception(), "error");
|
||||
logger.Error(1, 1, "error");
|
||||
|
||||
LoggerImpl.ThrowOnError = true;
|
||||
Assert.Throws<Exception>(() => logger.Error(new Exception()));
|
||||
|
||||
Assert.True(LoggerImpl.StartTime < DateTime.Now);
|
||||
Assert.True(LoggerImpl.LoggedWarnings);
|
||||
Assert.True(LoggerImpl.LoggedErrors);
|
||||
|
||||
LoggerImpl.SetFilename("logfile.txt", addDate: true);
|
||||
|
||||
LoggerImpl.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
92
SabreTools.IO.Test/PathToolTests.cs
Normal file
92
SabreTools.IO.Test/PathToolTests.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test
|
||||
{
|
||||
public class PathToolTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetDirectoriesOnly_NoAppendParent()
|
||||
{
|
||||
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
string expectedCurrent = Path.Combine(expectedParent, "Subdirectory");
|
||||
|
||||
List<string> inputs =
|
||||
[
|
||||
string.Empty,
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
|
||||
];
|
||||
var actual = PathTool.GetDirectoriesOnly(inputs, appendParent: true);
|
||||
Assert.NotEmpty(actual);
|
||||
|
||||
var first = actual[0];
|
||||
Assert.Equal(expectedCurrent, first.CurrentPath);
|
||||
Assert.Equal(expectedParent, first.ParentPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDirectoriesOnly_AppendParent()
|
||||
{
|
||||
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
string expectedCurrent = Path.Combine(expectedParent, "Subdirectory");
|
||||
|
||||
List<string> inputs =
|
||||
[
|
||||
string.Empty,
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
|
||||
];
|
||||
var actual = PathTool.GetDirectoriesOnly(inputs, appendParent: false);
|
||||
Assert.NotEmpty(actual);
|
||||
|
||||
var first = actual[0];
|
||||
Assert.Equal(expectedCurrent, first.CurrentPath);
|
||||
Assert.Equal(string.Empty, first.ParentPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFilesOnly_NoAppendParent()
|
||||
{
|
||||
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
string expectedCurrent = Path.Combine(expectedParent, "ascii.txt");
|
||||
|
||||
List<string> inputs =
|
||||
[
|
||||
string.Empty,
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "utf8bom.txt"),
|
||||
];
|
||||
var actual = PathTool.GetFilesOnly(inputs, appendParent: true);
|
||||
Assert.NotEmpty(actual);
|
||||
|
||||
var first = actual[0];
|
||||
Assert.Equal(expectedCurrent, first.CurrentPath);
|
||||
Assert.Equal(expectedParent, first.ParentPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFilesOnly_AppendParent()
|
||||
{
|
||||
string expectedParent = Path.Combine(Environment.CurrentDirectory, "TestData");
|
||||
string expectedCurrent = Path.Combine(expectedParent, "ascii.txt");
|
||||
|
||||
List<string> inputs =
|
||||
[
|
||||
string.Empty,
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "Subdir*"),
|
||||
Path.Combine(Environment.CurrentDirectory, "TestData", "utf8bom.txt"),
|
||||
];
|
||||
var actual = PathTool.GetFilesOnly(inputs, appendParent: false);
|
||||
Assert.NotEmpty(actual);
|
||||
|
||||
var first = actual[0];
|
||||
Assert.Equal(expectedCurrent, first.CurrentPath);
|
||||
Assert.Equal(string.Empty, first.ParentPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
SabreTools.IO.Test/ReadersWriters/ClrMameProTests.cs
Normal file
60
SabreTools.IO.Test/ReadersWriters/ClrMameProTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Readers;
|
||||
using SabreTools.IO.Writers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.ReadersWriters
|
||||
{
|
||||
public class ClrMameProTests
|
||||
{
|
||||
[Fact]
|
||||
public void EndToEndTest()
|
||||
{
|
||||
string expected = "header (\n\tstandalone \"value\"\n)\n\n# Comment\n\ngame (\n\titem ( attr \"value\" )\n)";
|
||||
|
||||
// Build and write the CMP file
|
||||
var stream = new MemoryStream();
|
||||
var writer = new ClrMameProWriter(stream, Encoding.UTF8);
|
||||
Assert.True(writer.Quotes);
|
||||
|
||||
writer.WriteStartElement("header");
|
||||
writer.WriteRequiredStandalone("standalone", "value");
|
||||
writer.WriteOptionalStandalone("optstand", null);
|
||||
writer.WriteFullEndElement();
|
||||
|
||||
writer.WriteString("\n\n# Comment\n");
|
||||
|
||||
writer.WriteStartElement("game");
|
||||
writer.WriteStartElement("item");
|
||||
writer.WriteRequiredAttributeString("attr", "value");
|
||||
writer.WriteOptionalAttributeString("optional", null);
|
||||
writer.WriteEndElement();
|
||||
writer.WriteFullEndElement();
|
||||
|
||||
writer.Flush();
|
||||
writer.Dispose();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(77, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
// Parse the CMP file
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var reader = new ClrMameProReader(stream, Encoding.UTF8);
|
||||
Assert.False(reader.DosCenter);
|
||||
Assert.True(reader.Quotes);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
bool hasNext = reader.ReadNextLine();
|
||||
Assert.True(hasNext);
|
||||
Assert.NotNull(reader.CurrentLine);
|
||||
Assert.True(reader.LineNumber >= 0);
|
||||
}
|
||||
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
50
SabreTools.IO.Test/ReadersWriters/IniTests.cs
Normal file
50
SabreTools.IO.Test/ReadersWriters/IniTests.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Readers;
|
||||
using SabreTools.IO.Writers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.ReadersWriters
|
||||
{
|
||||
public class IniTests
|
||||
{
|
||||
[Fact]
|
||||
public void EndToEndTest()
|
||||
{
|
||||
string expected = "[section1]\nkey1=value1\nkey2=value2\n\n;comment\n;string\n";
|
||||
|
||||
// Build and write the INI
|
||||
var stream = new MemoryStream();
|
||||
var writer = new IniWriter(stream, Encoding.UTF8);
|
||||
|
||||
writer.WriteSection("section1");
|
||||
writer.WriteKeyValuePair("key1", "value1");
|
||||
writer.WriteKeyValuePair("key2", "value2");
|
||||
writer.WriteLine();
|
||||
writer.WriteComment("comment");
|
||||
writer.WriteString(";string\n");
|
||||
|
||||
writer.Flush();
|
||||
writer.Dispose();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(56, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
// Parse the INI
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var reader = new IniReader(stream, Encoding.UTF8);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
bool hasNext = reader.ReadNextLine();
|
||||
Assert.True(hasNext);
|
||||
Assert.NotNull(reader.CurrentLine);
|
||||
Assert.True(reader.LineNumber >= 0);
|
||||
}
|
||||
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
SabreTools.IO.Test/ReadersWriters/SeparatedValueTests.cs
Normal file
60
SabreTools.IO.Test/ReadersWriters/SeparatedValueTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Readers;
|
||||
using SabreTools.IO.Writers;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.IO.Test.ReadersWriters
|
||||
{
|
||||
public class SeparatedValueTests
|
||||
{
|
||||
[Fact]
|
||||
public void EndToEndTest()
|
||||
{
|
||||
string expected = "\"col1\",\"col2\",\"col3\"\n\"value1\",\"value2\",\"value3\"\n\"value4\",\"value5\",\"value6\"\n";
|
||||
|
||||
// Build and write the CSV
|
||||
var stream = new MemoryStream();
|
||||
var writer = new SeparatedValueWriter(stream, Encoding.UTF8);
|
||||
Assert.True(writer.Quotes);
|
||||
Assert.Equal(',', writer.Separator);
|
||||
Assert.True(writer.VerifyFieldCount);
|
||||
|
||||
writer.WriteHeader(["col1", "col2", "col3"]);
|
||||
writer.WriteValues(["value1", "value2", "value3"]);
|
||||
writer.WriteString("\"value4\",\"value5\",\"value6\"\n");
|
||||
|
||||
writer.Flush();
|
||||
writer.Dispose();
|
||||
|
||||
// Length includes UTF-8 BOM
|
||||
Assert.Equal(78, stream.Length);
|
||||
string actual = Encoding.UTF8.GetString(stream.ToArray(), 3, (int)stream.Length - 3);
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
// Parse the CSV
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var reader = new SeparatedValueReader(stream, Encoding.UTF8);
|
||||
Assert.True(reader.Header);
|
||||
Assert.True(reader.Quotes);
|
||||
Assert.Equal(',', reader.Separator);
|
||||
Assert.True(reader.VerifyFieldCount);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
bool hasNext = reader.ReadNextLine();
|
||||
Assert.True(hasNext);
|
||||
Assert.NotNull(reader.CurrentLine);
|
||||
Assert.True(reader.LineNumber >= 0);
|
||||
|
||||
if (reader.LineNumber > 0)
|
||||
{
|
||||
Assert.NotNull(reader.GetValue(0));
|
||||
Assert.NotNull(reader.GetValue("col2"));
|
||||
}
|
||||
}
|
||||
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.IO\SabreTools.IO.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsNotAsErrors>CS0618</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="TestData\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="TestData\**">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.IO\SabreTools.IO.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SabreTools.IO.Streams;
|
||||
using Xunit;
|
||||
|
||||
@@ -13,7 +14,7 @@ namespace SabreTools.IO.Test.Streams
|
||||
Assert.Equal(0, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
|
||||
stream = new ReadOnlyBitStream(new MemoryStream(new byte[16]));
|
||||
stream = new ReadOnlyBitStream(new MemoryStream(new byte[16], 0, 16, true, true));
|
||||
Assert.Equal(16, stream.Length);
|
||||
Assert.Equal(0, stream.Position);
|
||||
}
|
||||
@@ -24,31 +25,95 @@ namespace SabreTools.IO.Test.Streams
|
||||
byte[] data = [0b01010101];
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
byte? bit = stream.ReadBit();
|
||||
|
||||
Assert.NotNull(bit);
|
||||
Assert.Equal((byte)0b00000001, bit);
|
||||
Assert.Equal(1, stream.Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBitsLSBTest()
|
||||
[Theory]
|
||||
[InlineData(4, 0b00000101, 1)]
|
||||
[InlineData(9, 0b10101010_1, 2)]
|
||||
public void ReadBitsBETest(int bits, uint expected, int position)
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
uint? bits = stream.ReadBitsLSB(4);
|
||||
Assert.NotNull(bits);
|
||||
Assert.Equal((byte)0b00000101, bits);
|
||||
Assert.Equal(1, stream.Position);
|
||||
uint? actual = stream.ReadBitsBE(bits);
|
||||
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected, actual);
|
||||
Assert.Equal(position, stream.Position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(4, 0b00001010, 1)]
|
||||
[InlineData(9, 0b10101010_1, 2)]
|
||||
public void ReadBitsLETest(int bits, uint expected, int position)
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
uint? actual = stream.ReadBitsLE(bits);
|
||||
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected, actual);
|
||||
Assert.Equal(position, stream.Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBitsMSBTest()
|
||||
public void ReadByteTest()
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
byte expected = 0b01010101;
|
||||
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
uint? bits = stream.ReadBitsMSB(4);
|
||||
Assert.NotNull(bits);
|
||||
Assert.Equal((byte)0b00001010, bits);
|
||||
Assert.Equal(1, stream.Position);
|
||||
byte? actual = stream.ReadByte();
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt16Test()
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
ushort expected = 0b0101010101010101;
|
||||
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
ushort? actual = stream.ReadUInt16();
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt32Test()
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
uint expected = 0b01010101010101010101010101010101;
|
||||
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
uint? actual = stream.ReadUInt32();
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadUInt64Test()
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
ulong? actual = stream.ReadUInt64();
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadBytesTest()
|
||||
{
|
||||
byte[] data = [0b01010101, 0b01010101, 0b01010101, 0b01010101];
|
||||
|
||||
var stream = new ReadOnlyBitStream(new MemoryStream(data));
|
||||
byte[]? actual = stream.ReadBytes(4);
|
||||
Assert.NotNull(actual);
|
||||
Assert.True(data.SequenceEqual(actual));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.IO.Streams;
|
||||
|
||||
1
SabreTools.IO.Test/TestData/Subdirectory/sample.txt
Normal file
1
SabreTools.IO.Test/TestData/Subdirectory/sample.txt
Normal file
@@ -0,0 +1 @@
|
||||
Sample file for subdirectories
|
||||
1
SabreTools.IO.Test/TestData/ascii.txt
Normal file
1
SabreTools.IO.Test/TestData/ascii.txt
Normal file
@@ -0,0 +1 @@
|
||||
This doesn't match anything
|
||||
1
SabreTools.IO.Test/TestData/utf16bebom.txt
Normal file
1
SabreTools.IO.Test/TestData/utf16bebom.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
SabreTools.IO.Test/TestData/utf16lebom.txt
Normal file
1
SabreTools.IO.Test/TestData/utf16lebom.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
BIN
SabreTools.IO.Test/TestData/utf32bom.txt
Normal file
BIN
SabreTools.IO.Test/TestData/utf32bom.txt
Normal file
Binary file not shown.
1
SabreTools.IO.Test/TestData/utf7bom.txt
Normal file
1
SabreTools.IO.Test/TestData/utf7bom.txt
Normal file
@@ -0,0 +1 @@
|
||||
+/v
|
||||
1
SabreTools.IO.Test/TestData/utf8bom.txt
Normal file
1
SabreTools.IO.Test/TestData/utf8bom.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
9
SabreTools.IO/ExtensionAttribute.cs
Normal file
9
SabreTools.IO/ExtensionAttribute.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
#if NET20
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
internal sealed class ExtensionAttribute : Attribute {}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Big endian reading overloads for BinaryReader
|
||||
/// Extensions for BinaryReader
|
||||
/// </summary>
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
@@ -30,104 +37,869 @@ 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);
|
||||
byte[] buffer = reader.ReadBytes(2);
|
||||
return (short)(buffer[1]
|
||||
| (buffer[0] << 8));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt32"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt32BigEndian(this BinaryReader reader)
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt16"/>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static short ReadInt16LittleEndian(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);
|
||||
return (short)(buffer[0]
|
||||
| (buffer[1] << 8));
|
||||
}
|
||||
|
||||
/// <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);
|
||||
return (ushort)(buffer[1]
|
||||
| (buffer[0] << 8));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadUInt16"/>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static ushort ReadUInt16LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(2);
|
||||
return (ushort)(buffer[0]
|
||||
| (buffer[1] << 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a WORD (2-byte) from the base stream
|
||||
/// </summary>
|
||||
public static ushort ReadWORD(this BinaryReader reader)
|
||||
=> reader.ReadUInt16();
|
||||
|
||||
/// <summary>
|
||||
/// Read a WORD (2-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ushort ReadWORDBigEndian(this BinaryReader reader)
|
||||
=> reader.ReadUInt16BigEndian();
|
||||
|
||||
/// <summary>
|
||||
/// Read a WORD (2-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static ushort ReadWORDLittleEndian(this BinaryReader reader)
|
||||
=> reader.ReadUInt16LittleEndian();
|
||||
|
||||
// Half was introduced in net5.0 but doesn't have a BitConverter implementation until net6.0
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <inheritdoc cref="BinaryReader.ReadHalf"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static Half ReadHalfBigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToHalf(buffer, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int24 encoded as an Int32 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in machine native format</remarks>
|
||||
public static int ReadInt24(this BinaryReader reader)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
return reader.ReadInt24LittleEndian();
|
||||
else
|
||||
return reader.ReadInt24BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int24 encoded as an Int32 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt24BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(3);
|
||||
return (int)(buffer[2]
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[0] << 16));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int24 encoded as an Int32 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static int ReadInt24LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(3);
|
||||
return (int)(buffer[0]
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[2] << 16));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt24 encoded as a UInt32 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in machine native format</remarks>
|
||||
public static uint ReadUInt24(this BinaryReader reader)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
return reader.ReadUInt24LittleEndian();
|
||||
else
|
||||
return reader.ReadUInt24BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt24 encoded as a UInt32 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static uint ReadUInt24BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(3);
|
||||
return (uint)(buffer[2]
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[0] << 16));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt24 encoded as a UInt32 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static uint ReadUInt24LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(3);
|
||||
return (uint)(buffer[0]
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[2] << 16));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt32"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static int ReadInt32BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(4);
|
||||
return (int)(buffer[3]
|
||||
| (buffer[2] << 8)
|
||||
| (buffer[1] << 16)
|
||||
| (buffer[0] << 24));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt32"/>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static int ReadInt32LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(4);
|
||||
return (int)(buffer[0]
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[2] << 16)
|
||||
| (buffer[3] << 24));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadUInt32"/>
|
||||
/// <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);
|
||||
return (uint)(buffer[3]
|
||||
| (buffer[2] << 8)
|
||||
| (buffer[1] << 16)
|
||||
| (buffer[0] << 24));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadUInt32"/>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static uint ReadUInt32LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(4);
|
||||
return (uint)(buffer[0]
|
||||
| (buffer[1] << 8)
|
||||
| (buffer[2] << 16)
|
||||
| (buffer[3] << 24));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a DWORD (4-byte) from the base stream
|
||||
/// </summary>
|
||||
public static uint ReadDWORD(this BinaryReader reader)
|
||||
=> reader.ReadUInt32();
|
||||
|
||||
/// <summary>
|
||||
/// Read a DWORD (4-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static uint ReadDWORDBigEndian(this BinaryReader reader)
|
||||
=> reader.ReadUInt32BigEndian();
|
||||
|
||||
/// <summary>
|
||||
/// Read a DWORD (4-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static uint ReadDWORDLittleEndian(this BinaryReader reader)
|
||||
=> reader.ReadUInt32LittleEndian();
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int48 encoded as an Int64 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in machine native format</remarks>
|
||||
public static long ReadInt48(this BinaryReader reader)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
return reader.ReadInt48LittleEndian();
|
||||
else
|
||||
return reader.ReadInt48BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int48 encoded as an Int64 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static long ReadInt48BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(6);
|
||||
return ((long)buffer[5] << 0)
|
||||
| ((long)buffer[4] << 8)
|
||||
| ((long)buffer[3] << 16)
|
||||
| ((long)buffer[2] << 24)
|
||||
| ((long)buffer[1] << 32)
|
||||
| ((long)buffer[0] << 40);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int48 encoded as an Int64 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static long ReadInt48LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(6);
|
||||
return ((long)buffer[0] << 0)
|
||||
| ((long)buffer[1] << 8)
|
||||
| ((long)buffer[2] << 16)
|
||||
| ((long)buffer[3] << 24)
|
||||
| ((long)buffer[4] << 32)
|
||||
| ((long)buffer[5] << 40);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt48 encoded as a UInt64 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in machine native format</remarks>
|
||||
public static ulong ReadUInt48(this BinaryReader reader)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
return reader.ReadUInt48LittleEndian();
|
||||
else
|
||||
return reader.ReadUInt48BigEndian();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt48 encoded as a UInt64 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ulong ReadUInt48BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(6);
|
||||
return ((ulong)buffer[5] << 0)
|
||||
| ((ulong)buffer[4] << 8)
|
||||
| ((ulong)buffer[3] << 16)
|
||||
| ((ulong)buffer[2] << 24)
|
||||
| ((ulong)buffer[1] << 32)
|
||||
| ((ulong)buffer[0] << 40);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an UInt48 encoded as an UInt64 from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static ulong ReadUInt48LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(6);
|
||||
return ((ulong)buffer[0] << 0)
|
||||
| ((ulong)buffer[1] << 8)
|
||||
| ((ulong)buffer[2] << 16)
|
||||
| ((ulong)buffer[3] << 24)
|
||||
| ((ulong)buffer[4] << 32)
|
||||
| ((ulong)buffer[5] << 40);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt64"/>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static long ReadInt64BigEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(8);
|
||||
return ((long)buffer[7] << 0)
|
||||
| ((long)buffer[6] << 8)
|
||||
| ((long)buffer[5] << 16)
|
||||
| ((long)buffer[4] << 24)
|
||||
| ((long)buffer[3] << 32)
|
||||
| ((long)buffer[2] << 40)
|
||||
| ((long)buffer[1] << 48)
|
||||
| ((long)buffer[0] << 56);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadInt64"/>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static long ReadInt64LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(8);
|
||||
return ((long)buffer[0] << 0)
|
||||
| ((long)buffer[1] << 8)
|
||||
| ((long)buffer[2] << 16)
|
||||
| ((long)buffer[3] << 24)
|
||||
| ((long)buffer[4] << 32)
|
||||
| ((long)buffer[5] << 40)
|
||||
| ((long)buffer[6] << 48)
|
||||
| ((long)buffer[7] << 56);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
return ((ulong)buffer[7] << 0)
|
||||
| ((ulong)buffer[6] << 8)
|
||||
| ((ulong)buffer[5] << 16)
|
||||
| ((ulong)buffer[4] << 24)
|
||||
| ((ulong)buffer[3] << 32)
|
||||
| ((ulong)buffer[2] << 40)
|
||||
| ((ulong)buffer[1] << 48)
|
||||
| ((ulong)buffer[0] << 56);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryReader.ReadUInt64"/>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static ulong ReadUInt64LittleEndian(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(8);
|
||||
return ((ulong)buffer[0] << 0)
|
||||
| ((ulong)buffer[1] << 8)
|
||||
| ((ulong)buffer[2] << 16)
|
||||
| ((ulong)buffer[3] << 24)
|
||||
| ((ulong)buffer[4] << 32)
|
||||
| ((ulong)buffer[5] << 40)
|
||||
| ((ulong)buffer[6] << 48)
|
||||
| ((ulong)buffer[7] << 56);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a QWORD (8-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in machine native format</remarks>
|
||||
public static ulong ReadQWORD(this BinaryReader reader)
|
||||
=> reader.ReadUInt64();
|
||||
|
||||
/// <summary>
|
||||
/// Read a QWORD (8-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in big-endian format</remarks>
|
||||
public static ulong ReadQWORDBigEndian(this BinaryReader reader)
|
||||
=> reader.ReadUInt64BigEndian();
|
||||
|
||||
/// <summary>
|
||||
/// Read a QWORD (8-byte) from the base stream
|
||||
/// </summary>
|
||||
/// <remarks>Reads in little-endian format</remarks>
|
||||
public static ulong ReadQWORDLittleEndian(this BinaryReader reader)
|
||||
=> reader.ReadUInt64LittleEndian();
|
||||
|
||||
/// <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 lo = BitConverter.ToInt32(buffer, 0);
|
||||
int mid = BitConverter.ToInt32(buffer, 4);
|
||||
int hi = BitConverter.ToInt32(buffer, 8);
|
||||
int flags = BitConverter.ToInt32(buffer, 12);
|
||||
|
||||
return new decimal([lo, mid, hi, flags]);
|
||||
}
|
||||
|
||||
/// <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 (Int128)new BigInteger(buffer);
|
||||
}
|
||||
|
||||
/// <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 (Int128)new BigInteger(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 from the underlying stream
|
||||
/// </summary>
|
||||
public static UInt128 ReadUInt128(this BinaryReader reader)
|
||||
{
|
||||
byte[] buffer = reader.ReadBytes(16);
|
||||
return (UInt128)new BigInteger(buffer);
|
||||
}
|
||||
|
||||
/// <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 (UInt128)new BigInteger(buffer);
|
||||
}
|
||||
#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.UTF8))
|
||||
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;
|
||||
|
||||
List<byte> buffer = [];
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte ch = reader.ReadByte();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
buffer.Add(ch);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-8 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF8String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull1Byte(reader);
|
||||
return Encoding.ASCII.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-16 (Unicode) string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUnicodeString(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull2Byte(reader);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated UTF-32 string from the underlying stream
|
||||
/// </summary>
|
||||
public static string? ReadNullTerminatedUTF32String(this BinaryReader reader)
|
||||
{
|
||||
if (reader.BaseStream.Position >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = ReadUntilNull4Byte(reader);
|
||||
return Encoding.UTF32.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 * 2) >= reader.BaseStream.Length)
|
||||
return null;
|
||||
|
||||
byte[] buffer = reader.ReadBytes(size * 2);
|
||||
return Encoding.Unicode.GetString(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <typeparamref name="T"/> from the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are read by value, not by reference
|
||||
/// - Complex objects are read by value, not by reference
|
||||
/// - Enumeration values are read by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are deserialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static T? ReadType<T>(this BinaryReader reader)
|
||||
=> (T?)reader.ReadType(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are read by value, not by reference
|
||||
/// - Complex objects are read by value, not by reference
|
||||
/// - Enumeration values are read by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are deserialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static object? ReadType(this BinaryReader reader, Type type)
|
||||
{
|
||||
// Handle special struct cases
|
||||
if (type == typeof(Guid))
|
||||
return reader.ReadGuid();
|
||||
#if NET6_0_OR_GREATER
|
||||
else if (type == typeof(Half))
|
||||
return reader.ReadHalf();
|
||||
#endif
|
||||
#if NET7_0_OR_GREATER
|
||||
else if (type == typeof(Int128))
|
||||
return reader.ReadInt128();
|
||||
else if (type == typeof(UInt128))
|
||||
return reader.ReadUInt128();
|
||||
#endif
|
||||
|
||||
if (type.IsClass || (type.IsValueType && !type.IsEnum && !type.IsPrimitive))
|
||||
return ReadComplexType(reader, type);
|
||||
else if (type.IsValueType && type.IsEnum)
|
||||
return ReadNormalType(reader, Enum.GetUnderlyingType(type));
|
||||
else
|
||||
return ReadNormalType(reader, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the underlying stream
|
||||
/// </summary>
|
||||
private static object? ReadNormalType(BinaryReader reader, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
int typeSize = Marshal.SizeOf(type);
|
||||
byte[] buffer = reader.ReadBytes(typeSize); ;
|
||||
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
var data = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), type);
|
||||
handle.Free();
|
||||
|
||||
return data;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the underlying stream
|
||||
/// </summary>
|
||||
private static object? ReadComplexType(BinaryReader reader, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to create an instance of the type
|
||||
var instance = Activator.CreateInstance(type);
|
||||
if (instance == null)
|
||||
return null;
|
||||
|
||||
// Get the layout information
|
||||
var layoutAttr = MarshalHelpers.GetAttribute<StructLayoutAttribute>(type);
|
||||
LayoutKind layoutKind = MarshalHelpers.DetermineLayoutKind(layoutAttr, type);
|
||||
Encoding encoding = MarshalHelpers.DetermineEncoding(layoutAttr);
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = reader.BaseStream.Position;
|
||||
|
||||
// Generate the fields by parent first
|
||||
var fields = MarshalHelpers.GetFields(type);
|
||||
|
||||
// Loop through the fields and set them
|
||||
foreach (var fi in fields)
|
||||
{
|
||||
// If we have an explicit layout, move accordingly
|
||||
if (layoutKind == LayoutKind.Explicit)
|
||||
{
|
||||
var fieldOffset = MarshalHelpers.GetAttribute<FieldOffsetAttribute>(fi);
|
||||
reader.BaseStream.Seek(currentOffset + fieldOffset?.Value ?? 0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
SetField(reader, encoding, fields, instance, fi);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a single field on an object
|
||||
/// </summary>
|
||||
private static void SetField(BinaryReader reader, Encoding encoding, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
if (fi.FieldType.IsAssignableFrom(typeof(string)))
|
||||
{
|
||||
var value = ReadStringType(reader, encoding, fi);
|
||||
fi.SetValue(instance, value);
|
||||
}
|
||||
else if (fi.FieldType.IsArray)
|
||||
{
|
||||
var value = ReadArrayType(reader, fields, instance, fi);
|
||||
if (value.GetType() == fi.FieldType)
|
||||
fi.SetValue(instance, value);
|
||||
else
|
||||
fi.SetValue(instance, Convert.ChangeType(value, fi.FieldType));
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = reader.ReadType(fi.FieldType);
|
||||
fi.SetValue(instance, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an array type field for an object
|
||||
/// </summary>
|
||||
private static Array ReadArrayType(BinaryReader reader, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
if (marshalAsAttr == null)
|
||||
return new object[0];
|
||||
|
||||
// Get the number of elements expected
|
||||
int elementCount = MarshalHelpers.GetArrayElementCount(marshalAsAttr, fields, instance);
|
||||
if (elementCount < 0)
|
||||
return new object[0];
|
||||
|
||||
// Get the item type for the array
|
||||
Type elementType = fi.FieldType.GetElementType() ?? typeof(object);
|
||||
|
||||
// Loop through and build the array
|
||||
Array arr = Array.CreateInstance(elementType, elementCount);
|
||||
for (int i = 0; i < elementCount; i++)
|
||||
{
|
||||
var value = ReadType(reader, elementType);
|
||||
if (value != null && elementType.IsEnum)
|
||||
arr.SetValue(Enum.ToObject(elementType, value), i);
|
||||
else
|
||||
arr.SetValue(value, i);
|
||||
}
|
||||
|
||||
// Return the built array
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a string type field for an object
|
||||
/// </summary>
|
||||
private static string? ReadStringType(BinaryReader reader, Encoding encoding, FieldInfo? fi)
|
||||
{
|
||||
// If the FieldInfo is null
|
||||
if (fi == null)
|
||||
return null;
|
||||
|
||||
// Get all MarshalAs attributes for the field, if possible
|
||||
var attributes = fi.GetCustomAttributes(typeof(MarshalAsAttribute), true);
|
||||
if (attributes.Length == 0)
|
||||
return null;
|
||||
|
||||
// Use the first found attribute
|
||||
var marshalAsAttr = attributes[0] as MarshalAsAttribute;
|
||||
switch (marshalAsAttr?.Value)
|
||||
{
|
||||
case UnmanagedType.AnsiBStr:
|
||||
return reader.ReadPrefixedAnsiString();
|
||||
|
||||
case UnmanagedType.BStr:
|
||||
case UnmanagedType.TBStr: // Technically distinct; returns char[] instead
|
||||
return reader.ReadPrefixedUnicodeString();
|
||||
|
||||
case UnmanagedType.ByValTStr:
|
||||
int byvalLength = marshalAsAttr!.SizeConst;
|
||||
byte[] byvalBytes = reader.ReadBytes(byvalLength);
|
||||
return encoding.GetString(byvalBytes);
|
||||
|
||||
case UnmanagedType.LPStr:
|
||||
case UnmanagedType.LPTStr: // Technically distinct; possibly not null-terminated
|
||||
case null:
|
||||
return reader.ReadNullTerminatedAnsiString();
|
||||
|
||||
#if NET472_OR_GREATER || NETCOREAPP
|
||||
case UnmanagedType.LPUTF8Str:
|
||||
return reader.ReadNullTerminatedUTF8String();
|
||||
#endif
|
||||
|
||||
case UnmanagedType.LPWStr:
|
||||
return reader.ReadNullTerminatedUnicodeString();
|
||||
|
||||
// No other string types are recognized
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 1-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull1Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
byte next = reader.ReadByte();
|
||||
if (next == 0x00)
|
||||
break;
|
||||
|
||||
bytes.Add(next);
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 2-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull2Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
ushort next = reader.ReadUInt16();
|
||||
if (next == 0x0000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes until a 4-byte null terminator is found
|
||||
/// </summary>
|
||||
private static byte[] ReadUntilNull4Byte(BinaryReader reader)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
{
|
||||
uint next = reader.ReadUInt32();
|
||||
if (next == 0x00000000)
|
||||
break;
|
||||
|
||||
bytes.AddRange(BitConverter.GetBytes(next));
|
||||
}
|
||||
|
||||
return [.. bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
646
SabreTools.IO/Extensions/BinaryWriterExtensions.cs
Normal file
646
SabreTools.IO/Extensions/BinaryWriterExtensions.cs
Normal file
@@ -0,0 +1,646 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for BinaryWriter
|
||||
/// </summary>
|
||||
/// TODO: Handle proper negative values for Int24 and Int48
|
||||
public static class BinaryWriterExtensions
|
||||
{
|
||||
/// <inheritdoc cref="BinaryWriter.Write(byte[])"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, byte[] value)
|
||||
{
|
||||
Array.Reverse(value);
|
||||
return WriteFromBuffer(writer, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(char)"/>
|
||||
public static bool Write(this BinaryWriter writer, char value, Encoding encoding)
|
||||
{
|
||||
byte[] buffer = encoding.GetBytes($"{value}");
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(short)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, short value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(ushort)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, ushort value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
// Half was introduced in net5.0 but doesn't have a BitConverter implementation until net6.0
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <inheritdoc cref="BinaryWriter.Write(Half)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, Half value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 as an Int24 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsInt24(this BinaryWriter writer, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, reduced, 3);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 as an Int24 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsInt24BigEndian(this BinaryWriter writer, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, 1, reduced, 0, 3);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 as a UInt24 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsUInt24(this BinaryWriter writer, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, reduced, 3);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 as a UInt24 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsUInt24BigEndian(this BinaryWriter writer, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, 1, reduced, 0, 3);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(int)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(uint)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(float)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, float value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 as an Int48 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsInt48(this BinaryWriter writer, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, reduced, 6);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 as an Int48 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsInt48BigEndian(this BinaryWriter writer, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, 2, reduced, 0, 6);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 as a UInt48 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsUInt48(this BinaryWriter writer, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, reduced, 6);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 as a UInt48 to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsUInt48BigEndian(this BinaryWriter writer, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, 2, reduced, 0, 6);
|
||||
return WriteFromBuffer(writer, reduced);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(long)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(ulong)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(double)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, double value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BinaryWriter.Write(decimal)"/>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, decimal value)
|
||||
{
|
||||
int[] bits = decimal.GetBits(value);
|
||||
|
||||
byte[] lo = BitConverter.GetBytes(bits[0]);
|
||||
byte[] mid = BitConverter.GetBytes(bits[1]);
|
||||
byte[] hi = BitConverter.GetBytes(bits[2]);
|
||||
byte[] flags = BitConverter.GetBytes(bits[3]);
|
||||
|
||||
byte[] buffer = new byte[16];
|
||||
Array.Copy(lo, 0, buffer, 0, 4);
|
||||
Array.Copy(mid, 0, buffer, 4, 4);
|
||||
Array.Copy(hi, 0, buffer, 8, 4);
|
||||
Array.Copy(flags, 0, buffer, 12, 4);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Guid
|
||||
/// </summary>
|
||||
public static bool Write(this BinaryWriter writer, Guid value)
|
||||
{
|
||||
byte[] buffer = value.ToByteArray();
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Guid
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, Guid value)
|
||||
{
|
||||
byte[] buffer = value.ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write an Int128
|
||||
/// </summary>
|
||||
public static bool Write(this BinaryWriter writer, Int128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(writer, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int128
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, Int128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(writer, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt128
|
||||
/// </summary>
|
||||
public static bool Write(this BinaryWriter writer, UInt128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(writer, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt128
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this BinaryWriter writer, UInt128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(writer, padded);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this BinaryWriter writer, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.ASCII);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this BinaryWriter writer, string? value)
|
||||
=> writer.WriteNullTerminatedString(value, Encoding.UTF32);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
writer.Write((byte)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the underlying stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this BinaryWriter writer, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
writer.Write((ushort)value.Length);
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are written by value, not by reference
|
||||
/// - Complex objects are written by value, not by reference
|
||||
/// - Enumeration values are written by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are serialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static bool WriteType<T>(this BinaryWriter writer, T? value)
|
||||
=> writer.WriteType(value, typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the underlying stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are written by value, not by reference
|
||||
/// - Complex objects are written by value, not by reference
|
||||
/// - Enumeration values are written by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are serialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static bool WriteType(this BinaryWriter writer, object? value, Type type)
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
// Handle special struct cases
|
||||
if (type == typeof(Guid))
|
||||
return writer.Write((Guid)value);
|
||||
#if NET6_0_OR_GREATER
|
||||
else if (type == typeof(Half))
|
||||
{
|
||||
writer.Write((Half)value);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if NET7_0_OR_GREATER
|
||||
else if (type == typeof(Int128))
|
||||
return writer.Write((Int128)value);
|
||||
else if (type == typeof(UInt128))
|
||||
return writer.Write((UInt128)value);
|
||||
#endif
|
||||
|
||||
if (type.IsClass || (type.IsValueType && !type.IsEnum && !type.IsPrimitive))
|
||||
return WriteComplexType(writer, value, type);
|
||||
else if (type.IsValueType && type.IsEnum)
|
||||
return WriteNormalType(writer, value, Enum.GetUnderlyingType(type));
|
||||
else
|
||||
return WriteNormalType(writer, value, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the stream
|
||||
/// </summary>
|
||||
private static bool WriteNormalType(BinaryWriter writer, object? value, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
int typeSize = Marshal.SizeOf(type);
|
||||
if (value.GetType() != type)
|
||||
value = Convert.ChangeType(value, type);
|
||||
|
||||
var buffer = new byte[typeSize];
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);
|
||||
handle.Free();
|
||||
|
||||
return WriteFromBuffer(writer, buffer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the stream
|
||||
/// </summary>
|
||||
private static bool WriteComplexType(BinaryWriter writer, object? value, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
// Get the layout information
|
||||
var layoutAttr = MarshalHelpers.GetAttribute<StructLayoutAttribute>(type);
|
||||
LayoutKind layoutKind = MarshalHelpers.DetermineLayoutKind(layoutAttr, type);
|
||||
Encoding encoding = MarshalHelpers.DetermineEncoding(layoutAttr);
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = writer.BaseStream.Position;
|
||||
|
||||
// Generate the fields by parent first
|
||||
var fields = MarshalHelpers.GetFields(type);
|
||||
|
||||
// Loop through the fields and set them
|
||||
foreach (var fi in fields)
|
||||
{
|
||||
// If we have an explicit layout, move accordingly
|
||||
if (layoutKind == LayoutKind.Explicit)
|
||||
{
|
||||
var fieldOffset = MarshalHelpers.GetAttribute<FieldOffsetAttribute>(fi);
|
||||
writer.BaseStream.Seek(currentOffset + fieldOffset?.Value ?? 0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (!GetField(writer, encoding, fields, value, fi))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a single field from an object
|
||||
/// </summary>
|
||||
private static bool GetField(BinaryWriter writer, Encoding encoding, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
if (fi.FieldType.IsAssignableFrom(typeof(string)))
|
||||
{
|
||||
return WriteStringType(writer, encoding, instance, fi);
|
||||
}
|
||||
else if (fi.FieldType.IsArray)
|
||||
{
|
||||
return WriteArrayType(writer, fields, instance, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = fi.GetValue(instance);
|
||||
return writer.WriteType(value, fi.FieldType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array type field from an object
|
||||
/// </summary>
|
||||
private static bool WriteArrayType(BinaryWriter writer, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
if (marshalAsAttr == null)
|
||||
return false;
|
||||
|
||||
// Get the array
|
||||
Array? arr = fi.GetValue(instance) as Array;
|
||||
if (arr == null)
|
||||
return false;
|
||||
|
||||
// Get the number of elements expected
|
||||
int elementCount = MarshalHelpers.GetArrayElementCount(marshalAsAttr, fields, instance);
|
||||
if (elementCount < 0)
|
||||
return false;
|
||||
|
||||
// Get the item type for the array
|
||||
Type elementType = fi.FieldType.GetElementType() ?? typeof(object);
|
||||
|
||||
// Loop through and write the array
|
||||
for (int i = 0; i < elementCount; i++)
|
||||
{
|
||||
var value = arr.GetValue(i);
|
||||
if (!WriteType(writer, value, elementType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a string type field from an object
|
||||
/// </summary>
|
||||
private static bool WriteStringType(BinaryWriter writer, Encoding encoding, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
string? fieldValue = fi.GetValue(instance) as string;
|
||||
if (fieldValue == null)
|
||||
return true;
|
||||
|
||||
switch (marshalAsAttr?.Value)
|
||||
{
|
||||
case UnmanagedType.AnsiBStr:
|
||||
return writer.WritePrefixedAnsiString(fieldValue);
|
||||
|
||||
case UnmanagedType.BStr:
|
||||
case UnmanagedType.TBStr: // Technically distinct; returns char[] instead
|
||||
return writer.WritePrefixedUnicodeString(fieldValue);
|
||||
|
||||
case UnmanagedType.ByValTStr:
|
||||
int byvalLength = marshalAsAttr!.SizeConst;
|
||||
byte[] byvalBytes = encoding.GetBytes(fieldValue);
|
||||
byte[] byvalSizedBytes = new byte[byvalLength];
|
||||
Array.Copy(byvalBytes, byvalSizedBytes, Math.Min(byvalBytes.Length, byvalSizedBytes.Length));
|
||||
writer.Write(byvalSizedBytes);
|
||||
return true;
|
||||
|
||||
case UnmanagedType.LPStr:
|
||||
case UnmanagedType.LPTStr: // Technically distinct; possibly not null-terminated
|
||||
case null:
|
||||
return writer.WriteNullTerminatedAnsiString(fieldValue);
|
||||
|
||||
#if NET472_OR_GREATER || NETCOREAPP
|
||||
case UnmanagedType.LPUTF8Str:
|
||||
return writer.WriteNullTerminatedUTF8String(fieldValue);
|
||||
#endif
|
||||
|
||||
case UnmanagedType.LPWStr:
|
||||
return writer.WriteNullTerminatedUnicodeString(fieldValue);
|
||||
|
||||
// No other string types are recognized
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array of bytes to the underlying stream
|
||||
/// </summary>
|
||||
private static bool WriteFromBuffer(BinaryWriter writer, byte[] value)
|
||||
{
|
||||
// If the stream is not writable
|
||||
if (!writer.BaseStream.CanWrite)
|
||||
return false;
|
||||
|
||||
// Handle the 0-byte case
|
||||
if (value.Length == 0)
|
||||
return true;
|
||||
|
||||
// Handle the general case, forcing a write of the correct length
|
||||
writer.Write(value, 0, value.Length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,359 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for byte arrays
|
||||
/// </summary>
|
||||
/// <remarks>TODO: Add U/Int24 and U/Int48 methods</remarks>
|
||||
public static class ByteArrayExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a UInt8 and increment the pointer to an array
|
||||
/// Indicates whether the specified array is null or has a length of zero
|
||||
/// </summary>
|
||||
public static byte ReadByte(this byte[] content, ref int offset)
|
||||
public static bool IsNullOrEmpty(this Array? array)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 1);
|
||||
return buffer[0];
|
||||
return array == null || array.Length == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt8 and increment the pointer to an array
|
||||
/// Convert a byte array to a hex string
|
||||
/// </summary>
|
||||
public static byte ReadByteValue(this byte[] content, ref int offset)
|
||||
=> content.ReadByte(ref offset);
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt8[] and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static byte[] ReadBytes(this byte[] content, ref int offset, int count)
|
||||
=> ReadToBuffer(content, ref offset, count);
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int8 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static sbyte ReadSByte(this byte[] content, ref int offset)
|
||||
public static string? ToHexString(this byte[]? bytes)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 1);
|
||||
return (sbyte)buffer[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Char and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static char ReadChar(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 1);
|
||||
return (char)buffer[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static short ReadInt16(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 2);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int16 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static short ReadInt16BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static ushort ReadUInt16(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 2);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt16 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static ushort ReadUInt16BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static int ReadInt32(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int32 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static int ReadInt32BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static uint ReadUInt32(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt32 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static uint ReadUInt32BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Single and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static float ReadSingle(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Single in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static float ReadSingleBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static long ReadInt64(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int64 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static long ReadInt64BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static ulong ReadUInt64(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt64 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static ulong ReadUInt64BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Double and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static double ReadDouble(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Double in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static double ReadDoubleBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static Guid ReadGuid(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
return new Guid(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static Guid ReadGuidBigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 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 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static Int128 ReadInt128(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
return new Int128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int128 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static Int128 ReadInt128BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
Array.Reverse(buffer);
|
||||
return new Int128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static UInt128 ReadUInt128(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 in big-endian format and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static UInt128 ReadUInt128BigEndian(this byte[] content, ref int offset)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(content, ref offset, 16);
|
||||
Array.Reverse(buffer);
|
||||
return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadString(this byte[] content, ref int offset)
|
||||
=> content.ReadString(ref offset, Encoding.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the byte array
|
||||
/// </summary>
|
||||
public static string? ReadString(this byte[] content, ref int offset, Encoding encoding)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
// If we get null in, we send null out
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
byte[] nullTerminator = encoding.GetBytes("\0");
|
||||
int charWidth = nullTerminator.Length;
|
||||
|
||||
var keyChars = new List<char>();
|
||||
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');
|
||||
string hex = BitConverter.ToString(bytes);
|
||||
return hex.Replace("-", string.Empty).ToLowerInvariant();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a string that is terminated by a newline but contains a quoted portion that
|
||||
/// may also contain a newline from the stream
|
||||
/// Convert a hex string to a byte array
|
||||
/// </summary>
|
||||
public static string? ReadQuotedString(this byte[] content, ref int offset)
|
||||
=> content.ReadQuotedString(ref offset, 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 byte[] content, ref int offset, Encoding encoding)
|
||||
public static byte[]? FromHexString(this string? hex)
|
||||
{
|
||||
if (offset >= content.Length)
|
||||
// If we get null in, we send null out
|
||||
if (string.IsNullOrEmpty(hex))
|
||||
return null;
|
||||
|
||||
byte[] nullTerminator = encoding.GetBytes("\0");
|
||||
int charWidth = nullTerminator.Length;
|
||||
|
||||
var keyChars = new List<char>();
|
||||
bool openQuote = false;
|
||||
while (offset < content.Length)
|
||||
try
|
||||
{
|
||||
char c = encoding.GetChars(content, offset, charWidth)[0];
|
||||
keyChars.Add(c);
|
||||
offset += charWidth;
|
||||
int chars = hex!.Length;
|
||||
byte[] bytes = new byte[chars / 2];
|
||||
for (int i = 0; i < chars; i += 2)
|
||||
{
|
||||
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||
}
|
||||
|
||||
// If we have a quote, flip the flag
|
||||
if (c == '"')
|
||||
openQuote = !openQuote;
|
||||
|
||||
// If we have a newline not in a quoted string, exit the loop
|
||||
else if (c == (byte)'\n' && !openQuote)
|
||||
break;
|
||||
return bytes;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new string([.. keyChars]).TrimEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a number of bytes from the current byte array to a buffer
|
||||
/// </summary>
|
||||
private static byte[] ReadToBuffer(byte[] content, ref int offset, int length)
|
||||
{
|
||||
// If we have an invalid length
|
||||
if (length < 0)
|
||||
throw new ArgumentOutOfRangeException($"{nameof(length)} must be 0 or a positive value");
|
||||
|
||||
// Handle the 0-byte case
|
||||
if (length == 0)
|
||||
return [];
|
||||
|
||||
// If there are not enough bytes
|
||||
if (offset + length > content.Length)
|
||||
throw new System.IO.EndOfStreamException(nameof(content));
|
||||
|
||||
// Handle the general case, forcing a read of the correct length
|
||||
byte[] buffer = new byte[length];
|
||||
Array.Copy(content, offset, buffer, 0, length);
|
||||
offset += length;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
1088
SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs
Normal file
1088
SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs
Normal file
File diff suppressed because it is too large
Load Diff
798
SabreTools.IO/Extensions/ByteArrayWriterExtensions.cs
Normal file
798
SabreTools.IO/Extensions/ByteArrayWriterExtensions.cs
Normal file
@@ -0,0 +1,798 @@
|
||||
using System;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for byte arrays
|
||||
/// </summary>
|
||||
/// TODO: Handle proper negative values for Int24 and Int48
|
||||
public static class ByteArrayWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a UInt8 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, byte value)
|
||||
=> WriteFromBuffer(content, ref offset, [value]);
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt8[] and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, byte[] value)
|
||||
=> WriteFromBuffer(content, ref offset, value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt8[] and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, byte[] value)
|
||||
{
|
||||
Array.Reverse(value);
|
||||
return WriteFromBuffer(content, ref offset, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int8 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, sbyte value)
|
||||
=> WriteFromBuffer(content, ref offset, [(byte)value]);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Char and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, char value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Char with an Encoding and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, char value, Encoding encoding)
|
||||
{
|
||||
byte[] buffer = encoding.GetBytes($"{value}");
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, short value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, short value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, ushort value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt16 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, ushort value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
// Half was introduced in net5.0 but doesn't have a BitConverter implementation until net6.0
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a Half and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, Half value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Half and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, Half value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 as an Int24 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsInt24(this byte[] content, ref int offset, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, reduced, 3);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 as an Int24 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsInt24BigEndian(this byte[] content, ref int offset, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, 1, reduced, 0, 3);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 as a UInt24 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsUInt24(this byte[] content, ref int offset, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, reduced, 3);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 as a UInt24 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsUInt24BigEndian(this byte[] content, ref int offset, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, 1, reduced, 0, 3);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Single and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, float value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Single and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, float value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 as an Int48 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsInt48(this byte[] content, ref int offset, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, reduced, 6);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 as an Int48 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsInt48BigEndian(this byte[] content, ref int offset, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, 2, reduced, 0, 6);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 as a UInt48 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsUInt48(this byte[] content, ref int offset, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, reduced, 6);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 as a UInt48 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsUInt48BigEndian(this byte[] content, ref int offset, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, 2, reduced, 0, 6);
|
||||
return WriteFromBuffer(content, ref offset, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Double and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, double value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Double and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, double value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Decimal and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, decimal value)
|
||||
{
|
||||
int[] bits = decimal.GetBits(value);
|
||||
|
||||
byte[] lo = BitConverter.GetBytes(bits[0]);
|
||||
byte[] mid = BitConverter.GetBytes(bits[1]);
|
||||
byte[] hi = BitConverter.GetBytes(bits[2]);
|
||||
byte[] flags = BitConverter.GetBytes(bits[3]);
|
||||
|
||||
byte[] buffer = new byte[16];
|
||||
Array.Copy(lo, 0, buffer, 0, 4);
|
||||
Array.Copy(mid, 0, buffer, 4, 4);
|
||||
Array.Copy(hi, 0, buffer, 8, 4);
|
||||
Array.Copy(flags, 0, buffer, 12, 4);
|
||||
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Decimal and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, decimal value)
|
||||
{
|
||||
int[] bits = decimal.GetBits(value);
|
||||
|
||||
byte[] lo = BitConverter.GetBytes(bits[0]);
|
||||
byte[] mid = BitConverter.GetBytes(bits[1]);
|
||||
byte[] hi = BitConverter.GetBytes(bits[2]);
|
||||
byte[] flags = BitConverter.GetBytes(bits[3]);
|
||||
|
||||
byte[] buffer = new byte[16];
|
||||
Array.Copy(lo, 0, buffer, 0, 4);
|
||||
Array.Copy(mid, 0, buffer, 4, 4);
|
||||
Array.Copy(hi, 0, buffer, 8, 4);
|
||||
Array.Copy(flags, 0, buffer, 12, 4);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Guid and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, Guid value)
|
||||
{
|
||||
byte[] buffer = value.ToByteArray();
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Guid and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, Guid value)
|
||||
{
|
||||
byte[] buffer = value.ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write an Int128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, Int128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(content, ref offset, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, Int128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(content, ref offset, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this byte[] content, ref int offset, UInt128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(content, ref offset, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt128 and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this byte[] content, ref int offset, UInt128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(content, ref offset, padded);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this byte[] content, ref int offset, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.ASCII);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the byte array
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this byte[] content, ref int offset, string? value)
|
||||
=> content.WriteNullTerminatedString(ref offset, value, Encoding.UTF32);
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!content.Write(ref offset, (byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the byte array
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this byte[] content, ref int offset, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!content.Write(ref offset, (ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the byte array
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are written by value, not by reference
|
||||
/// - Complex objects are written by value, not by reference
|
||||
/// - Enumeration values are written by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are serialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static bool WriteType<T>(this byte[] content, ref int offset, T? value)
|
||||
=> content.WriteType(ref offset, value, typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the byte array
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are written by value, not by reference
|
||||
/// - Complex objects are written by value, not by reference
|
||||
/// - Enumeration values are written by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are serialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static bool WriteType(this byte[] content, ref int offset, object? value, Type type)
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
// Handle special struct cases
|
||||
if (type == typeof(Guid))
|
||||
return content.Write(ref offset, (Guid)value);
|
||||
#if NET6_0_OR_GREATER
|
||||
else if (type == typeof(Half))
|
||||
return content.Write(ref offset, (Half)value);
|
||||
#endif
|
||||
#if NET7_0_OR_GREATER
|
||||
else if (type == typeof(Int128))
|
||||
return content.Write(ref offset, (Int128)value);
|
||||
else if (type == typeof(UInt128))
|
||||
return content.Write(ref offset, (UInt128)value);
|
||||
#endif
|
||||
|
||||
if (type.IsClass || (type.IsValueType && !type.IsEnum && !type.IsPrimitive))
|
||||
return WriteComplexType(content, ref offset, value, type);
|
||||
else if (type.IsValueType && type.IsEnum)
|
||||
return WriteNormalType(content, ref offset, value, Enum.GetUnderlyingType(type));
|
||||
else
|
||||
return WriteNormalType(content, ref offset, value, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the stream
|
||||
/// </summary>
|
||||
private static bool WriteNormalType(byte[] content, ref int offset, object? value, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
int typeSize = Marshal.SizeOf(type);
|
||||
if (value.GetType() != type)
|
||||
value = Convert.ChangeType(value, type);
|
||||
|
||||
var buffer = new byte[typeSize];
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);
|
||||
handle.Free();
|
||||
|
||||
return WriteFromBuffer(content, ref offset, buffer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the stream
|
||||
/// </summary>
|
||||
private static bool WriteComplexType(byte[] content, ref int offset, object? value, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
// Get the layout information
|
||||
var layoutAttr = MarshalHelpers.GetAttribute<StructLayoutAttribute>(type);
|
||||
LayoutKind layoutKind = MarshalHelpers.DetermineLayoutKind(layoutAttr, type);
|
||||
Encoding encoding = MarshalHelpers.DetermineEncoding(layoutAttr);
|
||||
|
||||
// Cache the current offset
|
||||
int currentOffset = offset;
|
||||
|
||||
// Generate the fields by parent first
|
||||
var fields = MarshalHelpers.GetFields(type);
|
||||
|
||||
// Loop through the fields and set them
|
||||
foreach (var fi in fields)
|
||||
{
|
||||
// If we have an explicit layout, move accordingly
|
||||
if (layoutKind == LayoutKind.Explicit)
|
||||
{
|
||||
var fieldOffset = MarshalHelpers.GetAttribute<FieldOffsetAttribute>(fi);
|
||||
offset = currentOffset + fieldOffset?.Value ?? 0;
|
||||
}
|
||||
|
||||
if (!GetField(content, ref offset, encoding, fields, value, fi))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a single field from an object
|
||||
/// </summary>
|
||||
private static bool GetField(byte[] content, ref int offset, Encoding encoding, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
if (fi.FieldType.IsAssignableFrom(typeof(string)))
|
||||
{
|
||||
return WriteStringType(content, ref offset, encoding, instance, fi);
|
||||
}
|
||||
else if (fi.FieldType.IsArray)
|
||||
{
|
||||
return WriteArrayType(content, ref offset, fields, instance, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = fi.GetValue(instance);
|
||||
return content.WriteType(ref offset, value, fi.FieldType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array type field from an object
|
||||
/// </summary>
|
||||
private static bool WriteArrayType(byte[] content, ref int offset, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
if (marshalAsAttr == null)
|
||||
return false;
|
||||
|
||||
// Get the array
|
||||
Array? arr = fi.GetValue(instance) as Array;
|
||||
if (arr == null)
|
||||
return false;
|
||||
|
||||
// Get the number of elements expected
|
||||
int elementCount = MarshalHelpers.GetArrayElementCount(marshalAsAttr, fields, instance);
|
||||
if (elementCount < 0)
|
||||
return false;
|
||||
|
||||
// Get the item type for the array
|
||||
Type elementType = fi.FieldType.GetElementType() ?? typeof(object);
|
||||
|
||||
// Loop through and write the array
|
||||
for (int i = 0; i < elementCount; i++)
|
||||
{
|
||||
var value = arr.GetValue(i);
|
||||
if (!WriteType(content, ref offset, value, elementType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a string type field from an object
|
||||
/// </summary>
|
||||
private static bool WriteStringType(byte[] content, ref int offset, Encoding encoding, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
string? fieldValue = fi.GetValue(instance) as string;
|
||||
if (fieldValue == null)
|
||||
return true;
|
||||
|
||||
switch (marshalAsAttr?.Value)
|
||||
{
|
||||
case UnmanagedType.AnsiBStr:
|
||||
return content.WritePrefixedAnsiString(ref offset, fieldValue);
|
||||
|
||||
case UnmanagedType.BStr:
|
||||
case UnmanagedType.TBStr: // Technically distinct; returns char[] instead
|
||||
return content.WritePrefixedUnicodeString(ref offset, fieldValue);
|
||||
|
||||
case UnmanagedType.ByValTStr:
|
||||
int byvalLength = marshalAsAttr!.SizeConst;
|
||||
byte[] byvalBytes = encoding.GetBytes(fieldValue);
|
||||
byte[] byvalSizedBytes = new byte[byvalLength];
|
||||
Array.Copy(byvalBytes, byvalSizedBytes, Math.Min(byvalBytes.Length, byvalSizedBytes.Length));
|
||||
return content.Write(ref offset, byvalSizedBytes);
|
||||
|
||||
case UnmanagedType.LPStr:
|
||||
case UnmanagedType.LPTStr: // Technically distinct; possibly not null-terminated
|
||||
case null:
|
||||
return content.WriteNullTerminatedAnsiString(ref offset, fieldValue);
|
||||
|
||||
#if NET472_OR_GREATER || NETCOREAPP
|
||||
case UnmanagedType.LPUTF8Str:
|
||||
return content.WriteNullTerminatedUTF8String(ref offset, fieldValue);
|
||||
#endif
|
||||
|
||||
case UnmanagedType.LPWStr:
|
||||
return content.WriteNullTerminatedUnicodeString(ref offset, fieldValue);
|
||||
|
||||
// No other string types are recognized
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array of bytes to the byte array
|
||||
/// </summary>
|
||||
private static bool WriteFromBuffer(byte[] content, ref int offset, byte[] value)
|
||||
{
|
||||
// Handle the 0-byte case
|
||||
if (value.Length == 0)
|
||||
return true;
|
||||
|
||||
// If there are not enough bytes
|
||||
if (offset + value.Length > content.Length)
|
||||
throw new System.IO.EndOfStreamException(nameof(content));
|
||||
|
||||
// Handle the general case, forcing a write of the correct length
|
||||
Array.Copy(value, 0, content, offset, value.Length);
|
||||
offset += value.Length;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
SabreTools.IO/Extensions/EnumerableExtensions.cs
Normal file
57
SabreTools.IO/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
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
|
||||
IEnumerator<T> enumerator;
|
||||
try
|
||||
{
|
||||
enumerator = enumerable.GetEnumerator();
|
||||
}
|
||||
catch
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Iterate through and absorb any errors
|
||||
while (true)
|
||||
{
|
||||
// Attempt to move to the next item
|
||||
bool moved;
|
||||
try
|
||||
{
|
||||
moved = enumerator.MoveNext();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Specific case for collections that were modified
|
||||
yield break;
|
||||
}
|
||||
catch (System.IO.IOException ex) when (ex.Message.Contains("The file or directory is corrupted and unreadable."))
|
||||
{
|
||||
// Specific case we can't circumvent
|
||||
yield break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
@@ -50,13 +49,13 @@ namespace SabreTools.IO.Extensions
|
||||
// Try to open the file
|
||||
try
|
||||
{
|
||||
FileStream file = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
var file = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
if (file == null)
|
||||
return Encoding.Default;
|
||||
|
||||
// Read the BOM
|
||||
var bom = new byte[4];
|
||||
file.Read(bom, 0, 4);
|
||||
int read = file.Read(bom, 0, 4);
|
||||
file.Dispose();
|
||||
|
||||
// Disable warning about UTF7 usage
|
||||
@@ -110,31 +109,491 @@ namespace SabreTools.IO.Extensions
|
||||
public static List<string>? ListEmpty(this string? root)
|
||||
{
|
||||
// Check null or empty first
|
||||
if (string.IsNullOrEmpty(root))
|
||||
if (root == null)
|
||||
return null;
|
||||
|
||||
// Then, check if the root exists
|
||||
if (!Directory.Exists(root))
|
||||
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
|
||||
return [];
|
||||
|
||||
// Otherwise, get the complete list
|
||||
#if NET20 || NET35
|
||||
return Directory.GetDirectories(root, "*", SearchOption.AllDirectories)
|
||||
.Where(dir => !Directory.GetFiles(dir, "*", SearchOption.AllDirectories).Any())
|
||||
.ToList();
|
||||
#else
|
||||
return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories)
|
||||
.Where(dir => !Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Any())
|
||||
.ToList();
|
||||
#endif
|
||||
var empty = new List<string>();
|
||||
foreach (var dir in SafeGetDirectories(root, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (SafeGetFiles(dir).Length == 0)
|
||||
empty.Add(dir);
|
||||
}
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
#region Safe Directory Enumeration
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetDirectories(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetDirectories(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetDirectories(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetDirectories(path, searchPattern);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string, SearchOption)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetDirectories(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetDirectories(path, searchPattern, searchOption);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetFiles(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetFiles(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string, string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetFiles(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetFiles(path, searchPattern);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFiles(string, string, SearchOption)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetFiles(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetFiles(path, searchPattern, searchOption);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetFileSystemEntries(string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetFileSystemEntries(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetFileSystemEntries(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.GetDirectories(string, string)"/>
|
||||
/// <remarks>Returns an empty enumerable on any exception</remarks>
|
||||
public static string[] SafeGetFileSystemEntries(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Directory.GetFileSystemEntries(path, searchPattern);
|
||||
}
|
||||
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);
|
||||
#elif NET40_OR_GREATER
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateDirectories(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateFiles(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
#else
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
string searchPattern = "*";
|
||||
SearchOption searchOption = SearchOption.TopDirectoryOnly;
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
SearchOption searchOption = SearchOption.TopDirectoryOnly;
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateDirectories(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateDirectories(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateDirectories(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
string searchPattern = "*";
|
||||
SearchOption searchOption = SearchOption.TopDirectoryOnly;
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
SearchOption searchOption = SearchOption.TopDirectoryOnly;
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFiles(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFiles(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateFiles(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
string searchPattern = "*";
|
||||
SearchOption searchOption = SearchOption.TopDirectoryOnly;
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string, string)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
SearchOption searchOption = SearchOption.TopDirectoryOnly;
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Directory.EnumerateFileSystemEntries(string, string, SearchOption)"/>
|
||||
public static IEnumerable<string> SafeEnumerateFileSystemEntries(this string path, string searchPattern, SearchOption searchOption)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enumerationOptions = FromSearchOption(searchOption);
|
||||
enumerationOptions.IgnoreInaccessible = true;
|
||||
var enumerable = Directory.EnumerateFileSystemEntries(path, searchPattern, enumerationOptions);
|
||||
return enumerable.SafeEnumerate();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="EnumerationOptions" /> class with the recommended default options.</summary>
|
||||
/// <see href="https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/IO/EnumerationOptions.cs#L42"</remarks>
|
||||
private static EnumerationOptions FromSearchOption(SearchOption searchOption)
|
||||
{
|
||||
if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
|
||||
throw new System.ArgumentOutOfRangeException(nameof(searchOption));
|
||||
|
||||
return searchOption == SearchOption.AllDirectories
|
||||
? new EnumerationOptions { RecurseSubdirectories = true, MatchType = MatchType.Win32, AttributesToSkip = 0, IgnoreInaccessible = false }
|
||||
: new EnumerationOptions { MatchType = MatchType.Win32, AttributesToSkip = 0, IgnoreInaccessible = false };
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
177
SabreTools.IO/Extensions/MarshalHelpers.cs
Normal file
177
SabreTools.IO/Extensions/MarshalHelpers.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Common methods for use during marshalling
|
||||
/// </summary>
|
||||
internal static class MarshalHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Get an attribute of the requested type
|
||||
/// </summary>
|
||||
public static T? GetAttribute<T>(FieldInfo? fi) where T : Attribute
|
||||
{
|
||||
// If the field info is invalid
|
||||
if (fi == null)
|
||||
return null;
|
||||
|
||||
// Get all matching attributes
|
||||
var attributes = fi.GetCustomAttributes(typeof(T), true);
|
||||
if (attributes == null || attributes.Length == 0)
|
||||
return null;
|
||||
|
||||
// Get the first attribute that matches
|
||||
return attributes[0] as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an attribute of the requested type
|
||||
/// </summary>
|
||||
public static T? GetAttribute<T>(Type? type) where T : Attribute
|
||||
{
|
||||
// If the field info is invalid
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
// Get all matching attributes
|
||||
var attributes = type.GetCustomAttributes(typeof(T), true);
|
||||
if (attributes == null || attributes.Length == 0)
|
||||
return null;
|
||||
|
||||
// Get the first attribute that matches
|
||||
return attributes[0] as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the layout kind for a type
|
||||
/// </summary>
|
||||
public static LayoutKind DetermineLayoutKind(StructLayoutAttribute? layoutAttr, Type type)
|
||||
{
|
||||
LayoutKind layoutKind = LayoutKind.Auto;
|
||||
|
||||
if (layoutAttr != null)
|
||||
layoutKind = layoutAttr.Value;
|
||||
else if (type.IsAutoLayout)
|
||||
layoutKind = LayoutKind.Auto;
|
||||
else if (type.IsExplicitLayout)
|
||||
layoutKind = LayoutKind.Explicit;
|
||||
else if (type.IsLayoutSequential)
|
||||
layoutKind = LayoutKind.Sequential;
|
||||
|
||||
return layoutKind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the encoding for a type
|
||||
/// </summary>
|
||||
public static Encoding DetermineEncoding(StructLayoutAttribute? layoutAttr)
|
||||
{
|
||||
return layoutAttr?.CharSet switch
|
||||
{
|
||||
CharSet.None => Encoding.ASCII,
|
||||
CharSet.Ansi => Encoding.ASCII,
|
||||
CharSet.Unicode => Encoding.Unicode,
|
||||
CharSet.Auto => Encoding.ASCII, // UTF-8 on Unix
|
||||
_ => Encoding.ASCII,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the parent hierarchy for a given type
|
||||
/// </summary>
|
||||
/// <remarks>Returns the highest parent as the first element</remarks>
|
||||
public static IEnumerable<Type> DetermineTypeLineage(Type type)
|
||||
{
|
||||
var lineage = new List<Type>();
|
||||
while (type != typeof(object) && type != typeof(ValueType))
|
||||
{
|
||||
lineage.Add(type);
|
||||
type = type.BaseType ?? typeof(object);
|
||||
}
|
||||
|
||||
lineage.Reverse();
|
||||
return lineage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an ordered set of fields from a type
|
||||
/// </summary>
|
||||
/// <remarks>Returns fields from the parents before fields from the type</remarks>
|
||||
public static FieldInfo[] GetFields(Type type)
|
||||
{
|
||||
// Get the type hierarchy for ensuring serialization order
|
||||
var lineage = DetermineTypeLineage(type);
|
||||
|
||||
// Generate the fields by parent first
|
||||
var fieldsList = new List<FieldInfo>();
|
||||
foreach (var nextType in lineage)
|
||||
{
|
||||
var nextFields = nextType.GetFields();
|
||||
foreach (var field in nextFields)
|
||||
{
|
||||
// Add fields if they aren't already included
|
||||
int index = fieldsList.FindIndex(f => f.Name == field.Name && f.FieldType == field.FieldType);
|
||||
if (index == -1)
|
||||
fieldsList.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return [.. fieldsList];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the expected array size for a field
|
||||
/// </summary>
|
||||
/// <returns>Array size on success, -1 on failure</returns>
|
||||
public static int GetArrayElementCount(MarshalAsAttribute marshalAsAttr, FieldInfo[] fields, object instance)
|
||||
{
|
||||
int elementCount = -1;
|
||||
if (marshalAsAttr.Value == UnmanagedType.ByValArray)
|
||||
{
|
||||
elementCount = marshalAsAttr.SizeConst;
|
||||
}
|
||||
else if (marshalAsAttr.Value == UnmanagedType.LPArray)
|
||||
{
|
||||
elementCount = marshalAsAttr.SizeConst;
|
||||
if (marshalAsAttr.SizeParamIndex >= 0)
|
||||
elementCount = GetLPArraySizeFromField(marshalAsAttr, fields, instance);
|
||||
}
|
||||
|
||||
return elementCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the expected LPArray size from a field
|
||||
/// </summary>
|
||||
public static int GetLPArraySizeFromField(MarshalAsAttribute marshalAsAttr, FieldInfo[] fields, object instance)
|
||||
{
|
||||
// If the index is invalid
|
||||
if (marshalAsAttr.SizeParamIndex < 0)
|
||||
return -1;
|
||||
|
||||
// Get the size field
|
||||
var sizeField = fields[marshalAsAttr.SizeParamIndex];
|
||||
if (sizeField == null)
|
||||
return -1;
|
||||
|
||||
// Cast based on the field type
|
||||
return sizeField.GetValue(instance) switch
|
||||
{
|
||||
sbyte val => val,
|
||||
byte val => val,
|
||||
short val => val,
|
||||
ushort val => val,
|
||||
int val => val,
|
||||
uint val => (int)val,
|
||||
long val => (int)val,
|
||||
ulong val => (int)val,
|
||||
_ => -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,327 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for Streams
|
||||
/// </summary>
|
||||
/// <remarks>TODO: Add U/Int24 and U/Int48 methods</remarks>
|
||||
public static class StreamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read a UInt8 from the stream
|
||||
/// Align the stream position to a byte-size boundary
|
||||
/// </summary>
|
||||
public static byte ReadByteValue(this Stream stream)
|
||||
/// <param name="input">Input stream to try aligning</param>
|
||||
/// <param name="alignment">Number of bytes to align on</param>
|
||||
/// <returns>True if the stream could be aligned, false otherwise</returns>
|
||||
public static bool AlignToBoundary(this Stream? input, byte alignment)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 1);
|
||||
return buffer[0];
|
||||
}
|
||||
// If the stream is invalid
|
||||
if (input == null || input.Length == 0 || !input.CanRead)
|
||||
return false;
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt8[] from the stream
|
||||
/// </summary>
|
||||
public static byte[] ReadBytes(this Stream stream, int count)
|
||||
=> ReadToBuffer(stream, count);
|
||||
// If already at the end of the stream
|
||||
if (input.Position >= input.Length)
|
||||
return false;
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int8 from the stream
|
||||
/// </summary>
|
||||
public static sbyte ReadSByte(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 1);
|
||||
return (sbyte)buffer[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Char from the stream
|
||||
/// </summary>
|
||||
public static char ReadChar(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 1);
|
||||
return (char)buffer[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int16 from the stream
|
||||
/// </summary>
|
||||
public static short ReadInt16(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 2);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int16 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static short ReadInt16BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt16 from the stream
|
||||
/// </summary>
|
||||
public static ushort ReadUInt16(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 2);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt16 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static ushort ReadUInt16BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 2);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int32 from the stream
|
||||
/// </summary>
|
||||
public static int ReadInt32(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int32 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static int ReadInt32BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt32 from the stream
|
||||
/// </summary>
|
||||
public static uint ReadUInt32(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt32 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static uint ReadUInt32BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Single from the stream
|
||||
/// </summary>
|
||||
public static float ReadSingle(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Single from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static float ReadSingleBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 4);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToSingle(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int64 from the stream
|
||||
/// </summary>
|
||||
public static long ReadInt64(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int64 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static long ReadInt64BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt64 from the stream
|
||||
/// </summary>
|
||||
public static ulong ReadUInt64(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt64 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static ulong ReadUInt64BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToUInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Double from the stream
|
||||
/// </summary>
|
||||
public static double ReadDouble(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Double from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static double ReadDoubleBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 8);
|
||||
Array.Reverse(buffer);
|
||||
return BitConverter.ToDouble(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid from the stream
|
||||
/// </summary>
|
||||
public static Guid ReadGuid(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
return new Guid(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a Guid from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static Guid ReadGuidBigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 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 stream
|
||||
/// </summary>
|
||||
public static Int128 ReadInt128(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
return new Int128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Int128 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static Int128 ReadInt128BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
Array.Reverse(buffer);
|
||||
return new Int128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 from the stream
|
||||
/// </summary>
|
||||
public static UInt128 ReadUInt128(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a UInt128 from the stream in big-endian format
|
||||
/// </summary>
|
||||
public static UInt128 ReadUInt128BigEndian(this Stream stream)
|
||||
{
|
||||
byte[] buffer = ReadToBuffer(stream, 16);
|
||||
Array.Reverse(buffer);
|
||||
return new UInt128(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, 8));
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadString(this Stream stream)
|
||||
=> stream.ReadString(Encoding.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Read a null-terminated string from the stream
|
||||
/// </summary>
|
||||
public static string? ReadString(this Stream stream, Encoding encoding)
|
||||
{
|
||||
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))
|
||||
// Align the stream position
|
||||
while (input.Position % alignment != 0 && input.Position < input.Length)
|
||||
{
|
||||
tempBuffer.AddRange(buffer);
|
||||
_ = input.ReadByteValue();
|
||||
}
|
||||
|
||||
return encoding.GetString([.. tempBuffer]);
|
||||
}
|
||||
|
||||
/// <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 Stream stream)
|
||||
=> stream.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 Stream stream, Encoding encoding)
|
||||
{
|
||||
if (stream.Position >= stream.Length)
|
||||
return null;
|
||||
|
||||
var bytes = new List<byte>();
|
||||
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();
|
||||
// Return if the alignment completed
|
||||
return input.Position % alignment == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -331,10 +37,6 @@ namespace SabreTools.IO.Extensions
|
||||
/// <param name="offset">Optional offset to seek to</param>
|
||||
public static long SeekIfPossible(this Stream input, long offset = 0)
|
||||
{
|
||||
// If the stream is null, don't even try
|
||||
if (input == null)
|
||||
return -1;
|
||||
|
||||
// If the input is not seekable, just return the current position
|
||||
if (!input.CanSeek)
|
||||
{
|
||||
@@ -352,37 +54,13 @@ namespace SabreTools.IO.Extensions
|
||||
{
|
||||
if (offset < 0)
|
||||
return input.Seek(offset, SeekOrigin.End);
|
||||
else if (offset >= 0)
|
||||
else
|
||||
return input.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
return input.Position;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a number of bytes from the current Stream to a buffer
|
||||
/// </summary>
|
||||
private static byte[] ReadToBuffer(Stream stream, int length)
|
||||
{
|
||||
// If we have an invalid length
|
||||
if (length < 0)
|
||||
throw new ArgumentOutOfRangeException($"{nameof(length)} must be 0 or a positive value");
|
||||
|
||||
// Handle the 0-byte case
|
||||
if (length == 0)
|
||||
return [];
|
||||
|
||||
// Handle the general case, forcing a read of the correct length
|
||||
byte[] buffer = new byte[length];
|
||||
int read = stream.Read(buffer, 0, length);
|
||||
if (read < length)
|
||||
throw new EndOfStreamException(nameof(stream));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1069
SabreTools.IO/Extensions/StreamReaderExtensions.cs
Normal file
1069
SabreTools.IO/Extensions/StreamReaderExtensions.cs
Normal file
File diff suppressed because it is too large
Load Diff
798
SabreTools.IO/Extensions/StreamWriterExtensions.cs
Normal file
798
SabreTools.IO/Extensions/StreamWriterExtensions.cs
Normal file
@@ -0,0 +1,798 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for Streams
|
||||
/// </summary>
|
||||
/// TODO: Handle proper negative values for Int24 and Int48
|
||||
public static class StreamWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a UInt8
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, byte value)
|
||||
=> WriteFromBuffer(stream, [value]);
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt8[]
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, byte[] value)
|
||||
=> WriteFromBuffer(stream, value);
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt8[]
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, byte[] value)
|
||||
{
|
||||
Array.Reverse(value);
|
||||
return WriteFromBuffer(stream, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int8
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, sbyte value)
|
||||
=> WriteFromBuffer(stream, [(byte)value]);
|
||||
|
||||
/// <summary>
|
||||
/// Write a Char
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, char value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Char with an Encoding
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, char value, Encoding encoding)
|
||||
{
|
||||
byte[] buffer = encoding.GetBytes($"{value}");
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int16
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, short value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int16
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, short value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt16
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, ushort value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt16
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, ushort value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
// Half was introduced in net5.0 but doesn't have a BitConverter implementation until net6.0
|
||||
#if NET6_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write a Half
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, Half value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Half
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, Half value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 as an Int24
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsInt24(this Stream stream, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, reduced, 3);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32 as an Int24
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsInt24BigEndian(this Stream stream, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, 1, reduced, 0, 3);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 as a UInt24
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsUInt24(this Stream stream, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, reduced, 3);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32 as a UInt24
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top byte</remarks>
|
||||
public static bool WriteAsUInt24BigEndian(this Stream stream, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[3];
|
||||
Array.Copy(buffer, 1, reduced, 0, 3);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int32
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, int value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt32
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, uint value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Single
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, float value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Single
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, float value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 as an Int48
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsInt48(this Stream stream, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, reduced, 6);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64 as an Int48
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsInt48BigEndian(this Stream stream, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, 2, reduced, 0, 6);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 as a UInt48
|
||||
/// </summary>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsUInt48(this Stream stream, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, reduced, 6);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64 as a UInt48
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
/// <remarks>Throws away top 2 bytes</remarks>
|
||||
public static bool WriteAsUInt48BigEndian(this Stream stream, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] reduced = new byte[6];
|
||||
Array.Copy(buffer, 2, reduced, 0, 6);
|
||||
return WriteFromBuffer(stream, reduced);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int64
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, long value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt64
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, ulong value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Double
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, double value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Double
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, double value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Decimal and increment the pointer to an array
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, decimal value)
|
||||
{
|
||||
int[] bits = decimal.GetBits(value);
|
||||
|
||||
byte[] lo = BitConverter.GetBytes(bits[0]);
|
||||
byte[] mid = BitConverter.GetBytes(bits[1]);
|
||||
byte[] hi = BitConverter.GetBytes(bits[2]);
|
||||
byte[] flags = BitConverter.GetBytes(bits[3]);
|
||||
|
||||
byte[] buffer = new byte[16];
|
||||
Array.Copy(lo, 0, buffer, 0, 4);
|
||||
Array.Copy(mid, 0, buffer, 4, 4);
|
||||
Array.Copy(hi, 0, buffer, 8, 4);
|
||||
Array.Copy(flags, 0, buffer, 12, 4);
|
||||
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Decimal and increment the pointer to an array
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, decimal value)
|
||||
{
|
||||
int[] bits = decimal.GetBits(value);
|
||||
|
||||
byte[] lo = BitConverter.GetBytes(bits[0]);
|
||||
byte[] mid = BitConverter.GetBytes(bits[1]);
|
||||
byte[] hi = BitConverter.GetBytes(bits[2]);
|
||||
byte[] flags = BitConverter.GetBytes(bits[3]);
|
||||
|
||||
byte[] buffer = new byte[16];
|
||||
Array.Copy(lo, 0, buffer, 0, 4);
|
||||
Array.Copy(mid, 0, buffer, 4, 4);
|
||||
Array.Copy(hi, 0, buffer, 8, 4);
|
||||
Array.Copy(flags, 0, buffer, 12, 4);
|
||||
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Guid
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, Guid value)
|
||||
{
|
||||
byte[] buffer = value.ToByteArray();
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Guid
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, Guid value)
|
||||
{
|
||||
byte[] buffer = value.ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Write an Int128
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, Int128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(stream, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an Int128
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, Int128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(stream, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt128
|
||||
/// </summary>
|
||||
public static bool Write(this Stream stream, UInt128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(stream, padded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a UInt128
|
||||
/// </summary>
|
||||
/// <remarks>Writes in big-endian format</remarks>
|
||||
public static bool WriteBigEndian(this Stream stream, UInt128 value)
|
||||
{
|
||||
byte[] buffer = ((BigInteger)value).ToByteArray();
|
||||
Array.Reverse(buffer);
|
||||
|
||||
byte[] padded = new byte[16];
|
||||
Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length);
|
||||
return WriteFromBuffer(stream, padded);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedString(this Stream stream, string? value, Encoding encoding)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Add the null terminator and write
|
||||
value += "\0";
|
||||
byte[] buffer = encoding.GetBytes(value);
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated ASCII string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedAnsiString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.ASCII);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-8 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF8String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.UTF8);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-16 (Unicode) string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUnicodeString(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.Unicode);
|
||||
|
||||
/// <summary>
|
||||
/// Write a null-terminated UTF-32 string to the stream
|
||||
/// </summary>
|
||||
public static bool WriteNullTerminatedUTF32String(this Stream stream, string? value)
|
||||
=> stream.WriteNullTerminatedString(value, Encoding.UTF32);
|
||||
|
||||
//// <summary>
|
||||
/// Write a byte-prefixed ASCII string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedAnsiString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.ASCII.GetBytes(value);
|
||||
|
||||
// Write the length as a byte
|
||||
if (!stream.Write((byte)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a ushort-prefixed Unicode string to the stream
|
||||
/// </summary>
|
||||
public static bool WritePrefixedUnicodeString(this Stream stream, string? value)
|
||||
{
|
||||
// If the value is null
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
// Get the buffer
|
||||
byte[] buffer = Encoding.Unicode.GetBytes(value);
|
||||
|
||||
// Write the length as a ushort
|
||||
if (!stream.Write((ushort)value.Length))
|
||||
return false;
|
||||
|
||||
// Write the buffer
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are written by value, not by reference
|
||||
/// - Complex objects are written by value, not by reference
|
||||
/// - Enumeration values are written by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are serialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static bool WriteType<T>(this Stream stream, T? value)
|
||||
=> stream.WriteType(value, typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Write a <typeparamref name="T"/> to the stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is different than standard marshalling in a few notable ways:
|
||||
/// - Strings are written by value, not by reference
|
||||
/// - Complex objects are written by value, not by reference
|
||||
/// - Enumeration values are written by the underlying value type
|
||||
/// - Arrays of the above are handled sequentially as above
|
||||
/// - Inherited fields from parents are serialized BEFORE fields in the child
|
||||
/// </remarks>
|
||||
public static bool WriteType(this Stream stream, object? value, Type type)
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
// Handle special struct cases
|
||||
if (type == typeof(Guid))
|
||||
return stream.Write((Guid)value);
|
||||
#if NET6_0_OR_GREATER
|
||||
else if (type == typeof(Half))
|
||||
return stream.Write((Half)value);
|
||||
#endif
|
||||
#if NET7_0_OR_GREATER
|
||||
else if (type == typeof(Int128))
|
||||
return stream.Write((Int128)value);
|
||||
else if (type == typeof(UInt128))
|
||||
return stream.Write((UInt128)value);
|
||||
#endif
|
||||
|
||||
if (type.IsClass || (type.IsValueType && !type.IsEnum && !type.IsPrimitive))
|
||||
return WriteComplexType(stream, value, type);
|
||||
else if (type.IsValueType && type.IsEnum)
|
||||
return WriteNormalType(stream, value, Enum.GetUnderlyingType(type));
|
||||
else
|
||||
return WriteNormalType(stream, value, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the stream
|
||||
/// </summary>
|
||||
private static bool WriteNormalType(Stream stream, object? value, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
int typeSize = Marshal.SizeOf(type);
|
||||
if (value.GetType() != type)
|
||||
value = Convert.ChangeType(value, type);
|
||||
|
||||
var buffer = new byte[typeSize];
|
||||
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);
|
||||
handle.Free();
|
||||
|
||||
return WriteFromBuffer(stream, buffer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a <paramref name="type"/> from the stream
|
||||
/// </summary>
|
||||
private static bool WriteComplexType(Stream stream, object? value, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Null values cannot be written
|
||||
if (value == null)
|
||||
return true;
|
||||
|
||||
// Get the layout information
|
||||
var layoutAttr = MarshalHelpers.GetAttribute<StructLayoutAttribute>(type);
|
||||
LayoutKind layoutKind = MarshalHelpers.DetermineLayoutKind(layoutAttr, type);
|
||||
Encoding encoding = MarshalHelpers.DetermineEncoding(layoutAttr);
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = stream.Position;
|
||||
|
||||
// Generate the fields by parent first
|
||||
var fields = MarshalHelpers.GetFields(type);
|
||||
|
||||
// Loop through the fields and set them
|
||||
foreach (var fi in fields)
|
||||
{
|
||||
// If we have an explicit layout, move accordingly
|
||||
if (layoutKind == LayoutKind.Explicit)
|
||||
{
|
||||
var fieldOffset = MarshalHelpers.GetAttribute<FieldOffsetAttribute>(fi);
|
||||
stream.Seek(currentOffset + fieldOffset?.Value ?? 0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (!GetField(stream, encoding, fields, value, fi))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a single field from an object
|
||||
/// </summary>
|
||||
private static bool GetField(Stream stream, Encoding encoding, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
if (fi.FieldType.IsAssignableFrom(typeof(string)))
|
||||
{
|
||||
return WriteStringType(stream, encoding, instance, fi);
|
||||
}
|
||||
else if (fi.FieldType.IsArray)
|
||||
{
|
||||
return WriteArrayType(stream, fields, instance, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = fi.GetValue(instance);
|
||||
return stream.WriteType(value, fi.FieldType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array type field from an object
|
||||
/// </summary>
|
||||
private static bool WriteArrayType(Stream stream, FieldInfo[] fields, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
if (marshalAsAttr == null)
|
||||
return false;
|
||||
|
||||
// Get the array
|
||||
Array? arr = fi.GetValue(instance) as Array;
|
||||
if (arr == null)
|
||||
return false;
|
||||
|
||||
// Get the number of elements expected
|
||||
int elementCount = MarshalHelpers.GetArrayElementCount(marshalAsAttr, fields, instance);
|
||||
if (elementCount < 0)
|
||||
return false;
|
||||
|
||||
// Get the item type for the array
|
||||
Type elementType = fi.FieldType.GetElementType() ?? typeof(object);
|
||||
|
||||
// Loop through and write the array
|
||||
for (int i = 0; i < elementCount; i++)
|
||||
{
|
||||
var value = arr.GetValue(i);
|
||||
if (!WriteType(stream, value, elementType))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the built array
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a string type field from an object
|
||||
/// </summary>
|
||||
private static bool WriteStringType(Stream stream, Encoding encoding, object instance, FieldInfo fi)
|
||||
{
|
||||
var marshalAsAttr = MarshalHelpers.GetAttribute<MarshalAsAttribute>(fi);
|
||||
string? fieldValue = fi.GetValue(instance) as string;
|
||||
if (fieldValue == null)
|
||||
return true;
|
||||
|
||||
switch (marshalAsAttr?.Value)
|
||||
{
|
||||
case UnmanagedType.AnsiBStr:
|
||||
return stream.WritePrefixedAnsiString(fieldValue);
|
||||
|
||||
case UnmanagedType.BStr:
|
||||
case UnmanagedType.TBStr: // Technically distinct; returns char[] instead
|
||||
return stream.WritePrefixedUnicodeString(fieldValue);
|
||||
|
||||
case UnmanagedType.ByValTStr:
|
||||
int byvalLength = marshalAsAttr!.SizeConst;
|
||||
byte[] byvalBytes = encoding.GetBytes(fieldValue);
|
||||
byte[] byvalSizedBytes = new byte[byvalLength];
|
||||
Array.Copy(byvalBytes, byvalSizedBytes, Math.Min(byvalBytes.Length, byvalSizedBytes.Length));
|
||||
return Write(stream, byvalSizedBytes);
|
||||
|
||||
case UnmanagedType.LPStr:
|
||||
case UnmanagedType.LPTStr: // Technically distinct; possibly not null-terminated
|
||||
case null:
|
||||
return stream.WriteNullTerminatedAnsiString(fieldValue);
|
||||
|
||||
#if NET472_OR_GREATER || NETCOREAPP
|
||||
case UnmanagedType.LPUTF8Str:
|
||||
return stream.WriteNullTerminatedUTF8String(fieldValue);
|
||||
#endif
|
||||
|
||||
case UnmanagedType.LPWStr:
|
||||
return stream.WriteNullTerminatedUnicodeString(fieldValue);
|
||||
|
||||
// No other string types are recognized
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an array of bytes to the stream
|
||||
/// </summary>
|
||||
private static bool WriteFromBuffer(Stream stream, byte[] value)
|
||||
{
|
||||
// If the stream is not writable
|
||||
if (!stream.CanWrite)
|
||||
return false;
|
||||
|
||||
// Handle the 0-byte case
|
||||
if (value.Length == 0)
|
||||
return true;
|
||||
|
||||
// Handle the general case, forcing a write of the correct length
|
||||
stream.Write(value, 0, value.Length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace SabreTools.IO.Extensions
|
||||
/// <param name="localName">Name of the element</param>
|
||||
/// <param name="value">Value to write in the element</param>
|
||||
/// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param>
|
||||
public static void WriteRequiredAttributeString(this XmlTextWriter writer, string localName, string value, bool throwOnError = false)
|
||||
public static void WriteRequiredAttributeString(this XmlTextWriter writer, string localName, string? value, bool throwOnError = false)
|
||||
{
|
||||
// Throw an exception if we are configured to
|
||||
if (value == null && throwOnError)
|
||||
@@ -31,7 +31,7 @@ namespace SabreTools.IO.Extensions
|
||||
/// <param name="localName">Name of the element</param>
|
||||
/// <param name="value">Value to write in the element</param>
|
||||
/// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param>
|
||||
public static void WriteRequiredElementString(this XmlTextWriter writer, string localName, string value, bool throwOnError = false)
|
||||
public static void WriteRequiredElementString(this XmlTextWriter writer, string localName, string? value, bool throwOnError = false)
|
||||
{
|
||||
// Throw an exception if we are configured to
|
||||
if (value == null && throwOnError)
|
||||
@@ -51,7 +51,7 @@ namespace SabreTools.IO.Extensions
|
||||
/// <param name="writer">XmlTextWriter to write out with</param>
|
||||
/// <param name="localName">Name of the attribute</param>
|
||||
/// <param name="value">Value to write in the attribute</param>
|
||||
public static void WriteOptionalAttributeString(this XmlTextWriter writer, string localName, string value)
|
||||
public static void WriteOptionalAttributeString(this XmlTextWriter writer, string localName, string? value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
writer.WriteAttributeString(localName, value);
|
||||
@@ -63,7 +63,7 @@ namespace SabreTools.IO.Extensions
|
||||
/// <param name="writer">XmlTextWriter to write out with</param>
|
||||
/// <param name="localName">Name of the element</param>
|
||||
/// <param name="value">Value to write in the element</param>
|
||||
public static void WriteOptionalElementString(this XmlTextWriter writer, string localName, string value)
|
||||
public static void WriteOptionalElementString(this XmlTextWriter writer, string localName, string? value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
writer.WriteElementString(localName, value);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Readers;
|
||||
using SabreTools.IO.Writers;
|
||||
@@ -14,13 +13,12 @@ namespace SabreTools.IO
|
||||
/// </summary>
|
||||
public class IniFile : IDictionary<string, string?>
|
||||
{
|
||||
private Dictionary<string, string?>? _keyValuePairs = [];
|
||||
private readonly Dictionary<string, string?> _keyValuePairs = [];
|
||||
|
||||
public string? this[string? key]
|
||||
{
|
||||
get
|
||||
{
|
||||
_keyValuePairs ??= [];
|
||||
key = key?.ToLowerInvariant() ?? string.Empty;
|
||||
if (_keyValuePairs.ContainsKey(key))
|
||||
return _keyValuePairs[key];
|
||||
@@ -29,7 +27,6 @@ namespace SabreTools.IO
|
||||
}
|
||||
set
|
||||
{
|
||||
_keyValuePairs ??= [];
|
||||
key = key?.ToLowerInvariant() ?? string.Empty;
|
||||
_keyValuePairs[key] = value;
|
||||
}
|
||||
@@ -47,16 +44,19 @@ namespace SabreTools.IO
|
||||
/// </summary>
|
||||
public IniFile(string path)
|
||||
{
|
||||
this.Parse(path);
|
||||
// If we don't have a file, we can't read it
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException(nameof(path));
|
||||
|
||||
using var fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
Parse(fileStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate an INI file from stream
|
||||
/// </summary>
|
||||
public IniFile(Stream stream)
|
||||
{
|
||||
this.Parse(stream);
|
||||
}
|
||||
=> Parse(stream);
|
||||
|
||||
/// <summary>
|
||||
/// Add or update a key and value to the INI file
|
||||
@@ -71,7 +71,7 @@ namespace SabreTools.IO
|
||||
/// </summary>
|
||||
public bool Remove(string key)
|
||||
{
|
||||
if (_keyValuePairs != null && _keyValuePairs.ContainsKey(key))
|
||||
if (_keyValuePairs.ContainsKey(key))
|
||||
{
|
||||
_keyValuePairs.Remove(key.ToLowerInvariant());
|
||||
return true;
|
||||
@@ -81,22 +81,84 @@ namespace SabreTools.IO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an INI file based on the path
|
||||
/// Write an INI file to a path
|
||||
/// </summary>
|
||||
public bool Parse(string path)
|
||||
public bool Write(string path)
|
||||
{
|
||||
// If we don't have a file, we can't read it
|
||||
if (!File.Exists(path))
|
||||
// If we don't have a valid dictionary with values, we can't write out
|
||||
if (_keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
using var fileStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
return Parse(fileStream);
|
||||
using var fileStream = File.OpenWrite(path);
|
||||
return Write(fileStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an INI file to a stream
|
||||
/// </summary>
|
||||
public bool Write(Stream stream)
|
||||
{
|
||||
// If we don't have a valid dictionary with values, we can't write out
|
||||
if (_keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
// If the stream is invalid, we can't output to it
|
||||
if (!stream.CanWrite)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using IniWriter writer = new(stream, Encoding.UTF8);
|
||||
|
||||
// Order the keys to link sections together
|
||||
var orderedKeys = new string[_keyValuePairs.Keys.Count];
|
||||
_keyValuePairs.Keys.CopyTo(orderedKeys, 0);
|
||||
Array.Sort(orderedKeys);
|
||||
|
||||
string section = string.Empty;
|
||||
for (int i = 0; i < orderedKeys.Length; i++)
|
||||
{
|
||||
// Retrive the key and value
|
||||
string key = orderedKeys[i];
|
||||
string? value = _keyValuePairs[key];
|
||||
|
||||
// We assume '.' is a section name separator
|
||||
if (key.Contains("."))
|
||||
{
|
||||
// Split the key by '.'
|
||||
string[] data = key.Split('.');
|
||||
|
||||
// If the key contains an '.', we need to put them back in
|
||||
string newSection = data[0].Trim();
|
||||
string[] keyArr = new string[data.Length - 1];
|
||||
Array.Copy(data, 1, keyArr, 0, keyArr.Length);
|
||||
key = string.Join(".", keyArr).Trim();
|
||||
|
||||
// If we have a new section, write it out
|
||||
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
writer.WriteSection(newSection);
|
||||
section = newSection;
|
||||
}
|
||||
}
|
||||
|
||||
// Now write out the key and value in a standardized way
|
||||
writer.WriteKeyValuePair(key, value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was, just catch and return
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an INI file from a stream
|
||||
/// </summary>
|
||||
public bool Parse(Stream? stream)
|
||||
private bool Parse(Stream? stream)
|
||||
{
|
||||
// If the stream is invalid or unreadable, we can't process it
|
||||
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
|
||||
@@ -148,82 +210,11 @@ namespace SabreTools.IO
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an INI file to a path
|
||||
/// </summary>
|
||||
public bool Write(string path)
|
||||
{
|
||||
// If we don't have a valid dictionary with values, we can't write out
|
||||
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
using var fileStream = File.OpenWrite(path);
|
||||
return Write(fileStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an INI file to a stream
|
||||
/// </summary>
|
||||
public bool Write(Stream stream)
|
||||
{
|
||||
// If we don't have a valid dictionary with values, we can't write out
|
||||
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
// If the stream is invalid or unwritable, we can't output to it
|
||||
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using IniWriter writer = new(stream, Encoding.UTF8);
|
||||
|
||||
// Order the dictionary by keys to link sections together
|
||||
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
|
||||
|
||||
string section = string.Empty;
|
||||
foreach (var keyValuePair in orderedKeyValuePairs)
|
||||
{
|
||||
// Extract the key and value
|
||||
string key = keyValuePair.Key;
|
||||
string? value = keyValuePair.Value;
|
||||
|
||||
// We assume '.' is a section name separator
|
||||
if (key.Contains("."))
|
||||
{
|
||||
// Split the key by '.'
|
||||
string[] data = keyValuePair.Key.Split('.');
|
||||
|
||||
// If the key contains an '.', we need to put them back in
|
||||
string newSection = data[0].Trim();
|
||||
key = string.Join(".", data.Skip(1).ToArray()).Trim();
|
||||
|
||||
// If we have a new section, write it out
|
||||
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
writer.WriteSection(newSection);
|
||||
section = newSection;
|
||||
}
|
||||
}
|
||||
|
||||
// Now write out the key and value in a standardized way
|
||||
writer.WriteKeyValuePair(key, value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was, just catch and return
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IDictionary Impelementations
|
||||
|
||||
public ICollection<string> Keys => _keyValuePairs?.Keys?.ToArray() ?? [];
|
||||
public ICollection<string> Keys => _keyValuePairs.Keys;
|
||||
|
||||
public ICollection<string?> Values => _keyValuePairs?.Values?.ToArray() ?? [];
|
||||
public ICollection<string?> Values => _keyValuePairs.Values;
|
||||
|
||||
public int Count => (_keyValuePairs as ICollection<KeyValuePair<string, string>>)?.Count ?? 0;
|
||||
|
||||
|
||||
47
SabreTools.IO/Logging/Converters.cs
Normal file
47
SabreTools.IO/Logging/Converters.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace SabreTools.IO.Logging
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region String to Enum
|
||||
|
||||
/// <summary>
|
||||
/// Get the LogLevel value for an input string, if possible
|
||||
/// </summary>
|
||||
/// <param name="value">String value to parse/param>
|
||||
/// <returns></returns>
|
||||
public static LogLevel AsLogLevel(this string? value)
|
||||
{
|
||||
return value?.ToLowerInvariant() switch
|
||||
{
|
||||
"verbose" => LogLevel.VERBOSE,
|
||||
"user" => LogLevel.USER,
|
||||
"warning" => LogLevel.WARNING,
|
||||
"error" => LogLevel.ERROR,
|
||||
_ => LogLevel.VERBOSE,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enum to String
|
||||
|
||||
/// <summary>
|
||||
/// Get string value from input LogLevel
|
||||
/// </summary>
|
||||
/// <param name="value">LogLevel to get value from</param>
|
||||
/// <returns>String corresponding to the LogLevel</returns>
|
||||
public static string? FromLogLevel(this LogLevel value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
LogLevel.VERBOSE => "VERBOSE",
|
||||
LogLevel.USER => "USER",
|
||||
LogLevel.WARNING => "WARNING",
|
||||
LogLevel.ERROR => "ERROR",
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
13
SabreTools.IO/Logging/Enums.cs
Normal file
13
SabreTools.IO/Logging/Enums.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace SabreTools.IO.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Severity of the logging statement
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
VERBOSE = 0,
|
||||
USER,
|
||||
WARNING,
|
||||
ERROR,
|
||||
}
|
||||
}
|
||||
61
SabreTools.IO/Logging/InternalStopwatch.cs
Normal file
61
SabreTools.IO/Logging/InternalStopwatch.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.IO.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Stopwatch class for keeping track of duration in the code
|
||||
/// </summary>
|
||||
public class InternalStopwatch
|
||||
{
|
||||
private string _subject;
|
||||
private DateTime _startTime;
|
||||
private readonly Logger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that initalizes the stopwatch
|
||||
/// </summary>
|
||||
public InternalStopwatch()
|
||||
{
|
||||
_subject = string.Empty;
|
||||
_logger = new Logger(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that initalizes the stopwatch with a subject and starts immediately
|
||||
/// </summary>
|
||||
/// <param name="subject">Subject of the stopwatch</param>
|
||||
public InternalStopwatch(string subject)
|
||||
{
|
||||
_subject = subject;
|
||||
_logger = new Logger(this);
|
||||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the stopwatch and display subject text
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
_startTime = DateTime.Now;
|
||||
_logger.User($"{_subject}...");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the stopwatch and display subject text
|
||||
/// </summary>
|
||||
/// <param name="subject">Text to show on stopwatch start</param>
|
||||
public void Start(string subject)
|
||||
{
|
||||
_subject = subject;
|
||||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End the stopwatch and display subject text
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
_logger.User($"{_subject} completed in {DateTime.Now.Subtract(_startTime):G}");
|
||||
}
|
||||
}
|
||||
}
|
||||
79
SabreTools.IO/Logging/LogEventArgs.cs
Normal file
79
SabreTools.IO/Logging/LogEventArgs.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.IO.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic delegate type for log events
|
||||
/// </summary>
|
||||
public delegate void LogEventHandler(object? sender, LogEventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Logging specific event arguments
|
||||
/// </summary>
|
||||
public class LogEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// LogLevel for the event
|
||||
/// </summary>
|
||||
public readonly LogLevel LogLevel;
|
||||
|
||||
/// <summary>
|
||||
/// Log statement to be printed
|
||||
/// </summary>
|
||||
public readonly string? Statement = null;
|
||||
|
||||
/// <summary>
|
||||
/// Exception to be passed along to the event handler
|
||||
/// </summary>
|
||||
public readonly Exception? Exception = null;
|
||||
|
||||
/// <summary>
|
||||
/// Total count for progress log events
|
||||
/// </summary>
|
||||
public readonly long? TotalCount = null;
|
||||
|
||||
/// <summary>
|
||||
/// Current count for progress log events
|
||||
/// </summary>
|
||||
public readonly long? CurrentCount = null;
|
||||
|
||||
/// <summary>
|
||||
/// Statement constructor
|
||||
/// </summary>
|
||||
public LogEventArgs(LogLevel logLevel, string statement)
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
Statement = statement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statement constructor
|
||||
/// </summary>
|
||||
public LogEventArgs(LogLevel logLevel, Exception exception)
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statement and exception constructor
|
||||
/// </summary>
|
||||
public LogEventArgs(LogLevel logLevel, string statement, Exception exception)
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
Statement = statement;
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Progress constructor
|
||||
/// </summary>
|
||||
public LogEventArgs(long total, long current, LogLevel logLevel, string? statement = null)
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
Statement = statement;
|
||||
TotalCount = total;
|
||||
CurrentCount = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
180
SabreTools.IO/Logging/Logger.cs
Normal file
180
SabreTools.IO/Logging/Logger.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.IO.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Per-class logging
|
||||
/// </summary>
|
||||
public class Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// Instance associated with this logger
|
||||
/// </summary>
|
||||
/// TODO: Derive class name for this object, if possible
|
||||
private readonly object? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public Logger(object? instance = null)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
#region Log Event Triggers
|
||||
|
||||
#region Verbose
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string as a verbose message to the log output
|
||||
/// </summary>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Verbose(string output)
|
||||
=> LoggerImpl.Verbose(_instance, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception as a verbose message to the log output
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Verbose(Exception ex)
|
||||
=> LoggerImpl.Verbose(_instance, ex);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception and string as a verbose message to the log output
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Verbose(Exception ex, string output)
|
||||
=> LoggerImpl.Verbose(_instance, ex, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given verbose progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public void Verbose(long total, long current, string? output = null)
|
||||
=> LoggerImpl.Verbose(_instance, total, current, output);
|
||||
|
||||
#endregion
|
||||
|
||||
#region User
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string as a user message to the log output
|
||||
/// </summary>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void User(string output)
|
||||
=> LoggerImpl.User(_instance, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception as a user message to the log output
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void User(Exception ex)
|
||||
=> LoggerImpl.User(_instance, ex);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception and string as a user message to the log output
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void User(Exception ex, string output)
|
||||
=> LoggerImpl.User(_instance, ex, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given user progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public void User(long total, long current, string? output = null)
|
||||
=> LoggerImpl.User(_instance, total, current, output);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Warning
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string as a warning to the log output
|
||||
/// </summary>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Warning(string output)
|
||||
=> LoggerImpl.Warning(_instance, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception as a warning to the log output
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Warning(Exception ex)
|
||||
=> LoggerImpl.Warning(_instance, ex);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception and string as a warning to the log output
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Warning(Exception ex, string output)
|
||||
=> LoggerImpl.Warning(_instance, ex, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given warning progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public void Warning(long total, long current, string? output = null)
|
||||
=> LoggerImpl.Warning(_instance, total, current, output);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Error
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given string as an error in the log
|
||||
/// </summary>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Error(string output)
|
||||
=> LoggerImpl.Error(_instance, output);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given exception as an error in the log
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Error(Exception ex)
|
||||
=> LoggerImpl.Error(_instance, ex);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given exception and string as an error in the log
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public void Error(Exception ex, string output)
|
||||
=> LoggerImpl.Error(_instance, ex, output);
|
||||
|
||||
/// <summary>
|
||||
/// Write the given error progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public void Error(long total, long current, string? output = null)
|
||||
=> LoggerImpl.Error(_instance, total, current, output);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
458
SabreTools.IO/Logging/LoggerImpl.cs
Normal file
458
SabreTools.IO/Logging/LoggerImpl.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace SabreTools.IO.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal logging implementation
|
||||
/// </summary>
|
||||
public static class LoggerImpl
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Optional output filename for logs
|
||||
/// </summary>
|
||||
public static string? Filename { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we're logging to file or not
|
||||
/// </summary>
|
||||
public static bool LogToFile { get { return !string.IsNullOrEmpty(Filename); } }
|
||||
|
||||
/// <summary>
|
||||
/// Optional output log directory
|
||||
/// </summary>
|
||||
public static string? LogDirectory { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the lowest log level to output
|
||||
/// </summary>
|
||||
public static LogLevel LowestLogLevel { get; set; } = LogLevel.VERBOSE;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether to prefix log lines with level and datetime
|
||||
/// </summary>
|
||||
public static bool AppendPrefix { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether to throw if an exception is logged
|
||||
/// </summary>
|
||||
public static bool ThrowOnError { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Logging start time for metrics
|
||||
/// </summary>
|
||||
public static DateTime StartTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there were errors logged
|
||||
/// </summary>
|
||||
public static bool LoggedErrors { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there were warnings logged
|
||||
/// </summary>
|
||||
public static bool LoggedWarnings { get; private set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
/// <summary>
|
||||
/// StreamWriter representing the output log file
|
||||
/// </summary>
|
||||
private static StreamWriter? _log;
|
||||
|
||||
/// <summary>
|
||||
/// Object lock for multithreaded logging
|
||||
/// </summary>
|
||||
private static readonly object _lock = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Control
|
||||
|
||||
/// <summary>
|
||||
/// Generate and set the log filename
|
||||
/// </summary>
|
||||
/// <param name="filename">Base filename to use</param>
|
||||
/// <param name="addDate">True to append a date to the filename, false otherwise</param>
|
||||
public static void SetFilename(string filename, bool addDate = true)
|
||||
{
|
||||
// Get the full log path
|
||||
string fullPath = Path.GetFullPath(filename);
|
||||
|
||||
// Set the log directory
|
||||
LogDirectory = Path.GetDirectoryName(fullPath);
|
||||
|
||||
// Set the
|
||||
if (addDate)
|
||||
Filename = $"{Path.GetFileNameWithoutExtension(fullPath)} ({DateTime.Now:yyyy-MM-dd HH-mm-ss}).{fullPath.GetNormalizedExtension()}";
|
||||
else
|
||||
Filename = Path.GetFileName(fullPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start logging by opening output file (if necessary)
|
||||
/// </summary>
|
||||
/// <returns>True if the logging was started correctly, false otherwise</returns>
|
||||
public static bool Start()
|
||||
{
|
||||
// Setup the logging handler to always use the internal log
|
||||
LogEventHandler += HandleLogEvent;
|
||||
|
||||
// Start the logging
|
||||
StartTime = DateTime.Now;
|
||||
if (!LogToFile)
|
||||
return true;
|
||||
|
||||
// Setup file output and perform initial log
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(LogDirectory) && !Directory.Exists(LogDirectory))
|
||||
Directory.CreateDirectory(LogDirectory);
|
||||
|
||||
FileStream logfile = File.Create(Path.Combine(LogDirectory ?? string.Empty, Filename ?? string.Empty));
|
||||
#if NET20 || NET35 || NET40
|
||||
_log = new StreamWriter(logfile, Encoding.UTF8, 4096)
|
||||
#else
|
||||
_log = new StreamWriter(logfile, Encoding.UTF8, 4096, true)
|
||||
#endif
|
||||
{
|
||||
AutoFlush = true
|
||||
};
|
||||
|
||||
_log.WriteLine($"Logging started {StartTime:yyyy-MM-dd HH:mm:ss}");
|
||||
_log.WriteLine($"Command run: {string.Join(" ", Environment.GetCommandLineArgs())}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End logging by closing output file (if necessary)
|
||||
/// </summary>
|
||||
/// <param name="suppress">True if all ending output is to be suppressed, false otherwise (default)</param>
|
||||
/// <returns>True if the logging was ended correctly, false otherwise</returns>
|
||||
public static bool Close(bool suppress = false)
|
||||
{
|
||||
if (!suppress)
|
||||
{
|
||||
if (LoggedWarnings)
|
||||
Console.WriteLine("There were warnings in the last run! Check the log for more details");
|
||||
|
||||
if (LoggedErrors)
|
||||
Console.WriteLine("There were errors in the last run! Check the log for more details");
|
||||
|
||||
TimeSpan span = DateTime.Now.Subtract(StartTime);
|
||||
|
||||
#if NET20 || NET35
|
||||
string total = span.ToString();
|
||||
#else
|
||||
// Special case for multi-day runs
|
||||
string total;
|
||||
if (span >= TimeSpan.FromDays(1))
|
||||
total = span.ToString(@"d\:hh\:mm\:ss");
|
||||
else
|
||||
total = span.ToString(@"hh\:mm\:ss");
|
||||
#endif
|
||||
|
||||
if (!LogToFile)
|
||||
{
|
||||
Console.WriteLine($"Total runtime: {total}");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_log?.WriteLine($"Logging ended {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
||||
_log?.WriteLine($"Total runtime: {total}");
|
||||
Console.WriteLine($"Total runtime: {total}");
|
||||
_log?.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_log?.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handling
|
||||
|
||||
/// <summary>
|
||||
/// Handler for log events
|
||||
/// </summary>
|
||||
public static event LogEventHandler LogEventHandler = delegate { };
|
||||
|
||||
/// <summary>
|
||||
/// Default log event handling
|
||||
/// </summary>
|
||||
public static void HandleLogEvent(object? sender, LogEventArgs args)
|
||||
{
|
||||
// Null args means we can't handle it
|
||||
if (args == null)
|
||||
return;
|
||||
|
||||
// If we have an exception and we're throwing on that
|
||||
if (ThrowOnError && args.Exception != null)
|
||||
throw args.Exception;
|
||||
|
||||
// If we have a warning or error, set the flags accordingly
|
||||
if (args.LogLevel == LogLevel.WARNING)
|
||||
LoggedWarnings = true;
|
||||
if (args.LogLevel == LogLevel.ERROR)
|
||||
LoggedErrors = true;
|
||||
|
||||
// Setup the statement based on the inputs
|
||||
string logLine;
|
||||
if (args.Exception != null)
|
||||
{
|
||||
logLine = $"{(args.Statement != null ? args.Statement + ": " : string.Empty)}{args.Exception}";
|
||||
}
|
||||
else if (args.TotalCount != null && args.CurrentCount != null)
|
||||
{
|
||||
double percentage = ((double)args.CurrentCount.Value / args.TotalCount.Value) * 100;
|
||||
logLine = $"{percentage:N2}%{(args.Statement != null ? ": " + args.Statement : string.Empty)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
logLine = args.Statement ?? string.Empty;
|
||||
}
|
||||
|
||||
// Then write to the log
|
||||
Log(logLine, args.LogLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string to the log output
|
||||
/// </summary>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <param name="loglevel">Severity of the information being logged</param>
|
||||
private static void Log(string output, LogLevel loglevel)
|
||||
{
|
||||
// If the log level is less than the filter level, we skip it but claim we didn't
|
||||
if (loglevel < LowestLogLevel)
|
||||
return;
|
||||
|
||||
// USER and ERROR writes to console
|
||||
if (loglevel == LogLevel.USER || loglevel == LogLevel.ERROR)
|
||||
Console.WriteLine((loglevel == LogLevel.ERROR && AppendPrefix ? loglevel.FromLogLevel() + " " : string.Empty) + output);
|
||||
|
||||
// If we're writing to file, use the existing stream
|
||||
if (LogToFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_log?.WriteLine((AppendPrefix ? $"{loglevel.FromLogLevel()} - {DateTime.Now} - " : string.Empty) + output);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ThrowOnError)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
Console.WriteLine("Could not write to log file!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Log Event Triggers
|
||||
|
||||
#region Verbose
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string as a verbose message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Verbose(object? instance, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.VERBOSE, output));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception as a verbose message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Verbose(object? instance, Exception ex)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.VERBOSE, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception and string as a verbose message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Verbose(object? instance, Exception ex, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.VERBOSE, output, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given verbose progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public static void Verbose(object? instance, long total, long current, string? output = null)
|
||||
=> LogEventHandler(instance, new LogEventArgs(total, current, LogLevel.VERBOSE, output));
|
||||
|
||||
#endregion
|
||||
|
||||
#region User
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string as a user message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void User(object? instance, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.USER, output));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception as a user message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void User(object? instance, Exception ex)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.USER, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception and string as a user message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void User(object? instance, Exception ex, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.USER, output, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given user progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public static void User(object? instance, long total, long current, string? output = null)
|
||||
=> LogEventHandler(instance, new LogEventArgs(total, current, LogLevel.USER, output));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Warning
|
||||
|
||||
/// <summary>
|
||||
/// Write the given string as a warning to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Warning(object? instance, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.WARNING, output));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given exception as a warning to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Warning(object? instance, Exception ex)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.WARNING, ex));
|
||||
|
||||
//// <summary>
|
||||
/// Write the given exception and string as a warning to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Warning(object? instance, Exception ex, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.WARNING, output, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given warning progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public static void Warning(object? instance, long total, long current, string? output = null)
|
||||
=> LogEventHandler(instance, new LogEventArgs(total, current, LogLevel.WARNING, output));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Error
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given string as an error in the log
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Error(object? instance, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.ERROR, output));
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given exception as an error in the log
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Error(object? instance, Exception ex)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.ERROR, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given exception and string as an error in the log
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="ex">Exception to be written log</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
/// <returns>True if the output could be written, false otherwise</returns>
|
||||
public static void Error(object? instance, Exception ex, string output)
|
||||
=> LogEventHandler(instance, new LogEventArgs(LogLevel.ERROR, output, ex));
|
||||
|
||||
/// <summary>
|
||||
/// Write the given error progress message to the log output
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance object that's the source of logging</param>
|
||||
/// <param name="total">Total count for progress</param>
|
||||
/// <param name="current">Current count for progres</param>
|
||||
/// <param name="output">String to be written log</param>
|
||||
public static void Error(object? instance, long total, long current, string? output = null)
|
||||
=> LogEventHandler(instance, new LogEventArgs(total, current, LogLevel.ERROR, output));
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,12 @@ namespace SabreTools.IO
|
||||
/// <summary>
|
||||
/// Current full path represented
|
||||
/// </summary>
|
||||
public string CurrentPath { get; private set; }
|
||||
public string CurrentPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Possible parent path represented (may be null or empty)
|
||||
/// </summary>
|
||||
public string? ParentPath { get; private set; }
|
||||
public string? ParentPath { get; }
|
||||
|
||||
public ParentablePath(string currentPath, string? parentPath = null)
|
||||
{
|
||||
@@ -32,14 +32,14 @@ namespace SabreTools.IO
|
||||
public string? GetNormalizedFileName(bool sanitize)
|
||||
{
|
||||
// If the current path is empty, we can't do anything
|
||||
if (string.IsNullOrEmpty(CurrentPath))
|
||||
if (CurrentPath.Length == 0)
|
||||
return null;
|
||||
|
||||
// Assume the current path is the filename
|
||||
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 (string.IsNullOrEmpty(CurrentPath))
|
||||
// If the current path is empty
|
||||
if (CurrentPath.Length == 0)
|
||||
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,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching.Compare;
|
||||
|
||||
namespace SabreTools.IO
|
||||
{
|
||||
@@ -13,9 +14,9 @@ namespace SabreTools.IO
|
||||
/// Retrieve a list of just directories from inputs
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of strings representing directories and files</param>
|
||||
/// <param name="appendparent">True if the parent name should be included in the ParentablePath, false otherwise (default)</param>
|
||||
/// <param name="appendParent">True if the parent name should be included in the ParentablePath, false otherwise (default)</param>
|
||||
/// <returns>List of strings representing just directories from the inputs</returns>
|
||||
public static List<ParentablePath> GetDirectoriesOnly(List<string> inputs, bool appendparent = false)
|
||||
public static List<ParentablePath> GetDirectoriesOnly(List<string> inputs, bool appendParent = false)
|
||||
{
|
||||
var outputs = new List<ParentablePath>();
|
||||
for (int i = 0; i < inputs.Count; i++)
|
||||
@@ -41,7 +42,7 @@ namespace SabreTools.IO
|
||||
List<string> directories = GetDirectoriesOrdered(input, pattern);
|
||||
foreach (string dir in directories)
|
||||
{
|
||||
outputs.Add(new ParentablePath(Path.GetFullPath(dir), appendparent ? parentPath : string.Empty));
|
||||
outputs.Add(new ParentablePath(Path.GetFullPath(dir), appendParent ? parentPath : string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -88,9 +89,9 @@ namespace SabreTools.IO
|
||||
/// Retrieve a list of just files from inputs
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of strings representing directories and files</param>
|
||||
/// <param name="appendparent">True if the parent name should be be included in the ParentablePath, false otherwise (default)</param>
|
||||
/// <param name="appendParent">True if the parent name should be be included in the ParentablePath, false otherwise (default)</param>
|
||||
/// <returns>List of strings representing just files from the inputs</returns>
|
||||
public static List<ParentablePath> GetFilesOnly(List<string> inputs, bool appendparent = false)
|
||||
public static List<ParentablePath> GetFilesOnly(List<string> inputs, bool appendParent = false)
|
||||
{
|
||||
var outputs = new List<ParentablePath>();
|
||||
for (int i = 0; i < inputs.Count; i++)
|
||||
@@ -116,12 +117,12 @@ namespace SabreTools.IO
|
||||
List<string> files = GetFilesOrdered(input, pattern);
|
||||
foreach (string file in files)
|
||||
{
|
||||
outputs.Add(new ParentablePath(Path.GetFullPath(file), appendparent ? parentPath : string.Empty));
|
||||
outputs.Add(new ParentablePath(Path.GetFullPath(file), appendParent ? parentPath : string.Empty));
|
||||
}
|
||||
}
|
||||
else if (File.Exists(input))
|
||||
{
|
||||
outputs.Add(new ParentablePath(Path.GetFullPath(input), appendparent ? parentPath : string.Empty));
|
||||
outputs.Add(new ParentablePath(Path.GetFullPath(input), appendParent ? parentPath : string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -295,13 +294,10 @@ namespace SabreTools.IO.Readers
|
||||
s = s.Trim();
|
||||
|
||||
// Now we get each string, divided up as cleanly as possible
|
||||
string[] matches = Regex
|
||||
.Matches(s, InternalPatternAttributesCMP)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Groups[0].Value)
|
||||
.ToArray();
|
||||
|
||||
return matches;
|
||||
var matchList = Regex.Matches(s, InternalPatternAttributesCMP);
|
||||
var matchArr = new Match[matchList.Count];
|
||||
matchList.CopyTo(matchArr, 0);
|
||||
return Array.ConvertAll(matchArr, m => m.Groups[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.IO.Readers
|
||||
@@ -118,7 +117,9 @@ namespace SabreTools.IO.Readers
|
||||
|
||||
// If the value field contains an '=', we need to put them back in
|
||||
string key = data[0].Trim();
|
||||
string value = string.Join("=", data.Skip(1).ToArray()).Trim();
|
||||
var valueArr = new string[data.Length - 1];
|
||||
Array.Copy(data, 1, valueArr, 0, valueArr.Length);
|
||||
string value = string.Join("=", valueArr).Trim();
|
||||
|
||||
KeyValuePair = new KeyValuePair<string, string>(key, value);
|
||||
RowType = IniRowType.KeyValue;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -124,7 +123,7 @@ namespace SabreTools.IO.Readers
|
||||
// https://stackoverflow.com/questions/3776458/split-a-comma-separated-string-with-both-quoted-and-unquoted-strings
|
||||
var lineSplitRegex = new Regex($"(?:^|{Separator})(\"(?:[^\"]+|\"\")*\"|[^{Separator}]*)");
|
||||
var temp = new List<string>();
|
||||
foreach (Match? match in lineSplitRegex.Matches(fullLine).Cast<Match?>())
|
||||
foreach (Match? match in lineSplitRegex.Matches(fullLine))
|
||||
{
|
||||
string? curr = match?.Value;
|
||||
if (curr == null)
|
||||
@@ -143,7 +142,9 @@ namespace SabreTools.IO.Readers
|
||||
// Otherwise, just split on the delimiter
|
||||
else
|
||||
{
|
||||
Line = fullLine.Split(Separator).Select(f => f.Trim()).ToList();
|
||||
var lineArr = fullLine.Split(Separator);
|
||||
lineArr = Array.ConvertAll(lineArr, f => f.Trim());
|
||||
Line = [.. lineArr];
|
||||
}
|
||||
|
||||
// If we don't have a header yet and are expecting one, read this as the header
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.3.7</Version>
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<NoWarn>CS0618</NoWarn>
|
||||
<Nullable>enable</Nullable>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.6.3</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Common IO utilities by other SabreTools projects</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools.IO</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>io reading writing ini csv ssv tsv</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Common IO utilities by other SabreTools projects</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2025</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools.IO</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>io reading writing ini csv ssv tsv</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../README.md" Pack="true" PackagePath=""/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="../README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))">
|
||||
<PackageReference Include="Net30.LinqBridge" Version="1.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Matching" Version="1.5.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Matching" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -88,10 +88,11 @@ namespace SabreTools.IO.Streams
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a multiple bits in LSB, if possible
|
||||
/// Read a multiple bits MSB-first, if possible
|
||||
/// </summary>
|
||||
/// <returns>The next bits encoded in a UInt32, null on error or end of stream</returns>
|
||||
public uint? ReadBitsLSB(int bits)
|
||||
/// <remarks>[76543210] order within a byte, appended to output [76543210]</remarks>
|
||||
public uint? ReadBitsBE(int bits)
|
||||
{
|
||||
uint value = 0;
|
||||
for (int i = 0; i < bits; i++)
|
||||
@@ -101,18 +102,19 @@ namespace SabreTools.IO.Streams
|
||||
if (bitValue == null)
|
||||
return null;
|
||||
|
||||
// Add the bit shifted by the current index
|
||||
value += (uint)(bitValue.Value << i);
|
||||
// Append the bit shifted by the current index
|
||||
value |= (uint)(bitValue.Value << i);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a multiple bits in MSB, if possible
|
||||
/// Read a multiple bits in LSB-first, if possible
|
||||
/// </summary>
|
||||
/// <returns>The next bits encoded in a UInt32, null on error or end of stream</returns>
|
||||
public uint? ReadBitsMSB(int bits)
|
||||
/// <remarks>[76543210] order within a byte, appended to output [01234567]</remarks>
|
||||
public uint? ReadBitsLE(int bits)
|
||||
{
|
||||
uint value = 0;
|
||||
for (int i = 0; i < bits; i++)
|
||||
@@ -122,8 +124,8 @@ namespace SabreTools.IO.Streams
|
||||
if (bitValue == null)
|
||||
return null;
|
||||
|
||||
// Add the bit shifted by the current index
|
||||
value = (value << 1) + bitValue.Value;
|
||||
// Append the bit shifted by the current index
|
||||
value = (value << 1) | bitValue.Value;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace SabreTools.IO.Streams
|
||||
{
|
||||
@@ -110,7 +109,7 @@ namespace SabreTools.IO.Streams
|
||||
/// </summary>
|
||||
public ReadOnlyCompositeStream(IEnumerable<Stream> streams)
|
||||
{
|
||||
_streams = streams.ToList();
|
||||
_streams = new List<Stream>(streams);
|
||||
_length = 0;
|
||||
_position = 0;
|
||||
|
||||
@@ -148,7 +147,7 @@ namespace SabreTools.IO.Streams
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
// Determine which stream we start reading from
|
||||
(int streamIndex, long streamOffset) = DetermineStreamIndex(_position);
|
||||
int streamIndex = DetermineStreamIndex(_position, out long streamOffset);
|
||||
if (streamIndex == -1)
|
||||
return 0;
|
||||
|
||||
@@ -227,12 +226,16 @@ namespace SabreTools.IO.Streams
|
||||
/// <summary>
|
||||
/// Determine the index of the stream that contains a particular offset
|
||||
/// </summary>
|
||||
/// <returns>Index of the stream containing the offset and the real offset in the stream, (-1, -1) on error</returns>
|
||||
private (int index, long realOffset) DetermineStreamIndex(long offset)
|
||||
/// <param name="realOffset">Output parameter representing the real offset in the stream, -1 on error</param>
|
||||
/// <returns>Index of the stream containing the offset, -1 on error</returns>
|
||||
private int DetermineStreamIndex(long offset, out long realOffset)
|
||||
{
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= _length)
|
||||
return (-1, -1);
|
||||
{
|
||||
realOffset = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Seek through until we hit the correct offset
|
||||
long currentLength = 0;
|
||||
@@ -241,13 +244,14 @@ namespace SabreTools.IO.Streams
|
||||
currentLength += _streams[i].Length;
|
||||
if (currentLength > offset)
|
||||
{
|
||||
long realOffset = offset - (currentLength - _streams[i].Length);
|
||||
return (i, realOffset);
|
||||
realOffset = offset - (currentLength - _streams[i].Length);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Should never happen
|
||||
return (-1, -1);
|
||||
realOffset = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -118,7 +118,11 @@ namespace SabreTools.IO.Writers
|
||||
/// </summary>
|
||||
public ClrMameProWriter(Stream stream, Encoding encoding)
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
sw = new StreamWriter(stream, encoding);
|
||||
#else
|
||||
sw = new StreamWriter(stream, encoding, 1024, leaveOpen: true);
|
||||
#endif
|
||||
Quotes = true;
|
||||
|
||||
// Element stack
|
||||
@@ -167,42 +171,6 @@ namespace SabreTools.IO.Writers
|
||||
InternalWriteEndElement(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a complete element with content
|
||||
/// </summary>
|
||||
public void WriteElementString(string name, string? value)
|
||||
{
|
||||
WriteStartElement(name);
|
||||
WriteString(value);
|
||||
WriteEndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure writing writing null values as empty strings
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the element</param>
|
||||
/// <param name="value">Value to write in the element</param>
|
||||
/// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param>
|
||||
public void WriteRequiredElementString(string name, string? value, bool throwOnError = false)
|
||||
{
|
||||
// Throw an exception if we are configured to
|
||||
if (value == null && throwOnError)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
WriteElementString(name, value ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write an element, if the value is not null or empty
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the element</param>
|
||||
/// <param name="value">Value to write in the element</param>
|
||||
public void WriteOptionalElementString(string name, string? value)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
WriteElementString(name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the start of an attribute node
|
||||
/// </summary>
|
||||
|
||||
@@ -24,7 +24,11 @@ namespace SabreTools.IO.Writers
|
||||
/// </summary>
|
||||
public IniWriter(Stream stream, Encoding encoding)
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
sw = new StreamWriter(stream, encoding);
|
||||
#else
|
||||
sw = new StreamWriter(stream, encoding, 1024, leaveOpen: true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -54,7 +54,11 @@ namespace SabreTools.IO.Writers
|
||||
/// </summary>
|
||||
public SeparatedValueWriter(Stream stream, Encoding encoding)
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
sw = new StreamWriter(stream, encoding);
|
||||
#else
|
||||
sw = new StreamWriter(stream, encoding, 1024, leaveOpen: true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,14 +145,6 @@ namespace SabreTools.IO.Writers
|
||||
sw.Write(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a newline
|
||||
/// </summary>
|
||||
public void WriteLine()
|
||||
{
|
||||
sw.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the underlying writer
|
||||
/// </summary>
|
||||
|
||||
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 9.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 "b" 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 9.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