1
0

Initial sync

This commit is contained in:
2024-03-15 14:44:21 +01:00
commit d2a1cabe35
59 changed files with 3783 additions and 0 deletions

8
Nbt/Tag/Array/NbtByteArray.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtByteArray(sbyte[] value) : NbtArray<sbyte>(value)
{
public override NbtTagType Type => NbtTagType.ByteArray;
protected override NbtByteArray NewInstance(sbyte[] value) => new(value);
}

8
Nbt/Tag/Array/NbtIntArray.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtIntArray(int[] value) : NbtArray<int>(value)
{
public override NbtTagType Type => NbtTagType.IntArray;
protected override NbtIntArray NewInstance(int[] value) => new(value);
}

8
Nbt/Tag/Array/NbtLongArray.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtLongArray(long[] value) : NbtArray<long>(value)
{
public override NbtTagType Type => NbtTagType.LongArray;
protected override NbtLongArray NewInstance(long[] value) => new(value);
}

3
Nbt/Tag/NamedTag.cs Executable file
View File

@@ -0,0 +1,3 @@
namespace Nbt.Tag;
public readonly record struct NamedTag(string Name, INbtTag Tag) { }

28
Nbt/Tag/NbtArray.cs Executable file
View File

@@ -0,0 +1,28 @@
using System.Collections;
namespace Nbt.Tag;
public interface INbtArray : INbtValue, IEnumerable
{
new Array Value { get; set; }
}
public interface INbtArray<T> : INbtArray, INbtValue<T[]>, IEnumerable<T> where T : notnull
{
new T[] Value { get; set; }
}
public abstract class NbtArray<T>(T[] value) : NbtValue<T[]>(value), INbtArray<T> where T : notnull
{
Array INbtArray.Value
{
get => Value;
set => Value = (T[])value;
}
protected override T[] CopyValue() => (T[])value.Clone();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => ((IEnumerable<T>)Value).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator();
}

117
Nbt/Tag/NbtCompound.cs Executable file
View File

@@ -0,0 +1,117 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nbt.Tag;
public interface INbtCompound : INbtTag, IEnumerable<NamedTag>
{
int Count { get; }
new INbtTag this[string tagName] { get; set; }
void Add(NamedTag namedTag);
void Add(string tagName, INbtTag tag);
void Set(NamedTag namedTag);
void Set(string tagName, INbtTag tag);
INbtTag Get(string tagName);
T Get<T>(string tagName) where T : INbtTag;
bool TryGet(string tagName, [MaybeNullWhen(false)] out INbtTag tag);
bool TryGet<T>(string tagName, [MaybeNullWhen(false)] out T tag) where T : INbtTag;
bool ContainsKey(string tagName);
bool Contains(NamedTag namedTag);
bool Contains(string tagName, INbtTag tag);
bool Remove(string tagName);
bool Remove(NamedTag namedTag);
bool Remove(string tagName, INbtTag tag);
bool Remove(string tagName, [MaybeNullWhen(false)] out INbtTag tag);
void Clear();
}
public class NbtCompound : INbtCompound
{
private readonly Dictionary<string, INbtTag> entries = [];
private static string EnsureName(string tagName) => tagName ?? throw new ArgumentNullException(nameof(tagName));
private static INbtTag EnsureValue(INbtTag tag) => tag ?? throw new ArgumentNullException(nameof(tag));
public NbtTagType Type => NbtTagType.Compound;
INbtCompound INbtTag.AsCompound() => this;
INbtTag INbtTag.Copy() => Copy();
public NbtCompound Copy()
{
var copy = new NbtCompound();
foreach (var e in entries)
{
copy.entries[e.Key] = e.Value.Copy();
}
return copy;
}
public int Count => entries.Count;
public INbtTag this[string tagName]
{
get => entries[tagName];
set => entries[EnsureName(tagName)] = EnsureValue(value);
}
INbtTag INbtTag.this[INbtPathElement tagPath]
{
get => tagPath is NbtName tagName ? this[tagName] : throw new UnsupportedPathElementException();
set => this[tagPath is NbtName tagName ? tagName : throw new UnsupportedPathElementException()] = value;
}
public void Add(NamedTag namedTag) => Add(namedTag.Name, namedTag.Tag);
public void Add(string tagName, INbtTag tag) => entries.Add(EnsureName(tagName), EnsureValue(tag));
public void Set(NamedTag namedTag) => Set(namedTag.Name, namedTag.Tag);
public void Set(string tagName, INbtTag tag) => this[tagName] = tag;
public INbtTag Get(string tagName) => this[tagName];
public T Get<T>(string tagName) where T : INbtTag => (T)Get(tagName);
public bool TryGet(string tagName, [MaybeNullWhen(false)] out INbtTag tag) => entries.TryGetValue(tagName, out tag);
public bool TryGet<T>(string tagName, [MaybeNullWhen(false)] out T tag) where T : INbtTag
{
if (TryGet(tagName, out var t))
{
tag = (T)t;
return true;
}
tag = default;
return false;
}
public bool ContainsKey(string tagName) => entries.ContainsKey(tagName);
public bool Contains(NamedTag namedTag) => Contains(namedTag.Name, namedTag.Tag);
public bool Contains(string tagName, INbtTag tag) => entries.Contains(KeyValuePair.Create(tagName, tag));
public bool Remove(string tagName) => entries.Remove(tagName);
public bool Remove(NamedTag namedTag) => Remove(namedTag.Name, namedTag.Tag);
public bool Remove(string tagName, INbtTag tag) => ((IDictionary<string, INbtTag>)entries).Remove(KeyValuePair.Create(tagName, tag));
public bool Remove(string tagName, [MaybeNullWhen(false)] out INbtTag tag) => entries.Remove(tagName, out tag);
public void Clear() => entries.Clear();
public bool Equals(INbtTag? other) => ReferenceEquals(other, this) || other is NbtCompound o && Utils.DictionaryEquals(entries, o.entries);
public override bool Equals(object? obj) => ReferenceEquals(obj, this) || obj is INbtCompound other && Equals(other);
public override int GetHashCode() => entries.GetHashCode();
public IEnumerator<NamedTag> GetEnumerator() => entries.Select(e => new NamedTag(e.Key, e.Value)).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)entries).GetEnumerator();
}

