--[[ Copyright (c) 2024 Srlion 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. ]] local pairs = pairs local type = type local getmetatable = getmetatable local table_concat = table.concat local math_floor = math.floor local math_ldexp = math.ldexp local math_frexp = math.frexp -- string.char is not jit compiled in luajit 2.0.5 local chars = {}; do for i = 0, 255 do chars[i] = string.char(i) end end -- local TYPES = {} local new_type; do local type_count = -1 function new_type(name, n) n = n or 1 if type_count + n > 255 then error("types count cannot be more than 256", 2) end local start_type = type_count + 1 type_count = type_count + n TYPES[name] = { start = start_type, max = n - 1 } if n == 1 then return start_type else return start_type, type_count - start_type end end end -- Simple types local NIL = new_type("nil") local FALSE = new_type("false") local TRUE = new_type("true") local FLOAT = new_type("float") local DOUBLE = new_type("double") -- Garry's Mod types local ENTITY = new_type("entity") local PLAYER = new_type("player") local VECTOR = new_type("vector") local ANGLE = new_type("angle") local MATRIX = new_type("matrix") local COLOR = new_type("color") -- reserved for future use to not break backwards compatibility incase we need to add more types local _ = new_type("reserved_1") local _ = new_type("reserved_2") local _ = new_type("reserved_3") local _ = new_type("reserved_4") -- -- local POSITIVE_FIXED_START, POSITIVE_FIXED_MAX = new_type("positive_fixed", 101) local POSITIVE_U8 = new_type("positive_u8") local POSITIVE_U16 = new_type("positive_u16") local POSITIVE_U32 = new_type("positive_u32") local POSITIVE_U53 = new_type("positive_u53") local NEGATIVE_FIXED_START, NEGATIVE_FIXED_MAX = new_type("negative_fixed", 55) local NEGATIVE_U8 = new_type("negative_u8") local NEGATIVE_U16 = new_type("negative_u16") local NEGATIVE_U32 = new_type("negative_u32") local NEGATIVE_U53 = new_type("negative_u53") local STRING_FIXED_START, STRING_FIXED_MAX = new_type("string_fixed", 56) local STRING_U8 = new_type("string_u8") local STRING_U16 = new_type("string_u16") local STRING_U32 = new_type("string_u32") local ARRAY = new_type("array") local TABLE = new_type("table") local ARRAY_AND_TABLE = new_type("array_and_table") local ENDING = new_type("ending") -- type used to end arrays and tables, can be used for custom types as well -- -- For user defined types local CUSTOM_START, CUSTOM_MAX = new_type("custom", 14) -- -- To tell Entity(0) (world) and NULL apart (since both have ent index 0), we use this (min i16 number) to represent NULL -- -1 = non networked entities -- 0 - 8192 = networked entities -- Thanks to Redox for reporting the -1 case and to RaphaelIT7 for explaining it local NULL_ENT_INDEX = -0x8000 local STRING_TYPES = { [STRING_U8] = true, [STRING_U16] = true, [STRING_U32] = true, } for i = 0, STRING_FIXED_MAX do STRING_TYPES[STRING_FIXED_START + i] = true end local encoders = {} local Encoder = { encoders = encoders, ENDING = ENDING } do local function write_str(buf, str) local buf_len = buf[0] + 1 buf[0] = buf_len buf[buf_len] = str end Encoder.write_str = write_str local function get_encoder_impl(t) local encoder = encoders[getmetatable(t)] if encoder == nil then encoder = encoders[type(t)] end return encoder end Encoder.get_encoder_impl = get_encoder_impl local function get_encoder(buf, t) local encoder = get_encoder_impl(t) if encoder == nil then write_str(buf, "unsupported type: ") write_str(buf, type(t)) return nil end return encoder end Encoder.get_encoder = get_encoder local function write_value(buf, val) local encoder = get_encoder(buf, val) if encoder == nil then return true end if encoder(buf, val) == true then return true end return false end Encoder.write_value = write_value local function write_byte(buf, chr) local buf_len = buf[0] + 1 buf[0] = buf_len -- if chars[chr] == nil then -- print(true, chr) -- error("stop") -- return -- end buf[buf_len] = chars[chr] end Encoder.write_byte = write_byte local function write_u8(buf, num) write_byte(buf, num) end Encoder.write_u8 = write_u8 local function write_u16(buf, num) write_byte(buf, math_floor(num / 0x100)) write_byte(buf, num % 0x100) end Encoder.write_u16 = write_u16 local function write_u32(buf, num) write_byte(buf, math_floor(num / 0x1000000) % 0x100) write_byte(buf, math_floor(num / 0x10000) % 0x100) write_byte(buf, math_floor(num / 0x100) % 0x100) write_byte(buf, num % 0x100) end Encoder.write_u32 = write_u32 local function write_u53(buf, num) write_byte(buf, math_floor(num / 0x1000000000000) % 0x100) write_byte(buf, math_floor(num / 0x10000000000) % 0x100) write_byte(buf, math_floor(num / 0x100000000) % 0x100) write_byte(buf, math_floor(num / 0x1000000) % 0x100) write_byte(buf, math_floor(num / 0x10000) % 0x100) write_byte(buf, math_floor(num / 0x100) % 0x100) write_byte(buf, num % 0x100) end Encoder.write_u53 = write_u53 local function write_i8(buf, num) write_byte(buf, num % 0x100) end Encoder.write_i8 = write_i8 local function write_i16(buf, num) write_u16(buf, num % 0x10000) end Encoder.write_i16 = write_i16 local function write_i32(buf, num) write_u32(buf, num % 0x100000000) end Encoder.write_i32 = write_i32 local function write_i53(buf, num) write_u53(buf, num % 0x20000000000000) end Encoder.write_i53 = write_i53 local function write_varint(buf, tag, num) if num <= 255 then -- 0 - 255 (8 bits) write_byte(buf, tag) write_u8(buf, num) elseif num <= 65535 then -- 0 - 65535 (16 bits) write_byte(buf, tag + 1) write_u16(buf, num) elseif num <= 4294967295 then -- 0 - 4294967295 (32 bits) write_byte(buf, tag + 2) write_u32(buf, num) else -- 0 - 9007199254740992 (53 bits) write_byte(buf, tag + 3) write_u53(buf, num) end end Encoder.write_varint = write_varint -- write float expects a float not a double (you need to pass values that are actually floats) -- this is here for gmod types that are floats (eg. Vector, Angle) local function write_float(buf, num) local u32 = 0 if num == 0 then u32 = 0x00000000 -- Positive zero if 1 / num < 0 then u32 = 0x80000000 -- Negative zero end write_u32(buf, u32) return u32 elseif num ~= num then -- NaN check u32 = 0x7FFFFFFF write_u32(buf, u32) return u32 end local sign = num < 0 and 1 or 0 num = sign == 1 and -num or num if num == 1 / 0 then -- math.huge -- (sign << 31) + (0xFF << 23) u32 = (sign * (2 ^ 31)) + (0xFF * (2 ^ 23)) write_u32(buf, u32) return u32 end local mantissa, exponent = math_frexp(num) mantissa = mantissa * 2 exponent = exponent - 1 local ieee_exponent = exponent + 127 -- IEEE 754 bias if ieee_exponent <= 0 then -- Handle subnormal numbers mantissa = math_ldexp(mantissa, ieee_exponent - 1) ieee_exponent = 0 elseif ieee_exponent >= 255 then -- Handle overflow ieee_exponent = 255 mantissa = 0 end -- Scale mantissa to 23 bits and round local mantissa_bits = math_floor( ((mantissa - 1) * (2 ^ 23)) + 0.5 ) -- Ensure mantissa doesn't exceed 23 bits mantissa_bits = mantissa_bits % (2 ^ 23) -- Combine all parts -- (sign << 31) | (ieee_exponent << 23) | mantissa_bits u32 = (sign * (2 ^ 31)) + (ieee_exponent * (2 ^ 23)) + mantissa_bits write_u32(buf, u32) return u32 end Encoder.write_float = write_float local function write_double(buf, num) local u32_1 = 0 local u32_2 = 0 if num == 0 then u32_1 = 0x00000000 if 1 / num < 0 then u32_1 = 0x80000000 end write_u32(buf, u32_1) write_u32(buf, u32_2) return elseif num ~= num then -- NaN check u32_1 = 0x7FFFFFFF u32_2 = 1 write_u32(buf, u32_1) write_u32(buf, u32_2) return end local sign = num < 0 and 1 or 0 num = sign == 1 and -num or num if num == 1 / 0 then -- Infinity -- (sign << 31) | (0x7FF << 20) u32_1 = (sign * (2 ^ 31)) + (0x7FF * (2 ^ 20)) write_u32(buf, u32_1) write_u32(buf, u32_2) return end local mantissa, exponent = math_frexp(num) local ieee_exponent = exponent + 1022 if ieee_exponent > 0 then -- Normal numbers local mantissa_scaled = (mantissa * 2 - 1) * (2 ^ 52) local mantissa_upper = math_floor(mantissa_scaled / (2 ^ 32)) -- (mantissa_scaled >> 32) local mantissa_lower = mantissa_scaled % (2 ^ 32) -- (mantissa_scaled & 0xFFFFFFFF) -- (sign << 31) | (ieee_exponent << 20) | (mantissa_upper % 2^20) u32_1 = (sign * (2 ^ 31)) + (ieee_exponent * (2 ^ 20)) + (mantissa_upper % (2 ^ 20)) u32_2 = mantissa_lower else -- Subnormal numbers local mantissa_scaled = mantissa * math_ldexp(1, 52 + ieee_exponent) local mantissa_upper = math_floor(mantissa_scaled / (2 ^ 32)) -- (mantissa_scaled >> 32) local mantissa_lower = mantissa_scaled % (2 ^ 32) -- (mantissa_scaled & 0xFFFFFFFF) -- (sign << 31) | (mantissa_upper & 0xFFFFF) u32_1 = (sign * (2 ^ 31)) + (mantissa_upper % (2 ^ 20)) u32_2 = mantissa_lower end write_u32(buf, u32_1) write_u32(buf, u32_2) end Encoder.write_double = write_double local function write_array(buf, arr, size) for i = 1, size do if write_value(buf, arr[i]) then return true end end end Encoder.write_array = write_array local function write_table(buf, tbl) for key, val in pairs(tbl) do if write_value(buf, key) or write_value(buf, val) then return true end end end Encoder.write_table = write_table function Encoder.read_error(buf) return table_concat(buf, nil, buf[0] - 1, buf[0]) end do local buffer = { [0] = 0 -- length } function Encoder.encode(val, max_cache_size) max_cache_size = max_cache_size or 2000 buffer[0] = 0 -- returns true when failed to encode -- error is never compiled, so we never error to avoid that -- concatenating in luajit 2.0.5 is NYI, we make sure that all encoders' functions get jit compiled if write_value(buffer, val) then return nil, Encoder.read_error(buffer) end local result = table_concat(buffer, nil, 1, buffer[0]) if #buffer > max_cache_size then buffer = { [0] = 0 -- buffer length } end return result end end encoders["nil"] = function(buf, v) write_byte(buf, NIL) end encoders.boolean = function(buf, v) if v == false then write_byte(buf, FALSE) else write_byte(buf, TRUE) end end encoders.float = function(buf, num) write_byte(buf, FLOAT) write_float(buf, num) end encoders.double = function(buf, num) write_byte(buf, DOUBLE) write_double(buf, num) end encoders.string = function(buf, str) local str_len = #str if str_len <= STRING_FIXED_MAX then write_byte(buf, STRING_FIXED_START + str_len) else write_varint(buf, STRING_U8, str_len) end if str_len > 0 then write_str(buf, str) end end encoders.array = function(buf, arr, size) if size then if size < 0 then write_str(buf, "array size cannot be negative: ") write_str(buf, size) return true end else size = #arr end write_byte(buf, ARRAY) write_array(buf, arr, size) write_byte(buf, ENDING) end encoders.table = function(buf, tbl) -- save position where we'll write the tag local tag_pos = buf[0] + 1 buf[0] = tag_pos buf[tag_pos] = chars[ARRAY] -- assume array initially local n = 0 local is_pure_array = true for k, v in pairs(tbl) do if is_pure_array then -- check if this key breaks array assumption if type(k) ~= "number" or k ~= n + 1 or k % 1 ~= 0 then -- if it's pure hash, then write as table directly if n == 0 then buf[tag_pos] = chars[TABLE] else buf[tag_pos] = chars[ARRAY_AND_TABLE] -- write ENDING to mark end of array part write_byte(buf, ENDING) end is_pure_array = false -- write current key-value (the one that broke the array) if write_value(buf, k) or write_value(buf, v) then return true end else -- still an array n = n + 1 if write_value(buf, v) then return true end end else -- already in hash mode, write key-value if write_value(buf, k) or write_value(buf, v) then return true end end end write_byte(buf, ENDING) end encoders.number = function(buf, num) -- a number like 1.7976931348623e308 will fail with % 1 ~= 0, but if you subtract 1 from it, it will still equal itself if num % 1 ~= 0 or num - 1 == num then -- DOUBLE write_byte(buf, DOUBLE) write_double(buf, num) return end -- check if it's a positive number (this is weird but to check for -0) if 1 / num > 0 then if num <= POSITIVE_FIXED_MAX then write_byte(buf, POSITIVE_FIXED_START + num) else write_varint(buf, POSITIVE_U8, num) end else num = -num if num <= NEGATIVE_FIXED_MAX then write_byte(buf, NEGATIVE_FIXED_START + num) else write_varint(buf, NEGATIVE_U8, num) end end end -- Garry's Mod types local Entity_EntIndex = FindMetaTable and FindMetaTable("Entity").EntIndex encoders.Entity = function(buf, ent) write_byte(buf, ENTITY) if ent == NULL then write_i16(buf, NULL_ENT_INDEX) else write_i16(buf, Entity_EntIndex(ent)) end end -- All of these are reported as their own type but are otherwise identical in handling to entities encoders.Weapon = encoders.Entity encoders.Vehicle = encoders.Entity encoders.NextBot = encoders.Entity encoders.NPC = encoders.Entity -- range between 1 and 128 for players, so we can safely use uint8 encoders.Player = function(buf, ply) write_byte(buf, PLAYER) write_u8(buf, Entity_EntIndex(ply)) end local Vector_Unpack = FindMetaTable and FindMetaTable("Vector").Unpack encoders.Vector = function(buf, vec) write_byte(buf, VECTOR) local x, y, z = Vector_Unpack(vec) write_float(buf, x) write_float(buf, y) write_float(buf, z) end local Angle_Unpack = FindMetaTable and FindMetaTable("Angle").Unpack encoders.Angle = function(buf, ang) write_byte(buf, ANGLE) local p, y, r = Angle_Unpack(ang) write_float(buf, p) write_float(buf, y) write_float(buf, r) end -- :] local MATRIX_ToTable = FindMetaTable and FindMetaTable("VMatrix").ToTable encoders.VMatrix = function(buf, mat) write_byte(buf, MATRIX) local matrix = MATRIX_ToTable(mat) for i = 1, 4 do for j = 1, 4 do write_float(buf, matrix[i][j]) end end end local COLOR_META = FindMetaTable and FindMetaTable("Color") if COLOR_META then encoders[COLOR_META] = function(buf, col) write_byte(buf, COLOR) write_u8(buf, math_floor(col.r)) write_u8(buf, math_floor(col.g)) write_u8(buf, math_floor(col.b)) write_u8(buf, math_floor(col.a)) end end -- function encoding will never be supported, so just skip it encoders["function"] = function() end end local decoders = {} local Decoder = { decoders = decoders } do local string_byte = string.byte local string_sub = string.sub -- Context Structure local context = { 1, -- index "", -- bytes 0, -- bytes length 1 / 0 -- max size for decode (math.huge) } local function peak_type(ctx) return string_byte(ctx[2], ctx[1]) end Decoder.peak_type = peak_type local function get_decoder(ctx) local tpy = peak_type(ctx) local decoder = decoders[tpy] if decoder == nil then return nil, "unsupported type: ", tpy end return decoder end Decoder.get_decoder = get_decoder local function read_value(ctx) local decoder, val, err, err2 decoder, err, err2 = get_decoder(ctx) if err then if err2 then return nil, err .. err2 end return nil, err end val, err = decoder(ctx) if err then return nil, err end return val end Decoder.read_value = read_value local function read_byte(ctx, size) local idx = ctx[1] local end_idx = idx + size - 1 if end_idx > ctx[3] then return nil, "bytes underflow" elseif end_idx > ctx[4] then return nil, "bytes overflow" end ctx[1] = end_idx + 1 return string_byte(ctx[2], idx, end_idx) end Decoder.read_byte = read_byte local function read_str(ctx, size) local idx = ctx[1] if idx + size - 1 > ctx[3] then -- ctx[3] bytes length return nil, "bytes underflow" elseif idx + size - 1 > ctx[4] then -- ctx[4] max size return nil, "bytes overflow" end ctx[1] = idx + size return string_sub(ctx[2], idx, idx + size - 1) end Decoder.read_str = read_str local function read_u8(ctx) local bty, err = read_byte(ctx, 1) if err then return nil, err end return bty end Decoder.read_u8 = read_u8 local function read_u16(ctx) local b1, b2 = read_byte(ctx, 2) -- read_byte returns multiple values, but error can be the second value if b1 == nil then return nil, b2 end return b1 * 0x100 + b2 end Decoder.read_u16 = read_u16 local function read_u32(ctx) local b1, b2, b3, b4 = read_byte(ctx, 4) if b1 == nil then return nil, b2 end return b1 * 0x1000000 + b2 * 0x10000 + b3 * 0x100 + b4 end Decoder.read_u32 = read_u32 local function read_u53(ctx) local b1, b2, b3, b4, b5, b6, b7 = read_byte(ctx, 7) if b1 == nil then return nil, b2 end return b1 * 0x1000000000000 + b2 * 0x10000000000 + b3 * 0x100000000 + b4 * 0x1000000 + b5 * 0x10000 + b6 * 0x100 + b7 end Decoder.read_u53 = read_u53 local function read_i8(ctx) local u, err = read_u8(ctx); if err then return nil, err end if u >= 0x80 then u = u - 0x100 end return u end Decoder.read_i8 = read_i8 local function read_i16(ctx) local u, err = read_u16(ctx); if err then return nil, err end if u >= 0x8000 then u = u - 0x10000 end return u end Decoder.read_i16 = read_i16 local function read_i32(ctx) local u, err = read_u32(ctx); if err then return nil, err end if u >= 0x80000000 then u = u - 0x100000000 end return u end Decoder.read_i32 = read_i32 local function read_i53(ctx) local u, err = read_u53(ctx); if err then return nil, err end if u >= 0x10000000000000 then u = u - 0x20000000000000 end return u end Decoder.read_i53 = read_i53 local function read_float(ctx) local u32, err = read_u32(ctx) if err then return nil, err end -- ((u32 >> 31) & 1) == 1 and -1 or 1 local sign = math_floor(u32 / (2 ^ 31)) % 2 == 1 and -1 or 1 -- (u32 >> 23) & 0xFF local exponent_field = math_floor(u32 / (2 ^ 23)) % (2 ^ 8) -- u32 & 0x7FFFFF local mantissa = u32 % (2 ^ 23) if exponent_field == 0xFF then if mantissa == 0 then return sign * (1 / 0) -- math.huge end return 0 / 0 -- NaN end if exponent_field == 0 and mantissa == 0 then return sign * 0 -- Zero end -- mantissa >> 23 local mantissa_scaled = mantissa / (2 ^ 23) if exponent_field ~= 0 then -- Normal numbers mantissa_scaled = mantissa_scaled + 1 local actual_exponent = exponent_field - 127 return sign * math_ldexp(mantissa_scaled, actual_exponent) else -- Subnormal numbers return sign * math_ldexp(mantissa_scaled, -126) end end Decoder.read_float = read_float local function read_double(ctx) local u32_1, u32_2, err u32_1, err = read_u32(ctx) if err then return nil, err end u32_2, err = read_u32(ctx) if err then return nil, err end -- ((u32_1 >> 31) & 1) == 1 and -1 or 1 local sign = math_floor(u32_1 / (2 ^ 31)) % 2 == 1 and -1 or 1 -- (u32_1 >> 20) & 0x7FF local exponent_field = math_floor(u32_1 / (2 ^ 20)) % (2 ^ 11) -- u32_1 & 0xFFFFF local mantissa_upper = u32_1 % (2 ^ 20) if exponent_field == 0x7FF then if mantissa_upper == 0 and u32_2 == 0 then return sign * (1 / 0) -- math.huge end return 0 / 0 -- NaN end if exponent_field == 0 and mantissa_upper == 0 and u32_2 == 0 then return sign * 0 -- Zero end -- mantissa_upper << 32 + u32_2 local mantissa_scaled = mantissa_upper * (2 ^ 32) + u32_2 if exponent_field ~= 0 then -- Normal numbers mantissa_scaled = (mantissa_scaled / (2 ^ 52)) + 1 local actual_exponent = exponent_field - 1023 return sign * math_ldexp(mantissa_scaled, actual_exponent) else -- Subnormal numbers mantissa_scaled = mantissa_scaled / (2 ^ 52) return sign * math_ldexp(mantissa_scaled, -1022) end end Decoder.read_double = read_double local function read_array(ctx, till) local arr = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil } -- initialize with size of 10 local size = 0 while peak_type(ctx) ~= till do local val, err = read_value(ctx) if err then return nil, err end size = size + 1 arr[size] = val end ctx[1] = ctx[1] + 1 -- skip the ending type return arr, nil, size end Decoder.read_array = read_array local function read_table(ctx, till) local tbl = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil } -- initialize with size of 10 while peak_type(ctx) ~= till do local key, val, err key, err = read_value(ctx) if err then return nil, err end val, err = read_value(ctx) if err then return nil, err end tbl[key] = val end ctx[1] = ctx[1] + 1 -- skip the ending type return tbl end Decoder.read_table = read_table function Decoder.setup_context(bytes, max_size) if type(bytes) ~= "string" then return nil, "bytes must be a string" end if max_size == nil then max_size = 1 / 0 -- math.huge elseif type(max_size) ~= "number" then return nil, "max_size must be a number" elseif max_size < 1 then return nil, "max_size must be greater than 0" end context[1] = 1 context[2] = bytes context[3] = #bytes context[4] = max_size if context[3] < 1 then return nil, "no bytes to decode" end return context end function Decoder.decode(bytes, max_size) do local _, err = Decoder.setup_context(bytes, max_size) if err then return nil, err end end local val, err = read_value(context) if err then return nil, err end return val end decoders[NIL] = function(ctx) ctx[1] = ctx[1] + 1 return nil end decoders[FALSE] = function(ctx) ctx[1] = ctx[1] + 1 return false end decoders[TRUE] = function(ctx) ctx[1] = ctx[1] + 1 return true end decoders[FLOAT] = function(ctx) ctx[1] = ctx[1] + 1 return read_float(ctx) end decoders[DOUBLE] = function(ctx) ctx[1] = ctx[1] + 1 return read_double(ctx) end -- Garry's Mod types local Entity = Entity decoders[ENTITY] = function(ctx) ctx[1] = ctx[1] + 1 local ent_idx, err = read_i16(ctx) if err then return nil, err end if ent_idx == NULL_ENT_INDEX then return NULL end return Entity(ent_idx) end decoders[PLAYER] = function(ctx) ctx[1] = ctx[1] + 1 local ply_uid, err = read_u8(ctx) if err then return nil, err end return Entity(ply_uid) end local Vector = Vector decoders[VECTOR] = function(ctx) ctx[1] = ctx[1] + 1 local x, y, z, err x, err = read_float(ctx) if err then return nil, err end y, err = read_float(ctx) if err then return nil, err end z, err = read_float(ctx) if err then return nil, err end return Vector(x, y, z) end local Angle = Angle decoders[ANGLE] = function(ctx) ctx[1] = ctx[1] + 1 local p, y, r, err p, err = read_float(ctx) if err then return nil, err end y, err = read_float(ctx) if err then return nil, err end r, err = read_float(ctx) if err then return nil, err end return Angle(p, y, r) end local Matrix = Matrix decoders[MATRIX] = function(ctx) ctx[1] = ctx[1] + 1 local matrix, err = {}, nil for i = 1, 4 do matrix[i] = {} for j = 1, 4 do matrix[i][j], err = read_float(ctx) if err then return nil, err end end end return Matrix(matrix) end local Color = Color decoders[COLOR] = function(ctx) ctx[1] = ctx[1] + 1 local r, g, b, a, err r, err = read_u8(ctx) if err then return nil, err end g, err = read_u8(ctx) if err then return nil, err end b, err = read_u8(ctx) if err then return nil, err end a, err = read_u8(ctx) if err then return nil, err end return Color(r, g, b, a) end -- decoders[POSITIVE_U8] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u8(ctx) if err then return nil, err end return num end decoders[POSITIVE_U16] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u16(ctx) if err then return nil, err end return num end decoders[POSITIVE_U32] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u32(ctx) if err then return nil, err end return num end decoders[POSITIVE_U53] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u53(ctx) if err then return nil, err end return num end -- -- decoders[NEGATIVE_U8] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u8(ctx) if err then return nil, err end return -num end decoders[NEGATIVE_U16] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u16(ctx) if err then return nil, err end return -num end decoders[NEGATIVE_U32] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u32(ctx) if err then return nil, err end return -num end decoders[NEGATIVE_U53] = function(ctx) ctx[1] = ctx[1] + 1 local num, err = read_u53(ctx) if err then return nil, err end return -num end -- -- decoders[STRING_U8] = function(ctx) ctx[1] = ctx[1] + 1 local str_len, str, err str_len, err = read_u8(ctx) if err then return nil, err end str, err = read_str(ctx, str_len) if err then return nil, err end return str end decoders[STRING_U16] = function(ctx) ctx[1] = ctx[1] + 1 local str_len, str, err str_len, err = read_u16(ctx) if err then return nil, err end str, err = read_str(ctx, str_len) if err then return nil, err end return str end decoders[STRING_U32] = function(ctx) ctx[1] = ctx[1] + 1 local str_len, str, err str_len, err = read_u32(ctx) if err then return nil, err end str, err = read_str(ctx, str_len) if err then return nil, err end return str end -- -- decoders[ARRAY] = function(ctx) ctx[1] = ctx[1] + 1 local arr, err = read_array(ctx, ENDING) if err then return nil, err end return arr end decoders[TABLE] = function(ctx) ctx[1] = ctx[1] + 1 local tbl, err = read_table(ctx, ENDING) if err then return nil, err end return tbl end decoders[ARRAY_AND_TABLE] = function(ctx) ctx[1] = ctx[1] + 1 -- array part first local arr, err = read_array(ctx, ENDING) if err then return nil, err end -- hash part while peak_type(ctx) ~= ENDING do local key, val key, err = read_value(ctx) if err then return nil, err end val, err = read_value(ctx) if err then return nil, err end arr[key] = val end ctx[1] = ctx[1] + 1 -- skip final ENDING return arr end -- decoders[STRING_FIXED_START] = function(ctx) local bty, str, err bty, err = read_byte(ctx, 1) if err then return nil, err end local str_len = bty - STRING_FIXED_START str, err = read_str(ctx, str_len) if err then return nil, err end return str end for i = 1, STRING_FIXED_MAX do decoders[STRING_FIXED_START + i] = decoders[STRING_FIXED_START] end -- -- decoders[POSITIVE_FIXED_START] = function(ctx) local bty, num, err bty, err = read_byte(ctx, 1) if err then return nil, err end num = bty - POSITIVE_FIXED_START return num end for i = 1, POSITIVE_FIXED_MAX do decoders[POSITIVE_FIXED_START + i] = decoders[POSITIVE_FIXED_START] end -- -- decoders[NEGATIVE_FIXED_START] = function(ctx) local bty, num, err bty, err = read_byte(ctx, 1) if err then return nil, err end num = bty - NEGATIVE_FIXED_START return -num end for i = 1, NEGATIVE_FIXED_MAX do decoders[NEGATIVE_FIXED_START + i] = decoders[NEGATIVE_FIXED_START] end -- decoders.string = function(ctx) local bty = peak_type(ctx) if not STRING_TYPES[bty] then return nil, "expected string type" end return read_value(ctx) end end local encode_to_hex, decode_from_hex; do local byte = string.byte local char = string.char local string_format = string.format local string_gsub = string.gsub local tonumber = tonumber local function hex(c) local b = byte(c) return (string_format("%02X", b)) end local function string_to_hex(str) return (string_gsub(str, ".", hex)) end function encode_to_hex(val) local encoded, err = Encoder.encode(val) if err then return nil, err end local hexed = string_to_hex(encoded) return hexed end local function from_hex(c) return char(tonumber(c, 16)) end local function hex_to_string(str) return (string_gsub(str, "..", from_hex)) end function decode_from_hex(str) local ok, unhexed = pcall(hex_to_string, str) if not ok then return nil, unhexed end local decoded, err = Decoder.decode(unhexed) if err then return nil, err end return decoded end end return { TYPES = TYPES, STRING_TYPES = STRING_TYPES, Encoder = Encoder, -- to allow usage of internal functions Decoder = Decoder, -- to allow usage of internal functions encode = Encoder.encode, decode = Decoder.decode, encode_to_hex = encode_to_hex, decode_from_hex = decode_from_hex, new_buffer = function() return { [0] = 0 } end, end_buffer = function(buf) return table_concat(buf, nil, 1, buf[0]) end, reset_buffer = function(buf) buf[0] = 0 end, set_type_function = function(t_fn) -- this is for me as I have custom type function in sam/scb to allow type function to get jit compiled :c type = t_fn end, add_custom_type = function(ty, encoder, decoder) if CUSTOM_START == CUSTOM_MAX then return error("cannot add more custom types") end if encoders[ty] or decoders[ty] then -- this just prints incase you mistakenly add a type that already exists ErrorNoHaltWithStack("type already exists: `" .. ty .. "`") end encoders[ty] = encoder decoders[CUSTOM_START] = decoder CUSTOM_START = CUSTOM_START + 1 return CUSTOM_START - 1 end, set_custom_type_with_id = function(id, ty, encoder, decoder) if encoders[ty] or decoders[ty] then -- this just prints incase you mistakenly add a type that already exists ErrorNoHaltWithStack("type already exists: `" .. ty .. "`") end encoders[ty] = encoder decoders[id] = decoder end, chars = chars, VERSION = "7.0.9" }