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 }