Make NBT Serialization binary compatible with NBT GUI editors

This commit is contained in:
Tara 2023-01-03 15:46:38 -07:00
parent 724f9aaa8c
commit 60ced631ba
23 changed files with 1101 additions and 279 deletions

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Configurations>Debug;Release;DebPub;KVPBuild</Configurations> <Configurations>Debug;Release;DebPub;KVPBuild</Configurations>

236
Serialization/NBTReader.cs Normal file
View file

@ -0,0 +1,236 @@
// 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 LibZNI.Serialization.ZNIFile;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
namespace LibZNI.Serialization
{
/// <summary> BinaryReader wrapper that takes care of reading primitives from an NBT stream,
/// while taking care of endianness, string encoding, and skipping. </summary>
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
}
}

263
Serialization/NBTWriter.cs Normal file
View file

@ -0,0 +1,263 @@
// 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 LibZNI.Serialization.ZNIFile;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
namespace LibZNI.Serialization
{
/// <summary> BinaryWriter wrapper that writes NBT primitives to a stream,
/// while taking care of endianness and string encoding, and counting bytes written. </summary>
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);
}
}
}

View file

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibZNI.Serialization
{
public static class SerializingHelper
{
private static int SEGMENT_BITS = 0x7F;
private static int CONTINUE_BIT = 0x80;
/// <summary>
/// Adapted from code from wiki.vg
/// </summary>
/// <param name="bw"></param>
/// <param name="value"></param>
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;
}
/// <summary>
/// Applies ZigZag Encoding to the integer
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public static int ZigZag(this int i)
{
return Math.Abs((i + i) + ((i < 0) ? 1 : 0));
}
/// <summary>
/// Undoes the zigzag encoding on a integer
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Applies ZigZag Encoding to the integer
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public static long ZigZag(this long i)
{
return Math.Abs((i + i) + ((i < 0L) ? 1L : 0L));
}
/// <summary>
/// Undoes the zigzag encoding on a integer
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
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) { }
}
}

View file

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -16,8 +17,9 @@ namespace LibZNI.Serialization
{ {
public static void WriteOnStream(Stream s, Tag x) public static void WriteOnStream(Stream s, Tag x)
{ {
BinaryWriter bw = new BinaryWriter(s); NBTWriter bw = new NBTWriter(s, true);
x.WriteTag(bw); x.WriteTag(bw);
x.WriteData(bw);
} }
public static Folder ReadFromStream(Stream s) public static Folder ReadFromStream(Stream s)
@ -26,8 +28,8 @@ namespace LibZNI.Serialization
{ {
Folder folder = new Folder(); Folder folder = new Folder();
BinaryReader br = new BinaryReader(s); NBTReader br = new NBTReader(s,true);
TagType type = (TagType)br.ReadInt32(); TagType type = (TagType)br.ReadByte();
if (type == TagType.FOLDER) if (type == TagType.FOLDER)
{ {
// Read the file! // Read the file!
@ -41,15 +43,23 @@ namespace LibZNI.Serialization
} }
} }
public static void SaveToFile(string FileName, Tag x) public static void SaveToFile(string FileName, Tag x, bool gz=false)
{ {
FileStream fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); 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); WriteOnStream(fs, x);
fs.Close(); fs.Close();
} }
public static Folder ReadFromFile(string FileName) public static Folder ReadFromFile(string FileName, bool gz = false)
{ {
FileStream fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); Stream fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
if (gz)
{
fs = new GZipStream(fs, CompressionMode.Decompress);
}
Folder f = ReadFromStream(fs); Folder f = ReadFromStream(fs);
fs.Close(); fs.Close();
return f; return f;

View file

@ -35,12 +35,9 @@ namespace LibZNI.Serialization.ZNIFile
return TagType.BOOL; return TagType.BOOL;
} }
} }
public override bool ReadTag(NBTReader br)
public override bool ReadTag(BinaryReader br)
{ {
Name = br.ReadString(); throw new NotImplementedException();
BoolVal = br.ReadBoolean();
return true;
} }
public override void Rename(string old, string newName) public override void Rename(string old, string newName)
@ -48,27 +45,49 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new BoolTag().ReadTag(br); _ = new BoolTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); 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(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value);
} }
public override object Clone() public override object Clone()
{ {
return new BoolTag(Name, Value); 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;
}
} }
} }

