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 NBTCommon(NBTType id, T, PrefixLength = int) 11 { 12 private: 13 string _name; 14 T _value; 15 public: 16 this(T value = T.init, string name = "") 17 { 18 _name = name; 19 _value = value; 20 } 21 22 @property NBTType type() { return id; } 23 @property int size() { static if(isArray!T) { return typeof(_value[0]).sizeof * _value.length + PrefixLength.sizeof; } else { return T.sizeof; } } 24 25 @property string name() { return _name; } 26 @property void name(string name) { assert(name.length < short.max, "Name is too long! (%s)".format(name.length)); _name = name; } 27 28 @property T value() { return _value; } 29 @property void value(T value) { _value = value; } 30 31 ubyte[] encode(bool compressed = true, bool writeName = true) 32 { 33 ubyte[] data; 34 if(writeName) data = new ubyte[3 + name.length + size]; 35 else data = new ubyte[size]; 36 37 if(writeName) 38 { 39 data[0] = cast(ubyte)type; 40 41 data.write!short(cast(short)name.length, 1); 42 data[3 .. 3 + name.length] = cast(ubyte[])name; 43 static if(isArray!T) 44 { 45 data.write!PrefixLength(cast(PrefixLength)value.length, 3 + name.length); 46 for(int i = 0; i < value.length; i++) 47 data.write!(typeof(_value[0]))(_value[i], 3 + PrefixLength.sizeof + name.length + i * typeof(_value[0]).sizeof); 48 } 49 else 50 { 51 data.write!T(_value, 3 + name.length); 52 } 53 } 54 else 55 { 56 static if(isArray!T) 57 { 58 data.write!PrefixLength(cast(PrefixLength)value.length, 0); 59 for(int i = 0; i < value.length; i++) 60 data.write!(typeof(_value[0]))(_value[i], PrefixLength.sizeof + i * typeof(_value[0]).sizeof); 61 } 62 else 63 { 64 data.write!T(_value, 0); 65 } 66 } 67 68 if(compressed) 69 { 70 return compressGZip(data); 71 } 72 return data; 73 } 74 75 void decode(ubyte[] data, bool compressed = true, bool hasName = true) 76 { 77 if(compressed) 78 { 79 data = uncompressGZip(data); 80 } 81 82 read(data, hasName); 83 } 84 85 void read(ref ubyte[] stream, bool hasName = true) 86 { 87 _name = ""; 88 if(hasName) 89 { 90 assert(stream.read!ubyte == cast(ubyte)this.type); 91 short nameLength = stream.read!short; 92 _name = cast(string)stream[0 .. nameLength]; 93 stream = stream[nameLength .. $]; 94 } 95 96 static if(isArray!T) 97 { 98 alias ElemType = typeof(_value[0]); 99 PrefixLength arrLength = stream.read!PrefixLength; 100 T arr; 101 for(int i = 0; i < arrLength; i++) 102 arr ~= stream.read!ElemType; 103 _value = arr; 104 } 105 else 106 { 107 _value = stream.read!T; 108 } 109 } 110 111 override string toString() 112 { 113 return format("%s('%s') = %s", type, name, value); 114 } 115 116 static if(isArray!T) 117 { 118 typeof(_value[0]) opIndex(size_t index) 119 { 120 return _value[index]; 121 } 122 123 T opIndex() 124 { 125 return _value[]; 126 } 127 128 T opSlice(size_t start, size_t end) 129 { 130 return _value[start .. end]; 131 } 132 133 size_t opDollar() 134 { 135 return _value.length; 136 } 137 138 @property size_t length() 139 { 140 return _value.length; 141 } 142 } 143 144 @property INBTItem dup() 145 { 146 auto copy = new typeof(this)(); 147 copy.name = name; 148 copy.value = value; 149 return copy; 150 } 151 } 152 153 class NBTByte : INBTItem 154 { 155 mixin NBTCommon!(NBTType.Byte, byte); 156 } 157 158 class NBTShort : INBTItem 159 { 160 mixin NBTCommon!(NBTType.Short, short); 161 } 162 163 class NBTInt : INBTItem 164 { 165 mixin NBTCommon!(NBTType.Int, int); 166 } 167 168 class NBTLong : INBTItem 169 { 170 mixin NBTCommon!(NBTType.Long, long); 171 } 172 173 class NBTFloat : INBTItem 174 { 175 mixin NBTCommon!(NBTType.Float, float); 176 } 177 178 class NBTDouble : INBTItem 179 { 180 mixin NBTCommon!(NBTType.Double, double); 181 } 182 183 class NBTByteArray : INBTItem 184 { 185 mixin NBTCommon!(NBTType.ByteArray, byte[]); 186 } 187 188 class NBTString : INBTItem 189 { 190 mixin NBTCommon!(NBTType.String, string, short); 191 } 192 193 class NBTIntArray : INBTItem 194 { 195 mixin NBTCommon!(NBTType.IntArray, int[]); 196 }