265 lines
7.1 KiB
GDScript
265 lines
7.1 KiB
GDScript
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}
|