View file

@ -35,12 +35,12 @@ namespace LibZNI.Serialization.ZNIFile
return TagType.BYTEARRAY; return TagType.BYTEARRAY;
} }
} }
public override bool ReadTag(NBTReader br)
public override bool ReadTag(BinaryReader br)
{ {
Name = br.ReadString(); if(!(Parent!= null && Parent.Type == TagType.LIST))
int count = br.ReadInt32(); Name = br.ReadString();
BArrVal = br.ReadBytes(count); BArrVal = br.ReadBytes(br.ReadInt32());
return true; return true;
} }
@ -49,28 +49,35 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new ByteArrayTag().ReadTag(br); _ = new ByteArrayTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException();
if (!(Parent != null && Parent.Type == TagType.LIST))
bw.Write(Name);
bw.Write(Value);
} }
public override void WriteTag(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type)); bw.Write(Type);
bw.Write(Name);
bw.Write(Value.Length);
bw.Write(Value);
} }
public override object Clone() public override object Clone()
{ {
return new ByteArrayTag(Name, Value); return new ByteArrayTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -36,9 +36,11 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString();
if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
ByteVal = br.ReadByte(); ByteVal = br.ReadByte();
return true; return true;
} }
@ -48,27 +50,34 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new ByteTag().ReadTag(br); _ = new ByteTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw)
public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
bw.Write(Name);
bw.Write(Value);
} }
public override void WriteTag(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type)); bw.Write(Type);
bw.Write(Name);
bw.Write(Value);
} }
public override object Clone() public override object Clone()
{ {
return new ByteTag(Name, Value); return new ByteTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -38,10 +38,10 @@ namespace LibZNI.Serialization.ZNIFile
{ {
return new DoubleTag(Name, Value); return new DoubleTag(Name, Value);
} }
public override bool ReadTag(NBTReader br)
public override bool ReadTag(BinaryReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
DoubleVal = br.ReadDouble(); DoubleVal = br.ReadDouble();
return true; return true;
} }
@ -51,20 +51,27 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new DoubleTag().ReadTag(br); _ = new DoubleTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) 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(); throw new NotImplementedException();
} }
public override void WriteTag(BinaryWriter bw)
{
bw.Write(Name);
bw.Write(Value);
}
} }
} }

View file

