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 }