19
Nbt/Tag/NbtEnd.cs Executable file
View File

@@ -0,0 +1,19 @@
namespace Nbt.Tag;
public sealed class NbtEnd : INbtTag
{
public static readonly NbtEnd Value = new();
private NbtEnd() { }
public NbtTagType Type => NbtTagType.End;
INbtTag INbtTag.Copy() => this;
public bool Equals(INbtTag? other) => this == other;
public override bool Equals(object? obj) => this == obj;
public override int GetHashCode() => 0;
}

227
Nbt/Tag/NbtList.cs Executable file
View File

@@ -0,0 +1,227 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Nbt.Type;
namespace Nbt.Tag;
public interface INbtList : INbtTag, IEnumerable<INbtTag>
{
NbtTagType ElementType { get; }
bool IsElementTypeLocked { get; }
int Count { get; }
new INbtTag this[int tagIndex] { get; set; }
void Add(INbtTag tag);
void Insert(int tagIndex, INbtTag tag);
void Set(int tagIndex, INbtTag tag);
INbtTag GetAt(int tagIndex);
T GetAt<T>(int tagIndex) where T : INbtTag;
bool TryGetAt(int tagIndex, [MaybeNullWhen(false)] out INbtTag tag);
bool TryGetAt<T>(int tagIndex, [MaybeNullWhen(false)] out T tag) where T : INbtTag;
int IndexOf(INbtTag tag);
bool Contains(INbtTag tag);
INbtTag RemoveAt(int tagIndex);
bool Remove(INbtTag tag);
void Clear();
}
public class NbtList : INbtList
{
private readonly List<INbtTag> entries = [];
public NbtList() : this(NbtTagType.Unknown, false) { }
public NbtList(NbtTagType elementType) : this(elementType, elementType is not NbtTagType.Unknown or NbtTagType.End) { }
private NbtList(NbtTagType elementType, bool lockedType)
{
ElementType = elementType;
IsElementTypeLocked = lockedType;
}
public NbtTagType Type => NbtTagType.List;
INbtList INbtTag.AsList() => this;
INbtTag INbtTag.Copy() => Copy();
public NbtList Copy()
{
var copy = new NbtList(ElementType, IsElementTypeLocked);
foreach (var e in entries)
{
copy.entries.Add(e.Copy());
}
return copy;
}
private INbtTag EnsureType(INbtTag tag)
{
ArgumentNullException.ThrowIfNull(tag);
if (tag.Type is NbtTagType.End)
{
throw new WrongTagTypeException($"The {nameof(NbtTagType.End)} tag cannot appear in a list");
}
if ((Count > 0 || IsElementTypeLocked) && tag.Type != ElementType)
{
throw new WrongTagTypeException();
}
return tag;
}
private void OnItemAdded(INbtTag tag)
{
if (!IsElementTypeLocked && Count == 1)
{
ElementType = tag.Type;
}
}
private void OnItemRemoved()
{
if (!IsElementTypeLocked && Count == 0)
{
ElementType = NbtTagType.Unknown;
}
}
public NbtTagType ElementType { get; private set; }
public bool IsElementTypeLocked { get; }
public int Count => entries.Count;
public INbtTag this[int tagIndex]
{
get => entries[tagIndex];
set => entries[tagIndex] = EnsureType(value);
}
INbtTag INbtTag.this[INbtPathElement tagPath]
{
get => tagPath is NbtIndex tagIndex ? this[tagIndex] : throw new UnsupportedPathElementException();
set => this[tagPath is NbtIndex tagIndex ? tagIndex : throw new UnsupportedPathElementException()] = value;
}
public void Add(INbtTag tag)
{
entries.Add(EnsureType(tag));
OnItemAdded(tag);
}
public void Insert(int index, INbtTag tag)
{
entries.Insert(index, EnsureType(tag));
OnItemAdded(tag);
}
public void Set(int tagIndex, INbtTag tag) => this[tagIndex] = tag;
public INbtTag GetAt(int tagIndex) => this[tagIndex];
public T GetAt<T>(int tagIndex) where T : INbtTag => (T)this[tagIndex];
public bool TryGetAt(int tagIndex, [MaybeNullWhen(false)] out INbtTag tag)
{
if (tagIndex >= 0 && tagIndex < Count)
{
tag = this[tagIndex];
return true;
}
tag = default;
return false;
}
public bool TryGetAt<T>(int tagIndex, [MaybeNullWhen(false)] out T tag) where T : INbtTag
{
if (TryGetAt(tagIndex, out var result))
{
tag = (T)result;
return true;
}
tag = default;
return false;
}
public int IndexOf(INbtTag tag) => entries.IndexOf(tag);
public bool Contains(INbtTag tag) => entries.Contains(tag);
public INbtTag RemoveAt(int index)
{
var e = entries[index];
entries.RemoveAt(index);
OnItemRemoved();
return e;
}
public bool Remove(INbtTag tag)
{
var b = entries.Remove(tag);
if (b)
{
OnItemRemoved();
}
return b;
}
public void Clear()
{
entries.Clear();
OnItemRemoved();
}
public bool Equals(INbtTag? other) => ReferenceEquals(other, this) || other is NbtList o && Utils.EnumerableEquals(entries, o.entries);
public override bool Equals(object? obj) => ReferenceEquals(obj, this) || obj is INbtList other && Equals(other);
public override int GetHashCode() => entries.GetHashCode();
IEnumerator<INbtTag> IEnumerable<INbtTag>.GetEnumerator() => entries.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)entries).GetEnumerator();
public static INbtList FromValues<T>(params T[] values) where T : notnull
{
var elementType = NbtUtils.EnsureValueType<T>();
var t = (NbtValueType<T>)NbtUtils.GetTagType(elementType);
var result = new NbtList(elementType);
foreach (var value in values)
{
result.Add(t.CreateTag(value));
}
return result;
}
public static INbtList FromTags<T>(NbtTagType elementType, params T[] tags) where T : INbtTag
{
var result = new NbtList(elementType);
foreach (var tag in tags)
{
result.Add(tag);
}
return result;
}
}

