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