@ -35,10 +35,10 @@ namespace LibZNI.Serialization.ZNIFile
return TagType.FLOAT; return TagType.FLOAT;
} }
} }
public override bool ReadTag(NBTReader br)
public override bool ReadTag(BinaryReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
FloatVal = br.ReadSingle(); FloatVal = br.ReadSingle();
return true; return true;
} }
@ -48,27 +48,32 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new FloatTag().ReadTag(br); _ = new FloatTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
bw.Write(Name);
bw.Write(Value);
} }
public override void WriteTag(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type)); bw.Write(Type);
bw.Write(Name);
bw.Write(Value);
} }
public override object Clone() public override object Clone()
{ {
return new FloatTag(Name, Value); return new FloatTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -140,27 +140,22 @@ namespace LibZNI.Serialization.ZNIFile
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
if(Parent != null) // 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))
SkipTag(br); Name = br.ReadString();
return false;
}
Name = br.ReadString(); // Per ZNIFile standards, each tag reads its own name!
while(true)
while (true)
{ {
TagType next = (TagType)br.ReadInt32(); TagType next = br.ReadTagType();
Tag _next = null; Tag _next = null;
switch (next) switch (next)
{ {
case TagType.FOLDER: case TagType.FOLDER:
_next = new Folder(); _next = new Folder();
break; break;
case TagType.BOOL:
_next = new BoolTag();
break;
case TagType.BYTE: case TagType.BYTE:
_next = new ByteTag(); _next = new ByteTag();
break; break;
@ -193,37 +188,80 @@ namespace LibZNI.Serialization.ZNIFile
break; break;
case TagType.END: case TagType.END:
return true; return true;
case TagType.SHORT:
_next = new ShortTag();
break;
} }
_next.Parent = this;
if (_next.ReadTag(br)) 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); Tags.Add(_next);
} }
_next.Parent = this;
} }
return true;
}
public bool HasNamedTag(string Name)
{
foreach(Tag t in Tags)
{
if(t.Name == Name) return true;
}
return false;
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new Folder().ReadTag(br); _ = new Folder().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw)
{
throw new NotImplementedException();
}
public override void WriteTag(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
bw.Write(((int)Type)); // Write int (0), folder if (!(Parent != null && Parent.Type == TagType.LIST))
bw.Write(Name); bw.Write(Name);
foreach (Tag t in Tags) foreach (Tag t in Tags)
{ {
t.WriteTag(bw); t.WriteTag(bw);
t.WriteData(bw);
} }
bw.Write(((int)TagType.END)); bw.Write(TagType.END);
}
public override void WriteTag(NBTWriter bw)
{
bw.Write(Type); // Write int (0), folder
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
@ -246,5 +284,10 @@ namespace LibZNI.Serialization.ZNIFile
Name = existing.Name; Name = existing.Name;
Tags = new Collection<Tag>(Tags.ToArray()); Tags = new Collection<Tag>(Tags.ToArray());
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibZNI.Serialization.ZNIFile
{
public class Header : Serializable
{
public const string SIGNATURE = "ZNIFile";
public Version VERSION = new Version(1,0,0,1,0,0);
public override void load(Folder f)
{
Folder x = f["Header"] as Folder;
if (x == null) return;
if (x["Signature"].StringValue == SIGNATURE)
{
Version ver = new Version();
ver.load(f);
if (VERSION.Compare(ver) == 0)
{
return;
} else throw new VersionNumberDifferentException(VERSION, ver);
}else
{
throw new Exception("Header failed validation");
}
}
public override void save(Folder f)
{
Folder x = new Folder("Header");
x.Add(new StringTag("Signature", SIGNATURE));
VERSION.save(x);
f.Add(x);
}
public static Folder GetHeader()
{
Folder f = new Folder("temp"); // Initialize a temporary header
Header x = new Header();
x.save(f);
return f["Header"] as Folder;
}
}
}

View file

@ -36,16 +36,18 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
int count = br.ReadInt32(); int count = br.ReadInt32();
BArrVal = new int[count]; BArrVal = new int[count];
for(int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
BArrVal[i] = br.ReadInt32(); BArrVal[i] = br.ReadInt32();
} }
return true; return true;
} }
@ -54,31 +56,38 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new IntArrayTag().ReadTag(br); _ = new IntArrayTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException();
}
public override void WriteTag(BinaryWriter bw) if (!(Parent != null && Parent.Type == TagType.LIST))
{ bw.Write(Name);
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value.Length); bw.Write(Value.Length);
foreach(int i in Value) foreach (int i in Value)
{ {
bw.Write(i); bw.Write(i);
} }
} }
public override void WriteTag(NBTWriter bw)
{
bw.Write(Type);
}
public override object Clone() public override object Clone()
{ {
return new IntArrayTag(Name, Value); return new IntArrayTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -36,28 +36,31 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
IntVal = br.ReadInt32(); IntVal = br.ReadInt32();
return true; return true;
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new IntTag().ReadTag(br); _ = new IntTag().ReadTag(br);
} }
public override void WriteTag(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type)); bw.Write(Type);
bw.Write(Name);
bw.Write(Value);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
bw.Write(Name);
bw.Write(Value);
} }
public override void Rename(string old, string newName) public override void Rename(string old, string newName)
{ {
@ -68,5 +71,10 @@ namespace LibZNI.Serialization.ZNIFile
{ {
return new IntTag(Name, Value); return new IntTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -5,12 +5,13 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace LibZNI.Serialization.ZNIFile namespace LibZNI.Serialization.ZNIFile
{ {
public class ListTag : Tag, IList<Tag>, IList public class ListTag : Tag, IList<Tag>, IList
{ {
private TagType _subtype; private TagType _subtype = TagType.INVALID;
private List<Tag> _tags; private List<Tag> _tags;
public List<Tag> Value public List<Tag> Value
{ {
@ -105,15 +106,19 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
_subtype = (TagType)br.ReadInt32(); //_subtype = (TagType)br.ReadInt32();
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
while (true) Name = br.ReadString();
_subtype = br.ReadTagType();
int count = br.ReadInt32();
TagType next = _subtype;
for (int i = 0; i < count; i++)
{ {
TagType next = (TagType)br.ReadInt32();
Tag _next = null; Tag _next = null;
switch(next) // read sub-tags
switch (next)
{ {
case TagType.FOLDER: case TagType.FOLDER:
_next = new Folder(); _next = new Folder();
@ -151,17 +156,45 @@ namespace LibZNI.Serialization.ZNIFile
case TagType.LONGARRAY: case TagType.LONGARRAY:
_next = new LongArrayTag(); _next = new LongArrayTag();
break; break;
case TagType.END: case TagType.SHORT:
return true; _next = new ShortTag();
} break;
if (_next.ReadTag(br))
{
_tags.Add(_next);
} }
_next.Parent = this; _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) public override void Rename(string old, string newName)
@ -169,28 +202,31 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new ListTag(_subtype).ReadTag(br); _ = new ListTag(_subtype).ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException();
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(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type)); bw.Write(Type);
bw.Write(((int)_subtype));
bw.Write(Name);
foreach(Tag x in _tags)
{
x.WriteTag(bw);
}
bw.Write(((int)TagType.END));
//bw.Write(((int)TagType.END));
} }
public int Add(object value) public int Add(object value)
@ -290,5 +326,10 @@ namespace LibZNI.Serialization.ZNIFile
{ {
return _tags.GetEnumerator(); return _tags.GetEnumerator();
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -36,10 +36,11 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
int count = br.ReadInt32(); Name = br.ReadString();
int count = br.ReadVarInt();
BArrVal = new long[count]; BArrVal = new long[count];
for(int i = 0; i < count; i++) for(int i = 0; i < count; i++)
{ {
@ -54,31 +55,36 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new LongArrayTag().ReadTag(br); _ = new LongArrayTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
} bw.Write(Name);
public override void WriteTag(BinaryWriter bw)
{
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value.Length); bw.Write(Value.Length);
foreach(int i in Value) foreach (long i in Value)
{ {
bw.Write(i); bw.Write(i);
} }
} }
public override void WriteTag(NBTWriter bw)
{
bw.Write(Type);
}
public override object Clone() public override object Clone()
{ {
return new LongArrayTag(Name, Value); return new LongArrayTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -36,9 +36,10 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
LongVal = br.ReadInt64(); LongVal = br.ReadInt64();
return true; return true;
} }
@ -48,27 +49,32 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new LongTag().ReadTag(br); _ = new LongTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
} bw.Write(Name);
public override void WriteTag(BinaryWriter bw)
{
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value); bw.Write(Value);
} }
public override void WriteTag(NBTWriter bw)
{
bw.Write(Type);
}
public override object Clone() public override object Clone()
{ {
return new LongTag(Name, Value); return new LongTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -36,9 +36,10 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
FloatVal = br.ReadInt16(); FloatVal = br.ReadInt16();
return true; return true;
} }
@ -48,27 +49,32 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new ShortTag().ReadTag(br); _ = new ShortTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
} bw.Write(Name);
public override void WriteTag(BinaryWriter bw)
{
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value); bw.Write(Value);
} }
public override void WriteTag(NBTWriter bw)
{
bw.Write(Type);
}
public override object Clone() public override object Clone()
{ {
return new ShortTag(Name, Value); return new ShortTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -37,9 +37,10 @@ namespace LibZNI.Serialization.ZNIFile
StrVal = Val; StrVal = Val;
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); if (!(Parent != null && Parent.Type == TagType.LIST))
Name = br.ReadString();
StrVal = br.ReadString(); StrVal = br.ReadString();
return true; return true;
} }
@ -49,27 +50,32 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = br.ReadString(); _ = new StringTag().ReadTag(br);
_ = br.ReadString();
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); if (!(Parent != null && Parent.Type == TagType.LIST))
bw.Write(Name);
bw.Write(StrVal);
} }
public override void WriteTag(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type)); bw.Write(Type);
bw.Write(Name);
bw.Write(Value);
} }
public override object Clone() public override object Clone()
{ {
return new StringTag(Name, Value); return new StringTag(Name, Value);
} }
public override void CastFrom(Folder F)
{
throw new NotImplementedException();
}
} }
} }