68
Nbt/Tag/NbtTag.cs Executable file
View File

@@ -0,0 +1,68 @@
namespace Nbt.Tag;
public interface INbtTag : IEquatable<INbtTag>
{
NbtTagType Type { get; }
INbtTag this[string tagName] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
INbtTag this[int tagIndex] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
INbtTag this[INbtPathElement tagPath] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
INbtTag this[INbtPath tagPath]
{
get
{
var count = tagPath.Count;
if (count == 0)
{
return this;
}
var pathElt = tagPath.PopFirst(out var pathRest);
var childTag = this[pathElt];
return childTag[pathRest];
}
set
{
var count = tagPath.Count;
if (count == 0)
{
throw new ArgumentException("Path cannot be empty", nameof(value));
}
var pathElt = tagPath.PopFirst(out var pathRest);
if (count == 1)
{
this[pathElt] = value;
}
else
{
var childTag = this[pathElt];
childTag[pathRest] = value;
}
}
}
INbtList AsList() => (INbtList)this;
INbtCompound AsCompound() => (INbtCompound)this;
INbtValue AsValue() => (INbtValue)this;
INbtValue<T> AsValue<T>() where T : notnull => (INbtValue<T>)this;
INbtArray AsArray() => (INbtArray)this;
INbtArray<T> AsArray<T>() where T : notnull => (INbtArray<T>)this;
T As<T>() where T : INbtTag => (T)this;
INbtTag Copy();
}

