class_name JSONPacker enum Type { NUM = 0x00, LIT = 0x40, ARR = 0x80, BOL = 0xA0, OBJ = 0xC0, STR = 0xE0, MULTI = 0x10, INT = 0x00, FLOAT = 0x04, L8 = 0x00, L16 = 0x01, L32 = 0x02, L64 = 0x03, NULL = 0x00, UNDEFINED = 0x01, NAN = 0x02 } static func encode(value) -> PackedByteArray: if typeof(value) == TYPE_NIL: #print("packing NIL") return PackedByteArray([Type.LIT | Type.NULL]) elif typeof(value) == TYPE_BOOL: #print("packing bool") return PackedByteArray([Type.BOL | (1 if value else 0)]) elif typeof(value) == TYPE_INT or typeof(value) == TYPE_FLOAT: #print("packing int") if typeof(value) == TYPE_FLOAT and is_nan(value): #print("packing float") return PackedByteArray([Type.LIT | Type.NAN]) var encoded = _encode_number(value) if encoded.type == 0: return encoded.data return _concat([PackedByteArray([Type.NUM | encoded.type]), encoded.data]) elif typeof(value) == TYPE_STRING: #print("packing string") var utf8 = value.to_utf8_buffer() var length = utf8.size() if length > 0x0F: var encoded = _encode_number(length) return _concat([PackedByteArray([Type.STR | encoded.type]), encoded.data, utf8]) return _concat([PackedByteArray([Type.STR | length]), utf8]) elif typeof(value) == TYPE_ARRAY: #print("packing array") var length = value.size() var items = [] if length > 0x0F: var encoded = _encode_number(length) items.append(PackedByteArray([Type.ARR | encoded.type])) items.append(encoded.data) else: items.append(PackedByteArray([Type.ARR | length])) for v in value: items.append(encode(v)) return _concat(items) elif typeof(value) == TYPE_DICTIONARY: #print("packing object") var keys = value.keys() var items = [] if keys.size() > 0x0F: var encoded = _encode_number(keys.size()) items.append(PackedByteArray([Type.OBJ | encoded.type])) items.append(encoded.bfr) else: items.append(PackedByteArray([Type.OBJ | keys.size()])) for key in keys: items.append(encode(str(key))) items.append(encode(value[key])) return _concat(items) else: push_error("Unsupported type: " + str(typeof(value))) return PackedByteArray() static func _encode_number(value): var isInt = typeof(value) == TYPE_INT #print("compiling value") #print(value) if isInt and value >= 0 and value <= 0x0F: #print("int between 0x0 & 0x0F") return { "type": 0, "data": PackedByteArray([value]) } elif isInt and value >= -0x7F and value <= 0x7F: #print("int between -07F & 0x7F") return { "type": Type.MULTI | Type.INT | Type.L8, "data": PackedByteArray([value]) } elif isInt and value >= -0x7FFF and value <= 0x7FFF: #print("int between -07FFF & 0x7FFF") var bfr = PackedByteArray() bfr.resize(2) bfr.encode_s16(0, value) return { "type": Type.MULTI | Type.INT | Type.L16, "data": bfr } elif isInt: #print("int for other cases") var bfr = PackedByteArray() bfr.resize(4) bfr.encode_s32(0, value) return { "type": Type.MULTI | Type.INT | Type.L32, "data": bfr } elif typeof(value) == TYPE_FLOAT: #print("float") var bfr = PackedByteArray() bfr.resize(8) bfr.encode_double(0, value) return { "type": Type.MULTI | Type.FLOAT | Type.L64, "data": bfr } push_error("Unsupported number type") return { "type": 0, "data": PackedByteArray() } static func _concat(args: Array) -> PackedByteArray: print("concat", args) var result = PackedByteArray() for arr in args: print("res", arr) result.append_array(arr) return result static func decode(bfr: PackedByteArray) -> Variant: return decode_with_end(bfr)["value"] static func decode_with_end(bfr: PackedByteArray) -> Dictionary: var firstByte = bfr[0] var type = firstByte & 0xE0 var isMulti = firstByte & 0x10 if type == Type.LIT: var subtype = firstByte & 0x0F match subtype: Type.NULL: return {"value": null, "end": 1} Type.UNDEFINED: return {"value": null, "end": 1} Type.NAN: return {"value": float('nan'), "end": 1} if (firstByte & 0xC0) == 0 && !isMulti: return {"value": firstByte, "end": 1} elif (firstByte & 0xC0) == 0 && isMulti: return _decode_number(firstByte, bfr) match type: Type.STR: if !isMulti: var len = firstByte & 0x0F return { "value": bfr.slice(1, len + 1).get_string_from_utf8(), "end": len + 1} else: var tmp = _decode_number(firstByte, bfr) var len = tmp.value var lenlen = tmp.end return { "value": bfr.slice(lenlen, lenlen + len).get_string_from_utf8(), "end": lenlen + len} Type.BOL: return {"value": bool(firstByte), "end": 1} Type.ARR: if !isMulti: var len = firstByte & 0x0F var offset = 1 var list = [] for i in len: var tmp = decode_with_end(bfr.slice(offset)) var value = tmp.value var newOffset = tmp.end list.append(value) offset += newOffset return {"value": list, "end": offset} else: var tmp1 = _decode_number(firstByte, bfr) var len = tmp1.value var lenlen = tmp1.end var offset = lenlen var list = [] for i in len: var tmp = decode_with_end(bfr.slice(offset)) var value = tmp.value var newOffset = tmp.end list.append(value) offset += newOffset return {"value": list, "end": offset} Type.OBJ: var dict := {} if !isMulti: var len = firstByte & 0x0F var offset = 1 for i in len: var tmpA = decode_with_end(bfr.slice(offset)) var key = tmpA.value var tmpOffset = tmpA.end var tmpB = decode_with_end(bfr.slice(offset + tmpOffset)) var value = tmpB.value var newOffset = tmpB.end dict[key] = value offset += tmpOffset + newOffset return {"value": dict, "end": offset} else: var tmp = _decode_number(firstByte, bfr) var len = tmp.value var lenlen = tmp.end var offset = lenlen for i in len: var tmpA = decode_with_end(bfr.slice(offset)) var key = tmpA.value var tmpOffset = tmpA.end var tmpB = decode_with_end(bfr.slice(offset + tmpOffset)) var value = tmpB.value var newOffset = tmpB.end dict[key] = value offset += tmpOffset + newOffset return {"value": dict, "end": offset} _: push_error("Unknown type: " + str(type)) return {"value": null, "end": 0} return {"value": null, "end": 0} static func _decode_float(bfr: PackedByteArray, offset: int = 0, bigEndian = false) -> float: if bigEndian: return bfr.decode_float(offset) return PackedByteArray([bfr[offset+3], bfr[offset+2], bfr[offset+1], bfr[offset+0]]).decode_float(0) static func _decode_number(type: int, bfr: PackedByteArray) -> Dictionary: var subtype := type & 0x0C var len := type & 0x03 var bytes := 1 var value match subtype: Type.FLOAT: if len == Type.L32: #print(bfr.slice(1, 5)) value = _decode_float(bfr, 1) #print(value) bytes = 4 elif len == Type.L64: value = bfr.decode_double(1) bytes = 8 Type.INT: if len == Type.L8: value = bfr.decode_s8(1) elif len == Type.L16: value = bfr.decode_s16(1) bytes = 2 elif len == Type.L32: value = bfr.decode_s32(1) bytes = 4 #if value < 0: #print("possible underflow") #print(subtype) #print(len) #print(value) return {"value": value, "end": bytes + 1}