Implemented TagWriter class to support Java, Bedrock file, and Bedrock network specs

This commit is contained in:
ForeverZer0 2021-08-24 09:48:49 -04:00
parent b34702d06c
commit 3c12e34eec
2 changed files with 95 additions and 12 deletions

View File

@ -0,0 +1,44 @@
using System.IO;
using System.IO.Compression;
using System.Reflection;
using Xunit;
using Xunit.Abstractions;
namespace SharpNBT.Tests
{
public class ReadWriteTest
{
private readonly ITestOutputHelper output;
public ReadWriteTest(ITestOutputHelper output)
{
this.output = output;
}
private static Stream GetFile(string filename)
{
var assembly = Assembly.GetExecutingAssembly();
return assembly.GetManifestResourceStream($"SharpNBT.Tests.Data.{filename}");
}
[Fact]
public void ReadUncompressed()
{
using var stream = GetFile("hello_world.nbt");
using var reader = new TagReader(stream, FormatOptions.Java);
var compound = reader.ReadTag<CompoundTag>();
output.WriteLine(compound.PrettyPrinted());
}
[Fact]
public void ReadGZipped()
{
using var stream = GetFile("bigtest.nbt");
using var gzip = new GZipStream(stream, CompressionMode.Decompress);
using var reader = new TagReader(gzip, FormatOptions.Java);
var compound = reader.ReadTag<CompoundTag>();
output.WriteLine(compound.PrettyPrinted());
}
}
}

View File

@ -56,7 +56,10 @@ namespace SharpNBT
public virtual void WriteShort(ShortTag tag) public virtual void WriteShort(ShortTag tag)
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.Write(GetBytes(tag.Value), 0, sizeof(short)); if (UseVarInt)
VarInt.Write(BaseStream, tag.Value, ZigZagEncoding);
else
BaseStream.Write(GetBytes(tag.Value), 0, sizeof(short));
} }
/// <summary> /// <summary>
@ -66,7 +69,10 @@ namespace SharpNBT
public virtual void WriteInt(IntTag tag) public virtual void WriteInt(IntTag tag)
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.Write(GetBytes(tag.Value), 0, sizeof(int)); if (UseVarInt)
VarInt.Write(BaseStream, tag.Value, ZigZagEncoding);
else
BaseStream.Write(GetBytes(tag.Value), 0, sizeof(int));
} }
/// <summary> /// <summary>
@ -76,7 +82,10 @@ namespace SharpNBT
public virtual void WriteLong(LongTag tag) public virtual void WriteLong(LongTag tag)
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.Write(GetBytes(tag.Value), 0, sizeof(long)); if (UseVarInt)
VarLong.Write(BaseStream, tag.Value, ZigZagEncoding);
else
BaseStream.Write(GetBytes(tag.Value), 0, sizeof(long));
} }
/// <summary> /// <summary>
@ -116,7 +125,7 @@ namespace SharpNBT
public virtual void WriteByteArray(ByteArrayTag tag) public virtual void WriteByteArray(ByteArrayTag tag)
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); WriteCount(tag);
BaseStream.Write(tag.ToArray(), 0, tag.Count); BaseStream.Write(tag.ToArray(), 0, tag.Count);
} }
@ -127,15 +136,21 @@ namespace SharpNBT
public virtual void WriteIntArray(IntArrayTag tag) public virtual void WriteIntArray(IntArrayTag tag)
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); WriteCount(tag);
var values = new Span<int>(tag.ToArray()); var values = new Span<int>(tag.ToArray());
if (UseVarInt)
{
// VarInt is effectively always little-endian
foreach (var n in values)
VarInt.Write(BaseStream, n, ZigZagEncoding);
return;
}
if (SwapEndian) if (SwapEndian)
{ {
for (var i = 0; i < values.Length; i++) for (var i = 0; i < values.Length; i++)
values[i] = values[i].SwapEndian(); values[i] = values[i].SwapEndian();
} }
BaseStream.Write(MemoryMarshal.AsBytes(values)); BaseStream.Write(MemoryMarshal.AsBytes(values));
} }
@ -147,15 +162,22 @@ namespace SharpNBT
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); WriteCount(tag);
var values = new Span<long>(tag.ToArray()); var values = new Span<long>(tag.ToArray());
if (UseVarInt)
{
// VarLong is effectively always little-endian
foreach (var n in values)
VarLong.Write(BaseStream, n, ZigZagEncoding);
return;
}
if (SwapEndian) if (SwapEndian)
{ {
for (var i = 0; i < values.Length; i++) for (var i = 0; i < values.Length; i++)
values[i] = values[i].SwapEndian(); values[i] = values[i].SwapEndian();
} }
BaseStream.Write(MemoryMarshal.AsBytes(values)); BaseStream.Write(MemoryMarshal.AsBytes(values));
} }
@ -167,7 +189,7 @@ namespace SharpNBT
{ {
WriteTypeAndName(tag); WriteTypeAndName(tag);
BaseStream.WriteByte((byte) tag.ChildType); BaseStream.WriteByte((byte) tag.ChildType);
BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int)); WriteCount(tag);
foreach (var child in tag) foreach (var child in tag)
WriteTag(child); WriteTag(child);
@ -281,7 +303,7 @@ namespace SharpNBT
if (!leaveOpen) if (!leaveOpen)
await BaseStream.DisposeAsync(); await BaseStream.DisposeAsync();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteTypeAndName(Tag tag) private void WriteTypeAndName(Tag tag)
{ {
@ -295,14 +317,23 @@ namespace SharpNBT
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteUTF8String(string value) private void WriteUTF8String(string value)
{ {
// String length prefixes never use ZigZag encoding
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
BaseStream.Write(GetBytes((ushort) 0), 0, sizeof(ushort)); if (UseVarInt)
VarInt.Write(BaseStream, 0);
else
BaseStream.Write(GetBytes((ushort) 0), 0, sizeof(ushort));
} }
else else
{ {
var utf8 = Encoding.UTF8.GetBytes(value); var utf8 = Encoding.UTF8.GetBytes(value);
BaseStream.Write(GetBytes((ushort) utf8.Length), 0, sizeof(ushort)); if (UseVarInt)
VarInt.Write(BaseStream, utf8.Length);
else
BaseStream.Write(GetBytes((ushort) utf8.Length), 0, sizeof(ushort));
BaseStream.Write(utf8, 0, utf8.Length); BaseStream.Write(utf8, 0, utf8.Length);
} }
} }
@ -346,5 +377,13 @@ namespace SharpNBT
Array.Reverse(bytes); Array.Reverse(bytes);
return bytes; return bytes;
} }
private void WriteCount<T>(EnumerableTag<T> tag)
{
if (UseVarInt)
VarInt.Write(BaseStream, tag.Count, ZigZagEncoding);
else
BaseStream.Write(GetBytes(tag.Count), 0, sizeof(int));
}
} }
} }