1 module nbtd.NBTCompound;
2 
3 import nbtd;
4 
5 import std.zlib;
6 import std.bitmanip;
7 import std.format;
8 
9 class NBTCompound : INBTItem
10 {
11 private:
12 	string _name;
13 	INBTItem[] _value;
14 public:
15 	this(INBTItem[] items = [], string name = "")
16 	{
17 		_name = name;
18 		_value = items;
19 	}
20 
21 	@property NBTType type() { return NBTType.Compound; }
22 	@property int size()
23 	{
24 		int size = 1; // incl. EndTag
25 		foreach(INBTItem item; _value)
26 			size += item.size + 3 + item.name.length;
27 		return size;
28 	}
29 
30 	@property string name() { return _name; }
31 	@property void name(string name) { assert(name.length < short.max, "Name is too long! (%s)".format(name.length)); _name = name; }
32 
33 	@property INBTItem[] value() { return _value; }
34 	@property void value(INBTItem[] value) { _value = value; }
35 
36 	T get(T : INBTItem = INBTItem)(string name, bool throwOnError = true)
37 	{
38 		foreach(INBTItem item; _value)
39 			if(item.name == name)
40 			{
41 				if(cast(T)item)
42 					return cast(T)item;
43 				if(throwOnError)
44 					throw new Exception("Can't cast item to " ~ T.stringof);
45 				return null;
46 			}
47 		if(throwOnError)
48 			throw new Exception("Item " ~ name ~ " not found in Compound!");
49 		return null;
50 	}
51 
52 	ubyte[] encode(bool compressed = true, bool hasName = true)
53 	{
54 		ubyte[] data;
55 		if(hasName)
56 			data = new ubyte[size + 3 + _name.length];
57 		else
58 			data = new ubyte[size];
59 
60 		if(hasName)
61 		{
62 			data[0] = cast(ubyte)type;
63 			data.write(cast(short)_name.length, 1);
64 			data[3 .. 3 + _name.length] = cast(ubyte[])_name;
65 			int index = 3 + name.length;
66 			for(int i = 0; i < _value.length; i++)
67 			{
68 				ubyte[] buffer = _value[i].encode(false);
69 				data[index .. index + buffer.length] = buffer;
70 				index += buffer.length;
71 			}
72 			data[index] = cast(ubyte)0;
73 			assert(index == data.length - 1);
74 		}
75 		else
76 		{
77 			int index = 0;
78 			for(int i = 0; i < _value.length; i++)
79 			{
80 				ubyte[] buffer = _value[i].encode(false);
81 				data[index .. index + buffer.length] = buffer;
82 				index += buffer.length;
83 			}
84 			data[index] = cast(ubyte)0;
85 			assert(index == data.length - 1);
86 		}
87 
88 		if(compressed)
89 		{
90 			return compressGZip(data);
91 		}
92 		return data;
93 	}
94 
95 	void decode(ubyte[] data, bool compressed = true, bool hasName = true)
96 	{
97 		if(compressed)
98 		{
99 			data = uncompressGZip(data);
100 		}
101 
102 		read(data, hasName);
103 	}
104 
105 	void read(ref ubyte[] stream, bool hasName = true)
106 	{
107 		_name = "";
108 		if(hasName)
109 		{
110 			assert(stream.read!ubyte == cast(ubyte)type);
111 			short nameLength = stream.read!short;
112 			_name = cast(string)stream[0 .. nameLength];
113 			stream = stream[nameLength .. $];
114 		}
115 		INBTItem item;
116 		_value.length = 0;
117 		while((item = parseElement(stream)).type != NBTType.End)
118 			_value ~= item;
119 	}
120 
121 	@property INBTItem dup()
122 	{
123 		auto copy = new NBTCompound();
124 		copy.name = name;
125 		copy.value = value;
126 		return copy;
127 	}
128 
129 	override string toString()
130 	{
131 		return format("NBTCompound('%s') = {%s}", name, value);
132 	}
133 
134 	INBTItem opIndex(string index)
135 	{
136 		return get(index);
137 	}
138 
139 	INBTItem opIndexAssign(INBTItem item, string index)
140 	{
141 		int found = -1;
142 		item = item.dup;
143 		item.name = index;
144 		foreach(int i, INBTItem val; _value)
145 			if(val.name == index)
146 				found = i;
147 		if(found == -1)
148 		{
149 			_value ~= item;
150 		}
151 		else
152 		{
153 			_value[found] = item;
154 		}
155 		return item;
156 	}
157 }