1 module nbtd.NBTCommon;
2 
3 import nbtd;
4 
5 import std.bitmanip;
6 import std.zlib;
7 import std.traits;
8 import std.format;
9 
10 mixin template NBTCommon(NBTType id, T, PrefixLength = int)
11 {
12 private:
13 	string _name;
14 	T _value;
15 public:
16 	this(T value = T.init, string name = "")
17 	{
18 		_name = name;
19 		_value = value;
20 	}
21 
22 	@property NBTType type() { return id; }
23 	@property int size() { static if(isArray!T) { return typeof(_value[0]).sizeof * _value.length + PrefixLength.sizeof; } else { return T.sizeof; } }
24 
25 	@property string name() { return _name; }
26 	@property void name(string name) { assert(name.length < short.max, "Name is too long! (%s)".format(name.length)); _name = name; }
27 
28 	@property T value() { return _value; }
29 	@property void value(T value) { _value = value; }
30 
31 	ubyte[] encode(bool compressed = true, bool writeName = true)
32 	{
33 		ubyte[] data;
34 		if(writeName) data = new ubyte[3 + name.length + size];
35 		else data = new ubyte[size];
36 
37 		if(writeName)
38 		{
39 			data[0] = cast(ubyte)type;
40 
41 			data.write!short(cast(short)name.length, 1);
42 			data[3 .. 3 + name.length] = cast(ubyte[])name;
43 			static if(isArray!T)
44 			{
45 				data.write!PrefixLength(cast(PrefixLength)value.length, 3 + name.length);
46 				for(int i = 0; i < value.length; i++)
47 					data.write!(typeof(_value[0]))(_value[i], 3 + PrefixLength.sizeof + name.length + i * typeof(_value[0]).sizeof);
48 			}
49 			else
50 			{
51 				data.write!T(_value, 3 + name.length);
52 			}
53 		}
54 		else
55 		{
56 			static if(isArray!T)
57 			{
58 				data.write!PrefixLength(cast(PrefixLength)value.length, 0);
59 				for(int i = 0; i < value.length; i++)
60 					data.write!(typeof(_value[0]))(_value[i], PrefixLength.sizeof + i * typeof(_value[0]).sizeof);
61 			}
62 			else
63 			{
64 				data.write!T(_value, 0);
65 			}
66 		}
67 
68 		if(compressed)
69 		{
70 			return compressGZip(data);
71 		}
72 		return data;
73 	}
74 
75 	void decode(ubyte[] data, bool compressed = true, bool hasName = true)
76 	{
77 		if(compressed)
78 		{
79 			data = uncompressGZip(data);
80 		}
81 
82 		read(data, hasName);
83 	}
84 
85 	void read(ref ubyte[] stream, bool hasName = true)
86 	{
87 		_name = "";
88 		if(hasName)
89 		{
90 			assert(stream.read!ubyte == cast(ubyte)this.type);
91 			short nameLength = stream.read!short;
92 			_name = cast(string)stream[0 .. nameLength];
93 			stream = stream[nameLength .. $];
94 		}
95 
96 		static if(isArray!T)
97 		{
98 			alias ElemType = typeof(_value[0]);
99 			PrefixLength arrLength = stream.read!PrefixLength;
100 			T arr;
101 			for(int i = 0; i < arrLength; i++)
102 				arr ~= stream.read!ElemType;
103 			_value = arr;
104 		}
105 		else
106 		{
107 			_value = stream.read!T;
108 		}
109 	}
110 
111 	override string toString()
112 	{
113 		return format("%s('%s') = %s", type, name, value);
114 	}
115 
116 	static if(isArray!T)
117 	{
118 		typeof(_value[0]) opIndex(size_t index)
119 		{
120 			return _value[index];
121 		}
122 
123 		T opIndex()
124 		{
125 			return _value[];
126 		}
127 
128 		T opSlice(size_t start, size_t end)
129 		{
130 			return _value[start .. end];
131 		}
132 
133 		size_t opDollar()
134 		{
135 			return _value.length;
136 		}
137 
138 		@property size_t length()
139 		{
140 			return _value.length;
141 		}
142 	}
143 
144 	@property INBTItem dup()
145 	{
146 		auto copy = new typeof(this)();
147 		copy.name = name;
148 		copy.value = value;
149 		return copy;
150 	}
151 }
152 
153 class NBTByte : INBTItem
154 {
155 	mixin NBTCommon!(NBTType.Byte, byte);
156 }
157 
158 class NBTShort : INBTItem
159 {
160 	mixin NBTCommon!(NBTType.Short, short);
161 }
162 
163 class NBTInt : INBTItem
164 {
165 	mixin NBTCommon!(NBTType.Int, int);
166 }
167 
168 class NBTLong : INBTItem
169 {
170 	mixin NBTCommon!(NBTType.Long, long);
171 }
172 
173 class NBTFloat : INBTItem
174 {
175 	mixin NBTCommon!(NBTType.Float, float);
176 }
177 
178 class NBTDouble : INBTItem
179 {
180 	mixin NBTCommon!(NBTType.Double, double);
181 }
182 
183 class NBTByteArray : INBTItem
184 {
185 	mixin NBTCommon!(NBTType.ByteArray, byte[]);
186 }
187 
188 class NBTString : INBTItem
189 {
190 	mixin NBTCommon!(NBTType.String, string, short);
191 }
192 
193 class NBTIntArray : INBTItem
194 {
195 	mixin NBTCommon!(NBTType.IntArray, int[]);
196 }