diff --git a/res/modules/bit_converter.lua b/res/modules/bit_converter.lua new file mode 100644 index 00000000..38e69aa7 --- /dev/null +++ b/res/modules/bit_converter.lua @@ -0,0 +1,268 @@ +local bit_converter = { } + +local MAX_UINT16 = 65535 +local MIN_UINT16 = 0 +local MAX_UINT32 = 4294967295 +local MIN_UINT32 = 0 + +local MAX_INT16 = 32767 +local MIN_INT16 = -32768 +local MAX_INT32 = 2147483647 +local MIN_INT32 = -2147483648 +local MAX_INT64 = 9223372036854775807 +local MIN_INT64 = -9223372036854775808 + +local function intToByte(num) + return bit.band(num, 0xFF) +end + +local function reverse(tab) + for i = 1, math.floor(#tab, 2), 1 do + tab[i], tab[#tab-i+1] = tab[#tab-i+1], tab[i] + end + return tab +end + +function bit_converter.string_to_bytes(str) + local bytes = { } + + local len = string.len(str) + + local lenBytes = bit_converter.uint16_to_bytes(len) + + for i = 1, #lenBytes do + bytes[i] = lenBytes[i] + end + + for i = 1, len do + bytes[#bytes + 1] = string.byte(string.sub(str, i, i)) + end + + return bytes +end + +function bit_converter.bool_to_byte(bool) + return bool and 1 or 0 +end + +-- Credits to Iryont + +local function floatOrDoubleToBytes(val, opt) + local sign = 0 + + if val < 0 then + sign = 1 + val = -val + end + + local mantissa, exponent = math.frexp(val) + if val == 0 then + mantissa = 0 + exponent = 0 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24) + exponent = exponent + ((opt == 'd') and 1022 or 126) + end + + local bytes = {} + if opt == 'd' then + val = mantissa + for i = 1, 6 do + table.insert(bytes, math.floor(val) % (2 ^ 8)) + val = math.floor(val / (2 ^ 8)) + end + else + table.insert(bytes, math.floor(mantissa) % (2 ^ 8)) + val = math.floor(mantissa / (2 ^ 8)) + table.insert(bytes, math.floor(val) % (2 ^ 8)) + val = math.floor(val / (2 ^ 8)) + end + + table.insert(bytes, math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8)) + val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8)) + table.insert(bytes, math.floor(sign * 128 + val) % (2 ^ 8)) + val = math.floor((sign * 128 + val) / (2 ^ 8)) + + if not endianness then + reverse(bytes) + end + return bytes +end + +local function bytesToFloatOrDouble(bytes, opt) + local n = (opt == 'd') and 8 or 4 + + if not endianness then + reverse(bytes) + end + + local sign = 1 + local mantissa = bytes[n - 1] % ((opt == 'd') and 16 or 128) + for i = n - 2, 1, -1 do + mantissa = mantissa * (2 ^ 8) + bytes[i] + end + + if bytes[n] > 127 then + sign = -1 + end + + local exponent = (bytes[n] % 128) * ((opt == 'd') and 16 or 2) + math.floor(bytes[n - 1] / ((opt == 'd') and 16 or 128)) + if exponent == 0 then + return 0.0 + else + mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign + return math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127)) + end +end + +-- + +function bit_converter.single_to_bytes(float) + return floatOrDoubleToBytes(float, 'f') +end + +function bit_converter.double_to_bytes(double) + return floatOrDoubleToBytes(double, 'd') +end + +function bit_converter.uint32_to_bytes(int) + if int > MAX_UINT32 or int < MIN_UINT32 then + error("invalid uint32") + end + + return { + intToByte(bit.rshift(int, 24)), + intToByte(bit.rshift(int, 16)), + intToByte(bit.rshift(int, 8)), + intToByte(int) + } +end + +function bit_converter.uint16_to_bytes(int) + if int > MAX_UINT16 or int < MIN_UINT16 then + error("invalid uint16") + end + + return { + intToByte(bit.rshift(int, 8)), + intToByte(int) + } +end + +function bit_converter.int64_to_bytes(int) + if int > MAX_INT64 or int < MIN_INT64 then + error("invalid int64") + end + + return { + intToByte(bit.rshift(int, 56)), + intToByte(bit.rshift(int, 48)), + intToByte(bit.rshift(int, 40)), + intToByte(bit.rshift(int, 32)), + intToByte(bit.rshift(int, 24)), + intToByte(bit.rshift(int, 16)), + intToByte(bit.rshift(int, 8)), + intToByte(int) + } +end + +function bit_converter.int32_to_bytes(int) + if int > MAX_INT32 or int < MIN_INT32 then + error("invalid int32") + end + + return bit_converter.uint32_to_bytes(int + MAX_INT32) +end + +function bit_converter.int16_to_bytes(int) + if int > MAX_INT16 or int < MIN_INT16 then + error("invalid int16") + end + + return bit_converter.uint16_to_bytes(int + MAX_INT16) +end + +function bit_converter.bytes_to_single(bytes) + return bytesToFloatOrDouble(bytes, 'f') +end + +function bit_converter.bytes_to_double(bytes) + return bytesToFloatOrDouble(bytes, 'd') +end + +function bit_converter.bytes_to_string(bytes) + local len = bit_converter.bytes_to_uint16({ bytes[1], bytes[2] }) + + local str = "" + + for i = 1, len do + str = str..string.char(bytes[i]) + end + + return str +end + +function bit_converter.byte_to_bool(byte) + return byte ~= 0 +end + +function bit_converter.bytes_to_float(bytes) + if #bytes < 8 then + error("eof") + end + error("unsupported operation") +end + +function bit_converter.bytes_to_uint32(bytes) + if #bytes < 4 then + error("eof") + end + return + bit.bor( + bit.bor( + bit.bor( + bit.lshift(bytes[1], 24), + bit.lshift(bytes[2], 16)), + bit.lshift(bytes[3], 8)),bytes[4]) +end + +function bit_converter.bytes_to_uint16(bytes) + if #bytes < 2 then + error("eof") + end + return + bit.bor( + bit.lshift(bytes[1], 8), + bytes[2], 0) +end + +function bit_converter.bytes_to_int64(bytes) + if #bytes < 8 then + error("eof") + end + return + bit.bor( + bit.bor( + bit.bor( + bit.bor( + bit.bor( + bit.bor( + bit.bor( + bit.lshift(bytes[1], 56), + bit.lshift(bytes[2], 48)), + bit.lshift(bytes[3], 40)), + bit.lshift(bytes[4], 32)), + bit.lshift(bytes[5], 24)), + bit.lshift(bit.band(bytes[6], 0xFF), 16)), + bit.lshift(bit.band(bytes[7], 0xFF), 8)),bit.band(bytes[8], 0xFF)) +end + +function bit_converter.bytes_to_int32(bytes) + return bit_converter.bytes_to_uint32(bytes) - MAX_INT32 +end + +function bit_converter.bytes_to_int16(bytes) + return bit_converter.bytes_to_uint16(bytes) - MAX_INT16 +end + +return bit_converter \ No newline at end of file diff --git a/res/modules/data_buffer.lua b/res/modules/data_buffer.lua new file mode 100644 index 00000000..002c8ea1 --- /dev/null +++ b/res/modules/data_buffer.lua @@ -0,0 +1,236 @@ +local bit_converter = require "core:bit_converter" + +local MAX_UINT16 = 65535 +local MIN_UINT16 = 0 +local MAX_UINT32 = 4294967295 +local MIN_UINT32 = 0 + +local MAX_INT16 = 32767 +local MIN_INT16 = -32768 +local MAX_INT32 = 2147483647 +local MIN_INT32 = -2147483648 +local MAX_INT64 = 9223372036854775807 +local MIN_INT64 = -9223372036854775808 + +local TYPE_ZERO = 0 +local TYPE_UINT16 = 1 +local TYPE_UINT32 = 2 +local TYPE_INT16 = 3 +local TYPE_INT32 = 4 +local TYPE_INT64 = 5 +local TYPE_DOUBLE = 6 + +-- Data buffer + +local data_buffer = { } + +function data_buffer.__call(bytes) + return setmetatable({ pos = 1, bytes = bytes or { } }, { __index = data_buffer }) +end + +-- Push functions + +function data_buffer:put_byte(byte) + if byte < 0 or byte > 255 then + error("invalid byte") + end + + self.bytes[self.pos] = byte + + self.pos = self.pos + 1 +end + +function data_buffer:put_bytes(bytes) + for i = 1, #bytes do + self:put_byte(bytes[i]) + end +end + +function data_buffer:put_single(single) + self:put_bytes(bit_converter.single_to_bytes(single)) +end + +function data_buffer:put_double(double) + self:put_bytes(bit_converter.double_to_bytes(double)) +end + +function data_buffer:put_string(str) + self:put_bytes(bit_converter.string_to_bytes(str)) +end + +function data_buffer:put_bool(bool) + self:put_byte(bit_converter.bool_to_byte(bool)) +end + +function data_buffer:put_uint16(uint16) + self:put_bytes(bit_converter.uint16_to_bytes(uint16)) +end + +function data_buffer:put_uint32(uint32) + self:put_bytes(bit_converter.uint32_to_bytes(uint32)) +end + +function data_buffer:put_int16(int16) + self:put_bytes(bit_converter.int16_to_bytes(int16)) +end + +function data_buffer:put_int32(int32) + self:put_bytes(bit_converter.int32_to_bytes(int32)) +end + +function data_buffer:put_int64(int64) + self:put_bytes(bit_converter.int64_to_bytes(int64)) +end + +function data_buffer:put_number(num) + local bytes + local type + + if math.floor(num) ~= num then + type = TYPE_DOUBLE + bytes = bit_converter.double_to_bytes(num) + elseif num == 0 then + type = TYPE_ZERO + bytes = { } + elseif num > 0 then + if num <= MAX_UINT16 then + type = TYPE_UINT16 + bytes = bit_converter.uint16_to_bytes(num) + elseif num <= MAX_UINT32 then + type = TYPE_UINT32 + bytes = bit_converter.uint32_to_bytes(num) + elseif num <= MAX_INT64 then + type = TYPE_INT64 + bytes = bit_converter.int64_to_bytes(num) + end + elseif num < 0 then + if num >= MIN_INT16 then + type = TYPE_INT16 + bytes = bit_converter.int16_to_bytes(num) + elseif num >= MIN_INT32 then + type = TYPE_INT32 + bytes = bit_converter.int32_to_bytes(num) + elseif num >= MIN_INT64 then + type = TYPE_INT64 + bytes = bit_converter.int64_to_bytes(num) + end + end + + self:put_byte(type) + self:put_bytes(bytes) +end + +-- Get functions + +function data_buffer:get_byte() + local byte = self.bytes[self.pos] + self.pos = self.pos + 1 + return byte +end + +function data_buffer:get_number() + local type = self:get_byte() + + if type == TYPE_ZERO then + return 0 + elseif type == TYPE_UINT16 then + return self:get_uint16() + elseif type == TYPE_UINT32 then + return self:get_uint32() + elseif type == TYPE_INT16 then + return self:get_int16() + elseif type == TYPE_INT32 then + return self:get_int32() + elseif type == TYPE_INT64 then + return self:get_int64() + elseif type == TYPE_DOUBLE then + return self:get_double() + else + error("unknown lua number type: "..type) + end +end + +function data_buffer:get_single() + return bit_converter.bytes_to_single(self:get_bytes(4)) +end + +function data_buffer:get_double() + return bit_converter.bytes_to_double(self:get_bytes(8)) +end + +function data_buffer:get_string() + local len = self:get_bytes(2) + local str = self:get_bytes(bit_converter.bytes_to_uint16(len)) + local bytes = { } + + for i = 1, #len do + bytes[i] = len[i] + end + + for i = 1, #str do + bytes[#bytes + 1] = str[i] + end + + return bit_converter.bytes_to_string(bytes) +end + +function data_buffer:get_bool() + return bit_converter.byte_to_bool(self:get_byte()) +end + +function data_buffer:get_uint16() + return bit_converter.bytes_to_uint16(self:get_bytes(2)) +end + +function data_buffer:get_uint32() + return bit_converter.bytes_to_uint32(self:get_bytes(4)) +end + +function data_buffer:get_int16() + return bit_converter.bytes_to_int16(self:get_bytes(2)) +end + +function data_buffer:get_int32() + return bit_converter.bytes_to_int32(self:get_bytes(4)) +end + +function data_buffer:get_int64() + return bit_converter.bytes_to_int64(self:get_bytes(8)) +end + +function data_buffer:size() + return #self.bytes +end + +function data_buffer:get_bytes(n) + if n == nil then + return self.bytes + else + local bytes = { } + + for i = 1, n do + bytes[i] = self:get_byte() + end + + return bytes + end +end + +function data_buffer:set_position(pos) + self.pos = pos +end + +function data_buffer:set_bytes(bytes) + for i = 1, #bytes do + local byte = bytes[i] + if byte < 0 or byte > 255 then + error("invalid byte") + end + end + + self.bytes = bytes +end + +setmetatable(data_buffer, data_buffer) + +return data_buffer \ No newline at end of file diff --git a/src/logic/scripting/lua/api_lua.cpp b/src/logic/scripting/lua/api_lua.cpp index aaa490be..df1b18ef 100644 --- a/src/logic/scripting/lua/api_lua.cpp +++ b/src/logic/scripting/lua/api_lua.cpp @@ -140,21 +140,21 @@ int l_file_write_bytes(lua_State* L) { int i = 1; while(lua_next(L, bytesIndex) != 0) { - if(lua_isnumber(L, -1)) { - const int byte = lua_tointeger(L, -1); - - if(byte < 0 || byte > 255) { - return luaL_error(L, "byte '%i' at index '%i' is less than 0 or greater than 255", byte, i); - } - - bytes.push_back(byte); - - lua_pop(L, 1); - - i++; - } else { - return luaL_error(L, "number expected at index '%i'", i); + if(i >= len) { + break; } + + const int byte = lua_tointeger(L, -1); + + if(byte < 0 || byte > 255) { + return luaL_error(L, "byte '%i' at index '%i' is less than 0 or greater than 255", byte, i); + } + + bytes.push_back(byte); + + lua_pop(L, 1); + + i++; } lua_pushboolean(L, files::write_bytes(path, &bytes[0], len));