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 }