View file

@ -53,10 +53,12 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public abstract bool ReadTag(BinaryReader br); public abstract bool ReadTag(NBTReader br);
public abstract void SkipTag(BinaryReader br); public abstract void SkipTag(NBTReader br);
public abstract void WriteTag(BinaryWriter bw); public abstract void WriteTag(NBTWriter bw);
public abstract void WriteData(BinaryWriter bw); public abstract void WriteData(NBTWriter bw);
public abstract void CastFrom(Folder F);
private string Error = "Invalid tag type"; private string Error = "Invalid tag type";
public virtual Tag this[int index] public virtual Tag this[int index]

View file

@ -6,27 +6,28 @@ using System.Threading.Tasks;
namespace LibZNI.Serialization.ZNIFile namespace LibZNI.Serialization.ZNIFile
{ {
public enum TagType // Aria: Changed to a type of byte which keeps it to only one byte when writing out in serializing
public enum TagType : byte
{ {
END = 0, // Present at the end of a folder or list END = 0x00, // Present at the end of a folder
BYTE = 1, BYTE = 0x01,
SHORT = 2, SHORT = 0x02,
INTEGER = 3, INTEGER = 0x03,
LONG = 4, LONG = 0x04,
FLOAT = 5, FLOAT = 0x05,
DOUBLE = 6, DOUBLE = 0x06,
BYTEARRAY = 7, BYTEARRAY = 0x07,
STRING = 8, STRING = 0x08,
LIST = 9, // List can be any valid Tag Type LIST = 0x09, // List can be any valid Tag Type
FOLDER = 10, FOLDER = 0x0A,
INTARRAY = 11, INTARRAY = 0x0B,
LONGARRAY = 12, LONGARRAY = 0x0C,
BOOL = 13, BOOL = 0x0D,
ULONG=14, ULONG=0x0E,
UUID=15, UUID=0x0F,
INVALID=99 INVALID=0xFF
} }
} }

