diff --git a/Hashing.cs b/Hashing.cs deleted file mode 100644 index 24d1d9a..0000000 --- a/Hashing.cs +++ /dev/null @@ -1,188 +0,0 @@ -using LibAC.Serialization; -using LibAC.Serialization.ACFile; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC -{ - public class Hash : Serializable - { - public byte[] hashed; - private readonly byte signature = 0x9f; - public int passnum = 0; - - public Hash(int len) - { - hashed = new byte[len]; - //hashed[0] = signature; - - passnum = 0; - } - - public byte get_sig_at_pass(int pass) - { - byte start = signature; - for (int i = 0; i < pass; i++) - { - start = (byte)wrap_add((int)start, (int)signature); - } - - return start; - } - - public Hash(byte[] bs, int pass) - { - hashed = bs; - passnum = pass; - } - - public static Hash compute(int len, byte[] data) - { - if (len <= 0) throw new ArgumentOutOfRangeException(); - byte[] arr = new byte[len]; - int p = 0;//pointer to position in [arr] - - foreach (byte b in data) - { - //arr[p] += b;// treat as a number. - int exist = arr[p]; - exist += b; - while (exist > 255) exist -= 255; - - if (exist < 0) exist *= -1; - arr[p] = (byte)exist; - - p++; - if (p >= len) - { - p = 0;// reset the pointer - } - } - - - return new Hash(arr, 1); - - } - - public static Hash operator +(Hash a, Hash b) - { - Hash ret = new Hash(a.hashed, a.passnum); - int p = 2; // We do want to add the position signed bit. As that information is unique to a piece of source data - - for (p = 0; p < b.hashed.Length; p++) - { - int exist = a.hashed[p]; - exist = wrap_add(exist, b.hashed[p]); - - ret.hashed[p] = (byte)exist; - } - - ret.passnum++; - - - return ret; - } - - /// - /// Wrap adds, with a cap at 255 - /// - /// - /// - /// Wrapped integer - public static int wrap_add(int a, int b) - { - a = a + b; - while (a > 255) a -= 255; - if (a < 0) a *= -1; - return a; - } - /// - /// Wrap subtracts with a cap of 255 - /// - /// - /// - /// Wrapped integer - public static int wrap_sub(int a, int b) - { - int ret = a - b; - if (ret < 0) ret += 255; - return ret; - } - - public static Hash operator -(Hash a, Hash b) - { - Hash ret = new Hash(a.hashed, a.passnum); - int p = 1; // We do want to add the position signed bit. As that information is unique to a piece of source data - - for (p = 0; p < b.hashed.Length; p++) - { - int exist = a.hashed[p]; - exist = wrap_sub(exist, b.hashed[p]); - - ret.hashed[p] = (byte)exist; - } - - ret.passnum--; - /* - if (ret.get_sig_at_pass(ret.passnum) == ret.hashed[0]) - { - // success - } - else throw new Exception("Fatal error in pass. Validation failed"); - */ - - - return ret; - - - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - foreach (byte b in serialize()) - { - sb.Append(b.ToString("X2")); - } - - return sb.ToString(); - } - - public byte[] serialize() - { - MemoryStream ms = new MemoryStream(); - BinaryWriter bw = new BinaryWriter(ms); - bw.Write(passnum); - bw.Write(hashed.Length); - bw.Write(hashed); - - return ms.ToArray(); - } - - public void deserialize(byte[] streams) - { - MemoryStream ms = new MemoryStream(streams); - BinaryReader br = new BinaryReader(ms); - passnum = br.ReadInt32(); - hashed = br.ReadBytes(br.ReadInt32()); - - br.Close(); - } - - public override void save(Folder f) - { - f.Add(new IntTag("pass", passnum)); - f.Add(new ByteArrayTag("hash",hashed)); - } - - public override void load(Folder f) - { - passnum = f["pass"].IntValue; - hashed = (f["hash"] as ByteArrayTag).Value; - } - } -} diff --git a/LibZNI.csproj b/LibZNI.csproj index 32309d8..dd189fb 100644 --- a/LibZNI.csproj +++ b/LibZNI.csproj @@ -2,16 +2,16 @@ Library - net7.0 + net8.0 true false Debug;Release;DebPub;KVPBuild - - - + + + diff --git a/NBT/API/ByteLayer.cs b/NBT/API/ByteLayer.cs new file mode 100644 index 0000000..696c277 --- /dev/null +++ b/NBT/API/ByteLayer.cs @@ -0,0 +1,358 @@ +using System; +using System.IO; +using System.Text; +using System.Linq; +using System.IO.Compression; +using System.Collections.Generic; + +public class ByteLayer +{ + private byte[] _byteBuffer; + private int _position; + + public ByteLayer() + { + _byteBuffer = new byte[0]; + _position = 0; + } + + public int Length => _byteBuffer.Length; + + public int CurrentPosition => _position; + + public byte[] Bytes => _byteBuffer.Take(_position).ToArray(); + + private void EnsureCapacity(int additionalBytes) + { + int requiredCapacity = _position + additionalBytes; + if (requiredCapacity > _byteBuffer.Length) + { + Array.Resize(ref _byteBuffer, requiredCapacity); + } + } + + public void WriteInt(int value) + { + EnsureCapacity(4); + Array.Copy(BitConverter.GetBytes(value), 0, _byteBuffer, _position, 4); + if (BitConverter.IsLittleEndian) + Array.Reverse(_byteBuffer, _position, 4); + _position += 4; + } + + public int ReadInt() + { + if (_position + 4 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + var value = _byteBuffer.Skip(_position).Take(4).ToArray(); + if (BitConverter.IsLittleEndian) Array.Reverse(value); + _position += 4; + return BitConverter.ToInt32(value, 0); + } + + public void WriteDouble(double value) + { + EnsureCapacity(8); + var bytes = BitConverter.GetBytes(value); + if (BitConverter.IsLittleEndian) + Array.Reverse(bytes); + Array.Copy(bytes, 0, _byteBuffer, _position, 8); + _position += 8; + } + + public double ReadDouble() + { + if (_position + 8 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + var value = _byteBuffer.Skip(_position).Take(8).ToArray(); + if (BitConverter.IsLittleEndian) Array.Reverse(value); + _position += 8; + return BitConverter.ToDouble(value, 0); + } + + public void WriteFloat(float value) + { + EnsureCapacity(4); + var bytes = BitConverter.GetBytes(value); + if(BitConverter.IsLittleEndian) + Array.Reverse(bytes); + + Array.Copy(bytes, 0, _byteBuffer, _position, 4); + _position += 4; + } + + public float ReadFloat() + { + if (_position + 4 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + var value = _byteBuffer.Skip(_position).Take(4).ToArray(); + if (BitConverter.IsLittleEndian) Array.Reverse(value); + _position += 4; + return BitConverter.ToSingle(value, 0); + } + + public void WriteString(string value) + { + var encoded = Encoding.UTF8.GetBytes(value); + WriteShort((short)encoded.Length); + EnsureCapacity(encoded.Length); + Array.Copy(encoded, 0, _byteBuffer, _position, encoded.Length); + _position += encoded.Length; + } + + public string ReadString() + { + int length = ReadShort(); + if (_position + length > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + var value = Encoding.UTF8.GetString(_byteBuffer, _position, length); + _position += length; + return value; + } + + public void WriteShort(short value) + { + EnsureCapacity(2); + var bytes = BitConverter.GetBytes((short)value); + if (BitConverter.IsLittleEndian) + Array.Reverse(bytes); + Array.Copy(bytes, 0, _byteBuffer, _position, 2); + _position += 2; + } + + public short ReadShort() + { + if (_position + 2 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + var value = _byteBuffer.Skip(_position).Take(2).ToArray(); + if (BitConverter.IsLittleEndian) Array.Reverse(value); + _position += 2; + return BitConverter.ToInt16(value, 0); + } + + public void WriteByte(byte value) + { + EnsureCapacity(1); + _byteBuffer[_position] = value; + _position++; + } + + public void WriteBytes(List bytes) + { + EnsureCapacity(bytes.Count); + Array.Copy(bytes.ToArray(), 0, _byteBuffer, _position, bytes.Count); + _position += bytes.Count; + } + + public byte ReadByte() + { + if (_position >= _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + return _byteBuffer[_position++]; + } + + public void ResetPosition() => _position = 0; + + public void RestorePosition(int position) + { + if (position < 0 || position > _byteBuffer.Length) + throw new ArgumentOutOfRangeException(nameof(position)); + _position = position; + } + + public void Clear() + { + _position = 0; + _byteBuffer = new byte[0]; + } + + public void WriteToFile(string filePath) + { + File.WriteAllBytes(filePath, Bytes); + } + + public void ReadFromFile(string filePath) + { + if (!File.Exists(filePath)) throw new FileNotFoundException("File does not exist.", filePath); + _byteBuffer = File.ReadAllBytes(filePath); + ResetPosition(); + } + + public void Compress() + { + using var memoryStream = new MemoryStream(); + using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress)) + { + gzipStream.Write(_byteBuffer, 0, _byteBuffer.Length); + } + _byteBuffer = memoryStream.ToArray(); + _position = _byteBuffer.Length; + } + + public void Decompress() + { + using var memoryStream = new MemoryStream(_byteBuffer); + using var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress); + using var decompressedStream = new MemoryStream(); + gzipStream.CopyTo(decompressedStream); + _byteBuffer = decompressedStream.ToArray(); + _position = _byteBuffer.Length; + } + + public void WriteVarInt(int value) + { + while ((value & ~0x7F) != 0) + { + WriteByte((byte)((value & 0x7F) | 0x80)); + value >>= 7; + } + WriteByte((byte)(value & 0x7F)); + } + + public int ReadVarInt() + { + int result = 0; + int shift = 0; + int byteRead; + do + { + byteRead = ReadByte(); + result |= (byteRead & 0x7F) << shift; + shift += 7; + } while ((byteRead & 0x80) != 0); + return result; + } + + public void WriteVarIntNoZigZag(int value) + { + while ((value & ~0x7F) != 0) + { + WriteByte((byte)((value & 0x7F) | 0x80)); + value >>= 7; + } + WriteByte((byte)(value & 0x7F)); + } + + public int ReadVarIntNoZigZag() + { + int result = 0; + int shift = 0; + int byteRead; + do + { + byteRead = ReadByte(); + result |= (byteRead & 0x7F) << shift; + shift += 7; + } while ((byteRead & 0x80) != 0); + return result; + } + + public void WriteLong(long value) + { + EnsureCapacity(8); + Array.Copy(BitConverter.GetBytes(value), 0, _byteBuffer, _position, 8); + if (BitConverter.IsLittleEndian) + Array.Reverse(_byteBuffer, _position, 8); + _position += 8; + } + + public long ReadLong() + { + if (_position + 8 > _byteBuffer.Length) throw new InvalidOperationException("Buffer overflow."); + var value = _byteBuffer.Skip(_position).Take(8).ToArray(); + if (BitConverter.IsLittleEndian) Array.Reverse(value); + _position += 8; + return BitConverter.ToInt64(value, 0); + } + + public void WriteVarLongZigZag(long value) + { + value = (value << 1) ^ (value >> 63); + WriteVarLongNoZigZag(value); + } + + public void WriteVarLongNoZigZag(long value) + { + while ((value & ~0x7F) != 0) + { + WriteByte((byte)((value & 0x7F) | 0x80)); + value >>= 7; + } + WriteByte((byte)(value & 0x7F)); + } + + public long ReadVarLongZigZag() + { + long result = ReadVarLongNoZigZag(); + return (result >> 1) ^ -(result & 1); + } + + public long ReadVarLongNoZigZag() + { + long result = 0; + int shift = 0; + int byteRead; + do + { + byteRead = ReadByte(); + result |= (byteRead & 0x7F) << shift; + shift += 7; + } while ((byteRead & 0x80) != 0); + return result; + } + + public void SetBit(int position, byte maskToSet) + { + if (position < _byteBuffer.Length) + { + Seek(position); + byte current = ReadByte(); + Seek(position); + current |= maskToSet; + WriteByte(current); + } + } + + public void ClearBit(int position, byte maskToClear) + { + if (position < _byteBuffer.Length) + { + Seek(position); + byte current = ReadByte(); + current &= (byte)~maskToClear; + Seek(position); + WriteByte(current); + } + } + + public bool CheckBit(int position, byte mask) + { + if (position < _byteBuffer.Length) + { + Seek(position); + byte current = ReadByte(); + return (current & mask) == mask; + } + return false; + } + + public byte GetBit(int position) + { + if (position < _byteBuffer.Length) + { + Seek(position); + return ReadByte(); + } + return 0; + } + + public void Seek(int position) + { + if (position < 0 || position > _byteBuffer.Length) + throw new ArgumentOutOfRangeException(nameof(position)); + _position = position; + } + + public void InsertRandomBytes(int count) + { + Random rng = new Random(); + for (int i = 0; i < count; i++) + { + WriteByte((byte)rng.Next(256)); + } + } +} diff --git a/NBT/API/InvalidNbtDataException.cs b/NBT/API/InvalidNbtDataException.cs new file mode 100644 index 0000000..d8182ca --- /dev/null +++ b/NBT/API/InvalidNbtDataException.cs @@ -0,0 +1,8 @@ +using System; + +namespace LibAC.NBT.API +{ + public class InvalidNbtDataException : Exception + { + } +} \ No newline at end of file diff --git a/NBT/API/NBTAccountant.cs b/NBT/API/NBTAccountant.cs new file mode 100644 index 0000000..3f61e41 --- /dev/null +++ b/NBT/API/NBTAccountant.cs @@ -0,0 +1,32 @@ +namespace LibAC.NBT.API; + +using System; + +public class NBTAccountant +{ + private static int _prettyIndex = 0; + + public static void PrintRead(Tag tag) + { + tag.PrettyPrint(_prettyIndex, false); + } + + public static void VisitTag() + { + _prettyIndex++; + } + + public static void LeaveTag(Tag tag) + { + if (tag is CompoundTag ct) + { + ct.EndPrettyPrint(_prettyIndex); + } + else if (tag is ListTag lt) + { + lt.EndPrettyPrint(_prettyIndex); + } + + _prettyIndex--; + } +} \ No newline at end of file diff --git a/NBT/API/NbtIo.cs b/NBT/API/NbtIo.cs new file mode 100644 index 0000000..76fd553 --- /dev/null +++ b/NBT/API/NbtIo.cs @@ -0,0 +1,132 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using LibAC.NBT; + + +namespace LibAC.NBT.API +{ + public class NbtIo + { + private static ByteLayer _io = new ByteLayer(); + + private static void _Read(string file) + { + _io = new ByteLayer(); + _io.ReadFromFile(file); + } + + public static CompoundTag Read(string file) + { + _Read(file); + if (_io.ReadByte() == (byte)TagType.Compound) + { + _io.ResetPosition(); + return (CompoundTag)Tag.ReadNamedTag(_io); + } + else + { + return ReadCompressed(file); + } + } + + public static CompoundTag ReadCompressed(string file) + { + _io = new ByteLayer(); + _io.ReadFromFile(file); + _io.Decompress(); + _io.ResetPosition(); + return (CompoundTag)Tag.ReadNamedTag(_io); + } + + public static void Write(string file, CompoundTag tag) + { + _io = new ByteLayer(); + Tag.WriteNamedTag(tag, _io); + _io.WriteToFile(file); + } + + public static void WriteCompressedAsync(string file, CompoundTag tag) + { + _io = new ByteLayer(); + Tag.WriteNamedTag(tag, _io); + _io.Compress(); + _io.WriteToFile(file); + } + + public static ByteLayer GetStream() + { + return _io; + } + + public static string WriteBase64String(CompoundTag tag) + { + _io = new ByteLayer(); + Tag.WriteNamedTag(tag, _io); + _io.Compress(); + return Convert.ToBase64String(_io.Bytes); + } + + public static CompoundTag ReadBase64String(string encoded) + { + _io = new ByteLayer(); + byte[] bytes = Convert.FromBase64String(encoded); + foreach (byte b in bytes) + { + _io.WriteByte(b); + } + _io.Decompress(); + _io.ResetPosition(); + return (CompoundTag)Tag.ReadNamedTag(_io); + } + + public static byte[] WriteToStreamCompressed(CompoundTag tag) + { + _io = new ByteLayer(); + Tag.WriteNamedTag(tag, _io); + _io.Compress(); + return _io.Bytes; + } + + public static CompoundTag ReadFromStreamCompressed(byte[] list) + { + _io = new ByteLayer(); + try + { + _io.WriteBytes(list.ToList()); + _io.ResetPosition(); + _io.Decompress(); + _io.ResetPosition(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + return (CompoundTag)Tag.ReadNamedTag(_io); + } + + public static async Task WriteToStreamAsync(CompoundTag tag) + { + _io = new ByteLayer(); + Tag.WriteNamedTag(tag, _io); + return _io.Bytes; + } + + public static CompoundTag ReadFromStream(byte[] list) + { + _io = new ByteLayer(); + try + { + _io.WriteBytes(list.ToList()); + _io.ResetPosition(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + return (CompoundTag)Tag.ReadNamedTag(_io); + } + } +} diff --git a/NBT/API/NbtUUID.cs b/NBT/API/NbtUUID.cs new file mode 100644 index 0000000..58b4e0f --- /dev/null +++ b/NBT/API/NbtUUID.cs @@ -0,0 +1,43 @@ +using System; +using LibAC.Utilities; + +namespace LibAC.NBT.API +{ + public class NbtUUID + { + public long MSB { get; } + public long LSB { get; } + + public static readonly NbtUUID ZERO = new NbtUUID(UUID.ZERO); + + public NbtUUID(long msb, long lsb) + { + MSB = msb; + LSB = lsb; + } + + public NbtUUID(UUID uuid) + { + byte[] uuidBytes = uuid.GetBytes(); + MSB = BitConverter.ToInt64(uuidBytes, 0); + LSB = BitConverter.ToInt64(uuidBytes, 8); + } + + public long GetMostSignificantBits() => MSB; + + public long GetLeastSignificantBits() => LSB; + + public UUID ToUUID() + { + byte[] bytes = new byte[16]; + BitConverter.GetBytes(MSB).CopyTo(bytes, 0); + BitConverter.GetBytes(LSB).CopyTo(bytes, 8); + return new UUID(bytes); + } + + public override string ToString() + { + return ToUUID().ToString(); + } + } +} \ No newline at end of file diff --git a/NBT/API/NbtUtils.cs b/NBT/API/NbtUtils.cs new file mode 100644 index 0000000..530f232 --- /dev/null +++ b/NBT/API/NbtUtils.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using LibAC.NBT.API; +using LibAC.Utilities; + +namespace LibAC.NBT.API +{ + public static class NbtUtils + { + public static void WriteBoolean(CompoundTag tag, string name, bool value) + { + tag.Put(name, ByteTag.ValueOf(value ? (byte)1 : (byte)0)); + } + + public static bool ReadBoolean(CompoundTag tag, string name) + { + if (tag.ContainsKey(name)) + { + return tag.Get(name).AsByte() == 1; + } + return false; + } + + public static void WriteVector2d(CompoundTag tag, string name, Vector2d pos) + { + var posTag = new ListTag(); + posTag.Add(DoubleTag.ValueOf(pos.X)); + posTag.Add(DoubleTag.ValueOf(pos.Z)); + tag.Put(name, posTag); + } + + public static Vector2d ReadVector2d(CompoundTag tag, string name) + { + if (tag.ContainsKey(name)) + { + var listTag = (ListTag)tag.Get(name); + return new Vector2d(listTag.Get(0).AsDouble(), listTag.Get(1).AsDouble()); + } + return Vector2d.ZERO; + } + + public static void WriteVector2i(CompoundTag tag, string name, Vector2i pos) + { + var posTag = new ListTag(); + posTag.Add(IntTag.ValueOf(pos.X)); + posTag.Add(IntTag.ValueOf(pos.Z)); + tag.Put(name, posTag); + } + + public static Vector2i ReadVector2i(CompoundTag tag, string name) + { + if (tag.ContainsKey(name)) + { + var listTag = (ListTag)tag.Get(name); + return new Vector2i(listTag.Get(0).AsInt(), listTag.Get(1).AsInt()); + } + return Vector2i.ZERO; + } + + public static void WriteVector3d(CompoundTag tag, string name, Vector3d pos) + { + var posTag = new ListTag(); + posTag.Add(DoubleTag.ValueOf(pos.X)); + posTag.Add(DoubleTag.ValueOf(pos.Y)); + posTag.Add(DoubleTag.ValueOf(pos.Z)); + tag.Put(name, posTag); + } + + public static Vector3d ReadVector3d(CompoundTag tag, string name) + { + if (tag.ContainsKey(name)) + { + var listTag = (ListTag)tag.Get(name); + return new Vector3d( + listTag.Get(0).AsDouble(), + listTag.Get(1).AsDouble(), + listTag.Get(2).AsDouble()); + } + return Vector3d.ZERO; + } + + public static void WriteVector3i(CompoundTag tag, string name, Vector3i pos) + { + var posTag = new ListTag(); + posTag.Add(IntTag.ValueOf(pos.X)); + posTag.Add(IntTag.ValueOf(pos.Y)); + posTag.Add(IntTag.ValueOf(pos.Z)); + tag.Put(name, posTag); + } + + public static Vector3i ReadVector3i(CompoundTag tag, string name) + { + if (tag.ContainsKey(name)) + { + var listTag = (ListTag)tag.Get(name); + return new Vector3i( + listTag.Get(0).AsInt(), + listTag.Get(1).AsInt(), + listTag.Get(2).AsInt()); + } + return Vector3i.ZERO; + } + + private static int[] MsbLsbToIntArray(long msb, long lsb) + { + return new[] + { + (int)(msb >> 32), + (int)msb, + (int)(lsb >> 32), + (int)lsb + }; + } + + private static int[] UuidToIntArray(NbtUUID id) + { + return MsbLsbToIntArray(id.GetMostSignificantBits(), id.GetLeastSignificantBits()); + } + + private static NbtUUID UuidFromIntArray(int[] values) + { + return new NbtUUID( + ((long)values[0] << 32) | (values[1] & 0xFFFFFFFFL), + ((long)values[2] << 32) | (values[3] & 0xFFFFFFFFL)); + } + + public static void WriteUUID(CompoundTag tag, string name, NbtUUID id) + { + tag.Put(name, IntArrayTag.ValueOf(UuidToIntArray(id).ToList())); + } + + public static NbtUUID ReadUUID(CompoundTag tag, string name) + { + if (!tag.ContainsKey(name)) + { + return NbtUUID.ZERO; + } + return UuidFromIntArray(tag.Get(name).AsIntArray().ToArray()); + } + } +} diff --git a/NBT/API/SnbtIo.cs b/NBT/API/SnbtIo.cs new file mode 100644 index 0000000..35ab451 --- /dev/null +++ b/NBT/API/SnbtIo.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using System.Text; +using LibAC.NBT.API; + +namespace LibAC.NBT.API +{ + public static class SnbtIo + { + /// + /// Writes a CompoundTag to a file in SNBT format. + /// + /// The file path to write to. + /// The CompoundTag to write. + public static async Task WriteToFile(string file, CompoundTag tag) + { + var fileInfo = new FileInfo(file); + + if (fileInfo.Exists) + { + fileInfo.Delete(); // Ensure the file is reset to 0 bytes. + } + + var builder = new StringBuilder(); + Tag.WriteStringifiedNamedTag(tag, builder, 0); + + using (var writer = new StreamWriter(file)) + { + await writer.WriteAsync(builder.ToString()); + } + } + + /// + /// Reads a tag from a file in SNBT format. + /// + /// The file path to read from. + /// A Task resolving to the read Tag. + public static async Task ReadFromFile(string file) + { + var fileInfo = new FileInfo(file); + if (!fileInfo.Exists) + { + throw new FileNotFoundException($"File not found: {file}"); + } + + string data; + using (var reader = new StreamReader(file)) + { + data = await reader.ReadToEndAsync(); + } + + var stringReader = new StringReader(data); + Tag tag = new CompoundTag(); + + try + { + tag = Tag.ReadStringifiedNamedTag(stringReader); + } + catch (Exception ex) + { + Console.WriteLine($"FATAL ERROR OCCURRED AT LOCATION:\n{stringReader.GetSnapshot()}"); + Console.WriteLine(ex); + } + + return tag; + } + + /// + /// Converts a CompoundTag to its SNBT string representation. + /// + /// The CompoundTag to convert. + /// A string representing the CompoundTag in SNBT format. + public static string WriteToString(CompoundTag tag) + { + var builder = new StringBuilder(); + Tag.WriteStringifiedNamedTag(tag, builder, 0); + return builder.ToString(); + } + + /// + /// Reads a Tag from an SNBT string. + /// + /// The SNBT string to read. + /// A Task resolving to the read Tag. + public static Task ReadFromString(string data) + { + var stringReader = new StringReader(data); + return Task.FromResult(Tag.ReadStringifiedNamedTag(stringReader)); + } + } +} diff --git a/NBT/API/StringBuilder.cs b/NBT/API/StringBuilder.cs new file mode 100644 index 0000000..b598571 --- /dev/null +++ b/NBT/API/StringBuilder.cs @@ -0,0 +1,32 @@ +using System.Text; + +namespace LibAC.NBT.API +{ + public class StringBuilder + { + private string _buffer = string.Empty; + + public StringBuilder() + { + } + + public bool IsEmpty => string.IsNullOrEmpty(_buffer); + + public int Length => _buffer.Length; + + public void Append(string value) + { + _buffer += value; + } + + public void Clear() + { + _buffer = string.Empty; + } + + public override string ToString() + { + return _buffer; + } + } +} \ No newline at end of file diff --git a/NBT/API/StringReader.cs b/NBT/API/StringReader.cs new file mode 100644 index 0000000..e18e959 --- /dev/null +++ b/NBT/API/StringReader.cs @@ -0,0 +1,196 @@ +using System; +using System.Text; + +namespace LibAC.NBT.API +{ + public class StringReader + { + private readonly string _buffer; + private int _position = 0; + private int _lastPosition = 0; + private bool _quotedString = false; + + public StringReader(string buffer) + { + _buffer = buffer; + } + + // Check if there's more to read + public bool CanRead => _CanRead(); + + private bool _CanRead() + { + SkipWhitespace(); + return _position < _buffer.Length; + } + + // Get the number of chars seeked + public int Seeked => _lastPosition - _position; + + // Read the next character + public char Next() + { + if (CanRead) + { + SkipWhitespace(); + return _buffer[_position++]; + } + else + { + throw new Exception("End of buffer reached"); + } + } + + // Generates a snapshot of the text location if applicable + public string GetSnapshot() + { + if (CanRead) + { + if (_position + 64 < _buffer.Length) + { + return _buffer.Substring(_position, 64); + } + else + { + return _buffer.Substring(_position); + } + } + else + { + return string.Empty; + } + } + + // Peek the next character without advancing the position + public char Peek() + { + SkipWhitespace(); + if (CanRead) + { + return _buffer[_position]; + } + else + { + throw new Exception("End of buffer reached"); + } + } + + // Skip any whitespace characters + public void SkipWhitespace() + { + if (_quotedString) return; // We need whitespace for strings + while (_position < _buffer.Length && IsWhitespace(_buffer[_position])) + { + _position++; + } + } + + // Check if a character is a whitespace + private bool IsWhitespace(char character) + { + return char.IsWhiteSpace(character); + } + + // Read until a specific character is found + public string ReadUntil(char stopChar, int maxChars) + { + var result = new StringBuilder(); + int index = 0; + while (CanRead && Peek() != stopChar && index < maxChars) + { + result.Append(Next().ToString()); + index++; + } + return result.ToString(); + } + + // Read a string enclosed in double quotes + public string ReadQuotedString() + { + _quotedString = true; + if (Next() != '"') + { + throw new Exception("Expected double quotes at the start of a string"); + } + var result = new StringBuilder(); + while (CanRead) + { + char character = Next(); + if (character == '"') + { + break; + } + result.Append(character.ToString()); + } + _quotedString = false; + return result.ToString(); + } + + // Read a number (int or double) + public string ReadNumber() + { + var result = new StringBuilder(); + while (CanRead && (IsDigit(Peek()) || Peek() == '.' || Peek() == '-')) + { + result.Append(Next().ToString()); + } + return result.ToString(); + } + + // Check if a character is a digit + private bool IsDigit(char character) + { + return char.IsDigit(character); + } + + // Read an unquoted string (used for keys in SNBT) + public string ReadUnquotedString() + { + var result = new StringBuilder(); + while (CanRead && + !IsWhitespace(Peek()) && + Peek() != ':' && + Peek() != ',' && + Peek() != '{' && + Peek() != '}' && + Peek() != '[' && + Peek() != ']') + { + result.Append(Next().ToString()); + } + return result.ToString(); + } + + public string ReadString() + { + if (Peek() == '"') + { + return ReadQuotedString(); + } + else + { + return ReadUnquotedString(); + } + } + + // Read a specific character and throw an exception if it's not found + public void Expect(char expectedChar) + { + if (char.ToLower(Next()) != char.ToLower(expectedChar)) + { + throw new Exception($"Expected {expectedChar}"); + } + } + + public void StartSeek() + { + _lastPosition = _position; + } + + public void EndSeek() + { + _position = _lastPosition; + _lastPosition = 0; + } + } +} diff --git a/NBT/API/Tag.cs b/NBT/API/Tag.cs new file mode 100644 index 0000000..94fd0e0 --- /dev/null +++ b/NBT/API/Tag.cs @@ -0,0 +1,280 @@ +using System.Collections.Generic; + +namespace LibAC.NBT.API; + +using System; +using System.Text; + +public abstract class Tag +{ + private TagType _parentTagType = TagType.End; + private Tag _parentTag; + private string _key; + + public byte GetType() + { + return (byte)GetTagType(); + } + + public abstract TagType GetTagType(); + + public bool HasParent => _parentTag != null; + + public void UpdateParent(Tag tag) + { + if (tag == null) + { + _parentTag = null; + SetParentTagType(TagType.End); + } + else + { + _parentTag = tag; + SetParentTagType(tag.GetTagType()); + } + } + + public Tag GetParent => _parentTag; + + public TagType ParentTagType => _parentTagType; + + public void SetParentTagType(TagType type) + { + _parentTagType = type; + } + + public abstract void WriteValue(ByteLayer data); + public abstract void ReadValue(ByteLayer data); + + public string GetKey() + { + return _key ?? string.Empty; + } + + public void SetKey(string key) + { + _key = key; + } + + public abstract dynamic GetValue(); + public abstract void SetValue(dynamic val); + + public static Tag ReadNamedTag(ByteLayer data) + { + var type = data.ReadByte(); + if (type == 0) + { + return new EndTag(); + } + else + { + Tag tag = MakeTagOfType(TagTypeExtensions.GetTagTypeFromByte(type)); + tag._key = data.ReadString(); + tag.ReadValue(data); + + return tag; + } + } + + public static void WriteNamedTag(Tag tag, ByteLayer data) + { + data.WriteByte(tag.GetType()); + if (tag.GetType() != 0) + { + data.WriteString(tag.GetKey()); + tag.WriteValue(data); + } + } + + public static void WriteStringifiedNamedTag(Tag tag, StringBuilder builder, int indents) + { + if (tag.GetType() != 0) + { + if (builder.Length > 0) + { + if (string.IsNullOrEmpty(tag.GetKey())) + { + builder.Append(new string('\t', indents)); + } + else + { + if (tag.ShouldQuoteName()) + { + builder.Append(new string('\t', indents) + $"\"{tag.GetKey()}\": "); + } + else + { + builder.Append(new string('\t', indents) + $"{tag.GetKey()}: "); + } + } + } + + tag.WriteStringifiedValue(builder, indents + 1, false); + } + } + + public static Tag ReadStringifiedNamedTag(StringReader stringReader) + { + string name = stringReader.Peek() == '{' || stringReader.Peek() == '[' ? "" : stringReader.ReadString(); + if (!string.IsNullOrEmpty(name)) stringReader.Expect(':'); + + TagType type = TagTypeExtensions.GetStringifiedTagType(stringReader); + Tag tag = MakeTagOfType(type); + tag._key = name; + tag.ReadStringifiedValue(stringReader); + + return tag; + } + + public bool ShouldQuoteName() + { + if (string.IsNullOrEmpty(GetKey())) + { + return false; + } + else + { + string letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + foreach (char c in GetKey()) + { + if (!letters.Contains(c.ToString())) return true; + } + return false; + } + } + + public bool ShouldQuote(string value) + { + if (string.IsNullOrEmpty(value)) return true; + + string letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + foreach (char c in value) + { + if (!letters.Contains(c.ToString())) return true; + } + return false; + } + + public abstract void WriteStringifiedValue(StringBuilder builder, int indent, bool isList); + public abstract void ReadStringifiedValue(StringReader reader); + + public bool Equals(object obj) + { + if (obj == null || !(obj is Tag)) return false; + + var tag = (Tag)obj; + if (tag.GetType() != GetType()) return false; + if (GetKey() != tag.GetKey()) return false; + + return true; + } + + public static Tag MakeTagOfType(TagType type) + { + switch (type) + { + case TagType.Byte: return new ByteTag(); + case TagType.ByteArray: return new ByteArrayTag(); + case TagType.Compound: return new CompoundTag(); + case TagType.Double: return new DoubleTag(); + case TagType.End: return new EndTag(); + case TagType.Short: return new ShortTag(); + case TagType.Int: return new IntTag(); + case TagType.Long: return new LongTag(); + case TagType.Float: return new FloatTag(); + case TagType.IntArray: return new IntArrayTag(); + case TagType.LongArray: return new LongArrayTag(); + case TagType.List: return new ListTag(); + case TagType.String: return new StringTag(); + default: throw new ArgumentOutOfRangeException(); + } + } + + public int AsByte() + { + if (this is ByteTag byteTag) return byteTag.Value; + return 0; + } + + public List AsByteArray() + { + if (this is ByteArrayTag byteArrayTag) return byteArrayTag.Value; + return new List(); + } + + public double AsDouble() + { + if (this is DoubleTag doubleTag) return doubleTag.Value; + return 0.0; + } + + public double AsFloat() + { + if (this is FloatTag floatTag) return floatTag.value; + return 0.0; + } + + public List AsIntArray() + { + if (this is IntArrayTag intArrayTag) return intArrayTag.Value; + return new List(); + } + + public int AsInt() + { + if (this is IntTag intTag) return intTag.Value; + return 0; + } + + public List AsLongArray() + { + if (this is LongArrayTag longArrayTag) return longArrayTag.value; + return new List(); + } + + public long AsLong() + { + if (this is LongTag longTag) return longTag.value; + return 0; + } + + public short AsShort() + { + if (this is ShortTag shortTag) return shortTag.value; + return 0; + } + + public string AsString() + { + if (this is StringTag stringTag) return stringTag.value; + return string.Empty; + } + + public CompoundTag AsCompoundTag() + { + return this is CompoundTag compoundTag ? compoundTag : new CompoundTag(); + } + + public abstract void PrettyPrint(int indent, bool recurse); + + public static string GetCanonicalName(TagType type) + { + return type switch + { + TagType.String => "TAG_String", + TagType.List => "TAG_List", + TagType.LongArray => "TAG_LongArray", + TagType.Long => "TAG_Long", + TagType.IntArray => "TAG_IntArray", + TagType.Int => "TAG_Int", + TagType.Compound => "TAG_Compound", + TagType.ByteArray => "TAG_ByteArray", + TagType.Byte => "TAG_Byte", + TagType.Double => "TAG_Double", + TagType.Float => "TAG_Float", + TagType.Short => "TAG_Short", + TagType.End => "TAG_End", + _ => throw new ArgumentOutOfRangeException() + }; + } +} diff --git a/NBT/API/TagType.cs b/NBT/API/TagType.cs new file mode 100644 index 0000000..dc627b6 --- /dev/null +++ b/NBT/API/TagType.cs @@ -0,0 +1,18 @@ +namespace LibAC.NBT.API; + +public enum TagType +{ + End = 0, + Byte = 1, + Short = 2, + Int = 3, + Long = 4, + Float = 5, + Double = 6, + ByteArray = 7, + String = 8, + List = 9, + Compound = 10, + IntArray = 11, + LongArray = 12 +} \ No newline at end of file diff --git a/NBT/API/TagTypeExtensions.cs b/NBT/API/TagTypeExtensions.cs new file mode 100644 index 0000000..78d83a3 --- /dev/null +++ b/NBT/API/TagTypeExtensions.cs @@ -0,0 +1,145 @@ +namespace LibAC.NBT.API; + +using System; +using System.Text; +using System.Text.RegularExpressions; + +public static class TagTypeExtensions +{ + private static readonly string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + public static TagType GetTagTypeFromByte(int byteValue) + { + return Enum.IsDefined(typeof(TagType), byteValue) ? (TagType)byteValue : TagType.End; + } + + public static TagType GetStringifiedTagType(StringReader reader) + { + reader.StartSeek(); // Enter fake read mode + TagType ret = TagType.End; + bool isQuoted = false; + bool isNumeric = true; // Assume true until proven otherwise + bool isAlpha = true; // Assume true until proven otherwise + bool hasDecimalPoint = false; + StringBuilder buffer = new StringBuilder(); + + while (reader.CanRead) + { + var val = reader.Peek(); // Peek at the next character without consuming it + + if (val == ',' || val == '\n' || val == ']' || val == '}') + { + break; // Stop at comma or newline + } + + if (val == '"') + { + reader.Next(); // Consume the quote character + isQuoted = true; // Toggle quoted state + break; + } + + if (val == '{') + { + ret = TagType.Compound; // Detected a CompoundTag + reader.Next(); // Consume '{' + reader.EndSeek(); // Restore the original stream position + return ret; + } + + if (val == '[') + { + reader.Next(); // Consume '[' + // Peek ahead to differentiate between List and Array + var prefix = reader.ReadUntil(';', 2).ToUpper(); + + switch (prefix) + { + case "B": + ret = TagType.ByteArray; + break; + case "I": + ret = TagType.IntArray; + break; + case "L": + ret = TagType.LongArray; + break; + default: + ret = TagType.List; // No type prefix, it's a List + break; + } + + reader.EndSeek(); // Restore the original stream position + return ret; + } + + // Adjusting numeric and alphabetic checks + var current = reader.Next(); // Consume the character + + buffer.Append(current.ToString()); + + // Updated check to allow digits, decimal points, and numeric suffixes + if (!Regex.IsMatch(current.ToString(), @"^[0-9]$")) + { + if (current == '.' && !hasDecimalPoint) + { + hasDecimalPoint = true; // Allow only one decimal point + } + else if (!Regex.IsMatch(current.ToString(), @"^[sSbBiIlLfFdD]$")) + { + isNumeric = false; // Not purely numeric or allowed decimal/suffix + } + } + + // Check if current character is purely alphabetical + if (!Regex.IsMatch(current.ToString(), @"^[A-Za-z]$")) + { + isAlpha = false; // Not purely alphabetical + } + } + + var input = buffer.ToString().Trim(); + reader.EndSeek(); // Restore the original stream position + + if (string.IsNullOrEmpty(input)) + { + return TagType.String; // No input detected + } + + if (isQuoted) + { + return TagType.String; // Quoted string + } + + if (isNumeric) + { + // Check the last character for type indicator (only for numeric input) + var lastChar = input.Substring(input.Length - 1).ToUpper(); + switch (lastChar) + { + case "S": + return TagType.Short; + case "B": + return TagType.Byte; + case "I": + return TagType.Int; + case "F": + return TagType.Float; + case "D": + return TagType.Double; + case "L": + return TagType.Long; + default: + return TagType.Int; // Default to Int if purely numeric with no specific suffix + } + } + else if (isAlpha && !input.Contains(' ')) + { + return TagType.String; // Unquoted purely alphabetical string + } + else + { + return TagType.String; // Unquoted string with mixed content or spaces + } + } +} diff --git a/NBT/ByteArrayTag.cs b/NBT/ByteArrayTag.cs new file mode 100644 index 0000000..a3ead47 --- /dev/null +++ b/NBT/ByteArrayTag.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class ByteArrayTag : Tag + { + public List Value { get; private set; } = new List(); + + public ByteArrayTag() { } + + private ByteArrayTag(List value) + { + Value.AddRange(value); + } + + public static ByteArrayTag ValueOf(List value) + { + return new ByteArrayTag(value); + } + + public override void ReadValue(ByteLayer data) + { + int len = data.ReadInt(); + for (int i = 0; i < len; i++) + { + Value.Add(data.ReadByte()); + } + } + + public override void WriteValue(ByteLayer data) + { + data.WriteInt(Value.Count); + foreach (byte i in Value) + { + data.WriteByte(i); + } + } + + public override TagType GetTagType() + { + return TagType.ByteArray; + } + + public override dynamic GetValue() + { + return null; // Implementation-specific value retrieval + } + + public override void SetValue(dynamic val) + { + // Implementation-specific value setting + } + + public override void PrettyPrint(int indent, bool recurse) + { + string array = string.Join(", ", Value); + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: [{array}]"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}[B; {string.Join("B, ", Value)}B]"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + reader.Expect('['); + reader.Expect('B'); + reader.Expect(';'); + while (reader.Peek() != ']') + { + Value.Add(byte.Parse(reader.ReadNumber())); + reader.Expect('b'); + + if (reader.Peek() == ',') reader.Next(); + } + reader.Expect(']'); + } + } +} diff --git a/NBT/ByteTag.cs b/NBT/ByteTag.cs new file mode 100644 index 0000000..02a4d2e --- /dev/null +++ b/NBT/ByteTag.cs @@ -0,0 +1,67 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class ByteTag : Tag + { + public byte Value { get; private set; } = 0; + + public ByteTag() { } + + private ByteTag(byte value) + { + Value = value; + } + + public static ByteTag ValueOf(byte value) + { + return new ByteTag(value); + } + + public override void ReadValue(ByteLayer data) + { + Value = data.ReadByte(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteByte(Value); + } + + public override TagType GetTagType() + { + return TagType.Byte; + } + + public override dynamic GetValue() + { + return Value; + } + + public override void SetValue(dynamic val) + { + if (val is byte) + { + Value = (byte)val; + } + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {Value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{Value}b"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + string val = reader.ReadNumber(); + Value = byte.Parse(val); + reader.Expect('b'); + } + } +} \ No newline at end of file diff --git a/NBT/CompoundTag.cs b/NBT/CompoundTag.cs new file mode 100644 index 0000000..bd3bc67 --- /dev/null +++ b/NBT/CompoundTag.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class CompoundTag : Tag, IDictionary + { + private readonly Dictionary _value = new Dictionary(); + + public CompoundTag() { } + + public override void ReadValue(ByteLayer data) + { + _value.Clear(); + + while (true) + { + Tag tag = Tag.ReadNamedTag(data); + if (tag.GetType() == 0) + { + return; + } + + tag.SetParentTagType(TagType.Compound); + this[tag.GetKey()] = tag; + } + } + + public override void WriteValue(ByteLayer data) + { + foreach (var tag in _value.Values) + { + Tag.WriteNamedTag(tag, data); + } + + data.WriteByte((byte)TagType.End); + } + + public void Put(string name, Tag tag) + { + _value[name] = tag; + tag.SetKey(name); + tag.UpdateParent(this); + } + + public Tag? Get(string name) + { + return ContainsKey(name) ? _value[name] : null; + } + + public override TagType GetTagType() + { + return TagType.Compound; + } + + public override object GetValue() + { + return null; + } + + public override void SetValue(object val) { } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: [{_value.Count} entries]"); + Console.WriteLine($"{new string('\t', indent)}"); + if (recurse) + { + foreach (var tag in _value.Values) + { + tag.PrettyPrint(indent + 1, true); + } + } + } + + public void EndPrettyPrint(int indent) + { + Console.WriteLine($"{new string('\t', indent)}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent - 1) : "")}{{\n"); + + bool firstEntry = true; + foreach (var tag in _value.Values) + { + if (!firstEntry) + { + builder.Append(",\n"); + } + firstEntry = false; + Tag.WriteStringifiedNamedTag(tag, builder, indent); + } + + builder.Append($"\n{new string('\t', indent - 1)}}}"); + } + + public bool TryGetValue(string key, out Tag value) + { + return _value.TryGetValue(key, out value); + } + + public Tag? this[string key] + { + get => _value.ContainsKey(key) ? _value[key] : null; + set + { + if (value != null) + { + _value[key] = value; + value.UpdateParent(this); + } + } + } + + public void Add(string key, Tag value) + { + _value.Add(key, value); + value.UpdateParent(this); + } + + public void Add(KeyValuePair item) + { + _value.Add(item.Key, item.Value); + item.Value.UpdateParent(this); + } + + public void Clear() + { + UnsetParent(); + _value.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return _value.ContainsKey(item.Key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(KeyValuePair item) + { + return _value.Remove(item.Key); + } + + public void UnsetParent() + { + foreach (var entry in _value) + { + entry.Value.SetParentTagType(TagType.End); + entry.Value.UpdateParent(null); + } + } + + public bool ContainsKey(string key) + { + return _value.ContainsKey(key); + } + + public bool ContainsValue(Tag value) + { + return _value.ContainsValue(value); + } + + public void ForEach(Action action) + { + foreach (var entry in _value) + { + action(entry.Key, entry.Value); + } + } + + public int Count => _value.Count; + public bool IsReadOnly { get; } + + public bool IsEmpty => _value.Count == 0; + + public bool IsNotEmpty => _value.Count > 0; + + public ICollection Keys => _value.Keys; + + public ICollection Values => _value.Values; + + public override void ReadStringifiedValue(StringReader reader) + { + reader.Expect('{'); + + while (reader.Peek() != '}') + { + Tag tag = Tag.ReadStringifiedNamedTag(reader); + Put(tag.GetKey(), tag); + + if (reader.Peek() == ',') reader.Next(); + } + + reader.Expect('}'); + } + + public void AddAll(IDictionary other) + { + foreach (var entry in other) + { + _value[entry.Key] = entry.Value; + entry.Value.UpdateParent(this); + } + } + + public bool Remove(string key) + { + if (_value.ContainsKey(key)) + { + _value[key].UpdateParent(null); + return _value.Remove(key); + } + + return false; + } + + public void RemoveWhere(Predicate> predicate) + { + var toRemove = new List(); + foreach (var entry in _value) + { + if (predicate(entry)) + { + toRemove.Add(entry.Key); + } + } + + foreach (var key in toRemove) + { + _value[key].UpdateParent(null); + _value.Remove(key); + } + } + + public void UpdateAll(Action update) + { + foreach (var entry in _value) + { + update(entry.Key, entry.Value); + } + } + + public Tag PutIfAbsent(string key, Func ifAbsent) + { + if (!_value.ContainsKey(key)) + { + var tag = ifAbsent(); + tag.UpdateParent(this); + _value[key] = tag; + return tag; + } + + return _value[key]; + } + + public void AddEntries(IEnumerable> newEntries) + { + foreach (var entry in newEntries) + { + _value.Add(entry.Key, entry.Value); + entry.Value.UpdateParent(this); + } + } + + public IEnumerator> GetEnumerator() + { + return _value.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _value.GetEnumerator(); + } + } +} diff --git a/NBT/DoubleTag.cs b/NBT/DoubleTag.cs new file mode 100644 index 0000000..8015eb7 --- /dev/null +++ b/NBT/DoubleTag.cs @@ -0,0 +1,67 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class DoubleTag : Tag + { + public double Value { get; private set; } = 0.0; + + public DoubleTag() { } + + private DoubleTag(double value) + { + Value = value; + } + + public static DoubleTag ValueOf(double value) + { + return new DoubleTag(value); + } + + public override void ReadValue(ByteLayer data) + { + Value = data.ReadDouble(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteDouble(Value); + } + + public override TagType GetTagType() + { + return TagType.Double; + } + + public override dynamic GetValue() + { + return Value; + } + + public override void SetValue(dynamic val) + { + if (val is double) + { + Value = (double)val; + } + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {Value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}{Value}d"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + double val = double.Parse(reader.ReadNumber()); + Value = val; + reader.Expect('d'); + } + } +} \ No newline at end of file diff --git a/NBT/EndTag.cs b/NBT/EndTag.cs new file mode 100644 index 0000000..710c5bf --- /dev/null +++ b/NBT/EndTag.cs @@ -0,0 +1,42 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT; + +public class EndTag : Tag +{ + public override TagType GetTagType() + { + return TagType.End; + } + + public override void WriteValue(ByteLayer data) + { + } + + public override void ReadValue(ByteLayer data) + { + } + + public override dynamic GetValue() + { + return null; + } + + public override void SetValue(dynamic val) + { + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + } + + public override void ReadStringifiedValue(StringReader reader) + { + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{"".PadLeft(indent, '\t')}{Tag.GetCanonicalName(GetTagType())}"); + } +} \ No newline at end of file diff --git a/NBT/FloatTag.cs b/NBT/FloatTag.cs new file mode 100644 index 0000000..d9a30b6 --- /dev/null +++ b/NBT/FloatTag.cs @@ -0,0 +1,66 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class FloatTag : Tag + { + public float value { get; private set; } = 0.0f; + + public FloatTag() { } + + private FloatTag(float value) + { + this.value = value; + } + + public static FloatTag ValueOf(float value) + { + return new FloatTag(value); + } + + public override void ReadValue(ByteLayer data) + { + value = data.ReadFloat(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteFloat(value); + } + + public override TagType GetTagType() + { + return TagType.Float; + } + + public override object GetValue() + { + return value; + } + + public override void SetValue(object val) + { + if (val is float) + { + value = (float)val; + } + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}{value}f"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + value = float.Parse(reader.ReadNumber()); + reader.Expect('f'); + } + } +} \ No newline at end of file diff --git a/NBT/IntArrayTag.cs b/NBT/IntArrayTag.cs new file mode 100644 index 0000000..4eac559 --- /dev/null +++ b/NBT/IntArrayTag.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class IntArrayTag : Tag + { + public List Value { get; private set; } = new List(); + + public IntArrayTag() { } + + private IntArrayTag(List value) + { + this.Value.AddRange(value); + } + + public static IntArrayTag ValueOf(List value) + { + return new IntArrayTag(value); + } + + public override void ReadValue(ByteLayer data) + { + int count = data.ReadInt(); + for (int i = 0; i < count; i++) + { + Value.Add(data.ReadInt()); + } + } + + public override void WriteValue(ByteLayer data) + { + data.WriteInt(Value.Count); + foreach (int i in Value) + { + data.WriteInt(i); + } + } + + public override TagType GetTagType() + { + return TagType.IntArray; + } + + public override void SetValue(dynamic val) { } + + public override dynamic GetValue() { return null; } + + public override void PrettyPrint(int indent, bool recurse) + { + string array = string.Join(", ", Value); + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: [{array}]"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}[I; {string.Join("I, ", Value)}I]"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + reader.Expect('['); + reader.Expect('I'); + reader.Expect(';'); + while (reader.Peek() != ']') + { + Value.Add(int.Parse(reader.ReadNumber())); + + if (reader.Peek() == ',') + reader.Next(); + } + reader.Expect('I'); + } + } +} diff --git a/NBT/IntTag.cs b/NBT/IntTag.cs new file mode 100644 index 0000000..40b7993 --- /dev/null +++ b/NBT/IntTag.cs @@ -0,0 +1,70 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class IntTag : Tag + { + public int Value { get; private set; } = 0; + + public IntTag() { } + + private IntTag(int value) + { + this.Value = value; + } + + public static IntTag ValueOf(int value) + { + return new IntTag(value); + } + + public override void ReadValue(ByteLayer data) + { + Value = data.ReadInt(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteInt(Value); + } + + public override TagType GetTagType() + { + return TagType.Int; + } + + public override void SetValue(dynamic val) + { + if (val is int) + Value = val; + } + + public override dynamic GetValue() + { + return Value; + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {Value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}{Value}i"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + string val = reader.ReadNumber(); + Value = int.Parse(val); + + // Since a type indicator is optional for an int, check for a comma + if (reader.Peek() == ',') + return; + else + reader.Expect('i'); + } + } +} \ No newline at end of file diff --git a/NBT/ListTag.cs b/NBT/ListTag.cs new file mode 100644 index 0000000..afe1f06 --- /dev/null +++ b/NBT/ListTag.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class ListTag : Tag + { + private List value = new List(); + + public List GetList => new List(value); + + public ListTag() { } + + public TagType ListTagType + { + get + { + return value.Count == 0 ? TagType.End : value[0].GetTagType(); + } + } + + public override void ReadValue(ByteLayer data) + { + TagType type = TagTypeExtensions.GetTagTypeFromByte(data.ReadByte()); + int size = data.ReadInt(); + + for (int i = 0; i < size; i++) + { + Tag tag = Tag.MakeTagOfType(type); + tag.ReadValue(data); + Add(tag); + } + } + + public override void WriteValue(ByteLayer data) + { + TagType type = TagType.End; + if (Size() > 0) + { + type = value[0].GetTagType(); + } + + data.WriteByte((byte)type); + data.WriteInt(Size()); + for (int i = 0; i < Size(); i++) + { + value[i].WriteValue(data); + } + } + + public void Add(Tag tag) + { + TagType type = TagType.End; + if (Size() > 0) + { + type = value[0].GetTagType(); + } + if (type == TagType.End || type == tag.GetTagType()) + { + value.Add(tag); + tag.UpdateParent(this); + } + } + + public Tag Get(int index) + { + if (Size() > index) + { + return value[index]; + } + else + { + return new EndTag(); + } + } + + public void Remove(Tag tag) + { + value.Remove(tag); + tag.UpdateParent(null); + } + + public void RemoveAt(int index) + { + value[index].UpdateParent(null); + value.RemoveAt(index); + } + + public int IndexOf(Tag tag) + { + return value.IndexOf(tag); + } + + public override TagType GetTagType() + { + return TagType.List; + } + + public override void SetValue(dynamic val) { } + + public override dynamic GetValue() + { + return null; + } + + public int Size() + { + return value.Count; + } + + public void Clear() + { + // Clear the list + foreach (var entry in value) + { + entry.UpdateParent(null); + entry.SetParentTagType(TagType.End); + } + + value.Clear(); + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: [{value.Count} entries]"); + Console.WriteLine($"{new string('\t', indent)}["); + foreach (Tag tag in value) + { + tag.PrettyPrint(indent + 1, true); + } + } + + public void EndPrettyPrint(int indent) + { + Console.WriteLine($"{new string('\t', indent)}]"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent - 1) : "")}["); + + bool firstTag = true; + foreach (Tag tag in value) + { + if (!firstTag) + { + builder.Append(","); + } + + firstTag = false; + tag.WriteStringifiedValue(builder, indent + 1, true); + } + + builder.Append($"{new string('\t', indent - 1)}]"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + reader.Expect('['); + while (reader.Peek() != ']') + { + TagType type = TagTypeExtensions.GetStringifiedTagType(reader); + Tag newTag = Tag.MakeTagOfType(type); + newTag.ReadStringifiedValue(reader); + Add(newTag); + + if (reader.Peek() == ',') reader.Next(); + } + reader.Expect(']'); + } + } +} diff --git a/NBT/LongArrayTag.cs b/NBT/LongArrayTag.cs new file mode 100644 index 0000000..f71aa6c --- /dev/null +++ b/NBT/LongArrayTag.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class LongArrayTag : Tag + { + public List value { get; private set; } = new List(); + + public LongArrayTag() { } + + public LongArrayTag(List lst) + { + value.AddRange(lst); + } + + public static LongArrayTag ValueOf(List value) + { + return new LongArrayTag(value); + } + + public override void ReadValue(ByteLayer data) + { + int count = data.ReadInt(); + for (int i = 0; i < count; i++) + { + value.Add(data.ReadLong()); + } + } + + public override void WriteValue(ByteLayer data) + { + data.WriteInt(Size()); + foreach (long val in value) + { + data.WriteLong(val); + } + } + + public int Size() + { + return value.Count; + } + + public override TagType GetTagType() + { + return TagType.LongArray; + } + + public override void SetValue(dynamic val) { } + + public override dynamic GetValue() + { + return null; + } + + public override void PrettyPrint(int indent, bool recurse) + { + string array = string.Join(", ", value); + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: [{array}]"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}[L; {string.Join("L, ", value)}L]"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + reader.Expect('['); + reader.Expect('L'); + reader.Expect(';'); + while (reader.Peek() != ']') + { + value.Add(long.Parse(reader.ReadNumber())); + reader.Expect('l'); + + if (reader.Peek() == ',') reader.Next(); + } + reader.Expect(']'); + } + } +} diff --git a/NBT/LongTag.cs b/NBT/LongTag.cs new file mode 100644 index 0000000..26d846d --- /dev/null +++ b/NBT/LongTag.cs @@ -0,0 +1,65 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class LongTag : Tag + { + public long value { get; private set; } = 0; + + public LongTag() { } + + public LongTag(long value) + { + this.value = value; + } + + public static LongTag ValueOf(long value) + { + return new LongTag(value); + } + + public override void ReadValue(ByteLayer data) + { + value = data.ReadLong(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteLong(value); + } + + public override TagType GetTagType() + { + return TagType.Long; + } + + public override void SetValue(dynamic val) + { + if (val is long) value = val; + } + + public override dynamic GetValue() + { + return value; + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}{value}L"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + long val = long.Parse(reader.ReadNumber()); + value = val; + + reader.Expect('l'); + } + } +} \ No newline at end of file diff --git a/NBT/ShortTag.cs b/NBT/ShortTag.cs new file mode 100644 index 0000000..f5150c4 --- /dev/null +++ b/NBT/ShortTag.cs @@ -0,0 +1,65 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class ShortTag : Tag + { + public short value { get; private set; } + = 0; + public ShortTag() { } + + public ShortTag(short value) + { + this.value = value; + } + + public static ShortTag ValueOf(short value) + { + return new ShortTag(value); + } + + public override void ReadValue(ByteLayer data) + { + value = data.ReadShort(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteShort(value); + } + + public override TagType GetTagType() + { + return TagType.Short; + } + + public override void SetValue(dynamic val) + { + if (val is short) value = val; + } + + public override dynamic GetValue() + { + return value; + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + builder.Append($"{(isList ? new string('\t', indent) : "")}{value}S"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + short val = short.Parse(reader.ReadNumber()); + value = val; + + reader.Expect('s'); + } + } +} \ No newline at end of file diff --git a/NBT/StringTag.cs b/NBT/StringTag.cs new file mode 100644 index 0000000..959ea35 --- /dev/null +++ b/NBT/StringTag.cs @@ -0,0 +1,73 @@ +using System; +using LibAC.NBT.API; + +namespace LibAC.NBT +{ + public class StringTag : Tag + { + public string value { get; private set; } = ""; + + public StringTag() { } + + public StringTag(string str) + { + value = str; + } + + public static StringTag ValueOf(string str) + { + return new StringTag(str); + } + + public override void ReadValue(ByteLayer data) + { + value = data.ReadString(); + } + + public override void WriteValue(ByteLayer data) + { + data.WriteString(value); + } + + public override TagType GetTagType() + { + return TagType.String; + } + + public override void SetValue(dynamic val) + { + if (val is string) value = val; + } + + public override dynamic GetValue() + { + return value; + } + + public override void PrettyPrint(int indent, bool recurse) + { + Console.WriteLine($"{new string('\t', indent)}{Tag.GetCanonicalName(GetTagType())}: {value}"); + } + + public override void WriteStringifiedValue(StringBuilder builder, int indent, bool isList) + { + if (ShouldQuote(value)) + builder.Append($"{(isList ? new string('\t', indent) : "")}\"{value}\""); + else + builder.Append($"{(isList ? new string('\t', indent) : "")}{value}"); + } + + public override void ReadStringifiedValue(StringReader reader) + { + string str = reader.ReadString(); + value = str; + } + + // This method will need to be implemented for checking if a string should be quoted + private bool ShouldQuote(string str) + { + // Implement logic for determining if the string needs to be quoted + return false; + } + } +} \ No newline at end of file diff --git a/Serialization/NBTReader.cs b/Serialization/NBTReader.cs deleted file mode 100644 index 10bb6b1..0000000 --- a/Serialization/NBTReader.cs +++ /dev/null @@ -1,236 +0,0 @@ -// DISCLAIMER: Taken from fNBT to be altered to fit the needs of ZNI NBT -// All credit for the implementation of this file should go to the fNBT Authors, unless stated otherwise by comment! - - -using LibAC.Serialization.ACFile; -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Text; - -namespace LibAC.Serialization -{ - /// BinaryReader wrapper that takes care of reading primitives from an NBT stream, - /// while taking care of endianness, string encoding, and skipping. - public sealed class NBTReader : BinaryReader - { - readonly byte[] buffer = new byte[sizeof(double)]; - - byte[] seekBuffer; - const int SeekBufferSize = 8 * 1024; - readonly bool swapNeeded; - readonly byte[] stringConversionBuffer = new byte[64]; - - - public NBTReader(Stream input, bool bigEndian) - : base(input) - { - swapNeeded = (BitConverter.IsLittleEndian == bigEndian); - } - - - public TagType ReadTagType() - { - int type = ReadByte(); - if (type < 0) - { - throw new EndOfStreamException(); - } - else if (type > (int)TagType.UUID) // ZNI: Other data types are supported - { - throw new Exception("NBT tag type out of range: " + type); - } - return (TagType)type; - } - - - public override short ReadInt16() - { - if (swapNeeded) - { - return Swap(base.ReadInt16()); - } - else - { - return base.ReadInt16(); - } - } - - - public override int ReadInt32() - { - if (swapNeeded) - { - return Swap(base.ReadInt32()); - } - else - { - return base.ReadInt32(); - } - } - - - public override long ReadInt64() - { - if (swapNeeded) - { - return Swap(base.ReadInt64()); - } - else - { - return base.ReadInt64(); - } - } - - - public override float ReadSingle() - { - if (swapNeeded) - { - FillBuffer(sizeof(float)); - Array.Reverse(buffer, 0, sizeof(float)); - return BitConverter.ToSingle(buffer, 0); - } - else - { - return base.ReadSingle(); - } - } - - - public override double ReadDouble() - { - if (swapNeeded) - { - FillBuffer(sizeof(double)); - Array.Reverse(buffer); - return BitConverter.ToDouble(buffer, 0); - } - return base.ReadDouble(); - } - - - public override string ReadString() - { - short length = ReadInt16(); - if (length < 0) - { - throw new Exception("Negative string length given!"); - } - if (length < stringConversionBuffer.Length) - { - int stringBytesRead = 0; - while (stringBytesRead < length) - { - int bytesToRead = length - stringBytesRead; - int bytesReadThisTime = BaseStream.Read(stringConversionBuffer, stringBytesRead, bytesToRead); - if (bytesReadThisTime == 0) - { - throw new EndOfStreamException(); - } - stringBytesRead += bytesReadThisTime; - } - return Encoding.UTF8.GetString(stringConversionBuffer, 0, length); - } - else - { - byte[] stringData = ReadBytes(length); - if (stringData.Length < length) - { - throw new EndOfStreamException(); - } - return Encoding.UTF8.GetString(stringData); - } - } - - - public void Skip(int bytesToSkip) - { - if (bytesToSkip < 0) - { - throw new ArgumentOutOfRangeException("bytesToSkip"); - } - else if (BaseStream.CanSeek) - { - BaseStream.Position += bytesToSkip; - } - else if (bytesToSkip != 0) - { - if (seekBuffer == null) seekBuffer = new byte[SeekBufferSize]; - int bytesSkipped = 0; - while (bytesSkipped < bytesToSkip) - { - int bytesToRead = Math.Min(SeekBufferSize, bytesToSkip - bytesSkipped); - int bytesReadThisTime = BaseStream.Read(seekBuffer, 0, bytesToRead); - if (bytesReadThisTime == 0) - { - throw new EndOfStreamException(); - } - bytesSkipped += bytesReadThisTime; - } - } - } - - - new void FillBuffer(int numBytes) - { - int offset = 0; - do - { - int num = BaseStream.Read(buffer, offset, numBytes - offset); - if (num == 0) throw new EndOfStreamException(); - offset += num; - } while (offset < numBytes); - } - - - public void SkipString() - { - short length = ReadInt16(); - if (length < 0) - { - throw new Exception("Negative string length given!"); - } - Skip(length); - } - - - [DebuggerStepThrough] - static short Swap(short v) - { - unchecked - { - return (short)((v >> 8) & 0x00FF | - (v << 8) & 0xFF00); - } - } - - - [DebuggerStepThrough] - static int Swap(int v) - { - unchecked - { - var v2 = (uint)v; - return (int)((v2 >> 24) & 0x000000FF | - (v2 >> 8) & 0x0000FF00 | - (v2 << 8) & 0x00FF0000 | - (v2 << 24) & 0xFF000000); - } - } - - - [DebuggerStepThrough] - static long Swap(long v) - { - unchecked - { - return (Swap((int)v) & uint.MaxValue) << 32 | - Swap((int)(v >> 32)) & uint.MaxValue; - } - } - - // ZNI Removed selector. Not needed for the ZNI NBT implementation - } -} diff --git a/Serialization/NBTWriter.cs b/Serialization/NBTWriter.cs deleted file mode 100644 index 961cd1e..0000000 --- a/Serialization/NBTWriter.cs +++ /dev/null @@ -1,263 +0,0 @@ -// DISCLAIMER: Taken from fNBT to be altered to fit the needs of ZNI NBT -// All credit for the implementation of this file should go to the fNBT Authors, unless stated otherwise by comment! - -using LibAC.Serialization.ACFile; -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Text; - -namespace LibAC.Serialization -{ - /// BinaryWriter wrapper that writes NBT primitives to a stream, - /// while taking care of endianness and string encoding, and counting bytes written. - public sealed unsafe class NBTWriter - { - // Write at most 4 MiB at a time. - public const int MaxWriteChunk = 4 * 1024 * 1024; - - // Encoding can be shared among all instances of NbtBinaryWriter, because it is stateless. - static readonly UTF8Encoding Encoding = new UTF8Encoding(false, true); - - // Each instance has to have its own encoder, because it does maintain state. - readonly Encoder encoder = Encoding.GetEncoder(); - - public Stream BaseStream - { - get - { - stream.Flush(); - return stream; - } - } - - readonly Stream stream; - - // Buffer used for temporary conversion - const int BufferSize = 256; - - // UTF8 characters use at most 4 bytes each. - const int MaxBufferedStringLength = BufferSize / 4; - - // Each NbtBinaryWriter needs to have its own instance of the buffer. - readonly byte[] buffer = new byte[BufferSize]; - - // Swap is only needed if endianness of the runtime differs from desired NBT stream - readonly bool swapNeeded; - - - public NBTWriter( Stream input, bool bigEndian) - { - if (input == null) throw new ArgumentNullException("input"); - if (!input.CanWrite) throw new ArgumentException("Given stream must be writable", "input"); - stream = input; - swapNeeded = (BitConverter.IsLittleEndian == bigEndian); - } - - - public void Write(byte value) - { - stream.WriteByte(value); - } - - - public void Write(TagType value) - { - stream.WriteByte((byte)value); - } - - - public void Write(short value) - { - unchecked - { - if (swapNeeded) - { - buffer[0] = (byte)(value >> 8); - buffer[1] = (byte)value; - } - else - { - buffer[0] = (byte)value; - buffer[1] = (byte)(value >> 8); - } - } - stream.Write(buffer, 0, 2); - } - - - public void Write(int value) - { - unchecked - { - if (swapNeeded) - { - buffer[0] = (byte)(value >> 24); - buffer[1] = (byte)(value >> 16); - buffer[2] = (byte)(value >> 8); - buffer[3] = (byte)value; - } - else - { - buffer[0] = (byte)value; - buffer[1] = (byte)(value >> 8); - buffer[2] = (byte)(value >> 16); - buffer[3] = (byte)(value >> 24); - } - } - stream.Write(buffer, 0, 4); - } - - - public void Write(long value) - { - unchecked - { - if (swapNeeded) - { - buffer[0] = (byte)(value >> 56); - buffer[1] = (byte)(value >> 48); - buffer[2] = (byte)(value >> 40); - buffer[3] = (byte)(value >> 32); - buffer[4] = (byte)(value >> 24); - buffer[5] = (byte)(value >> 16); - buffer[6] = (byte)(value >> 8); - buffer[7] = (byte)value; - } - else - { - buffer[0] = (byte)value; - buffer[1] = (byte)(value >> 8); - buffer[2] = (byte)(value >> 16); - buffer[3] = (byte)(value >> 24); - buffer[4] = (byte)(value >> 32); - buffer[5] = (byte)(value >> 40); - buffer[6] = (byte)(value >> 48); - buffer[7] = (byte)(value >> 56); - } - } - stream.Write(buffer, 0, 8); - } - - - public void Write(float value) - { - ulong tmpValue = *(uint*)&value; - unchecked - { - if (swapNeeded) - { - buffer[0] = (byte)(tmpValue >> 24); - buffer[1] = (byte)(tmpValue >> 16); - buffer[2] = (byte)(tmpValue >> 8); - buffer[3] = (byte)tmpValue; - } - else - { - buffer[0] = (byte)tmpValue; - buffer[1] = (byte)(tmpValue >> 8); - buffer[2] = (byte)(tmpValue >> 16); - buffer[3] = (byte)(tmpValue >> 24); - } - } - stream.Write(buffer, 0, 4); - } - - - public void Write(double value) - { - ulong tmpValue = *(ulong*)&value; - unchecked - { - if (swapNeeded) - { - buffer[0] = (byte)(tmpValue >> 56); - buffer[1] = (byte)(tmpValue >> 48); - buffer[2] = (byte)(tmpValue >> 40); - buffer[3] = (byte)(tmpValue >> 32); - buffer[4] = (byte)(tmpValue >> 24); - buffer[5] = (byte)(tmpValue >> 16); - buffer[6] = (byte)(tmpValue >> 8); - buffer[7] = (byte)tmpValue; - } - else - { - buffer[0] = (byte)tmpValue; - buffer[1] = (byte)(tmpValue >> 8); - buffer[2] = (byte)(tmpValue >> 16); - buffer[3] = (byte)(tmpValue >> 24); - buffer[4] = (byte)(tmpValue >> 32); - buffer[5] = (byte)(tmpValue >> 40); - buffer[6] = (byte)(tmpValue >> 48); - buffer[7] = (byte)(tmpValue >> 56); - } - } - stream.Write(buffer, 0, 8); - } - - - // Based on BinaryWriter.Write(String) - public void Write(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - // Write out string length (as number of bytes) - int numBytes = Encoding.GetByteCount(value); - Write((short)numBytes); - - if (numBytes <= BufferSize) - { - // If the string fits entirely in the buffer, encode and write it as one - Encoding.GetBytes(value, 0, value.Length, buffer, 0); - stream.Write(buffer, 0, numBytes); - } - else - { - // Aggressively try to not allocate memory in this loop for runtime performance reasons. - // Use an Encoder to write out the string correctly (handling surrogates crossing buffer - // boundaries properly). - int charStart = 0; - int numLeft = value.Length; - while (numLeft > 0) - { - // Figure out how many chars to process this round. - int charCount = (numLeft > MaxBufferedStringLength) ? MaxBufferedStringLength : numLeft; - int byteLen; - fixed (char* pChars = value) - { - fixed (byte* pBytes = buffer) - { - byteLen = encoder.GetBytes(pChars + charStart, charCount, pBytes, BufferSize, - charCount == numLeft); - } - } - stream.Write(buffer, 0, byteLen); - charStart += charCount; - numLeft -= charCount; - } - } - } - - - public void Write(byte[] data, int offset, int count) - { - int written = 0; - while (written < count) - { - int toWrite = Math.Min(MaxWriteChunk, count - written); - stream.Write(data, offset + written, toWrite); - written += toWrite; - } - } - - // Aria : Added a NBT compatible shortcut method for writing a byte array with the length prefix - public void Write(byte[] bytes) - { - this.Write(bytes.Length); - this.Write(bytes, 0, bytes.Length); - } - } -} diff --git a/Serialization/Serializable.cs b/Serialization/Serializable.cs deleted file mode 100644 index b25f565..0000000 --- a/Serialization/Serializable.cs +++ /dev/null @@ -1,15 +0,0 @@ -using LibAC.Serialization.ACFile; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization -{ - public abstract class Serializable - { - public abstract void save(Folder f); - public abstract void load(Folder f); - } -} diff --git a/Serialization/SerializingHelper.cs b/Serialization/SerializingHelper.cs deleted file mode 100644 index 6fe7b75..0000000 --- a/Serialization/SerializingHelper.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization -{ - public static class SerializingHelper - { - private static int SEGMENT_BITS = 0x7F; - private static int CONTINUE_BIT = 0x80; - /// - /// Adapted from code from wiki.vg - /// - /// - /// - public static void WriteVarInt(this BinaryWriter bw, int value) - { - while (true) - { - if ((value & ~SEGMENT_BITS) == 0) - { - bw.Write((byte)value); - return; - } - - byte b = (byte)((value & SEGMENT_BITS) | CONTINUE_BIT); - bw.Write(b); - - value >>>= 7; - } - } - - public static int ReadVarInt(this BinaryReader br) - { - int value = 0; - int position = 0; - byte curByte; - - while (true) - { - curByte = br.ReadByte(); - value |= (curByte & SEGMENT_BITS) << position; - if ((curByte & CONTINUE_BIT) == 0) break; - - position += 7; - - if (position >= 32) throw new VarIntSizeException("Too large"); - } - - return value; - } - - public static void WriteVarLong(this BinaryWriter bw, long value) - { - while (true) - { - if ((value & ~((long)SEGMENT_BITS)) == 0) - { - byte bx = (byte)value; - bw.Write(bx); - return; - } - byte b = (byte)((value & SEGMENT_BITS) | CONTINUE_BIT); - bw.Write(b); - value >>>= 7; - } - } - - public static long ReadVarLong(this BinaryReader br) - { - long value = 0; - int position = 0; - byte curByte; - while (true) - { - curByte = br.ReadByte(); - value |= (long)(curByte & SEGMENT_BITS) << position; - - if ((curByte & CONTINUE_BIT) == 0) break; - - position += 7; - - if (position >= 64) throw new VarLongSizeException("Too large"); - } - - return value; - } - - /// - /// Applies ZigZag Encoding to the integer - /// - /// - /// - public static int ZigZag(this int i) - { - return Math.Abs((i + i) + ((i < 0) ? 1 : 0)); - } - /// - /// Undoes the zigzag encoding on a integer - /// - /// - /// - public static int DeZigZag(this int i) - { - if ((i % 2) == 0) - { - // Even number. Divide by two - return (i / 2); - } - else - { - int x = i - 1; - x = -i; - return x; - } - } - - - /// - /// Applies ZigZag Encoding to the integer - /// - /// - /// - public static long ZigZag(this long i) - { - return Math.Abs((i + i) + ((i < 0L) ? 1L : 0L)); - } - /// - /// Undoes the zigzag encoding on a integer - /// - /// - /// - public static long DeZigZag(this long i) - { - if ((i % 2) == 0) - { - // Even number. Divide by two - return (i / 2L); - } - else - { - long x = i - 1L; - x = -i; - return x; - } - } - - - } - - public class VarIntSizeException : Exception - { - public VarIntSizeException(string Message) : base(Message) { } - } - public class VarLongSizeException : Exception - { - public VarLongSizeException(string Message) : base(Message) { } - } -} diff --git a/Serialization/TagIO.cs b/Serialization/TagIO.cs deleted file mode 100644 index 0041033..0000000 --- a/Serialization/TagIO.cs +++ /dev/null @@ -1,68 +0,0 @@ -using LibAC.Serialization.ACFile; -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization -{ - /// - /// This class contains helper functions for interacting with streams and files that are encoded using ZNIFile's structure - /// Additionally, this provides a overlay as it interacts with the final data set, and can compress and or encrypt. - /// - public static class TagIO - { - public static void WriteOnStream(Stream s, Tag x) - { - NBTWriter bw = new NBTWriter(s, true); - x.WriteTag(bw); - x.WriteData(bw); - } - - public static Folder ReadFromStream(Stream s) - { - try - { - - Folder folder = new Folder(); - NBTReader br = new NBTReader(s,true); - TagType type = (TagType)br.ReadByte(); - if (type == TagType.FOLDER) - { - // Read the file! - folder.ReadTag(br); - } - - return folder; - }catch(Exception e) - { - return new Folder(); - } - } - - public static void SaveToFile(string FileName, Tag x, bool gz=false) - { - if(File.Exists(FileName))File.Delete(FileName); - Stream fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); - if (gz){ - fs = new GZipStream(fs, CompressionLevel.SmallestSize); - } - WriteOnStream(fs, x); - fs.Close(); - } - public static Folder ReadFromFile(string FileName, bool gz = false) - { - Stream fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); - if (gz) - { - fs = new GZipStream(fs, CompressionMode.Decompress); - } - Folder f = ReadFromStream(fs); - fs.Close(); - return f; - } - } -} diff --git a/Serialization/ZNIFile/BoolTag.cs b/Serialization/ZNIFile/BoolTag.cs deleted file mode 100644 index 067b9b4..0000000 --- a/Serialization/ZNIFile/BoolTag.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class BoolTag : Tag - { - private bool BoolVal; - public bool Value - { - get - { - return BoolVal; - } - } - - public BoolTag(string _Name, bool val) - { - Name = _Name; - BoolVal = val; - } - public BoolTag(bool boolVal) : this(null, boolVal) - { - } - public BoolTag() : this(null, false) { } - - public override TagType Type - { - get - { - return TagType.BOOL; - } - } - public override bool ReadTag(NBTReader br) - { - throw new NotImplementedException(); - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new BoolTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - Folder vFolder = new Folder(Name); - vFolder.Add(new ByteTag("val", (byte)(Value ? 1 : 0))); - vFolder.Add(new ByteTag("_virtcast_", (byte)Type)); - if(Parent != null) - { - if(Parent.Type == TagType.LIST) - { - vFolder.Add(new StringTag("name", Name)); - } - } - vFolder.WriteTag(bw); - vFolder.WriteData(bw); - } - - public override void WriteTag(NBTWriter bw) - { - - } - - public override object Clone() - { - return new BoolTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - if (!F.HasNamedTag("name")) - Name = F.Name; - else Name = F["name"].StringValue; - - int ret = F["val"].IntValue; - if (ret == 1) BoolVal = true; - else BoolVal = false; - - - } - - } -} diff --git a/Serialization/ZNIFile/ByteArrayTag.cs b/Serialization/ZNIFile/ByteArrayTag.cs deleted file mode 100644 index 8ffe556..0000000 --- a/Serialization/ZNIFile/ByteArrayTag.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class ByteArrayTag : Tag - { - private byte[] BArrVal; - public byte[] Value - { - get - { - return BArrVal; - } - } - - public ByteArrayTag(string _Name, byte[] val) - { - Name = _Name; - BArrVal = val; - } - public ByteArrayTag(byte[] boolVal) : this(null, boolVal) - { - } - public ByteArrayTag() : this(null, new byte[] {}) { } - - public override TagType Type - { - get - { - return TagType.BYTEARRAY; - } - } - public override bool ReadTag(NBTReader br) - { - if(!(Parent!= null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - BArrVal = br.ReadBytes(br.ReadInt32()); - - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new ByteArrayTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - - } - - public override object Clone() - { - return new ByteArrayTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - - } -} diff --git a/Serialization/ZNIFile/ByteTag.cs b/Serialization/ZNIFile/ByteTag.cs deleted file mode 100644 index 06cb0bb..0000000 --- a/Serialization/ZNIFile/ByteTag.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class ByteTag : Tag - { - private byte ByteVal; - public byte Value - { - get - { - return ByteVal; - } - } - - public ByteTag(string _Name, byte val) - { - Name = _Name; - ByteVal = val; - } - public ByteTag(byte _ByteVal) : this(null, _ByteVal) - { - } - public ByteTag() : this(null, 0) { } - - public override TagType Type - { - get - { - return TagType.BYTE; - } - } - - public override bool ReadTag(NBTReader br) - { - - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - ByteVal = br.ReadByte(); - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new ByteTag().ReadTag(br); - } - - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - } - - public override object Clone() - { - return new ByteTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - - } -} diff --git a/Serialization/ZNIFile/DoubleTag.cs b/Serialization/ZNIFile/DoubleTag.cs deleted file mode 100644 index 1b30627..0000000 --- a/Serialization/ZNIFile/DoubleTag.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class DoubleTag : Tag - { - public override TagType Type - { - get - { - return TagType.DOUBLE; - } - } - - private double DoubleVal; - public double Value - { - get - { - return DoubleVal; - } - } - - public DoubleTag() : this(null, 0) { } - public DoubleTag(double Val) : this(null, Val) { } - public DoubleTag(string DName, double val) - { - Name = DName; - DoubleVal = val; - } - - public override object Clone() - { - return new DoubleTag(Name, Value); - } - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - DoubleVal = br.ReadDouble(); - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new DoubleTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/FloatTag.cs b/Serialization/ZNIFile/FloatTag.cs deleted file mode 100644 index 7557c5b..0000000 --- a/Serialization/ZNIFile/FloatTag.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class FloatTag : Tag - { - private float FloatVal; - public float Value - { - get - { - return FloatVal; - } - } - - public FloatTag(string _Name, float val) - { - Name = _Name; - FloatVal = val; - } - public FloatTag(float _FloatVal) : this(null, _FloatVal) - { - } - public FloatTag() : this(null, 0f) { } - - public override TagType Type - { - get - { - return TagType.FLOAT; - } - } - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - FloatVal = br.ReadSingle(); - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new FloatTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - } - - public override object Clone() - { - return new FloatTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/Folder.cs b/Serialization/ZNIFile/Folder.cs deleted file mode 100644 index 81cd982..0000000 --- a/Serialization/ZNIFile/Folder.cs +++ /dev/null @@ -1,293 +0,0 @@ -using Microsoft.VisualBasic; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; -using System.Reflection.Metadata.Ecma335; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class Folder : Tag, ICollection, ICollection - { - public Folder() { } - public Folder(string name) - { - this.Name = name; - } - public Collection Tags { get; set; } = new Collection(); - public override TagType Type - { - get - { - return TagType.FOLDER; - } - } - - public int Count - { - get - { - return Tags.Count; - } - } - - public bool IsReadOnly - { - get - { - return false; - } - } - - public object SyncRoot - { - get - { - return (Tags as ICollection).SyncRoot; - } - } - - bool ICollection.IsSynchronized - { - get - { - return false; - } - } - - public void Add(Tag item) - { - if (item == null) throw new Exception("Bad item!"); - - Tags.Add(item); - item.Parent = this; - } - - public override Tag this[int index] - { - get - { - return Tags.ElementAt(index); - } - set - { - try - { - - string TagName = Tags.ElementAt(index).Name; - this[TagName] = value; - } - catch(Exception e) - { - Tags.Add(value); - } - - } - } - public override Tag this[string index] - { - get - { - return Tags.Where(x => x.Name == index).FirstOrDefault(); - } - set - { - if(Tags.Select(x=>x.Name == index).Count() != 0) - { - Tags.RemoveAt(Tags.IndexOf(Tags.Where(x => x.Name == index).FirstOrDefault())); - } - - Tags.Add(value); - } - } - - public void Clear() - { - foreach(Tag t in Tags) - { - t.Parent = null; - } - Tags = new Collection(); - } - - public bool Contains(Tag item) - { - return Tags.Contains(item); - } - - public void CopyTo(Tag[] array, int arrayIndex) - { - Tags.CopyTo(array, arrayIndex); - } - - public void CopyTo(Array array, int index) - { - CopyTo((Tag[])array, index); - } - - public IEnumerator GetEnumerator() - { - return Tags.GetEnumerator(); - } - - public bool Remove(Tag item) - { - return Tags.Remove(item); - } - - - public override bool ReadTag(NBTReader br) - { - // Aria: Removed a return on parent not being null because that is how the ZNI Parsing system works. - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - - - while (true) - { - TagType next = br.ReadTagType(); - Tag _next = null; - switch (next) - { - case TagType.FOLDER: - _next = new Folder(); - break; - case TagType.BYTE: - _next = new ByteTag(); - break; - case TagType.DOUBLE: - _next = new DoubleTag(); - break; - case TagType.FLOAT: - _next = new FloatTag(); - break; - case TagType.INTEGER: - _next = new IntTag(); - break; - case TagType.LIST: - _next = new ListTag(); - break; - case TagType.LONG: - _next = new LongTag(); - break; - case TagType.STRING: - _next = new StringTag(); - break; - case TagType.BYTEARRAY: - _next = new ByteArrayTag(); - break; - case TagType.INTARRAY: - _next = new IntArrayTag(); - break; - case TagType.LONGARRAY: - _next = new LongArrayTag(); - break; - case TagType.END: - return true; - - case TagType.SHORT: - _next = new ShortTag(); - break; - } - _next.Parent = this; - - if (_next.ReadTag(br)) - { - if (_next.Type == TagType.FOLDER) - { - Folder NextTag = _next as Folder; - if (NextTag.HasNamedTag("_virtcast_")) - { - ByteTag bt = NextTag["_virtcast_"] as ByteTag; - next = (TagType)bt.Value; - Tag temp = null; - switch (next) - { - case TagType.BOOL: - temp = new BoolTag(); - temp.CastFrom(NextTag); - break; - case TagType.ULONG: - temp = new uLongTag(); - temp.CastFrom(NextTag); - break; - case TagType.UUID: - temp = new UUIDTag(); - temp.CastFrom(NextTag); - break; - } - _next = temp; - } - - } - Tags.Add(_next); - } - } - return true; - } - - public bool HasNamedTag(string Name) - { - foreach(Tag t in Tags) - { - if(t.Name == Name) return true; - } - return false; - } - - - public override void SkipTag(NBTReader br) - { - _ = new Folder().ReadTag(br); - } - - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - foreach (Tag t in Tags) - { - t.WriteTag(bw); - t.WriteData(bw); - } - bw.Write(TagType.END); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); // Write int (0), folder - - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public override void Rename(string old, string newName) - { - // do nothing. The folder's collection will be automatically updated. - } - - public override object Clone() - { - return new Folder(this); - } - - public Folder(Folder existing) - { - Name = existing.Name; - Tags = new Collection(Tags.ToArray()); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/IntArrayTag.cs b/Serialization/ZNIFile/IntArrayTag.cs deleted file mode 100644 index e856eb1..0000000 --- a/Serialization/ZNIFile/IntArrayTag.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class IntArrayTag : Tag - { - private int[] BArrVal; - public int[] Value - { - get - { - return BArrVal; - } - } - - public IntArrayTag(string _Name, int[] val) - { - Name = _Name; - BArrVal = val; - } - public IntArrayTag(int[] boolVal) : this(null, boolVal) - { - } - public IntArrayTag() : this(null, new int[] {}) { } - - public override TagType Type - { - get - { - return TagType.INTARRAY; - } - } - - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - - int count = br.ReadInt32(); - - BArrVal = new int[count]; - for (int i = 0; i < count; i++) - { - BArrVal[i] = br.ReadInt32(); - } - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new IntArrayTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value.Length); - - foreach (int i in Value) - { - bw.Write(i); - } - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - } - - public override object Clone() - { - return new IntArrayTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/IntTag.cs b/Serialization/ZNIFile/IntTag.cs deleted file mode 100644 index b4ee5a8..0000000 --- a/Serialization/ZNIFile/IntTag.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class IntTag : Tag - { - - public IntTag() : this(null, 0) - { } - public IntTag(int Value) : this(null, Value) - { - } - public IntTag(string _Name, int Val) - { - this.Name = _Name; - IntVal = Val; - } - private int IntVal; - public int Value - { - get - { - return IntVal; - } - } - public override TagType Type - { - get - { - return TagType.INTEGER; - } - } - - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - - IntVal = br.ReadInt32(); - return true; - } - - - public override void SkipTag(NBTReader br) - { - _ = new IntTag().ReadTag(br); - } - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value); - } - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override object Clone() - { - return new IntTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/ListTag.cs b/Serialization/ZNIFile/ListTag.cs deleted file mode 100644 index a872962..0000000 --- a/Serialization/ZNIFile/ListTag.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static System.Net.Mime.MediaTypeNames; - -namespace LibAC.Serialization.ACFile -{ - public class ListTag : Tag, IList, IList - { - private TagType _subtype = TagType.INVALID; - private List _tags; - public List Value - { - get - { - return _tags; - } - } - - public ListTag() : this(TagType.STRING, null, new List()) - { - } - public ListTag(TagType sub) : this(sub, null, new List()) { } - public ListTag(TagType sub, string name) : this(sub,name, new List()) { } - public ListTag(TagType sub, string name, List tags) - { - _tags = tags; - Name = name; - setSubtype(sub); - } - public void setSubtype(TagType itemTypes) - { - _subtype = itemTypes; - } - public TagType getListType() - { - return _subtype; - } - public override TagType Type - { - get - { - return TagType.LIST; - } - } - - public bool IsFixedSize - { - get - { - return false; - } - } - - public bool IsReadOnly - { - get - { - return false; - } - } - - public int Count - { - get - { - return _tags.Count; - } - } - - public bool IsSynchronized - { - get - { - return false; - } - } - - public object SyncRoot - { - get - { - return ((IList)_tags).SyncRoot; - } - } - - object IList.this[int index] - { - get - { - return _tags[index]; - } - set - { - if (_tags.Count >= index) _tags[index] = (Tag)value; - else _tags.Add((Tag)value); - } - } - - public override object Clone() - { - throw new NotImplementedException(); - } - - public override bool ReadTag(NBTReader br) - { - //_subtype = (TagType)br.ReadInt32(); - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - _subtype = br.ReadTagType(); - int count = br.ReadInt32(); - TagType next = _subtype; - for (int i = 0; i < count; i++) - { - Tag _next = null; - // read sub-tags - switch (next) - { - case TagType.FOLDER: - _next = new Folder(); - break; - case TagType.BOOL: - _next = new BoolTag(); - break; - case TagType.BYTE: - _next = new ByteTag(); - break; - case TagType.DOUBLE: - _next = new DoubleTag(); - break; - case TagType.FLOAT: - _next = new FloatTag(); - break; - case TagType.INTEGER: - _next = new IntTag(); - break; - case TagType.LIST: - _next = new ListTag(); - break; - case TagType.LONG: - _next = new LongTag(); - break; - case TagType.STRING: - _next = new StringTag(); - break; - case TagType.BYTEARRAY: - _next = new ByteArrayTag(); - break; - case TagType.INTARRAY: - _next = new IntArrayTag(); - break; - case TagType.LONGARRAY: - _next = new LongArrayTag(); - break; - case TagType.SHORT: - _next = new ShortTag(); - break; - } - _next.Parent = this; - if (_next.ReadTag(br)) - { - if (_next.Type == TagType.FOLDER) - { - Folder nxt = _next as Folder; - if (nxt.HasNamedTag("_virtcast_")) - { - TagType tag = (TagType)nxt["_virtcast_"].ByteValue; - Tag temp = null; - switch (tag) - { - case TagType.BOOL: - temp = new BoolTag(); - temp.CastFrom(nxt); - break; - case TagType.ULONG: - temp = new uLongTag(); - temp.CastFrom(nxt); - break; - case TagType.UUID: - temp = new UUIDTag(); - temp.CastFrom(nxt); - break; - - } - _next = temp; - } - } - _tags.Add(_next); - } - - - } - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new ListTag(_subtype).ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(_subtype); - bw.Write(_tags.Count); - - foreach (Tag x in _tags) - { - x.WriteData(bw); - } - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - - - //bw.Write(((int)TagType.END)); - } - - public int Add(object value) - { - if (value is Tag) - { - Tag tx = (Tag)value; - Add(tx); - return _tags.IndexOf(tx); - } - else return -1; - } - - public void Clear() - { - foreach(Tag x in _tags) - { - x.Parent = null; - } - _tags.Clear(); - } - - public bool Contains(object value) - { - throw new NotImplementedException(); - } - - public int IndexOf(object value) - { - if (!(value is Tag)) return -1; - return _tags.IndexOf((Tag)value); - } - - public void Insert(int index, object value) - { - throw new NotImplementedException(); - } - - public void Remove(object value) - { - throw new NotImplementedException(); - } - - public void RemoveAt(int index) - { - throw new NotImplementedException(); - } - - public void CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - public IEnumerator GetEnumerator() - { - return _tags.GetEnumerator(); - } - - public int IndexOf(Tag item) - { - return _tags.IndexOf(item); - } - - public void Insert(int index, Tag item) - { - throw new NotImplementedException(); - } - - public void Add(Tag item) - { - item.Parent = this; - _tags.Add(item); - } - - public bool Contains(Tag item) - { - return _tags.Contains(item); - } - - public void CopyTo(Tag[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - public bool Remove(Tag item) - { - if (Contains(item)) - { - item.Parent = null; - _tags.Remove(item); - return true; - } - else return false; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _tags.GetEnumerator(); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/LongArrayTag.cs b/Serialization/ZNIFile/LongArrayTag.cs deleted file mode 100644 index d0a580f..0000000 --- a/Serialization/ZNIFile/LongArrayTag.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class LongArrayTag : Tag - { - private long[] BArrVal; - public long[] Value - { - get - { - return BArrVal; - } - } - - public LongArrayTag(string _Name, long[] val) - { - Name = _Name; - BArrVal = val; - } - public LongArrayTag(long[] boolVal) : this(null, boolVal) - { - } - public LongArrayTag() : this(null, new long[] {}) { } - - public override TagType Type - { - get - { - return TagType.LONGARRAY; - } - } - - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - int count = br.ReadVarInt(); - BArrVal = new long[count]; - for(int i = 0; i < count; i++) - { - BArrVal[i] = br.ReadInt64(); - } - - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new LongArrayTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - bw.Write(Value.Length); - - foreach (long i in Value) - { - bw.Write(i); - } - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - } - - public override object Clone() - { - return new LongArrayTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/LongTag.cs b/Serialization/ZNIFile/LongTag.cs deleted file mode 100644 index bd89fa2..0000000 --- a/Serialization/ZNIFile/LongTag.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class LongTag : Tag - { - private long LongVal; - public long Value - { - get - { - return LongVal; - } - } - - public LongTag(string _Name, long val) - { - Name = _Name; - LongVal = val; - } - public LongTag(long _LongVal) : this(null, _LongVal) - { - } - public LongTag() : this(null, 0) { } - - public override TagType Type - { - get - { - return TagType.LONG; - } - } - - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - LongVal = br.ReadInt64(); - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new LongTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - - bw.Write(Value); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - } - - public override object Clone() - { - return new LongTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/ShortTag.cs b/Serialization/ZNIFile/ShortTag.cs deleted file mode 100644 index c6a36d3..0000000 --- a/Serialization/ZNIFile/ShortTag.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class ShortTag : Tag - { - private short FloatVal; - public short Value - { - get - { - return FloatVal; - } - } - - public ShortTag(string _Name, short val) - { - Name = _Name; - FloatVal = val; - } - public ShortTag(short _FloatVal) : this(null, _FloatVal) - { - } - public ShortTag() : this(null, 0) { } - - public override TagType Type - { - get - { - return TagType.SHORT; - } - } - - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - FloatVal = br.ReadInt16(); - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new ShortTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - - bw.Write(Value); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - } - - public override object Clone() - { - return new ShortTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/StringTag.cs b/Serialization/ZNIFile/StringTag.cs deleted file mode 100644 index 8133a55..0000000 --- a/Serialization/ZNIFile/StringTag.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class StringTag : Tag - { - private string StrVal; - public string Value - { - get - { - return StrVal; - } - } - - public override TagType Type - { - get - { - return TagType.STRING; - } - } - - public StringTag() : this(null, "") - { } - public StringTag(string Value) : this(null, Value) - { - } - public StringTag(string _Name, string Val) - { - this.Name = _Name; - StrVal = Val; - } - - public override bool ReadTag(NBTReader br) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - Name = br.ReadString(); - StrVal = br.ReadString(); - return true; - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new StringTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - if (!(Parent != null && Parent.Type == TagType.LIST)) - bw.Write(Name); - - bw.Write(StrVal); - } - - public override void WriteTag(NBTWriter bw) - { - bw.Write(Type); - } - - public override object Clone() - { - return new StringTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - throw new NotImplementedException(); - } - } -} diff --git a/Serialization/ZNIFile/Tag.cs b/Serialization/ZNIFile/Tag.cs deleted file mode 100644 index f379855..0000000 --- a/Serialization/ZNIFile/Tag.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public abstract class Tag : ICloneable, IComparable - { - public Tag Parent { get; internal set; } - public abstract TagType Type { get; } - - public bool HasValue - { - get - { - switch(Type) - { - case TagType.FOLDER: - case TagType.LIST: - case TagType.END: - case TagType.INVALID: - return false; - default: - return true; - - } - } - } - - internal string _Name=""; - public string Name - { - get - { - return _Name; - } - set - { - if (_Name == value) return; - if (value == null) - { - value = ""; - } - Folder f = Parent as Folder; - if(f != null) - { - f.Rename(_Name, value); - } - _Name = value; - } - } - - public abstract bool ReadTag(NBTReader br); - public abstract void SkipTag(NBTReader br); - public abstract void WriteTag(NBTWriter bw); - public abstract void WriteData(NBTWriter bw); - - public abstract void CastFrom(Folder F); - - private string Error = "Invalid tag type"; - public virtual Tag this[int index] - { - get - { - throw new InvalidOperationException(Error); - } - set - { - throw new InvalidOperationException(Error); - } - } - - public virtual Tag this[string index] - { - get - { - throw new InvalidOperationException(Error); - } - set - { - throw new InvalidOperationException(Error); - } - } - - public static string GetCanonicalName(TagType type) - { - switch(type) - { - case TagType.FOLDER: - { - return "Folder"; - } - case TagType.STRING: - { - return "String"; - } - case TagType.INTEGER: - { - return "Integer"; - } - case TagType.LIST: - { - return "List"; - } - case TagType.BOOL: - { - return "Bool"; - } - case TagType.DOUBLE: - { - return "Double"; - } - case TagType.FLOAT: - { - return "Float"; - } - case TagType.LONG: - { - return "Long"; - } - case TagType.BYTE: - { - return "Invalid"; - } - } - return "Invalid"; - } - - public string StringValue - { - get - { - switch(Type) - { - case TagType.STRING: - { - return (this as StringTag).Value; - } - default: - { - throw new Exception("Invalid type"); - } - } - } - } - - public int IntValue - { - get - { - switch (Type) - { - case TagType.INTEGER: - return (this as IntTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - - public bool BoolValue - { - get - { - switch (Type) - { - case TagType.BOOL: - return (this as BoolTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - public double DoubleValue - { - get - { - switch (Type) - { - case TagType.DOUBLE: - return (this as DoubleTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - public float FloatValue - { - get - { - switch (Type) - { - case TagType.FLOAT: - return (this as FloatTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - public long LongValue - { - get - { - switch (Type) - { - case TagType.LONG: - return (this as LongTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - public byte ByteValue - { - get - { - switch (Type) - { - case TagType.BYTE: - return (this as ByteTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - public ulong uLongValue - { - get - { - switch (Type) - { - case TagType.ULONG: - return (this as uLongTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - public Guid UUIDValue - { - get - { - switch (Type) - { - case TagType.UUID: - return (this as UUIDTag).Value; - default: - throw new Exception("Invalid type"); - } - } - } - - public abstract void Rename(string old, string newName); - - public abstract object Clone(); - - public int CompareTo(Tag other) - { - if (ID == other.ID) return 0; - else return 1; - } - - private Guid ID { get; set; } = Guid.NewGuid(); - } -} diff --git a/Serialization/ZNIFile/TagType.cs b/Serialization/ZNIFile/TagType.cs deleted file mode 100644 index 98f4e29..0000000 --- a/Serialization/ZNIFile/TagType.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - // Aria: Changed to a type of byte which keeps it to only one byte when writing out in serializing - public enum TagType : byte - { - END = 0x00, // Present at the end of a folder - BYTE = 0x01, - SHORT = 0x02, - INTEGER = 0x03, - LONG = 0x04, - FLOAT = 0x05, - DOUBLE = 0x06, - BYTEARRAY = 0x07, - STRING = 0x08, - LIST = 0x09, // List can be any valid Tag Type - FOLDER = 0x0A, - INTARRAY = 0x0B, - LONGARRAY = 0x0C, - BOOL = 0x0D, - ULONG=0x0E, - UUID=0x0F, - - - - INVALID=0xFF - } -} diff --git a/Serialization/ZNIFile/UUIDTag.cs b/Serialization/ZNIFile/UUIDTag.cs deleted file mode 100644 index b0ab1e2..0000000 --- a/Serialization/ZNIFile/UUIDTag.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class UUIDTag : Tag - { - private Guid LongVal; - public Guid Value - { - get - { - return LongVal; - } - } - public static UUIDTag Random(string sName) - { - UUIDTag rnd = new UUIDTag(sName, Guid.NewGuid()); - - return rnd; - } - - public static UUIDTag Empty(string sName) - { - UUIDTag z = new UUIDTag(sName, Guid.Empty); - return z; - } - - public UUIDTag(string _Name, Guid val) - { - Name = _Name; - LongVal = val; - } - public UUIDTag(Guid _LongVal) : this(null, _LongVal) - { - } - public UUIDTag() : this(null, Guid.Empty) { } - - public override TagType Type - { - get - { - return TagType.UUID; - } - } - - public override bool ReadTag(NBTReader br) - { - throw new Exception("Must be virtcasted!"); - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new UUIDTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - Folder vCast = new Folder(Name); - vCast.Add(new ByteTag("_virtcast_", (byte)Type)); - vCast.Add(new ByteArrayTag("val", Value.ToByteArray())); - if (Parent != null) - { - if (Parent.Type == TagType.LIST) - { - vCast.Add(new StringTag("name", Name)); - } - } - vCast.WriteTag(bw); - vCast.WriteData(bw); - } - - public override void WriteTag(NBTWriter bw) - { - } - - public override object Clone() - { - return new UUIDTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - if (!F.HasNamedTag("name")) - Name = F.Name; - else Name = F["name"].StringValue; - ByteArrayTag bat = F["val"] as ByteArrayTag; - LongVal = new Guid(bat.Value); - } - } -} diff --git a/Serialization/ZNIFile/ulongTag.cs b/Serialization/ZNIFile/ulongTag.cs deleted file mode 100644 index b6fa32a..0000000 --- a/Serialization/ZNIFile/ulongTag.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LibAC.Serialization.ACFile -{ - public class uLongTag : Tag - { - private ulong LongVal; - public ulong Value - { - get - { - return LongVal; - } - } - - public uLongTag(string _Name, ulong val) - { - Name = _Name; - LongVal = val; - } - public uLongTag(ulong _LongVal) : this(null, _LongVal) - { - } - public uLongTag() : this(null, 0) { } - - public override TagType Type - { - get - { - return TagType.ULONG; - } - } - - public override bool ReadTag(NBTReader br) - { - throw new Exception("Not allowed"); // This type must be virtual casted - } - - public override void Rename(string old, string newName) - { - throw new NotImplementedException(); - } - - public override void SkipTag(NBTReader br) - { - _ = new uLongTag().ReadTag(br); - } - - public override void WriteData(NBTWriter bw) - { - Folder vCast = new Folder(Name); - vCast.Add(new ByteTag("_virtcast_", (byte)Type)); - vCast.Add(new StringTag("val", Value.ToString())); - if (Parent != null) - { - if (Parent.Type == TagType.LIST) - { - vCast.Add(new StringTag("name", Name)); - } - } - vCast.WriteTag(bw); - vCast.WriteData(bw); - } - - public override void WriteTag(NBTWriter bw) - { - - } - - public override object Clone() - { - return new uLongTag(Name, Value); - } - - public override void CastFrom(Folder F) - { - if (!F.HasNamedTag("name")) - Name = F.Name; - else Name = F["name"].StringValue; - LongVal = ulong.Parse(F["val"].StringValue); - } - } -} diff --git a/Utilities/UUID.cs b/Utilities/UUID.cs new file mode 100644 index 0000000..c2f986b --- /dev/null +++ b/Utilities/UUID.cs @@ -0,0 +1,146 @@ +using System; +using System.Text; +using System.Security.Cryptography; +using System.Linq; + +namespace LibAC.Utilities +{ + public class UUID + { + private readonly byte[] _bytes; + + public static readonly UUID ZERO = UUID.Generate(0); + + public UUID(byte[] bytes) + { + _bytes = bytes; + } + + public static bool Validate(string uuid) + { + return uuid.Length == (16 * 2) + 4; // Basic length check + } + + public static UUID Parse(string uuid) + { + if (Validate(uuid)) + { + string[] segments = uuid.Split('-'); + if (segments.Length != 5) + { + throw new FormatException("Invalid UUID format"); + } + + string msbString = string.Join("", segments.Take(3)); + string lsbString = string.Join("", segments.Skip(3)); + + byte[] byteArray = new byte[16]; + int i = 0; + for (i = 0; i < msbString.Length; i += 2) + { + byteArray[i / 2] = Convert.ToByte(msbString.Substring(i, 2), 16); + } + + for (i = 0; i < lsbString.Length; i += 2) + { + byteArray[msbString.Length / 2 + i / 2] = Convert.ToByte(lsbString.Substring(i, 2), 16); + } + + return new UUID(byteArray); + } + else + { + return ZERO; + } + } + + public override string ToString() + { + StringBuilder hexBuilder = new StringBuilder(); + foreach (byte b in _bytes) + { + hexBuilder.Append(b.ToString("x2")); + } + + return $"{hexBuilder.ToString().Substring(0, 8)}-{hexBuilder.ToString().Substring(8, 4)}-{hexBuilder.ToString().Substring(12, 4)}-{hexBuilder.ToString().Substring(16, 4)}-{hexBuilder.ToString().Substring(20, 12)}"; + } + + public static UUID Generate(int version, params object[] parameters) + { + if (version != 4 && version != 0 && version != 3 && version != 5) + { + return Generate(4); + } + + byte[] bytes0 = new byte[16]; + + + switch (version) + { + case 0: + return new UUID(bytes0); + case 3: + if (parameters.Length != 2) + { + throw new ArgumentException("UUID v3 requires two parameters: [namespace, name]"); + } + + string namespaceStr = parameters[0] as string; + string nameStr = parameters[1] as string; + + using (MD5 md5 = MD5.Create()) + { + byte[] namespaceBytes = Encoding.UTF8.GetBytes(namespaceStr); + byte[] nameBytes = Encoding.UTF8.GetBytes(nameStr); + byte[] combined = namespaceBytes.Concat(nameBytes).ToArray(); + + byte[] hash = md5.ComputeHash(combined); + + Array.Copy(hash, 0, bytes0, 0, 16); + bytes0[6] = (byte)(bytes0[6] & 0x0F | 0x30); // Set version to 3 + bytes0[8] = (byte)(bytes0[8] & 0x3F | 0x80); // Set variant to RFC4122 + return new UUID(bytes0); + } + case 4: + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + byte[] bytes4 = new byte[16]; + rng.GetBytes(bytes4); + bytes4[6] = (byte)(bytes4[6] & 0x0F | 0x40); // Set version to 4 + bytes4[8] = (byte)(bytes4[8] & 0x3F | 0x80); // Set variant to RFC4122 + return new UUID(bytes4); + } + case 5: + if (parameters.Length != 2) + { + throw new ArgumentException("UUID v5 requires two parameters: [namespace, name]"); + } + + string namespaceStr5 = parameters[0] as string; + string nameStr5 = parameters[1] as string; + + using (SHA1 sha1 = SHA1.Create()) + { + byte[] namespaceBytes5 = Encoding.UTF8.GetBytes(namespaceStr5); + byte[] nameBytes5 = Encoding.UTF8.GetBytes(nameStr5); + byte[] combined5 = namespaceBytes5.Concat(nameBytes5).ToArray(); + + byte[] hash = sha1.ComputeHash(combined5); + + byte[] bytes5 = new byte[16]; + Array.Copy(hash, 0, bytes5, 0, 16); + bytes5[6] = (byte)(bytes5[6] & 0x0F | 0x50); // Set version to 5 + bytes5[8] = (byte)(bytes5[8] & 0x3F | 0x80); // Set variant to RFC4122 + return new UUID(bytes5); + } + default: + throw new ArgumentException($"Unsupported UUID version: {version}"); + } + } + + public byte[] GetBytes() + { + return _bytes.Take(16).ToArray(); + } + } +} diff --git a/Utilities/Vector2d.cs b/Utilities/Vector2d.cs new file mode 100644 index 0000000..b628a5f --- /dev/null +++ b/Utilities/Vector2d.cs @@ -0,0 +1,90 @@ +using System; + +namespace LibAC.Utilities +{ + public class Vector2d + { + public static readonly Vector2d ZERO = new Vector2d(0, 0); + public double X { get; set; } + public double Z { get; set; } + + public Vector2d(double X = 0.0, double Z = 0.0) + { + this.X = X; + this.Z = Z; + } + + public override bool Equals(object other) + { + if (other is Vector2d) + { + Vector2d otherVector = (Vector2d)other; + return X == otherVector.X && Z == otherVector.Z; + } + return false; + } + + public static Vector2d operator +(Vector2d v1, Vector2d v2) + { + Vector2d result = v1.Clone(); + result.X += v2.X; + result.Z += v2.Z; + return result; + } + + public static Vector2d operator -(Vector2d v1, Vector2d v2) + { + Vector2d result = v1.Clone(); + result.X -= v2.X; + result.Z -= v2.Z; + return result; + } + + public static Vector2d operator *(Vector2d v1, Vector2d v2) + { + Vector2d result = v1.Clone(); + result.X *= v2.X; + result.Z *= v2.Z; + return result; + } + + public static Vector2d operator /(Vector2d v1, Vector2d v2) + { + Vector2d result = v1.Clone(); + result.X /= v2.X; + result.Z /= v2.Z; + return result; + } + + public static bool operator >(Vector2d v1, Vector2d v2) + { + return (v1.X > v2.X) || (v1.X > v2.Z); + } + + public static bool operator <(Vector2d v1, Vector2d v2) + { + return (v1.X < v2.X) || (v1.X < v2.Z); + } + + public Vector2d Clone() + { + return new Vector2d(X, Z); + } + + public override string ToString() + { + return $"<{X}, {Z}>"; + } + + public bool Inside(Vector2d min, Vector2d max) + { + return (min.X <= X && max.X >= X) && (min.Z <= Z && max.Z >= Z); + } + + // Override GetHashCode if you override Equals + public override int GetHashCode() + { + return HashCode.Combine(X, Z); + } + } +} diff --git a/Utilities/Vector2i.cs b/Utilities/Vector2i.cs new file mode 100644 index 0000000..a0f9679 --- /dev/null +++ b/Utilities/Vector2i.cs @@ -0,0 +1,90 @@ +using System; + +namespace LibAC.Utilities +{ + public class Vector2i + { + public static readonly Vector2i ZERO = new Vector2i(0, 0); + public int X { get; set; } + public int Z { get; set; } + + public Vector2i(int X = 0, int Z = 0) + { + this.X = X; + this.Z = Z; + } + + public override bool Equals(object other) + { + if (other is Vector2i) + { + Vector2i otherVector = (Vector2i)other; + return X == otherVector.X && Z == otherVector.Z; + } + return false; + } + + public static Vector2i operator +(Vector2i v1, Vector2i v2) + { + Vector2i result = v1.Clone(); + result.X += v2.X; + result.Z += v2.Z; + return result; + } + + public static Vector2i operator -(Vector2i v1, Vector2i v2) + { + Vector2i result = v1.Clone(); + result.X -= v2.X; + result.Z -= v2.Z; + return result; + } + + public static Vector2i operator *(Vector2i v1, Vector2i v2) + { + Vector2i result = v1.Clone(); + result.X *= v2.X; + result.Z *= v2.Z; + return result; + } + + public static Vector2i operator /(Vector2i v1, Vector2i v2) + { + Vector2i result = v1.Clone(); + result.X = (int)Math.Round((double)v1.X / v2.X); + result.Z = (int)Math.Round((double)v1.Z / v2.Z); + return result; + } + + public static bool operator >(Vector2i v1, Vector2i v2) + { + return (v1.X > v2.X) || (v1.X > v2.Z); + } + + public static bool operator <(Vector2i v1, Vector2i v2) + { + return (v1.X < v2.X) || (v1.X < v2.Z); + } + + public Vector2i Clone() + { + return new Vector2i(X, Z); + } + + public override string ToString() + { + return $"<{X}, {Z}>"; + } + + public bool Inside(Vector2i min, Vector2i max) + { + return (min.X <= X && max.X >= X) && (min.Z <= Z && max.Z >= Z); + } + + // Override GetHashCode if you override Equals + public override int GetHashCode() + { + return HashCode.Combine(X, Z); + } + } +} diff --git a/Utilities/Vector3d.cs b/Utilities/Vector3d.cs new file mode 100644 index 0000000..6016db3 --- /dev/null +++ b/Utilities/Vector3d.cs @@ -0,0 +1,96 @@ +using System; + +namespace LibAC.Utilities +{ + public class Vector3d + { + public static readonly Vector3d ZERO = new Vector3d(0, 0, 0); + public double X { get; set; } + public double Y { get; set; } + public double Z { get; set; } + + public Vector3d(double X = 0.0, double Y = 0.0, double Z = 0.0) + { + this.X = X; + this.Y = Y; + this.Z = Z; + } + + public override bool Equals(object other) + { + if (other is Vector3d) + { + Vector3d otherVector = (Vector3d)other; + return X == otherVector.X && Y == otherVector.Y && Z == otherVector.Z; + } + return false; + } + + public static Vector3d operator +(Vector3d v1, Vector3d v2) + { + Vector3d result = v1.Clone(); + result.X += v2.X; + result.Y += v2.Y; + result.Z += v2.Z; + return result; + } + + public static Vector3d operator -(Vector3d v1, Vector3d v2) + { + Vector3d result = v1.Clone(); + result.X -= v2.X; + result.Y -= v2.Y; + result.Z -= v2.Z; + return result; + } + + public static Vector3d operator *(Vector3d v1, Vector3d v2) + { + Vector3d result = v1.Clone(); + result.X *= v2.X; + result.Y *= v2.Y; + result.Z *= v2.Z; + return result; + } + + public static Vector3d operator /(Vector3d v1, Vector3d v2) + { + Vector3d result = v1.Clone(); + result.X /= v2.X; + result.Y /= v2.Y; + result.Z /= v2.Z; + return result; + } + + public static bool operator >(Vector3d v1, Vector3d v2) + { + return (v1.X > v2.X) || (v1.Y > v2.Y) || (v1.X > v2.Z); + } + + public static bool operator <(Vector3d v1, Vector3d v2) + { + return (v1.X < v2.X) || (v1.Y < v2.Y) || (v1.X < v2.Z); + } + + public Vector3d Clone() + { + return new Vector3d(X, Y, Z); + } + + public override string ToString() + { + return $"<{X}, {Y}, {Z}>"; + } + + public bool Inside(Vector3d min, Vector3d max) + { + return (min.X <= X && max.X >= X) && (min.Y <= Y && max.Y >= Y) && (min.Z <= Z && max.Z >= Z); + } + + // Override GetHashCode if you override Equals + public override int GetHashCode() + { + return HashCode.Combine(X, Y, Z); + } + } +} diff --git a/Utilities/Vector3i.cs b/Utilities/Vector3i.cs new file mode 100644 index 0000000..14cdfd5 --- /dev/null +++ b/Utilities/Vector3i.cs @@ -0,0 +1,96 @@ +using System; + +namespace LibAC.Utilities +{ + public class Vector3i + { + public static readonly Vector3i ZERO = new Vector3i(0, 0, 0); + public int X { get; set; } + public int Y { get; set; } + public int Z { get; set; } + + public Vector3i(int X = 0, int Y = 0, int Z = 0) + { + this.X = X; + this.Y = Y; + this.Z = Z; + } + + public override bool Equals(object other) + { + if (other is Vector3i) + { + Vector3i otherVector = (Vector3i)other; + return X == otherVector.X && Y == otherVector.Y && Z == otherVector.Z; + } + return false; + } + + public static Vector3i operator +(Vector3i v1, Vector3i v2) + { + Vector3i result = v1.Clone(); + result.X += v2.X; + result.Y += v2.Y; + result.Z += v2.Z; + return result; + } + + public static Vector3i operator -(Vector3i v1, Vector3i v2) + { + Vector3i result = v1.Clone(); + result.X -= v2.X; + result.Y -= v2.Y; + result.Z -= v2.Z; + return result; + } + + public static Vector3i operator *(Vector3i v1, Vector3i v2) + { + Vector3i result = v1.Clone(); + result.X *= v2.X; + result.Y *= v2.Y; + result.Z *= v2.Z; + return result; + } + + public static Vector3i operator /(Vector3i v1, Vector3i v2) + { + Vector3i result = v1.Clone(); + result.X = (int)Math.Round((double)result.X / v2.X); + result.Y = (int)Math.Round((double)result.Y / v2.Y); + result.Z = (int)Math.Round((double)result.Z / v2.Z); + return result; + } + + public static bool operator >(Vector3i v1, Vector3i v2) + { + return (v1.X > v2.X) || (v1.Y > v2.Y) || (v1.X > v2.Z); + } + + public static bool operator <(Vector3i v1, Vector3i v2) + { + return (v1.X < v2.X) || (v1.Y < v2.Y) || (v1.X < v2.Z); + } + + public Vector3i Clone() + { + return new Vector3i(X, Y, Z); + } + + public override string ToString() + { + return $"<{X}, {Y}, {Z}>"; + } + + public bool Inside(Vector3i min, Vector3i max) + { + return (min.X <= X && max.X >= X) && (min.Y <= Y && max.Y >= Y) && (min.Z <= Z && max.Z >= Z); + } + + // Override GetHashCode if you override Equals + public override int GetHashCode() + { + return HashCode.Combine(X, Y, Z); + } + } +} diff --git a/Versioning.cs b/Versioning.cs index 4d8de02..2debf2b 100644 --- a/Versioning.cs +++ b/Versioning.cs @@ -1,11 +1,11 @@ -using LibAC.Serialization; -using LibAC.Serialization.ACFile; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime; using System.Text; using System.Threading.Tasks; +using LibAC.NBT; +using LibAC.NBT.API; namespace LibAC { @@ -16,7 +16,7 @@ namespace LibAC { } } - public class Version : Serializable + public class Version { public List ver { get; set; } = new List(); @@ -104,26 +104,28 @@ namespace LibAC return $"{ver[0]}.{ver[1]}.{ver[2]}.{ver[3]}.{CYCLE}.{ver[5]}"; } - public override void load(Folder f) + public void load(CompoundTag f) { ListTag lt = f["Version"] as ListTag; ver = new List(); - foreach(Tag tag in lt) + foreach(Tag tag in lt.GetList) { IntTag it = tag as IntTag; - ver.Add(it.IntValue); + ver.Add(it.Value); } } - public override void save(Folder f) + public CompoundTag save() { - ListTag lt = new ListTag(TagType.INTEGER, "Version"); + CompoundTag ct = new CompoundTag(); + ListTag lt = new ListTag(); foreach(int v in ver) { - IntTag i = new IntTag(v); + IntTag i = IntTag.ValueOf(v); lt.Add(i); } - f.Add(lt); + ct.Put("Version", lt); + return ct; } } }