Another change

This commit is contained in:
SweetSea
2024-11-24 16:55:23 +07:00
parent 45b9af49a3
commit d78ea3cc6e
10 changed files with 1205 additions and 554 deletions

View File

@@ -1,7 +1,7 @@
-- binser.lua -- binser.lua
--[[ --[[
Copyright (c) 2016 Calvin Rose Copyright (c) 2016-2019 Calvin Rose
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
@@ -27,7 +27,6 @@ local select = select
local pairs = pairs local pairs = pairs
local getmetatable = getmetatable local getmetatable = getmetatable
local setmetatable = setmetatable local setmetatable = setmetatable
local tonumber = tonumber
local type = type local type = type
local loadstring = loadstring or load local loadstring = loadstring or load
local concat = table.concat local concat = table.concat
@@ -52,25 +51,6 @@ if not frexp then
end end
end end
-- NIL = 202
-- FLOAT = 203
-- TRUE = 204
-- FALSE = 205
-- STRING = 206
-- TABLE = 207
-- REFERENCE = 208
-- CONSTRUCTOR = 209
-- FUNCTION = 210
-- RESOURCE = 211
-- INT64 = 212
local mts = {}
local ids = {}
local serializers = {}
local deserializers = {}
local resources = {}
local resources_by_name = {}
local function pack(...) local function pack(...)
return {...}, select("#", ...) return {...}, select("#", ...)
end end
@@ -166,25 +146,36 @@ end
-- number deserialization code also modified from https://github.com/fperrad/lua-MessagePack -- number deserialization code also modified from https://github.com/fperrad/lua-MessagePack
local function number_from_str(str, index) local function number_from_str(str, index)
local b = byte(str, index) local b = byte(str, index)
if not b then error("Expected more bytes of input.") end
if b < 128 then if b < 128 then
return b - 27, index + 1 return b - 27, index + 1
elseif b < 192 then elseif b < 192 then
return byte(str, index + 1) + 0x100 * (b - 128) - 8192, index + 2 local b2 = byte(str, index + 1)
if not b2 then error("Expected more bytes of input.") end
return b2 + 0x100 * (b - 128) - 8192, index + 2
end end
local b1, b2, b3, b4, b5, b6, b7, b8 = byte(str, index + 1, index + 8) local b1, b2, b3, b4, b5, b6, b7, b8 = byte(str, index + 1, index + 8)
if (not b1) or (not b2) or (not b3) or (not b4) or
(not b5) or (not b6) or (not b7) or (not b8) then
error("Expected more bytes of input.")
end
if b == 212 then if b == 212 then
local flip = b1 >= 128 local flip = b1 >= 128
if flip then -- negative if flip then -- negative
b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4 b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4
b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8 b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8
end end
local n = ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 local n = ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) *
0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
if flip then if flip then
return (-n) - 1, index + 9 return (-n) - 1, index + 9
else else
return n, index + 9 return n, index + 9
end end
end end
if b ~= 203 then
error("Expected number")
end
local sign = b1 > 0x7F and -1 or 1 local sign = b1 > 0x7F and -1 or 1
local e = (b1 % 0x80) * 0x10 + floor(b2 / 0x10) local e = (b1 % 0x80) * 0x10 + floor(b2 / 0x10)
local m = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8 local m = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
@@ -207,35 +198,61 @@ local function number_from_str(str, index)
return n, index + 9 return n, index + 9
end end
local types = {}
types["nil"] = function(x, visited, accum) local function newbinser()
-- unique table key for getting next value
local NEXT = {}
local CTORSTACK = {}
-- NIL = 202
-- FLOAT = 203
-- TRUE = 204
-- FALSE = 205
-- STRING = 206
-- TABLE = 207
-- REFERENCE = 208
-- CONSTRUCTOR = 209
-- FUNCTION = 210
-- RESOURCE = 211
-- INT64 = 212
-- TABLE WITH META = 213
local mts = {}
local ids = {}
local serializers = {}
local deserializers = {}
local resources = {}
local resources_by_name = {}
local types = {}
types["nil"] = function(x, visited, accum)
accum[#accum + 1] = "\202" accum[#accum + 1] = "\202"
end end
function types.number(x, visited, accum) function types.number(x, visited, accum)
accum[#accum + 1] = number_to_str(x) accum[#accum + 1] = number_to_str(x)
end end
function types.boolean(x, visited, accum) function types.boolean(x, visited, accum)
accum[#accum + 1] = x and "\204" or "\205" accum[#accum + 1] = x and "\204" or "\205"
end end
function types.string(x, visited, accum) function types.string(x, visited, accum)
local alen = #accum local alen = #accum
if visited[x] then if visited[x] then
accum[alen + 1] = "\208" accum[alen + 1] = "\208"
accum[alen + 2] = number_to_str(visited[x]) accum[alen + 2] = number_to_str(visited[x])
else else
visited[x] = visited.next visited[x] = visited[NEXT]
visited.next = visited.next + 1 visited[NEXT] = visited[NEXT] + 1
accum[alen + 1] = "\206" accum[alen + 1] = "\206"
accum[alen + 2] = number_to_str(#x) accum[alen + 2] = number_to_str(#x)
accum[alen + 3] = x accum[alen + 3] = x
end end
end end
local function check_custom_type(x, visited, accum) local function check_custom_type(x, visited, accum)
local res = resources[x] local res = resources[x]
if res then if res then
accum[#accum + 1] = "\211" accum[#accum + 1] = "\211"
@@ -245,10 +262,11 @@ local function check_custom_type(x, visited, accum)
local mt = getmetatable(x) local mt = getmetatable(x)
local id = mt and ids[mt] local id = mt and ids[mt]
if id then if id then
if x == visited.temp then local constructing = visited[CTORSTACK]
if constructing[x] then
error("Infinite loop in constructor.") error("Infinite loop in constructor.")
end end
visited.temp = x constructing[x] = true
accum[#accum + 1] = "\209" accum[#accum + 1] = "\209"
types[type(id)](id, visited, accum) types[type(id)](id, visited, accum)
local args, len = pack(serializers[id](x)) local args, len = pack(serializers[id](x))
@@ -257,13 +275,15 @@ local function check_custom_type(x, visited, accum)
local arg = args[i] local arg = args[i]
types[type(arg)](arg, visited, accum) types[type(arg)](arg, visited, accum)
end end
visited[x] = visited.next visited[x] = visited[NEXT]
visited.next = visited.next + 1 visited[NEXT] = visited[NEXT] + 1
-- We finished constructing
constructing[x] = nil
return true return true
end end
end end
function types.userdata(x, visited, accum) function types.userdata(x, visited, accum)
if visited[x] then if visited[x] then
accum[#accum + 1] = "\208" accum[#accum + 1] = "\208"
accum[#accum + 1] = number_to_str(visited[x]) accum[#accum + 1] = number_to_str(visited[x])
@@ -271,18 +291,24 @@ function types.userdata(x, visited, accum)
if check_custom_type(x, visited, accum) then return end if check_custom_type(x, visited, accum) then return end
error("Cannot serialize this userdata.") error("Cannot serialize this userdata.")
end end
end end
function types.table(x, visited, accum) function types.table(x, visited, accum)
if visited[x] then if visited[x] then
accum[#accum + 1] = "\208" accum[#accum + 1] = "\208"
accum[#accum + 1] = number_to_str(visited[x]) accum[#accum + 1] = number_to_str(visited[x])
else else
if check_custom_type(x, visited, accum) then return end if check_custom_type(x, visited, accum) then return end
visited[x] = visited.next visited[x] = visited[NEXT]
visited.next = visited.next + 1 visited[NEXT] = visited[NEXT] + 1
local xlen = #x local xlen = #x
local mt = getmetatable(x)
if mt then
accum[#accum + 1] = "\213"
types.table(mt, visited, accum)
else
accum[#accum + 1] = "\207" accum[#accum + 1] = "\207"
end
accum[#accum + 1] = number_to_str(xlen) accum[#accum + 1] = number_to_str(xlen)
for i = 1, xlen do for i = 1, xlen do
local v = x[i] local v = x[i]
@@ -302,24 +328,24 @@ function types.table(x, visited, accum)
end end
end end
end end
end end
types["function"] = function(x, visited, accum) types["function"] = function(x, visited, accum)
if visited[x] then if visited[x] then
accum[#accum + 1] = "\208" accum[#accum + 1] = "\208"
accum[#accum + 1] = number_to_str(visited[x]) accum[#accum + 1] = number_to_str(visited[x])
else else
if check_custom_type(x, visited, accum) then return end if check_custom_type(x, visited, accum) then return end
visited[x] = visited.next visited[x] = visited[NEXT]
visited.next = visited.next + 1 visited[NEXT] = visited[NEXT] + 1
local str = dump(x) local str = dump(x)
accum[#accum + 1] = "\210" accum[#accum + 1] = "\210"
accum[#accum + 1] = number_to_str(#str) accum[#accum + 1] = number_to_str(#str)
accum[#accum + 1] = str accum[#accum + 1] = str
end end
end end
types.cdata = function(x, visited, accum) types.cdata = function(x, visited, accum)
if visited[x] then if visited[x] then
accum[#accum + 1] = "\208" accum[#accum + 1] = "\208"
accum[#accum + 1] = number_to_str(visited[x]) accum[#accum + 1] = number_to_str(visited[x])
@@ -327,45 +353,63 @@ types.cdata = function(x, visited, accum)
if check_custom_type(x, visited, #accum) then return end if check_custom_type(x, visited, #accum) then return end
error("Cannot serialize this cdata.") error("Cannot serialize this cdata.")
end end
end end
types.thread = function() error("Cannot serialize threads.") end types.thread = function() error("Cannot serialize threads.") end
local function deserialize_value(str, index, visited) local function deserialize_value(str, index, visited)
local t = byte(str, index) local t = byte(str, index)
if not t then return end if not t then return nil, index end
if t < 128 then if t < 128 then
return t - 27, index + 1 return t - 27, index + 1
elseif t < 192 then elseif t < 192 then
return byte(str, index + 1) + 0x100 * (t - 128) - 8192, index + 2 local b2 = byte(str, index + 1)
if not b2 then error("Expected more bytes of input.") end
return b2 + 0x100 * (t - 128) - 8192, index + 2
elseif t == 202 then elseif t == 202 then
return nil, index + 1 return nil, index + 1
elseif t == 203 then elseif t == 203 or t == 212 then
return number_from_str(str, index) return number_from_str(str, index)
elseif t == 204 then elseif t == 204 then
return true, index + 1 return true, index + 1
elseif t == 205 then elseif t == 205 then
return false, index + 1 return false, index + 1
elseif t == 206 then elseif t == 206 then
local length, dataindex = deserialize_value(str, index + 1, visited) local length, dataindex = number_from_str(str, index + 1)
local nextindex = dataindex + length local nextindex = dataindex + length
if not (length >= 0) then error("Bad string length") end
if #str < nextindex - 1 then error("Expected more bytes of string") end
local substr = sub(str, dataindex, nextindex - 1) local substr = sub(str, dataindex, nextindex - 1)
visited[#visited + 1] = substr visited[#visited + 1] = substr
return substr, nextindex return substr, nextindex
elseif t == 207 then elseif t == 207 or t == 213 then
local count, nextindex = number_from_str(str, index + 1) local mt, count, nextindex
local ret = {} local ret = {}
visited[#visited + 1] = ret visited[#visited + 1] = ret
nextindex = index + 1
if t == 213 then
mt, nextindex = deserialize_value(str, nextindex, visited)
if type(mt) ~= "table" then error("Expected table metatable") end
end
count, nextindex = number_from_str(str, nextindex)
for i = 1, count do for i = 1, count do
local oldindex = nextindex
ret[i], nextindex = deserialize_value(str, nextindex, visited) ret[i], nextindex = deserialize_value(str, nextindex, visited)
if nextindex == oldindex then error("Expected more bytes of input.") end
end end
count, nextindex = number_from_str(str, nextindex) count, nextindex = number_from_str(str, nextindex)
for i = 1, count do for i = 1, count do
local k, v local k, v
local oldindex = nextindex
k, nextindex = deserialize_value(str, nextindex, visited) k, nextindex = deserialize_value(str, nextindex, visited)
if nextindex == oldindex then error("Expected more bytes of input.") end
oldindex = nextindex
v, nextindex = deserialize_value(str, nextindex, visited) v, nextindex = deserialize_value(str, nextindex, visited)
if nextindex == oldindex then error("Expected more bytes of input.") end
if k == nil then error("Can't have nil table keys") end
ret[k] = v ret[k] = v
end end
if mt then setmetatable(ret, mt) end
return ret, nextindex return ret, nextindex
elseif t == 208 then elseif t == 208 then
local ref, nextindex = number_from_str(str, index + 1) local ref, nextindex = number_from_str(str, index + 1)
@@ -376,49 +420,59 @@ local function deserialize_value(str, index, visited)
count, nextindex = number_from_str(str, nextindex) count, nextindex = number_from_str(str, nextindex)
local args = {} local args = {}
for i = 1, count do for i = 1, count do
local oldindex = nextindex
args[i], nextindex = deserialize_value(str, nextindex, visited) args[i], nextindex = deserialize_value(str, nextindex, visited)
if nextindex == oldindex then error("Expected more bytes of input.") end
end
if not name or not deserializers[name] then
error(("Cannot deserialize class '%s'"):format(tostring(name)))
end end
local ret = deserializers[name](unpack(args)) local ret = deserializers[name](unpack(args))
visited[#visited + 1] = ret visited[#visited + 1] = ret
return ret, nextindex return ret, nextindex
elseif t == 210 then elseif t == 210 then
local length, dataindex = deserialize_value(str, index + 1, visited) local length, dataindex = number_from_str(str, index + 1)
local nextindex = dataindex + length local nextindex = dataindex + length
if not (length >= 0) then error("Bad string length") end
if #str < nextindex - 1 then error("Expected more bytes of string") end
local ret = loadstring(sub(str, dataindex, nextindex - 1)) local ret = loadstring(sub(str, dataindex, nextindex - 1))
visited[#visited + 1] = ret visited[#visited + 1] = ret
return ret, nextindex return ret, nextindex
elseif t == 211 then elseif t == 211 then
local res, nextindex = deserialize_value(str, index + 1, visited) local resname, nextindex = deserialize_value(str, index + 1, visited)
return resources_by_name[res], nextindex if resname == nil then error("Got nil resource name") end
elseif t == 212 then local res = resources_by_name[resname]
return number_from_str(str, index) if res == nil then
error(("No resources found for name '%s'"):format(tostring(resname)))
end
return res, nextindex
else else
error("Could not deserialize type byte " .. t .. ".") error("Could not deserialize type byte " .. t .. ".")
end end
end end
local function serialize(...) local function serialize(...)
local visited = {next = 1} local visited = {[NEXT] = 1, [CTORSTACK] = {}}
local accum = {} local accum = {}
for i = 1, select("#", ...) do for i = 1, select("#", ...) do
local x = select(i, ...) local x = select(i, ...)
types[type(x)](x, visited, accum) types[type(x)](x, visited, accum)
end end
return concat(accum) return concat(accum)
end end
local function make_file_writer(file) local function make_file_writer(file)
return setmetatable({}, { return setmetatable({}, {
__newindex = function(_, _, v) __newindex = function(_, _, v)
file:write(v) file:write(v)
end end
}) })
end end
local function serialize_to_file(path, mode, ...) local function serialize_to_file(path, mode, ...)
local file, err = io.open(path, mode) local file, err = io.open(path, mode)
assert(file, err) assert(file, err)
local visited = {next = 1} local visited = {[NEXT] = 1, [CTORSTACK] = {}}
local accum = make_file_writer(file) local accum = make_file_writer(file)
for i = 1, select("#", ...) do for i = 1, select("#", ...) do
local x = select(i, ...) local x = select(i, ...)
@@ -427,34 +481,38 @@ local function serialize_to_file(path, mode, ...)
-- flush the writer -- flush the writer
file:flush() file:flush()
file:close() file:close()
end end
local function writeFile(path, ...) local function writeFile(path, ...)
return serialize_to_file(path, "wb", ...) return serialize_to_file(path, "wb", ...)
end end
local function appendFile(path, ...) local function appendFile(path, ...)
return serialize_to_file(path, "ab", ...) return serialize_to_file(path, "ab", ...)
end end
local function deserialize(str, index) local function deserialize(str, index)
assert(type(str) == "string", "Expected string to deserialize.") assert(type(str) == "string", "Expected string to deserialize.")
local vals = {} local vals = {}
index = index or 1 index = index or 1
local visited = {} local visited = {}
local len = 0 local len = 0
local val local val
while index do while true do
val, index = deserialize_value(str, index, visited) local nextindex
if index then val, nextindex = deserialize_value(str, index, visited)
if nextindex > index then
len = len + 1 len = len + 1
vals[len] = val vals[len] = val
index = nextindex
else
break
end end
end end
return vals, len return vals, len
end end
local function deserializeN(str, n, index) local function deserializeN(str, n, index)
assert(type(str) == "string", "Expected string to deserialize.") assert(type(str) == "string", "Expected string to deserialize.")
n = n or 1 n = n or 1
assert(type(n) == "number", "Expected a number for parameter n.") assert(type(n) == "number", "Expected a number for parameter n.")
@@ -464,52 +522,54 @@ local function deserializeN(str, n, index)
local visited = {} local visited = {}
local len = 0 local len = 0
local val local val
while index and len < n do while len < n do
val, index = deserialize_value(str, index, visited) local nextindex
if index then val, nextindex = deserialize_value(str, index, visited)
if nextindex > index then
len = len + 1 len = len + 1
vals[len] = val vals[len] = val
index = nextindex
else
break
end end
end end
vals[len + 1] = index vals[len + 1] = index
return unpack(vals, 1, n + 1) return unpack(vals, 1, n + 1)
end
local function readFile(path)
local file, err = io.open(path, "rb")
if file == nil then
return nil, 0
end end
local function readFile(path)
local file, err = io.open(path, "rb")
assert(file, err)
local str = file:read("*all") local str = file:read("*all")
file:close() file:close()
return deserialize(str) return deserialize(str)
end
local function default_deserialize(metatable)
return function(...)
local ret = {}
for i = 1, select("#", ...), 2 do
ret[select(i, ...)] = select(i + 1, ...)
end end
return setmetatable(ret, metatable)
-- Resources
local function registerResource(resource, name)
type_check(name, "string", "name")
assert(not resources[resource],
"Resource already registered.")
assert(not resources_by_name[name],
format("Resource %q already exists.", name))
resources_by_name[name] = resource
resources[resource] = name
return resource
end end
end
local function default_serialize(x) local function unregisterResource(name)
assert(type(x) == "table", type_check(name, "string", "name")
"Default serialization for custom types only works for tables.") assert(resources_by_name[name], format("Resource %q does not exist.", name))
local args = {} local resource = resources_by_name[name]
local len = 0 resources_by_name[name] = nil
for k, v in pairs(x) do resources[resource] = nil
args[len + 1], args[len + 2] = k, v return resource
len = len + 2
end end
return unpack(args, 1, len)
end
-- Templating -- Templating
local function normalize_template(template) local function normalize_template(template)
local ret = {} local ret = {}
for i = 1, #template do for i = 1, #template do
ret[i] = template[i] ret[i] = template[i]
@@ -529,9 +589,9 @@ local function normalize_template(template)
ret[#ret + 1] = {name, normalize_template(template[name])} ret[#ret + 1] = {name, normalize_template(template[name])}
end end
return ret return ret
end end
local function templatepart_serialize(part, argaccum, x, len) local function templatepart_serialize(part, argaccum, x, len)
local extras = {} local extras = {}
local extracount = 0 local extracount = 0
for k, v in pairs(x) do for k, v in pairs(x) do
@@ -539,15 +599,19 @@ local function templatepart_serialize(part, argaccum, x, len)
extracount = extracount + 1 extracount = extracount + 1
end end
for i = 1, #part do for i = 1, #part do
extracount = extracount - 1 local name
if type(part[i]) == "table" then if type(part[i]) == "table" then
extras[part[i][1]] = nil name = part[i][1]
len = templatepart_serialize(part[i][2], argaccum, x[part[i][1]], len) len = templatepart_serialize(part[i][2], argaccum, x[name], len)
else else
extras[part[i]] = nil name = part[i]
len = len + 1 len = len + 1
argaccum[len] = x[part[i]] argaccum[len] = x[part[i]]
end end
if extras[name] ~= nil then
extracount = extracount - 1
extras[name] = nil
end
end end
if extracount > 0 then if extracount > 0 then
argaccum[len + 1] = extras argaccum[len + 1] = extras
@@ -555,9 +619,9 @@ local function templatepart_serialize(part, argaccum, x, len)
argaccum[len + 1] = nil argaccum[len + 1] = nil
end end
return len + 1 return len + 1
end end
local function templatepart_deserialize(ret, part, values, vindex) local function templatepart_deserialize(ret, part, values, vindex)
for i = 1, #part do for i = 1, #part do
local name = part[i] local name = part[i]
if type(name) == "table" then if type(name) == "table" then
@@ -576,51 +640,59 @@ local function templatepart_deserialize(ret, part, values, vindex)
end end
end end
return vindex + 1 return vindex + 1
end end
local function template_serializer_and_deserializer(metatable, template) local function template_serializer_and_deserializer(metatable, template)
return function(x) return function(x)
argaccum = {} local argaccum = {}
local len = templatepart_serialize(template, argaccum, x, 0) local len = templatepart_serialize(template, argaccum, x, 0)
return unpack(argaccum, 1, len) return unpack(argaccum, 1, len)
end, function(...) end, function(...)
local ret = {} local ret = {}
local len = select("#", ...)
local args = {...} local args = {...}
templatepart_deserialize(ret, template, args, 1) templatepart_deserialize(ret, template, args, 1)
return setmetatable(ret, metatable) return setmetatable(ret, metatable)
end end
end end
local function register(metatable, name, serialize, deserialize) -- Used to serialize classes withh custom serializers and deserializers.
-- If no _serialize or _deserialize (or no _template) value is found in the
-- metatable, then the metatable is registered as a resources.
local function register(metatable, name, serialize, deserialize)
if type(metatable) == "table" then
name = name or metatable.name name = name or metatable.name
serialize = serialize or metatable._serialize serialize = serialize or metatable._serialize
deserialize = deserialize or metatable._deserialize deserialize = deserialize or metatable._deserialize
if not serialize then if (not serialize) or (not deserialize) then
if metatable._template then if metatable._template then
-- Register as template
local t = normalize_template(metatable._template) local t = normalize_template(metatable._template)
serialize, deserialize = template_serializer_and_deserializer(metatable, t) serialize, deserialize = template_serializer_and_deserializer(metatable, t)
elseif not deserialize then
serialize = default_serialize
deserialize = default_deserialize(metatable)
else else
serialize = metatable -- Register the metatable as a resource. This is semantically
-- similar and more flexible (handles cycles).
registerResource(metatable, name)
return
end end
end end
type_check(metatable, "table", "metatable") elseif type(metatable) == "string" then
name = name or metatable
end
type_check(name, "string", "name") type_check(name, "string", "name")
type_check(serialize, "function", "serialize") type_check(serialize, "function", "serialize")
type_check(deserialize, "function", "deserialize") type_check(deserialize, "function", "deserialize")
assert(not ids[metatable], "Metatable already registered.") assert((not ids[metatable]) and (not resources[metatable]),
assert(not mts[name], ("Name %q already registered."):format(name)) "Metatable already registered.")
assert((not mts[name]) and (not resources_by_name[name]),
("Name %q already registered."):format(name))
mts[name] = metatable mts[name] = metatable
ids[metatable] = name ids[metatable] = name
serializers[name] = serialize serializers[name] = serialize
deserializers[name] = deserialize deserializers[name] = deserialize
return metatable return metatable
end end
local function unregister(item) local function unregister(item)
local name, metatable local name, metatable
if type(item) == "string" then -- assume name if type(item) == "string" then -- assume name
name, metatable = item, mts[item] name, metatable = item, mts[item]
@@ -628,15 +700,18 @@ local function unregister(item)
name, metatable = ids[item], item name, metatable = ids[item], item
end end
type_check(name, "string", "name") type_check(name, "string", "name")
type_check(metatable, "table", "metatable")
mts[name] = nil mts[name] = nil
if (metatable) then
resources[metatable] = nil
ids[metatable] = nil ids[metatable] = nil
end
serializers[name] = nil serializers[name] = nil
deserializers[name] = nil deserializers[name] = nil
resources_by_name[name] = nil;
return metatable return metatable
end end
local function registerClass(class, name) local function registerClass(class, name)
name = name or class.name name = name or class.name
if class.__instanceDict then -- middleclass if class.__instanceDict then -- middleclass
register(class.__instanceDict, name) register(class.__instanceDict, name)
@@ -644,29 +719,10 @@ local function registerClass(class, name)
register(class, name) register(class, name)
end end
return class return class
end end
local function registerResource(resource, name) return {
type_check(name, "string", "name") VERSION = "0.0-8",
assert(not resources[resource],
"Resource already registered.")
assert(not resources_by_name[name],
format("Resource %q already exists.", name))
resources_by_name[name] = resource
resources[resource] = name
return resource
end
local function unregisterResource(name)
type_check(name, "string", "name")
assert(resources_by_name[name], format("Resource %q does not exist.", name))
local resource = resources_by_name[name]
resources_by_name[name] = nil
resources[resource] = nil
return resource
end
return {
-- aliases -- aliases
s = serialize, s = serialize,
d = deserialize, d = deserialize,
@@ -685,5 +741,10 @@ return {
unregister = unregister, unregister = unregister,
registerResource = registerResource, registerResource = registerResource,
unregisterResource = unregisterResource, unregisterResource = unregisterResource,
registerClass = registerClass registerClass = registerClass,
}
newbinser = newbinser
}
end
return newbinser()

View File

@@ -17,7 +17,7 @@ function love.load()
require "load.version" require "load.version"
require "funcs" require "funcs"
TOUCH_SETTINGS = require 'mobile_libs.settings' TOUCH_SETTINGS = require 'mobile_libs.touch_settings'
BUTTON = require 'mobile_libs.simple-button' BUTTON = require 'mobile_libs.simple-button'
BUTTON.setDefaultOption{ BUTTON.setDefaultOption{
draw = function(self) draw = function(self)
@@ -62,6 +62,8 @@ function love.load()
font=font_3x5_2, font=font_3x5_2,
} }
require 'mobile_libs.vctrl' require 'mobile_libs.vctrl'
VCTRL.new(TOUCH_SETTINGS.bind[1])
VCTRL.toggle(true)
loadSave() loadSave()
require "scene" require "scene"
@@ -136,6 +138,7 @@ function love.draw()
love.graphics.scale(scale_factor) love.graphics.scale(scale_factor)
scene:render() scene:render()
if scene.title ~= TouchConfigScene.title then VCTRL.draw() end
if config.gamesettings.display_gamemode == 1 or scene.title == "Title" then if config.gamesettings.display_gamemode == 1 or scene.title == "Title" then
love.graphics.setFont(font_3x5_2) love.graphics.setFont(font_3x5_2)
@@ -335,10 +338,51 @@ function love.joystickhat(joystick, hat, direction)
end end
end end
function love.mousepressed(x, y, b, isTouch, presses)
if isTouch then return end
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
if scene.title ~= 'Touchscreen configuration' and VCTRL.press(x, y, 1) then
return -- Avoid duplicate
end
scene:onInputPress{type = "mouse", x = x, y = y, presses = presses}
end
function love.mousereleased(x, y, b, isTouch, presses)
if isTouch then return end
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
if scene.title ~= 'Touchscreen configuration' and VCTRL.release(1) then
return -- Avoid duplicate
end
scene:onInputRelease{type = "mouse", x = x, y = y, presses = presses}
end
function love.mousemoved(x, y, dx, dy, isTouch)
if isTouch then return end
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
local dx,dy=dx/SCREEN_SCALE_FACTOR,dy/SCREEN_SCALE_FACTOR
scene:onInputMove{type = "mouse", x = x, y = y, dx = dx, dy = dy}
end
function love.wheelmoved(x, y) function love.wheelmoved(x, y)
scene:onInputPress({input=nil, type="wheel", x=x, y=y}) scene:onInputPress({input=nil, type="wheel", x=x, y=y})
end end
function love.touchpressed(id,x,y)
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
if scene.title ~= 'Touchscreen configuration' and VCTRL.press(x, y, id) then
return -- Avoid duplicate
end
scene:onInputPress{type = "touch", x = x, y = y, dx = 0, dy = 0, id = id}
end
function love.touchreleased(id,x,y)
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
if scene.title ~= 'Touchscreen configuration' and VCTRL.release(id) then
return -- Avoid duplicate
end
scene:onInputRelease{type = "touch", x = x, y = y, dx = 0, dy = 0, id = id}
end
function love.touchmoved(id,x,y,dx,dy)
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
scene:onInputMove{type = "touch", x = x, y = y, dx = dx, dy = dy, id = id}
end
function love.resize(w, h) function love.resize(w, h)
GLOBAL_CANVAS:release() GLOBAL_CANVAS:release()
GLOBAL_CANVAS = love.graphics.newCanvas(w, h) GLOBAL_CANVAS = love.graphics.newCanvas(w, h)

496
mobile_libs/bitser.lua Normal file
View File

@@ -0,0 +1,496 @@
--[[
Copyright (c) 2020, Jasmijn Wellner
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
]]
local VERSION = '1.1'
local floor = math.floor
local pairs = pairs
local type = type
local insert = table.insert
local getmetatable = getmetatable
local setmetatable = setmetatable
local ffi = require("ffi")
local buf_pos = 0
local buf_size = -1
local buf = nil
local buf_is_writable = true
local writable_buf = nil
local writable_buf_size = nil
local includeMetatables = true -- togglable with bitser.includeMetatables(false)
local SEEN_LEN = {}
local function Buffer_prereserve(min_size)
if buf_size < min_size then
buf_size = min_size
buf = ffi.new("uint8_t[?]", buf_size)
buf_is_writable = true
end
end
local function Buffer_clear()
buf_size = -1
buf = nil
buf_is_writable = true
writable_buf = nil
writable_buf_size = nil
end
local function Buffer_makeBuffer(size)
if not buf_is_writable then
buf = writable_buf
buf_size = writable_buf_size
writable_buf = nil
writable_buf_size = nil
buf_is_writable = true
end
buf_pos = 0
Buffer_prereserve(size)
end
local function Buffer_newReader(str)
Buffer_makeBuffer(#str)
ffi.copy(buf, str, #str)
end
local function Buffer_newDataReader(data, size)
if buf_is_writable then
writable_buf = buf
writable_buf_size = buf_size
end
buf_is_writable = false
buf_pos = 0
buf_size = size
buf = ffi.cast("uint8_t*", data)
end
local function Buffer_reserve(additional_size)
while buf_pos + additional_size > buf_size do
buf_size = buf_size * 2
local oldbuf = buf
buf = ffi.new("uint8_t[?]", buf_size)
buf_is_writable = true
ffi.copy(buf, oldbuf, buf_pos)
end
end
local function Buffer_write_byte(x)
Buffer_reserve(1)
buf[buf_pos] = x
buf_pos = buf_pos + 1
end
local function Buffer_write_raw(data, len)
Buffer_reserve(len)
ffi.copy(buf + buf_pos, data, len)
buf_pos = buf_pos + len
end
local function Buffer_write_string(s)
Buffer_write_raw(s, #s)
end
local function Buffer_write_data(ct, len, ...)
Buffer_write_raw(ffi.new(ct, ...), len)
end
local function Buffer_ensure(numbytes)
if buf_pos + numbytes > buf_size then
error("malformed serialized data")
end
end
local function Buffer_read_byte()
Buffer_ensure(1)
local x = buf[buf_pos]
buf_pos = buf_pos + 1
return x
end
local function Buffer_read_string(len)
Buffer_ensure(len)
local x = ffi.string(buf + buf_pos, len)
buf_pos = buf_pos + len
return x
end
local function Buffer_read_raw(data, len)
ffi.copy(data, buf + buf_pos, len)
buf_pos = buf_pos + len
return data
end
local function Buffer_read_data(ct, len)
return Buffer_read_raw(ffi.new(ct), len)
end
local resource_registry = {}
local resource_name_registry = {}
local class_registry = {}
local class_name_registry = {}
local classkey_registry = {}
local class_deserialize_registry = {}
local serialize_value
local function write_number(value, _)
if floor(value) == value and value >= -2147483648 and value <= 2147483647 then
if value >= -27 and value <= 100 then
--small int
Buffer_write_byte(value + 27)
elseif value >= -32768 and value <= 32767 then
--short int
Buffer_write_byte(250)
Buffer_write_data("int16_t[1]", 2, value)
else
--long int
Buffer_write_byte(245)
Buffer_write_data("int32_t[1]", 4, value)
end
else
--double
Buffer_write_byte(246)
Buffer_write_data("double[1]", 8, value)
end
end
local function write_string(value, _)
if #value < 32 then
--short string
Buffer_write_byte(192 + #value)
else
--long string
Buffer_write_byte(244)
write_number(#value)
end
Buffer_write_string(value)
end
local function write_nil(_, _)
Buffer_write_byte(247)
end
local function write_boolean(value, _)
Buffer_write_byte(value and 249 or 248)
end
local function write_table(value, seen)
local classkey
local metatable = getmetatable(value)
local classname = (class_name_registry[value.class] -- MiddleClass
or class_name_registry[value.__baseclass] -- SECL
or class_name_registry[metatable] -- hump.class
or class_name_registry[value.__class__] -- Slither
or class_name_registry[value.__class]) -- Moonscript class
if classname then
classkey = classkey_registry[classname]
Buffer_write_byte(242)
serialize_value(classname, seen)
elseif includeMetatables and metatable then
Buffer_write_byte(253)
else
Buffer_write_byte(240)
end
local len = #value
write_number(len, seen)
for i = 1, len do
serialize_value(value[i], seen)
end
local klen = 0
for k in pairs(value) do
if (type(k) ~= 'number' or floor(k) ~= k or k > len or k < 1) and k ~= classkey then
klen = klen + 1
end
end
write_number(klen, seen)
for k, v in pairs(value) do
if (type(k) ~= 'number' or floor(k) ~= k or k > len or k < 1) and k ~= classkey then
serialize_value(k, seen)
serialize_value(v, seen)
end
end
if includeMetatables and metatable and not classname then
serialize_value(metatable, seen)
end
end
local function write_cdata(value, seen)
local ty = ffi.typeof(value)
if ty == value then
-- ctype
Buffer_write_byte(251)
serialize_value(tostring(ty):sub(7, -2), seen)
return
end
-- cdata
Buffer_write_byte(252)
serialize_value(ty, seen)
local len = ffi.sizeof(value)
write_number(len)
Buffer_write_raw(ffi.typeof('$[1]', ty)(value), len)
end
local types = {number = write_number, string = write_string, table = write_table, boolean = write_boolean, ["nil"] = write_nil, cdata = write_cdata}
serialize_value = function(value, seen)
if seen[value] then
local ref = seen[value]
if ref < 64 then
--small reference
Buffer_write_byte(128 + ref)
else
--long reference
Buffer_write_byte(243)
write_number(ref, seen)
end
return
end
local t = type(value)
if t ~= 'number' and t ~= 'boolean' and t ~= 'nil' and t ~= 'cdata' then
seen[value] = seen[SEEN_LEN]
seen[SEEN_LEN] = seen[SEEN_LEN] + 1
end
if resource_name_registry[value] then
local name = resource_name_registry[value]
if #name < 16 then
--small resource
Buffer_write_byte(224 + #name)
Buffer_write_string(name)
else
--long resource
Buffer_write_byte(241)
write_string(name, seen)
end
return
end
(types[t] or
error("cannot serialize type " .. t)
)(value, seen)
end
local function serialize(value)
Buffer_makeBuffer(4096)
local seen = {[SEEN_LEN] = 0}
serialize_value(value, seen)
end
local function add_to_seen(value, seen)
insert(seen, value)
return value
end
local function reserve_seen(seen)
insert(seen, 42)
return #seen
end
local function deserialize_value(seen)
local t = Buffer_read_byte()
if t < 128 then
--small int
return t - 27
elseif t < 192 then
--small reference
return seen[t - 127]
elseif t < 224 then
--small string
return add_to_seen(Buffer_read_string(t - 192), seen)
elseif t < 240 then
--small resource
return add_to_seen(resource_registry[Buffer_read_string(t - 224)], seen)
elseif t == 240 or t == 253 then
--table
local v = add_to_seen({}, seen)
local len = deserialize_value(seen)
for i = 1, len do
v[i] = deserialize_value(seen)
end
len = deserialize_value(seen)
for _ = 1, len do
local key = deserialize_value(seen)
v[key] = deserialize_value(seen)
end
if t == 253 then
if includeMetatables then
setmetatable(v, deserialize_value(seen))
end
end
return v
elseif t == 241 then
--long resource
local idx = reserve_seen(seen)
local value = resource_registry[deserialize_value(seen)]
seen[idx] = value
return value
elseif t == 242 then
--instance
local instance = add_to_seen({}, seen)
local classname = deserialize_value(seen)
local class = class_registry[classname]
local classkey = classkey_registry[classname]
local deserializer = class_deserialize_registry[classname]
local len = deserialize_value(seen)
for i = 1, len do
instance[i] = deserialize_value(seen)
end
len = deserialize_value(seen)
for _ = 1, len do
local key = deserialize_value(seen)
instance[key] = deserialize_value(seen)
end
if classkey then
instance[classkey] = class
end
return deserializer(instance, class)
elseif t == 243 then
--reference
return seen[deserialize_value(seen) + 1]
elseif t == 244 then
--long string
return add_to_seen(Buffer_read_string(deserialize_value(seen)), seen)
elseif t == 245 then
--long int
return Buffer_read_data("int32_t[1]", 4)[0]
elseif t == 246 then
--double
return Buffer_read_data("double[1]", 8)[0]
elseif t == 247 then
--nil
return nil
elseif t == 248 then
--false
return false
elseif t == 249 then
--true
return true
elseif t == 250 then
--short int
return Buffer_read_data("int16_t[1]", 2)[0]
elseif t == 251 then
--ctype
return ffi.typeof(deserialize_value(seen))
elseif t == 252 then
local ctype = deserialize_value(seen)
local len = deserialize_value(seen)
local read_into = ffi.typeof('$[1]', ctype)()
Buffer_read_raw(read_into, len)
return ctype(read_into[0])
else
error("unsupported serialized type " .. t)
end
end
local function deserialize_MiddleClass(instance, class)
return setmetatable(instance, class.__instanceDict)
end
local function deserialize_SECL(instance, class)
return setmetatable(instance, getmetatable(class))
end
local deserialize_humpclass = setmetatable
local function deserialize_Slither(instance, class)
return getmetatable(class).allocate(instance)
end
local function deserialize_Moonscript(instance, class)
return setmetatable(instance, class.__base)
end
return {dumps = function(value)
serialize(value)
return ffi.string(buf, buf_pos)
end, dumpLoveFile = function(fname, value)
serialize(value)
assert(love.filesystem.write(fname, ffi.string(buf, buf_pos)))
end, loadLoveFile = function(fname)
local serializedData, error = love.filesystem.newFileData(fname)
assert(serializedData, error)
Buffer_newDataReader(serializedData:getPointer(), serializedData:getSize())
local value = deserialize_value({})
-- serializedData needs to not be collected early in a tail-call
-- so make sure deserialize_value returns before loadLoveFile does
return value
end, loadData = function(data, size)
if size == 0 then
error('cannot load value from empty data')
end
Buffer_newDataReader(data, size)
return deserialize_value({})
end, loads = function(str)
if #str == 0 then
error('cannot load value from empty string')
end
Buffer_newReader(str)
return deserialize_value({})
end, includeMetatables = function(bool)
includeMetatables = not not bool
end, register = function(name, resource)
assert(not resource_registry[name], name .. " already registered")
resource_registry[name] = resource
resource_name_registry[resource] = name
return resource
end, unregister = function(name)
resource_name_registry[resource_registry[name]] = nil
resource_registry[name] = nil
end, registerClass = function(name, class, classkey, deserializer)
if not class then
class = name
name = class.__name__ or class.name or class.__name
end
if not classkey then
if class.__instanceDict then
-- assume MiddleClass
classkey = 'class'
elseif class.__baseclass then
-- assume SECL
classkey = '__baseclass'
end
-- assume hump.class, Slither, Moonscript class or something else that doesn't store the
-- class directly on the instance
end
if not deserializer then
if class.__instanceDict then
-- assume MiddleClass
deserializer = deserialize_MiddleClass
elseif class.__baseclass then
-- assume SECL
deserializer = deserialize_SECL
elseif class.__index == class then
-- assume hump.class
deserializer = deserialize_humpclass
elseif class.__name__ then
-- assume Slither
deserializer = deserialize_Slither
elseif class.__base then
-- assume Moonscript class
deserializer = deserialize_Moonscript
else
error("no deserializer given for unsupported class library")
end
end
class_registry[name] = class
classkey_registry[name] = classkey
class_deserialize_registry[name] = deserializer
class_name_registry[class] = name
return class
end, unregisterClass = function(name)
class_name_registry[class_registry[name]] = nil
classkey_registry[name] = nil
class_deserialize_registry[name] = nil
class_registry[name] = nil
end, reserveBuffer = Buffer_prereserve, clearBuffer = Buffer_clear, version = VERSION}

View File

@@ -1,6 +1,6 @@
local FILE = {} local FILE = {}
local binser = require "libs.binser" local binser = require "libs.binser"
-- local bitser = require "libs.bitser" local bitser = require "mobile_libs.bitser"
local serializer_used local serializer_used
@@ -37,5 +37,5 @@ end
return function(lib_name) return function(lib_name)
assert(lib_name == 'bitser' or lib_name == 'binser', '[lib_name] must be "bitser" or "binser"') assert(lib_name == 'bitser' or lib_name == 'binser', '[lib_name] must be "bitser" or "binser"')
serializer_used = lib_name serializer_used = lib_name
_G.FILE = FILE return FILE
end end

View File

@@ -1,38 +0,0 @@
local fs = love.filesystem
local CONFIG_FILE = '/mobile/touch_config.txt'
local _settings = fs.read(CONFIG_FILE) ~= nil and FILE.read(CONFIG_FILE) or {}
local _defaultSettings = {
firstTime = true,
__default__={
{type='button',x= 70,y=280,key= 'up',r=45,iconSize=60,alpha=0.4},
{type='button',x= 70,y=430,key= 'down',r=45,iconSize=60,alpha=0.4},
{type='button',x= -5,y=355,key= 'left',r=45,iconSize=60,alpha=0.4},
{type='button',x= 145,y=355,key= 'right',r=45,iconSize=60,alpha=0.4},
{type='button',x=640- -5,y=355,key= 'rotate_left',r=45,iconSize=60,alpha=0.4},
{type='button',x=640-145,y=355,key= 'rotate_left2',r=45,iconSize=60,alpha=0.4},
{type='button',x=640- 70,y=430,key= 'rotate_right',r=45,iconSize=60,alpha=0.4},
{type='button',x=640- 70,y=280,key='rotate_right2',r=45,iconSize=60,alpha=0.4},
{type='button',x=320, y=420,key= 'restart',r=35,iconSize=60,alpha=0.4},
},
---@type table<string,string>[]
bind = {},
}
return setmetatable(
{__default__ = _defaultSettings},
{
__index = function(_, k)
if _settings[k] == nil then
_settings[k] = _defaultSettings[k]
FILE.write(CONFIG_FILE,_settings)
end
return _settings[k]
end,
__newindex = function(_, k, v)
_settings[k] = v
FILE.write(CONFIG_FILE,_settings)
end
}
)

View File

@@ -0,0 +1,67 @@
local fs = love.filesystem
local FILE=require'mobile_libs.file''binser'
local CONFIG_FILE = 'mobile/touch_config.txt'
love.filesystem.createDirectory('mobile')
local _settings = fs.read(CONFIG_FILE) ~= nil and FILE.read(CONFIG_FILE) or {}
local _defaultSettings = {
firstTime = true,
---@type table<string,string>[]
bind = {
{
{type='button',x= 1 * 70 + 120,y = 1 * 70 + 120, r = 30, key= 'up',iconSize=50},
{type='button',x= 2 * 70 + 120,y = 1 * 70 + 120, r = 30, key= 'down',iconSize=50},
{type='button',x= 3 * 70 + 120,y = 1 * 70 + 120, r = 30, key= 'left',iconSize=50},
{type='button',x= 4 * 70 + 120,y = 1 * 70 + 120, r = 30, key= 'right',iconSize=50},
{type='button',x= 5 * 70 + 120,y = 1 * 70 + 120, r = 30, key= 'menu_decide',iconSize=50},
{type='button',x= 1 * 70 + 120,y = 2 * 70 + 120, r = 30, key= 'rotate_left',iconSize=50},
{type='button',x= 2 * 70 + 120,y = 2 * 70 + 120, r = 30, key= 'rotate_left2',iconSize=50},
{type='button',x= 3 * 70 + 120,y = 2 * 70 + 120, r = 30, key= 'rotate_right',iconSize=50},
{type='button',x= 4 * 70 + 120,y = 2 * 70 + 120, r = 30, key= 'rotate_right2',iconSize=50},
{type='button',x= 5 * 70 + 120,y = 2 * 70 + 120, r = 30, key= 'retry',iconSize=50},
{type='button',x= 1 * 70 + 120,y = 3 * 70 + 120, r = 30, key= '1',iconSize=50},
{type='button',x= 2 * 70 + 120,y = 3 * 70 + 120, r = 30, key= '2',iconSize=50},
{type='button',x= 3 * 70 + 120,y = 3 * 70 + 120, r = 30, key= '3',iconSize=50},
{type='button',x= 4 * 70 + 120,y = 3 * 70 + 120, r = 30, key= '4',iconSize=50},
{type='button',x= 5 * 70 + 120,y = 3 * 70 + 120, r = 30, key= '5',iconSize=50},
-- {type='button',x= 1 * 70 + 120,y = 4 * 70 + 120, r = 30, key= 'up',iconSize=50},
{type='button',x= 2 * 70 + 120,y = 4 * 70 + 120, r = 30, key='touch_settings',iconSize=50},
{type='button',x= 3 * 70 + 120,y = 4 * 70 + 120, r = 30, key= 'align_view',iconSize=50},
{type='button',x= 4 * 70 + 120,y = 4 * 70 + 120, r = 30, key= 'menu_back',iconSize=50},
-- {type='button',x= 5 * 70 + 120,y = 4 * 70 + 120, r = 30, key= 'up',iconSize=50},
}, -- 1
{
{type='button',x= 70,y=280,key= 'up',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x= 70,y=430,key= 'down',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x= -5,y=355,key= 'left',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x= 145,y=355,key= 'right',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x=640- -5,y=355,key= 'rotate_left',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x=640-145,y=355,key= 'rotate_left2',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x=640- 70,y=430,key= 'rotate_right',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x=640- 70,y=280,key='rotate_right2',r=45,iconSize=60,alpha=0.4,shape='circle'},
{type='button',x=320, y=420,key= 'retry',r=35,iconSize=60,alpha=0.4,shape='circle'},
} -- 2
},
}
return setmetatable(
{__default__ = _defaultSettings},
{
__index = function(_, k)
if _settings[k] == nil then
_settings[k] = _defaultSettings[k]
FILE.write(CONFIG_FILE,_settings)
end
return _settings[k]
end,
__newindex = function(_, k, v)
_settings[k] = v
FILE.write(CONFIG_FILE,_settings)
end
}
)

View File

@@ -15,34 +15,34 @@ local function mDrawQ(obj,quad,x,y,a,k)
love.graphics.draw(obj,quad,x,y,a,k,nil,w*.5,h*.5) love.graphics.draw(obj,quad,x,y,a,k,nil,w*.5,h*.5)
end end
local empty_quad local alternativePfunction={}
local alternativeRfunction={
touch_settings=function()
if (
scene.title ~= 'Game' and
scene.title ~= TouchConfigScene.title
) then
scene = TouchConfigScene()
end
end
}
local empty_quad=gc_newQuad(1,1,1,1,1,1)
-- A table containing quads used to draw icons for virtual control system. -- A table containing quads used to draw icons for virtual control system.
-- local virtual_quad=setmetatable((function()
-- local t={}
-- local w=180
-- empty_quad=gc_newQuad(0,0,1,1,5*w,7*w)
-- for i,name in next,{
-- 'left','right','up','down','',
-- 'rotate_right','rotate_left','','','',
-- '','','','','',
-- '','','','','',
-- '','','menu_back','','',
-- '','','','','',
-- '','','','','menu_decide',
-- } do if #name>0 then t[name]=gc_newQuad((i-1)%5*w,math.floor((i-1)/5)*w,w,w,5*w,7*w) end end
-- t.rotate_right2, t.rotate_left2 = t.rotate_right, t.rotate_left
-- return t
-- end)(),{
-- __index=function() return empty_quad end
-- })
local virtual_quad=setmetatable((function() local virtual_quad=setmetatable((function()
local t={} local t={}
local w=180 local w=180
empty_quad=gc_newQuad(0,0,1,1,5*w,2*w) empty_quad=gc_newQuad(0,0,1,1,5*w,7*w)
for i,name in next,{ for i,name in next,{
'left','right','up','down','restart', 'left','right','up','down','',
'rotate_right','rotate_left','rotate_right2','rotate_left2' 'rotate_right','rotate_left','rotate_180','hold','align_view',
} do if #name>0 then t[name]=gc_newQuad((i-1)%5*w,math.floor((i-1)/5)*w,w,w,5*w,2*w) end end '','','','','',
'1','2','3','4','5',
'','retry','','menu_back','',
'touch_settings','','','','',
'','','','','menu_decide',
} do if #name>0 then t[name]=gc_newQuad((i-1)%5*w,math.floor((i-1)/5)*w,w,w,5*w,7*w) end end
t.rotate_right2, t.rotate_left2 = t.rotate_right, t.rotate_left
return t return t
end)(),{ end)(),{
__index=function() return empty_quad end __index=function() return empty_quad end
@@ -61,11 +61,11 @@ function control_type.button:new(data)
x=data.x or 320, x=data.x or 320,
y=data.y or 240, y=data.y or 240,
r=data.r or 80, -- size r=data.r or 80, -- size
shape=data.shape or 'circle', shape=data.shape or 'square',
key=data.key or 'X', key=data.key or 'X',
iconSize=data.iconSize or 60, iconSize=data.iconSize or 60,
alpha=data.alpha or 0.75, alpha=data.alpha or 0.75,
quad=virtual_quad[data.key] quad=virtual_quad[data.key],
},self) },self)
end end
function control_type.button:export() function control_type.button:export()
@@ -78,7 +78,7 @@ function control_type.button:export()
shape = self.shape, shape = self.shape,
key = self.key, key = self.key,
iconSize = self.iconSize, iconSize = self.iconSize,
alpha = self.alpha alpha = self.alpha,
} }
end end
function control_type.button:reset() function control_type.button:reset()
@@ -91,13 +91,21 @@ function control_type.button:press(_,_,id)
self.lastPressTime=love.timer.getTime() self.lastPressTime=love.timer.getTime()
self.pressingID=id self.pressingID=id
-- love.keypressed(self.key, love.keyboard.getScancodeFromKey(self.key)) -- love.keypressed(self.key, love.keyboard.getScancodeFromKey(self.key))
SCENE:onInputPress{input=self.key,type="virtual"} if alternativePfunction[self.key] then
alternativePfunction[self.key]()
else
scene:onInputPress{input=self.key,type="virtual"}
end
end end
function control_type.button:release() function control_type.button:release()
self.pressed=false self.pressed=false
self.pressingID=false self.pressingID=false
-- love.keyreleased(self.key,love.keyboard.getScancodeFromKey(self.key)) -- love.keyreleased(self.key,love.keyboard.getScancodeFromKey(self.key))
SCENE:onInputRelease{input=self.key,type="virtual"} if alternativeRfunction[self.key] then
alternativeRfunction[self.key]()
else
scene:onInputRelease{input=self.key,type="virtual"}
end
end end
function control_type.button:drag(dx,dy) function control_type.button:drag(dx,dy)
self.x,self.y=self.x+dx,self.y+dy self.x,self.y=self.x+dx,self.y+dy
@@ -158,6 +166,8 @@ VCTRL.hasChanged = false
---@field iconSize? number ---@field iconSize? number
---@field alpha? number ---@field alpha? number
---@field show? boolean ---@field show? boolean
---@field pressFunc function|nil
---@field releaseFunc function|nil
---@param ... VCTRL.data[] ---@param ... VCTRL.data[]
---Adding (multiple) virtual button(s) ---Adding (multiple) virtual button(s)
@@ -165,6 +175,14 @@ function VCTRL.new(...)
for _,d in pairs(...) do table.insert(VCTRL,control_type[d.type]:new(d)) end for _,d in pairs(...) do table.insert(VCTRL,control_type[d.type]:new(d)) end
end end
---@param key string
---@param pfunc function|nil
---@param rfunc function|nil
function VCTRL.setAltFunction(key,pfunc,rfunc)
alternativePfunction[key] = pfunc
alternativeRfunction[key] = rfunc
end
---@param toggle boolean|false ---@param toggle boolean|false
---Enabling virtual control or not ---Enabling virtual control or not
function VCTRL.toggle(toggle) function VCTRL.toggle(toggle)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -1,6 +1,6 @@
---@diagnostic disable: cast-local-type ---@diagnostic disable: cast-local-type
local TouchConfigScene = Scene:extend() local TouchConfigScene = Scene:extend()
TouchConfigScene.title = "Touchscreen config" TouchConfigScene.title = "Touchscreen configuration"
local function roundUnit(n,u) local function roundUnit(n,u)
local u = u or 1 local u = u or 1
@@ -12,6 +12,12 @@ local function drawText(text, x, y, size, orientation, color)
love.graphics.setColor(color) love.graphics.setColor(color)
love.graphics.printf(text, x, y, size*2, orientation, nil, 0.5) love.graphics.printf(text, x, y, size*2, orientation, nil, 0.5)
end end
local function math_clamp(x, min, max)
if max < min then
min, max = max, min
end
return x < min and min or (x > max and max or x)
end
local buttonList local buttonList
local sliderList = {} local sliderList = {}
@@ -25,9 +31,9 @@ local function exitSceneFunc(saved)
VCTRL.release() VCTRL.release()
BUTTON.release(buttonList) BUTTON.release(buttonList)
if TOUCH_SETTINGS.firstTime and not saved then if TOUCH_SETTINGS.firstTime and not saved then
SCENE = InputConfigScene(true) scene = InputConfigScene(true)
else else
SCENE = TitleScene() scene = TitleScene()
TOUCH_SETTINGS.firstTime = false TOUCH_SETTINGS.firstTime = false
end end
end end
@@ -56,7 +62,7 @@ buttonList = {
-- codeWhenReleased = function() -- codeWhenReleased = function()
-- VCTRL.release() -- VCTRL.release()
-- BUTTON.release(buttonList) -- BUTTON.release(buttonList)
-- SCENE = TouchConfigPreviewScene() -- scene = TouchConfigPreviewScene()
-- end -- end
-- }, -- },
resetAll = BUTTON.new{ resetAll = BUTTON.new{
@@ -72,8 +78,8 @@ buttonList = {
VCTRL.focus = nil; focusingButton = nil VCTRL.focus = nil; focusingButton = nil
VCTRL.hasChanged = false VCTRL.hasChanged = false
VCTRL.clearAll() VCTRL.clearAll()
VCTRL.new(TOUCH_SETTINGS.__default__) VCTRL.new(TOUCH_SETTINGS.__default__.bind[1])
TOUCH_SETTINGS.bind[1] = TOUCH_SETTINGS.__default__ TOUCH_SETTINGS.bind[1] = TOUCH_SETTINGS.__default__.bind[1]
end end
end end
}, },
@@ -94,7 +100,7 @@ buttonList = {
exitSceneFunc(true) exitSceneFunc(true)
elseif selection == 2 then elseif selection == 2 then
VCTRL.clearAll() VCTRL.clearAll()
VCTRL.new(TOUCH_SETTINGS.bind[1]) VCTRL.new(TOUCH_SETTINGS.bind[1] or {})
-- love.window.showMessageBox("Discarded!", "Your changes was discarded!") -- love.window.showMessageBox("Discarded!", "Your changes was discarded!")
exitSceneFunc() exitSceneFunc()
@@ -239,16 +245,12 @@ function TouchConfigScene:render()
for _, v in ipairs(VCTRL) do for _, v in ipairs(VCTRL) do
if v ~= focusingButton then if v ~= focusingButton then
v:draw( v:draw(v.alpha * (focusingButton and 0.25 or 1))
focusingButton and
(v.show and 0.5 or 0.1) or
(v.show and 1 or 0.5)
)
end end
end end
if focusingButton then if focusingButton then
focusingButton:draw( focusingButton:draw(
math.clamp( math_clamp(
math.sin(love.timer.getTime()*4)*.5+0.1, math.sin(love.timer.getTime()*4)*.5+0.1,
focusingButton.show and 1 or 0.1, 1 focusingButton.show and 1 or 0.1, 1
) )
@@ -259,7 +261,7 @@ function TouchConfigScene:render()
BUTTON.draw(buttonList) BUTTON.draw(buttonList)
end end
---@param e SCENE_onInput
function TouchConfigScene:onInputMove(e) function TouchConfigScene:onInputMove(e)
if e.type == "touch" or (e.type == "mouse" and love.mouse.isDown(1)) then if e.type == "touch" or (e.type == "mouse" and love.mouse.isDown(1)) then
if VCTRL.drag(e.dx, e.dy, e.id or 1) then VCTRL.hasChanged = true end if VCTRL.drag(e.dx, e.dy, e.id or 1) then VCTRL.hasChanged = true end
@@ -267,7 +269,7 @@ function TouchConfigScene:onInputMove(e)
BUTTON.checkHovering(buttonList, e.x, e.y) BUTTON.checkHovering(buttonList, e.x, e.y)
end end
end end
---@param e SCENE_onInput
function TouchConfigScene:onInputPress(e) function TouchConfigScene:onInputPress(e)
if e.type == "mouse" or e.type == "touch" then if e.type == "mouse" or e.type == "touch" then
if not ( if not (
@@ -281,7 +283,7 @@ function TouchConfigScene:onInputPress(e)
end end
end end
end end
---@param e SCENE_onInput
function TouchConfigScene:onInputRelease(e) function TouchConfigScene:onInputRelease(e)
if e.type == "mouse" or e.type == "touch" then if e.type == "mouse" or e.type == "touch" then
if not BUTTON.release(buttonList, e.x, e.y, e.id) then if not BUTTON.release(buttonList, e.x, e.y, e.id) then

View File

@@ -5,8 +5,9 @@ Scene = Object:extend()
function Scene:new() end function Scene:new() end
function Scene:update() end function Scene:update() end
function Scene:render() end function Scene:render() end
function Scene:onInputPress() end function Scene:onInputMove(e) end
function Scene:onInputRelease() end function Scene:onInputPress(e) end
function Scene:onInputRelease(e) end
ExitScene = require "scene.exit" ExitScene = require "scene.exit"
GameScene = require "scene.game" GameScene = require "scene.game"