View file

@ -48,11 +48,9 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); throw new Exception("Must be virtcasted!");
LongVal = Guid.Parse(br.ReadString());
return true;
} }
public override void Rename(string old, string newName) public override void Rename(string old, string newName)
@ -60,27 +58,43 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new UUIDTag().ReadTag(br); _ = new UUIDTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); 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(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value.ToString());
} }
public override object Clone() public override object Clone()
{ {
return new UUIDTag(Name, Value); 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);
}
} }
} }

View file

@ -36,11 +36,9 @@ namespace LibZNI.Serialization.ZNIFile
} }
} }
public override bool ReadTag(BinaryReader br) public override bool ReadTag(NBTReader br)
{ {
Name = br.ReadString(); throw new Exception("Not allowed"); // This type must be virtual casted
LongVal = br.ReadUInt64();
return true;
} }
public override void Rename(string old, string newName) public override void Rename(string old, string newName)
@ -48,27 +46,43 @@ namespace LibZNI.Serialization.ZNIFile
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void SkipTag(BinaryReader br) public override void SkipTag(NBTReader br)
{ {
_ = new uLongTag().ReadTag(br); _ = new uLongTag().ReadTag(br);
} }
public override void WriteData(BinaryWriter bw) public override void WriteData(NBTWriter bw)
{ {
throw new NotImplementedException(); 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(BinaryWriter bw) public override void WriteTag(NBTWriter bw)
{ {
bw.Write(((int)Type));
bw.Write(Name);
bw.Write(Value);
} }
public override object Clone() public override object Clone()
{ {
return new uLongTag(Name, Value); 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);
}
} }
} }