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