64
Nbt/Tag/NbtValue.cs Executable file
View File

@@ -0,0 +1,64 @@
namespace Nbt.Tag;
public interface INbtValue : INbtTag
{
object Value { get; set; }
}
public interface INbtValue<T> : INbtValue where T : notnull
{
new T Value { get; set; }
}
public abstract class NbtValue<T> : INbtValue<T> where T : notnull
{
private static readonly EqualityComparer<T> ValueComparer = EqualityComparer<T>.Default;
protected T value;
internal protected NbtValue(T value) : base() => this.value = value;
public abstract NbtTagType Type { get; }
INbtTag INbtTag.this[string tagName] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
INbtTag INbtTag.this[int tagIndex] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
INbtTag INbtTag.this[INbtPathElement tagPath] { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
INbtList INbtTag.AsList() => throw new NotSupportedException();
INbtCompound INbtTag.AsCompound() => throw new NotSupportedException();
INbtValue INbtTag.AsValue() => this;
INbtValue<U> INbtTag.AsValue<U>() => (INbtValue<U>)this;
INbtArray INbtTag.AsArray() => (INbtArray)this;
INbtArray<U> INbtTag.AsArray<U>() => (INbtArray<U>)this;
INbtTag INbtTag.Copy() => Copy();
public NbtValue<T> Copy() => NewInstance(CopyValue());
object INbtValue.Value
{
get => Value;
set => Value = (T)Convert.ChangeType(value, typeof(T));
}
public T Value
{
get => value;
set => this.value = value ?? throw new ArgumentNullException(nameof(value));
}
protected virtual T CopyValue() => value;
protected abstract NbtValue<T> NewInstance(T value);
public bool Equals(INbtTag? other) => ReferenceEquals(other, this) || other is NbtValue<T> o && ValueComparer.Equals(value, o.value);
public override bool Equals(object? obj) => ReferenceEquals(obj, this) || obj is NbtValue<T> other && ValueComparer.Equals(value, other.value);
public override int GetHashCode() => value.GetHashCode();
}

8
Nbt/Tag/Value/NbtByte.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtByte(sbyte value) : NbtValue<sbyte>(value)
{
public override NbtTagType Type => NbtTagType.Byte;
protected override NbtByte NewInstance(sbyte value) => new(value);
}

8
Nbt/Tag/Value/NbtDouble.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtDouble(double value) : NbtValue<double>(value)
{
public override NbtTagType Type => NbtTagType.Double;
protected override NbtDouble NewInstance(double value) => new(value);
}

8
Nbt/Tag/Value/NbtFloat.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtFloat(float value) : NbtValue<float>(value)
{
public override NbtTagType Type => NbtTagType.Float;
protected override NbtFloat NewInstance(float value) => new(value);
}

8
Nbt/Tag/Value/NbtInt.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtInt(int value) : NbtValue<int>(value)
{
public override NbtTagType Type => NbtTagType.Int;
protected override NbtInt NewInstance(int value) => new(value);
}

8
Nbt/Tag/Value/NbtLong.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtLong(long value) : NbtValue<long>(value)
{
public override NbtTagType Type => NbtTagType.Long;
protected override NbtLong NewInstance(long value) => new(value);
}

8
Nbt/Tag/Value/NbtShort.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtShort(short value) : NbtValue<short>(value)
{
public override NbtTagType Type => NbtTagType.Short;
protected override NbtShort NewInstance(short value) => new(value);
}

8
Nbt/Tag/Value/NbtString.cs Executable file
View File

@@ -0,0 +1,8 @@
namespace Nbt.Tag;
public class NbtString(string value) : NbtValue<string>(value)
{
public override NbtTagType Type => NbtTagType.String;
protected override NbtString NewInstance(string value) => new(value);
}