Make NBT Serialization binary compatible with NBT GUI editors
This commit is contained in:
parent
724f9aaa8c
commit
60ced631ba
23 changed files with 1101 additions and 279 deletions
236
Serialization/NBTReader.cs
Normal file
236
Serialization/NBTReader.cs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue