// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; namespace ClassicalSharp.Map { public struct NbtTag { public string Name; public object Value; public NbtTagType TagId; } public class NbtList { public NbtTagType ChildTagId; public object[] ChildrenValues; } public enum NbtTagType : byte { End, Int8, Int16, Int32, Int64, Real32, Real64, Int8Array, String, List, Compound, Int32Array, Invalid = 255, } public sealed class NbtFile { readonly BinaryReader reader; readonly BinaryWriter writer; public NbtFile( BinaryReader reader ) { this.reader = reader; } public NbtFile( BinaryWriter writer ) { this.writer = writer; } public void Write( NbtTagType v ) { writer.Write( (byte)v ); } public void WriteInt64( long v ) { writer.Write( IPAddress.HostToNetworkOrder( v ) ); } public void WriteInt32( int v ) { writer.Write( IPAddress.HostToNetworkOrder( v ) ); } public void WriteInt16( short v ) { writer.Write( IPAddress.HostToNetworkOrder( v ) ); } public void WriteInt8( sbyte v ) { writer.Write( (byte)v ); } public void WriteUInt8( int v ) { writer.Write( (byte)v ); } public void WriteUInt8( byte v ) { writer.Write( v ); } public void WriteBytes( byte[] v ) { writer.Write( v ); } public void WriteBytes( byte[] v, int index, int count ) { writer.Write( v, index, count ); } public void Write( string value ) { ushort len = (ushort)value.Length; byte[] data = Encoding.UTF8.GetBytes( value ); WriteInt16( (short)len ); writer.Write( data ); } public void WriteCpeExtCompound( string name, int version ) { Write( NbtTagType.Compound ); Write( name ); Write( NbtTagType.Int32 ); Write( "ExtensionVersion" ); WriteInt32( version ); } public long ReadInt64() { return IPAddress.HostToNetworkOrder( reader.ReadInt64() ); } public int ReadInt32() { return IPAddress.HostToNetworkOrder( reader.ReadInt32() ); } public short ReadInt16() { return IPAddress.HostToNetworkOrder( reader.ReadInt16() ); } public string ReadString() { int len = (ushort)ReadInt16(); byte[] data = reader.ReadBytes( len ); return Encoding.UTF8.GetString( data ); } public unsafe NbtTag ReadTag( byte typeId, bool readTagName ) { NbtTag tag = default( NbtTag ); if( typeId == 0 ) { tag.TagId = NbtTagType.Invalid; return tag; } tag.Name = readTagName ? ReadString() : null; tag.TagId = (NbtTagType)typeId; switch( (NbtTagType)typeId ) { case NbtTagType.Int8: tag.Value = reader.ReadByte(); break; case NbtTagType.Int16: tag.Value = ReadInt16(); break; case NbtTagType.Int32: tag.Value = ReadInt32(); break; case NbtTagType.Int64: tag.Value = ReadInt64(); break; case NbtTagType.Real32: int temp32 = ReadInt32(); tag.Value = *((float*)&temp32); break; case NbtTagType.Real64: long temp64 = ReadInt64(); tag.Value = *((double*)&temp64); break; case NbtTagType.Int8Array: tag.Value = reader.ReadBytes( ReadInt32() ); break; case NbtTagType.String: tag.Value = ReadString(); break; case NbtTagType.List: NbtList list = new NbtList(); list.ChildTagId = (NbtTagType)reader.ReadByte(); list.ChildrenValues = new object[ReadInt32()]; for( int i = 0; i < list.ChildrenValues.Length; i++ ) list.ChildrenValues[i] = ReadTag( (byte)list.ChildTagId, false ).Value; tag.Value = list; break; case NbtTagType.Compound: Dictionary children = new Dictionary(); NbtTag child; while( (child = ReadTag( reader.ReadByte(), true )).TagId != NbtTagType.Invalid ) children[child.Name] = child; tag.Value = children; break; case NbtTagType.Int32Array: int[] array = new int[ReadInt32()]; for( int i = 0; i < array.Length; i++ ) array[i] = ReadInt32(); tag.Value = array; break; default: throw new InvalidDataException( "Unrecognised tag id: " + typeId ); } return tag; } } }