240 lines
7.4 KiB
Haxe
Raw Normal View History

2025-01-22 16:18:30 +01:00
// Msgpack parser with typed arrays
// Based on https://github.com/aaulia/msgpack-haxe
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package iron.system;
import haxe.io.Bytes;
import haxe.io.BytesInput;
import haxe.io.BytesOutput;
import haxe.io.Eof;
import iron.data.SceneFormat;
#if (!macro)
import kha.arrays.Float32Array;
import kha.arrays.Uint32Array;
import kha.arrays.Int16Array;
#end
class LnxPack {
public static inline function decode<T>(b: Bytes): T {
var i = new BytesInput(b);
i.bigEndian = false;
return read(i);
}
static function read(i: BytesInput, key = "", parentKey = ""): Any {
try {
var b = i.readByte();
switch (b) {
case 0xc0: return null;
case 0xc2: return false;
case 0xc3: return true;
case 0xc4: return i.read(i.readByte());
case 0xc5: return i.read(i.readUInt16());
case 0xc6: return i.read(i.readInt32());
case 0xca: return i.readFloat();
case 0xcc: return i.readByte();
case 0xcd: return i.readUInt16();
case 0xce: return i.readInt32();
case 0xd0: return i.readInt8();
case 0xd1: return i.readInt16();
case 0xd2: return i.readInt32();
// case 0xd3: return Int64.make(i.readInt32(), i.readInt32());
case 0xd9: return i.readString(i.readByte());
case 0xda: return i.readString(i.readUInt16());
case 0xdb: return i.readString(i.readInt32());
case 0xdc: return readArray(i, i.readUInt16(), key, parentKey);
case 0xdd: return readArray(i, i.readInt32(), key, parentKey);
case 0xde: return readMap(i, i.readUInt16(), key, parentKey);
case 0xdf: return readMap(i, i.readInt32(), key, parentKey);
default: {
if (b < 0x80) return b; // positive fix num
else if (b < 0x90) return readMap(i, (0xf & b), key, parentKey); // fix map
else if (b < 0xa0) return readArray(i, (0xf & b), key, parentKey); // fix array
else if (b < 0xc0) return i.readString(0x1f & b); // fix string
else if (b > 0xdf) return 0xffffff00 | b; // negative fix num
}
}
}
catch (e: Eof) {}
return null;
}
static function readArray(i: BytesInput, length: Int, key = "", parentKey = ""): Any {
var b = i.readByte();
i.position--;
if (b == 0xca) { // Typed float32
i.position++;
var a = new Float32Array(length);
for (x in 0...length) a[x] = i.readFloat();
return a;
}
else if (b == 0xd2) { // Typed int32
i.position++;
var a = new Uint32Array(length);
for (x in 0...length) a[x] = i.readInt32();
return a;
}
else if (b == 0xd1) { // Typed int16
i.position++;
var a = new Int16Array(length);
for (x in 0...length) a[x] = i.readInt16();
return a;
}
else { // Dynamic type-value
var a: Array<Dynamic> = [];
for (x in 0...length) a.push(read(i, key, parentKey));
return a;
}
}
static function readMap(i: BytesInput, length: Int, key = "", parentKey = ""): Any {
#if js
var out = {};
#else
var out = Type.createEmptyInstance(getClass(key, parentKey));
#end
for (n in 0...length) {
var k = Std.string(read(i));
var v = read(i, k, key);
Reflect.setField(out, k, v);
}
return out;
}
#if (!js)
static function getClass(key: String, parentKey: String): Class<Dynamic> {
return switch (key) {
case "": TSceneFormat;
case "mesh_datas": TMeshData;
case "light_datas": TLightData;
case "probe_datas": TProbeData;
case "probe": TProbeData;
case "camera_datas": TCameraData;
case "material_datas": TMaterialData;
case "particle_datas": TParticleData;
case "shader_datas": TShaderData;
case "speaker_datas": TSpeakerData;
case "world_datas": TWorldData;
case "terrain_datas": TTerrainData;
case "tilesheet_datas": TTilesheetData;
case "objects": TObj;
case "children": TObj;
case "groups": TGroup;
case "traits": TTrait;
case "properties": TProperty;
case "vertex_arrays": TVertexArray;
case "index_arrays": TIndexArray;
case "skin": TSkin;
case "transform": TTransform;
case "constraints": TConstraint;
case "contexts": parentKey == "material_datas" ? TMaterialContext : TShaderContext;
case "override_context": TShaderOverride;
case "bind_constants": TBindConstant;
case "bind_textures": TBindTexture;
case "vertex_elements": TVertexElement;
case "constants": TShaderConstant;
case "texture_units": TTextureUnit;
case "actions": TTilesheetAction;
case "particle_refs": TParticleReference;
case "lods": TLod;
case "anim": TAnimation;
case "tracks": TTrack;
case "morph_target": TMorphTarget;
case _: TSceneFormat;
}
}
#end
#if (!macro && armorcore)
public static inline function encode(d: Dynamic): Bytes {
var o = new BytesOutput();
o.bigEndian = false;
write(o, d);
return o.getBytes();
}
static function write(o: BytesOutput, d: Dynamic) {
switch (Type.typeof(d)) {
case TNull: o.writeByte(0xc0);
case TBool: o.writeByte(d ? 0xc3 : 0xc2);
case TInt: { o.writeByte(0xd2); o.writeInt32(d); }
case TFloat: { o.writeByte(0xca); o.writeFloat(d); }
case TClass(c): {
switch (Type.getClassName(c)) {
case "String": {
o.writeByte(0xdb);
var b = Bytes.ofString(d);
o.writeInt32(b.length);
o.writeFullBytes(b, 0, b.length);
}
case "Array", null: { // kha.arrays give null
o.writeByte(0xdd);
o.writeInt32(d.length);
var isInt16 = Std.isOfType(d, #if js js.lib.Int16Array #else Int16ArrayPrivate #end);
var isInt = Std.isOfType(d[0], Int) && !Std.isOfType(d, #if js js.lib.Float32Array #else Float32ArrayPrivate #end);
var isFloat = Std.isOfType(d[0], Float);
if (isInt16) { // Int16Array
o.writeByte(0xd1);
for (i in 0...d.length) o.writeInt16(d[i]);
}
else if (isFloat && !isInt) { // Float32Array
o.writeByte(0xca);
for (i in 0...d.length) o.writeFloat(d[i]);
}
else if (isInt) { // Uint32Array
o.writeByte(0xd2);
for (i in 0...d.length) o.writeInt32(d[i]);
}
else for (i in 0...d.length) write(o, d[i]); // Array
}
case "haxe.io.Bytes": {
o.writeByte(0xc6);
o.writeInt32(d.length);
o.writeFullBytes(d, 0, d.length);
}
default: writeObject(o, d);
}
}
case TObject: writeObject(o, d);
default: {}
}
}
static function writeObject(o: BytesOutput, d: Dynamic) {
var f = Reflect.fields(d);
o.writeByte(0xdf);
o.writeInt32(f.length);
for (k in f) {
o.writeByte(0xdb);
var b = Bytes.ofString(k);
o.writeInt32(b.length);
o.writeFullBytes(b, 0, b.length);
write(o, Reflect.field(d, k));
}
}
#end
}