Files
godot-vscode-plugin/src/debugger/godot4/variables/variant_decoder.ts
David Kincaid 489db36e85 Add debugger support for Typed Dictionaries (#764)
Add decoding support for Typed Dictionaries
2025-01-03 21:40:31 -05:00

720 lines
18 KiB
TypeScript

import {
GDScriptTypes,
type BufferModel,
Vector3,
Vector2,
Basis,
AABB,
Color,
NodePath,
ObjectId,
Plane,
Quat,
Rect2,
Transform3D,
Transform2D,
RawObject,
Vector2i,
Vector3i,
Rect2i,
Vector4,
Vector4i,
StringName,
Projection,
ENCODE_FLAG_64,
ENCODE_FLAG_OBJECT_AS_ID,
ENCODE_FLAG_TYPED_ARRAY_MASK,
ENCODE_FLAG_TYPED_DICT_MASK,
ContainerTypeFlags,
RID,
Callable,
Signal,
} from "./variants";
export class VariantDecoder {
public decode_variant(model: BufferModel) {
const type = this.decode_UInt32(model);
switch (type & 0xff) {
case GDScriptTypes.BOOL:
return this.decode_UInt32(model) !== 0;
case GDScriptTypes.INT:
if (type & ENCODE_FLAG_64) {
return this.decode_Int64(model);
} else {
return this.decode_Int32(model);
}
case GDScriptTypes.FLOAT:
if (type & ENCODE_FLAG_64) {
return this.decode_Float64(model);
} else {
return this.decode_Float32(model);
}
case GDScriptTypes.STRING:
return this.decode_String(model);
case GDScriptTypes.VECTOR2:
if (type & ENCODE_FLAG_64) {
return this.decode_Vector2d(model);
} else {
return this.decode_Vector2f(model);
}
case GDScriptTypes.VECTOR2I:
return this.decode_Vector2i(model);
case GDScriptTypes.RECT2:
if (type & ENCODE_FLAG_64) {
return this.decode_Rect2d(model);
} else {
return this.decode_Rect2f(model);
}
case GDScriptTypes.RECT2I:
return this.decode_Rect2i(model);
case GDScriptTypes.VECTOR3:
if (type & ENCODE_FLAG_64) {
return this.decode_Vector3d(model);
} else {
return this.decode_Vector3f(model);
}
case GDScriptTypes.VECTOR3I:
return this.decode_Vector3i(model);
case GDScriptTypes.TRANSFORM2D:
if (type & ENCODE_FLAG_64) {
return this.decode_Transform2Dd(model);
} else {
return this.decode_Transform2Df(model);
}
case GDScriptTypes.PLANE:
if (type & ENCODE_FLAG_64) {
return this.decode_Planed(model);
} else {
return this.decode_Planef(model);
}
case GDScriptTypes.VECTOR4:
if (type & ENCODE_FLAG_64) {
return this.decode_Vector4d(model);
} else {
return this.decode_Vector4f(model);
}
case GDScriptTypes.VECTOR4I:
return this.decode_Vector4i(model);
case GDScriptTypes.QUATERNION:
if (type & ENCODE_FLAG_64) {
return this.decode_Quaterniond(model);
} else {
return this.decode_Quaternionf(model);
}
case GDScriptTypes.AABB:
if (type & ENCODE_FLAG_64) {
return this.decode_AABBd(model);
} else {
return this.decode_AABBf(model);
}
case GDScriptTypes.BASIS:
if (type & ENCODE_FLAG_64) {
return this.decode_Basisd(model);
} else {
return this.decode_Basisf(model);
}
case GDScriptTypes.TRANSFORM3D:
if (type & ENCODE_FLAG_64) {
return this.decode_Transform3Dd(model);
} else {
return this.decode_Transform3Df(model);
}
case GDScriptTypes.PROJECTION:
if (type & ENCODE_FLAG_64) {
return this.decode_Projectiond(model);
} else {
return this.decode_Projectionf(model);
}
case GDScriptTypes.COLOR:
return this.decode_Color(model);
case GDScriptTypes.STRING_NAME:
return this.decode_StringName(model);
case GDScriptTypes.NODE_PATH:
return this.decode_NodePath(model);
case GDScriptTypes.RID:
return this.decode_RID(model);
case GDScriptTypes.OBJECT:
if (type & ENCODE_FLAG_OBJECT_AS_ID) {
return this.decode_Object_id(model);
} else {
return this.decode_Object(model);
}
case GDScriptTypes.CALLABLE:
return this.decode_Callable(model);
case GDScriptTypes.SIGNAL:
return this.decode_Signal(model);
case GDScriptTypes.DICTIONARY:
return this.decode_Dictionary(model, type);
case GDScriptTypes.ARRAY:
return this.decode_Array(model, type);
case GDScriptTypes.PACKED_BYTE_ARRAY:
return this.decode_PackedByteArray(model);
case GDScriptTypes.PACKED_INT32_ARRAY:
return this.decode_PackedInt32Array(model);
case GDScriptTypes.PACKED_INT64_ARRAY:
return this.decode_PackedInt64Array(model);
case GDScriptTypes.PACKED_FLOAT32_ARRAY:
return this.decode_PackedFloat32Array(model);
case GDScriptTypes.PACKED_FLOAT64_ARRAY:
return this.decode_PackedFloat32Array(model);
case GDScriptTypes.PACKED_STRING_ARRAY:
return this.decode_PackedStringArray(model);
case GDScriptTypes.PACKED_VECTOR2_ARRAY:
if (type & ENCODE_FLAG_OBJECT_AS_ID) {
return this.decode_PackedVector2dArray(model);
} else {
return this.decode_PackedVector2fArray(model);
}
case GDScriptTypes.PACKED_VECTOR3_ARRAY:
if (type & ENCODE_FLAG_OBJECT_AS_ID) {
return this.decode_PackedVector3dArray(model);
} else {
return this.decode_PackedVector3fArray(model);
}
case GDScriptTypes.PACKED_VECTOR4_ARRAY:
if (type & ENCODE_FLAG_OBJECT_AS_ID) {
return this.decode_PackedVector4dArray(model);
} else {
return this.decode_PackedVector4fArray(model);
}
case GDScriptTypes.PACKED_COLOR_ARRAY:
return this.decode_PackedColorArray(model);
default:
return undefined;
}
}
public get_dataset(buffer: Buffer) {
const len = buffer.readUInt32LE(0);
if (buffer.length !== len + 4) {
return undefined;
}
const model: BufferModel = {
buffer: buffer,
offset: 4, // data starts after the initial length
len: len,
};
const output = [];
output.push(len + 4);
do {
const value = this.decode_variant(model);
if (value === undefined) {
throw new Error("Unable to decode variant.");
}
output.push(value);
} while (model.len > 0);
return output;
}
private decode_ContainerTypeFlag(model: BufferModel, type: GDScriptTypes, bitOffset: number) {
const shiftedType = (type >> bitOffset) & 0b11;
if (shiftedType === ContainerTypeFlags.BUILTIN) {
return this.decode_UInt32(model);
} else {
return this.decode_String(model);
}
}
private decode_AABBf(model: BufferModel) {
return new AABB(this.decode_Vector3f(model), this.decode_Vector3f(model));
}
private decode_AABBd(model: BufferModel) {
return new AABB(this.decode_Vector3d(model), this.decode_Vector3d(model));
}
private decode_Array(model: BufferModel, type: GDScriptTypes) {
const output = [];
let arrayType = null;
if (type & ENCODE_FLAG_TYPED_ARRAY_MASK) {
arrayType = this.decode_ContainerTypeFlag(model, type, 16);
}
// TODO: the type information is currently discarded
// it needs to be decoded and then packed into the output somehow
const count = this.decode_UInt32(model);
for (let i = 0; i < count; i++) {
const value = this.decode_variant(model);
output.push(value);
}
return output;
}
private decode_Basisf(model: BufferModel) {
return new Basis(
this.decode_Vector3f(model),
this.decode_Vector3f(model),
this.decode_Vector3f(model), //
);
}
private decode_Basisd(model: BufferModel) {
return new Basis(
this.decode_Vector3d(model),
this.decode_Vector3d(model),
this.decode_Vector3d(model), //
);
}
private decode_Color(model: BufferModel) {
const rgb = this.decode_Vector3f(model);
const a = this.decode_Float32(model);
return new Color(rgb.x, rgb.y, rgb.z, a);
}
private decode_Dictionary(model: BufferModel, type: GDScriptTypes) {
const output = new Map();
let keyType = null;
let valueType = null;
if (type & ENCODE_FLAG_TYPED_DICT_MASK) {
keyType = this.decode_ContainerTypeFlag(model, type, 16);
valueType = this.decode_ContainerTypeFlag(model, type, 18);
// console.log("type:", (type >> 16) & 0b11, "keyType:", keyType);
// console.log("type:", type >> 18, "valueType:", valueType);
}
// TODO: the type information is currently discarded
// it needs to be decoded and then packed into the output somehow
const count = this.decode_UInt32(model);
for (let i = 0; i < count; i++) {
const key = this.decode_variant(model);
const value = this.decode_variant(model);
output.set(key, value);
}
return output;
}
private decode_Float32(model: BufferModel) {
const f = model.buffer.readFloatLE(model.offset);
model.offset += 4;
model.len -= 4;
return f; // + (f < 0 ? -1e-10 : 1e-10);
}
private decode_Float64(model: BufferModel) {
const f = model.buffer.readDoubleLE(model.offset);
model.offset += 8;
model.len -= 8;
return f; // + (f < 0 ? -1e-10 : 1e-10);
}
private decode_Int32(model: BufferModel) {
const result = model.buffer.readInt32LE(model.offset);
model.len -= 4;
model.offset += 4;
return result;
}
private decode_UInt32(model: BufferModel) {
const result = model.buffer.readUInt32LE(model.offset);
model.len -= 4;
model.offset += 4;
return result;
}
private decode_Int64(model: BufferModel) {
const result = model.buffer.readBigInt64LE(model.offset);
model.len -= 8;
model.offset += 8;
return result;
}
private decode_UInt64(model: BufferModel) {
const result = model.buffer.readBigUInt64LE(model.offset);
model.len -= 8;
model.offset += 8;
return result;
}
private decode_NodePath(model: BufferModel) {
const name_count = this.decode_UInt32(model) & 0x7fffffff;
let subname_count = this.decode_UInt32(model);
const flags = this.decode_UInt32(model);
const is_absolute = (flags & 1) === 1;
if (flags & 2) {
//Obsolete format with property separate from subPath
subname_count++;
}
const total = name_count + subname_count;
const names: string[] = [];
const sub_names: string[] = [];
for (let i = 0; i < total; i++) {
const str = this.decode_String(model);
if (i < name_count) {
names.push(str);
} else {
sub_names.push(str);
}
}
return new NodePath(names, sub_names, is_absolute);
}
private decode_Object(model: BufferModel) {
const class_name = this.decode_String(model);
const prop_count = this.decode_UInt32(model);
const output = new RawObject(class_name);
for (let i = 0; i < prop_count; i++) {
const name = this.decode_String(model);
const value = this.decode_variant(model);
output.set(name, value);
}
return output;
}
private decode_Object_id(model: BufferModel) {
const id = this.decode_UInt64(model);
return new ObjectId(id);
}
private decode_RID(model: BufferModel) {
const id = this.decode_UInt64(model);
return new RID(id);
}
private decode_Callable(model: BufferModel) {
return new Callable();
}
private decode_Signal(model: BufferModel) {
return new Signal(this.decode_String(model), this.decode_Object_id(model));
}
private decode_Planef(model: BufferModel) {
const x = this.decode_Float32(model);
const y = this.decode_Float32(model);
const z = this.decode_Float32(model);
const d = this.decode_Float32(model);
return new Plane(x, y, z, d);
}
private decode_Planed(model: BufferModel) {
const x = this.decode_Float64(model);
const y = this.decode_Float64(model);
const z = this.decode_Float64(model);
const d = this.decode_Float64(model);
return new Plane(x, y, z, d);
}
private decode_PackedByteArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: number[] = [];
for (let i = 0; i < count; i++) {
output.push(model.buffer.readUInt8(model.offset));
model.offset++;
model.len--;
}
return output;
}
private decode_PackedColorArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Color[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Color(model));
}
return output;
}
private decode_PackedFloat32Array(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: number[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Float32(model));
}
return output;
}
private decode_PackedFloat64Array(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: number[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Float64(model));
}
return output;
}
private decode_PackedInt32Array(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: number[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Int32(model));
}
return output;
}
private decode_PackedInt64Array(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: bigint[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Int64(model));
}
return output;
}
private decode_PackedStringArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: string[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_String(model));
}
return output;
}
private decode_PackedVector2fArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Vector2[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Vector2f(model));
}
return output;
}
private decode_PackedVector3fArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Vector3[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Vector3f(model));
}
return output;
}
private decode_PackedVector4fArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Vector4[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Vector4f(model));
}
return output;
}
private decode_PackedVector2dArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Vector2[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Vector2d(model));
}
return output;
}
private decode_PackedVector3dArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Vector3[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Vector3d(model));
}
return output;
}
private decode_PackedVector4dArray(model: BufferModel) {
const count = this.decode_UInt32(model);
const output: Vector4[] = [];
for (let i = 0; i < count; i++) {
output.push(this.decode_Vector4d(model));
}
return output;
}
private decode_Quaternionf(model: BufferModel) {
const x = this.decode_Float32(model);
const y = this.decode_Float32(model);
const z = this.decode_Float32(model);
const w = this.decode_Float32(model);
return new Quat(x, y, z, w);
}
private decode_Quaterniond(model: BufferModel) {
const x = this.decode_Float64(model);
const y = this.decode_Float64(model);
const z = this.decode_Float64(model);
const w = this.decode_Float64(model);
return new Quat(x, y, z, w);
}
private decode_Rect2f(model: BufferModel) {
return new Rect2(this.decode_Vector2f(model), this.decode_Vector2f(model));
}
private decode_Rect2d(model: BufferModel) {
return new Rect2(this.decode_Vector2d(model), this.decode_Vector2d(model));
}
private decode_Rect2i(model: BufferModel) {
return new Rect2i(this.decode_Vector2f(model), this.decode_Vector2f(model));
}
private decode_String(model: BufferModel) {
let len = this.decode_UInt32(model);
let pad = 0;
if (len % 4 !== 0) {
pad = 4 - (len % 4);
}
const str = model.buffer.toString("utf8", model.offset, model.offset + len);
len += pad;
model.offset += len;
model.len -= len;
return str;
}
private decode_StringName(model: BufferModel) {
return new StringName(this.decode_String(model));
}
private decode_Transform3Df(model: BufferModel) {
return new Transform3D(this.decode_Basisf(model), this.decode_Vector3f(model));
}
private decode_Transform3Dd(model: BufferModel) {
return new Transform3D(this.decode_Basisd(model), this.decode_Vector3d(model));
}
private decode_Projectionf(model: BufferModel) {
return new Projection(
this.decode_Vector4f(model),
this.decode_Vector4f(model),
this.decode_Vector4f(model),
this.decode_Vector4f(model),
);
}
private decode_Projectiond(model: BufferModel) {
return new Projection(
this.decode_Vector4d(model),
this.decode_Vector4d(model),
this.decode_Vector4d(model),
this.decode_Vector4d(model),
);
}
private decode_Transform2Df(model: BufferModel) {
return new Transform2D(
this.decode_Vector2f(model),
this.decode_Vector2f(model),
this.decode_Vector2f(model), //
);
}
private decode_Transform2Dd(model: BufferModel) {
return new Transform2D(
this.decode_Vector2d(model),
this.decode_Vector2d(model),
this.decode_Vector2d(model), //
);
}
private decode_Vector2f(model: BufferModel) {
const x = this.decode_Float32(model);
const y = this.decode_Float32(model);
return new Vector2(x, y);
}
private decode_Vector2d(model: BufferModel) {
const x = this.decode_Float64(model);
const y = this.decode_Float64(model);
return new Vector2(x, y);
}
private decode_Vector2i(model: BufferModel) {
const x = this.decode_Int32(model);
const y = this.decode_Int32(model);
return new Vector2i(x, y);
}
private decode_Vector3f(model: BufferModel) {
const x = this.decode_Float32(model);
const y = this.decode_Float32(model);
const z = this.decode_Float32(model);
return new Vector3(x, y, z);
}
private decode_Vector3d(model: BufferModel) {
const x = this.decode_Float64(model);
const y = this.decode_Float64(model);
const z = this.decode_Float64(model);
return new Vector3(x, y, z);
}
private decode_Vector3i(model: BufferModel) {
const x = this.decode_Int32(model);
const y = this.decode_Int32(model);
const z = this.decode_Int32(model);
return new Vector3i(x, y, z);
}
private decode_Vector4f(model: BufferModel) {
const x = this.decode_Float32(model);
const y = this.decode_Float32(model);
const z = this.decode_Float32(model);
const w = this.decode_Float32(model);
return new Vector4(x, y, z, w);
}
private decode_Vector4d(model: BufferModel) {
const x = this.decode_Float64(model);
const y = this.decode_Float64(model);
const z = this.decode_Float64(model);
const w = this.decode_Float64(model);
return new Vector4(x, y, z, w);
}
private decode_Vector4i(model: BufferModel) {
const x = this.decode_Int32(model);
const y = this.decode_Int32(model);
const z = this.decode_Int32(model);
const w = this.decode_Int32(model);
return new Vector4i(x, y, z, w);
}
}