mirror of
https://gitea.com/SweetSea-ButImNotSweet/tromi_mobile.git
synced 2025-01-08 17:33:09 +08:00
Replace `binser with bitser`
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
local Object = require 'libs.classic'
|
local Object = require 'libs.classic'
|
||||||
local bit = require("bit")
|
local bit = require("bit")
|
||||||
local lualzw = require 'libs.lualzw'
|
local lualzw = require 'libs.lualzw'
|
||||||
local binser = require 'libs.binser'
|
local bitser = require 'libs.bitser'
|
||||||
require 'funcs'
|
require 'funcs'
|
||||||
require 'load.save'
|
require 'load.save'
|
||||||
|
|
||||||
@@ -141,10 +141,8 @@ function GameMode:new(player_name, input_file, replay_grade)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:readGradeHistory()
|
function GameMode:readGradeHistory()
|
||||||
outfile = love.filesystem.newFile(SAVE_DIR..self.player_name.."_grade_history.sav", 'r')
|
if love.filesystem.getInfo(SAVE_DIR..self.player_name.."_grade_history.sav") then
|
||||||
if outfile ~= nil then
|
self.grade_history = bitser.loadLoveFile(SAVE_DIR..self.player_name.."_grade_history.sav")
|
||||||
self.grade_history = binser.deserialize(outfile:read('a'))[1]
|
|
||||||
outfile:close()
|
|
||||||
else
|
else
|
||||||
self.grade_history = {1,2,0,0}
|
self.grade_history = {1,2,0,0}
|
||||||
end
|
end
|
||||||
@@ -153,17 +151,13 @@ function GameMode:readGradeHistory()
|
|||||||
if self.grade > 1 then
|
if self.grade > 1 then
|
||||||
temp_grade = copy(self.grade_history)
|
temp_grade = copy(self.grade_history)
|
||||||
temp_grade[2] = 0
|
temp_grade[2] = 0
|
||||||
gradefile = love.filesystem.newFile(SAVE_DIR..self.player_name.."_grade_history.sav", 'w')
|
bitser.dumpLoveFile(SAVE_DIR..self.player_name.."_grade_history.sav", temp_grade)
|
||||||
gradefile:write(binser.serialize(temp_grade))
|
end
|
||||||
gradefile:close()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function GameMode:readHiScores()
|
function GameMode:readHiScores()
|
||||||
outfile = love.filesystem.newFile(HIscoreFILE, 'r')
|
if love.filesystem.getInfo(HIscoreFILE) then
|
||||||
if outfile ~= nil then
|
self.hi_scores = bitser.loadLoveFile(HIscoreFILE)
|
||||||
self.hi_scores = binser.deserialize(outfile:read())[1]
|
|
||||||
outfile:close()
|
|
||||||
else
|
else
|
||||||
self.hi_scores = {"TRO",0,"MIT",0,"ROM",0,"ITR",0,"OMI",0}
|
self.hi_scores = {"TRO",0,"MIT",0,"ROM",0,"ITR",0,"OMI",0}
|
||||||
end
|
end
|
||||||
@@ -231,9 +225,7 @@ function GameMode:updateHiScores()
|
|||||||
self.hi_scores[score_position] = self.grade_score
|
self.hi_scores[score_position] = self.grade_score
|
||||||
hiscore_pos = {score_position-1, score_position}
|
hiscore_pos = {score_position-1, score_position}
|
||||||
end
|
end
|
||||||
local scoresfile = love.filesystem.newFile(HIscoreFILE, 'w')
|
bitser.dumpLoveFile(HIscoreFILE, self.hi_scores)
|
||||||
scoresfile:write(binser.serialize(self.hi_scores))
|
|
||||||
scoresfile:close()
|
|
||||||
return hiscore_pos
|
return hiscore_pos
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -718,9 +710,7 @@ function GameMode:onGameOver()
|
|||||||
self.grade_score = self.grade_score + self.speed_level
|
self.grade_score = self.grade_score + self.speed_level
|
||||||
promo_string = self:updateGradeHistory()
|
promo_string = self:updateGradeHistory()
|
||||||
hiscore_pos = self:updateHiScores()
|
hiscore_pos = self:updateHiScores()
|
||||||
gradefile = love.filesystem.newFile(SAVE_DIR..self.player_name.."_grade_history.sav", 'w')
|
bitser.dumpLoveFile(SAVE_DIR..self.player_name.."_grade_history.sav", self.grade_history)
|
||||||
gradefile:write(binser.serialize(self.grade_history))
|
|
||||||
gradefile:close()
|
|
||||||
self.did_grades = true
|
self.did_grades = true
|
||||||
end
|
end
|
||||||
self:drawEndScoringInfo()
|
self:drawEndScoringInfo()
|
||||||
|
|||||||
689
libs/binser.lua
689
libs/binser.lua
@@ -1,689 +0,0 @@
|
|||||||
-- binser.lua
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Copyright (c) 2016 Calvin Rose
|
|
||||||
|
|
||||||
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 assert = assert
|
|
||||||
local error = error
|
|
||||||
local select = select
|
|
||||||
local pairs = pairs
|
|
||||||
local getmetatable = getmetatable
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
local tonumber = tonumber
|
|
||||||
local type = type
|
|
||||||
local loadstring = loadstring or load
|
|
||||||
local concat = table.concat
|
|
||||||
local char = string.char
|
|
||||||
local byte = string.byte
|
|
||||||
local format = string.format
|
|
||||||
local sub = string.sub
|
|
||||||
local dump = string.dump
|
|
||||||
local floor = math.floor
|
|
||||||
local frexp = math.frexp
|
|
||||||
local unpack = unpack or table.unpack
|
|
||||||
|
|
||||||
-- Lua 5.3 frexp polyfill
|
|
||||||
-- From https://github.com/excessive/cpml/blob/master/modules/utils.lua
|
|
||||||
if not frexp then
|
|
||||||
local log, abs, floor = math.log, math.abs, math.floor
|
|
||||||
local log2 = log(2)
|
|
||||||
frexp = function(x)
|
|
||||||
if x == 0 then return 0, 0 end
|
|
||||||
local e = floor(log(abs(x)) / log2 + 1)
|
|
||||||
return x / 2 ^ e, e
|
|
||||||
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(...)
|
|
||||||
return {...}, select("#", ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function not_array_index(x, len)
|
|
||||||
return type(x) ~= "number" or x < 1 or x > len or x ~= floor(x)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function type_check(x, tp, name)
|
|
||||||
assert(type(x) == tp,
|
|
||||||
format("Expected parameter %q to be of type %q.", name, tp))
|
|
||||||
end
|
|
||||||
|
|
||||||
local bigIntSupport = false
|
|
||||||
local isInteger
|
|
||||||
if math.type then -- Detect Lua 5.3
|
|
||||||
local mtype = math.type
|
|
||||||
bigIntSupport = loadstring[[
|
|
||||||
local char = string.char
|
|
||||||
return function(n)
|
|
||||||
local nn = n < 0 and -(n + 1) or n
|
|
||||||
local b1 = nn // 0x100000000000000
|
|
||||||
local b2 = nn // 0x1000000000000 % 0x100
|
|
||||||
local b3 = nn // 0x10000000000 % 0x100
|
|
||||||
local b4 = nn // 0x100000000 % 0x100
|
|
||||||
local b5 = nn // 0x1000000 % 0x100
|
|
||||||
local b6 = nn // 0x10000 % 0x100
|
|
||||||
local b7 = nn // 0x100 % 0x100
|
|
||||||
local b8 = nn % 0x100
|
|
||||||
if n < 0 then
|
|
||||||
b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4
|
|
||||||
b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8
|
|
||||||
end
|
|
||||||
return char(212, b1, b2, b3, b4, b5, b6, b7, b8)
|
|
||||||
end]]()
|
|
||||||
isInteger = function(x)
|
|
||||||
return mtype(x) == 'integer'
|
|
||||||
end
|
|
||||||
else
|
|
||||||
isInteger = function(x)
|
|
||||||
return floor(x) == x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Copyright (C) 2012-2015 Francois Perrad.
|
|
||||||
-- number serialization code modified from https://github.com/fperrad/lua-MessagePack
|
|
||||||
-- Encode a number as a big-endian ieee-754 double, big-endian signed 64 bit integer, or a small integer
|
|
||||||
local function number_to_str(n)
|
|
||||||
if isInteger(n) then -- int
|
|
||||||
if n <= 100 and n >= -27 then -- 1 byte, 7 bits of data
|
|
||||||
return char(n + 27)
|
|
||||||
elseif n <= 8191 and n >= -8192 then -- 2 bytes, 14 bits of data
|
|
||||||
n = n + 8192
|
|
||||||
return char(128 + (floor(n / 0x100) % 0x100), n % 0x100)
|
|
||||||
elseif bigIntSupport then
|
|
||||||
return bigIntSupport(n)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local sign = 0
|
|
||||||
if n < 0.0 then
|
|
||||||
sign = 0x80
|
|
||||||
n = -n
|
|
||||||
end
|
|
||||||
local m, e = frexp(n) -- mantissa, exponent
|
|
||||||
if m ~= m then
|
|
||||||
return char(203, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
|
||||||
elseif m == 1/0 then
|
|
||||||
if sign == 0 then
|
|
||||||
return char(203, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
|
||||||
else
|
|
||||||
return char(203, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
e = e + 0x3FE
|
|
||||||
if e < 1 then -- denormalized numbers
|
|
||||||
m = m * 2 ^ (52 + e)
|
|
||||||
e = 0
|
|
||||||
else
|
|
||||||
m = (m * 2 - 1) * 2 ^ 52
|
|
||||||
end
|
|
||||||
return char(203,
|
|
||||||
sign + floor(e / 0x10),
|
|
||||||
(e % 0x10) * 0x10 + floor(m / 0x1000000000000),
|
|
||||||
floor(m / 0x10000000000) % 0x100,
|
|
||||||
floor(m / 0x100000000) % 0x100,
|
|
||||||
floor(m / 0x1000000) % 0x100,
|
|
||||||
floor(m / 0x10000) % 0x100,
|
|
||||||
floor(m / 0x100) % 0x100,
|
|
||||||
m % 0x100)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Copyright (C) 2012-2015 Francois Perrad.
|
|
||||||
-- number deserialization code also modified from https://github.com/fperrad/lua-MessagePack
|
|
||||||
local function number_from_str(str, index)
|
|
||||||
local b = byte(str, index)
|
|
||||||
if b < 128 then
|
|
||||||
return b - 27, index + 1
|
|
||||||
elseif b < 192 then
|
|
||||||
return byte(str, index + 1) + 0x100 * (b - 128) - 8192, index + 2
|
|
||||||
end
|
|
||||||
local b1, b2, b3, b4, b5, b6, b7, b8 = byte(str, index + 1, index + 8)
|
|
||||||
if b == 212 then
|
|
||||||
local flip = b1 >= 128
|
|
||||||
if flip then -- negative
|
|
||||||
b1, b2, b3, b4 = 0xFF - b1, 0xFF - b2, 0xFF - b3, 0xFF - b4
|
|
||||||
b5, b6, b7, b8 = 0xFF - b5, 0xFF - b6, 0xFF - b7, 0xFF - b8
|
|
||||||
end
|
|
||||||
local n = ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
|
||||||
if flip then
|
|
||||||
return (-n) - 1, index + 9
|
|
||||||
else
|
|
||||||
return n, index + 9
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local sign = b1 > 0x7F and -1 or 1
|
|
||||||
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 n
|
|
||||||
if e == 0 then
|
|
||||||
if m == 0 then
|
|
||||||
n = sign * 0.0
|
|
||||||
else
|
|
||||||
n = sign * (m / 2 ^ 52) * 2 ^ -1022
|
|
||||||
end
|
|
||||||
elseif e == 0x7FF then
|
|
||||||
if m == 0 then
|
|
||||||
n = sign * (1/0)
|
|
||||||
else
|
|
||||||
n = 0.0/0.0
|
|
||||||
end
|
|
||||||
else
|
|
||||||
n = sign * (1.0 + m / 2 ^ 52) * 2 ^ (e - 0x3FF)
|
|
||||||
end
|
|
||||||
return n, index + 9
|
|
||||||
end
|
|
||||||
|
|
||||||
local types = {}
|
|
||||||
|
|
||||||
types["nil"] = function(x, visited, accum)
|
|
||||||
accum[#accum + 1] = "\202"
|
|
||||||
end
|
|
||||||
|
|
||||||
function types.number(x, visited, accum)
|
|
||||||
accum[#accum + 1] = number_to_str(x)
|
|
||||||
end
|
|
||||||
|
|
||||||
function types.boolean(x, visited, accum)
|
|
||||||
accum[#accum + 1] = x and "\204" or "\205"
|
|
||||||
end
|
|
||||||
|
|
||||||
function types.string(x, visited, accum)
|
|
||||||
local alen = #accum
|
|
||||||
if visited[x] then
|
|
||||||
accum[alen + 1] = "\208"
|
|
||||||
accum[alen + 2] = number_to_str(visited[x])
|
|
||||||
else
|
|
||||||
visited[x] = visited.next
|
|
||||||
visited.next = visited.next + 1
|
|
||||||
accum[alen + 1] = "\206"
|
|
||||||
accum[alen + 2] = number_to_str(#x)
|
|
||||||
accum[alen + 3] = x
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_custom_type(x, visited, accum)
|
|
||||||
local res = resources[x]
|
|
||||||
if res then
|
|
||||||
accum[#accum + 1] = "\211"
|
|
||||||
types[type(res)](res, visited, accum)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local mt = getmetatable(x)
|
|
||||||
local id = mt and ids[mt]
|
|
||||||
if id then
|
|
||||||
if x == visited.temp then
|
|
||||||
error("Infinite loop in constructor.")
|
|
||||||
end
|
|
||||||
visited.temp = x
|
|
||||||
accum[#accum + 1] = "\209"
|
|
||||||
types[type(id)](id, visited, accum)
|
|
||||||
local args, len = pack(serializers[id](x))
|
|
||||||
accum[#accum + 1] = number_to_str(len)
|
|
||||||
for i = 1, len do
|
|
||||||
local arg = args[i]
|
|
||||||
types[type(arg)](arg, visited, accum)
|
|
||||||
end
|
|
||||||
visited[x] = visited.next
|
|
||||||
visited.next = visited.next + 1
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function types.userdata(x, visited, accum)
|
|
||||||
if visited[x] then
|
|
||||||
accum[#accum + 1] = "\208"
|
|
||||||
accum[#accum + 1] = number_to_str(visited[x])
|
|
||||||
else
|
|
||||||
if check_custom_type(x, visited, accum) then return end
|
|
||||||
error("Cannot serialize this userdata.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function types.table(x, visited, accum)
|
|
||||||
if visited[x] then
|
|
||||||
accum[#accum + 1] = "\208"
|
|
||||||
accum[#accum + 1] = number_to_str(visited[x])
|
|
||||||
else
|
|
||||||
if check_custom_type(x, visited, accum) then return end
|
|
||||||
visited[x] = visited.next
|
|
||||||
visited.next = visited.next + 1
|
|
||||||
local xlen = #x
|
|
||||||
accum[#accum + 1] = "\207"
|
|
||||||
accum[#accum + 1] = number_to_str(xlen)
|
|
||||||
for i = 1, xlen do
|
|
||||||
local v = x[i]
|
|
||||||
types[type(v)](v, visited, accum)
|
|
||||||
end
|
|
||||||
local key_count = 0
|
|
||||||
for k in pairs(x) do
|
|
||||||
if not_array_index(k, xlen) then
|
|
||||||
key_count = key_count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
accum[#accum + 1] = number_to_str(key_count)
|
|
||||||
for k, v in pairs(x) do
|
|
||||||
if not_array_index(k, xlen) then
|
|
||||||
types[type(k)](k, visited, accum)
|
|
||||||
types[type(v)](v, visited, accum)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
types["function"] = function(x, visited, accum)
|
|
||||||
if visited[x] then
|
|
||||||
accum[#accum + 1] = "\208"
|
|
||||||
accum[#accum + 1] = number_to_str(visited[x])
|
|
||||||
else
|
|
||||||
if check_custom_type(x, visited, accum) then return end
|
|
||||||
visited[x] = visited.next
|
|
||||||
visited.next = visited.next + 1
|
|
||||||
local str = dump(x)
|
|
||||||
accum[#accum + 1] = "\210"
|
|
||||||
accum[#accum + 1] = number_to_str(#str)
|
|
||||||
accum[#accum + 1] = str
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
types.cdata = function(x, visited, accum)
|
|
||||||
if visited[x] then
|
|
||||||
accum[#accum + 1] = "\208"
|
|
||||||
accum[#accum + 1] = number_to_str(visited[x])
|
|
||||||
else
|
|
||||||
if check_custom_type(x, visited, #accum) then return end
|
|
||||||
error("Cannot serialize this cdata.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
types.thread = function() error("Cannot serialize threads.") end
|
|
||||||
|
|
||||||
local function deserialize_value(str, index, visited)
|
|
||||||
local t = byte(str, index)
|
|
||||||
if not t then return end
|
|
||||||
if t < 128 then
|
|
||||||
return t - 27, index + 1
|
|
||||||
elseif t < 192 then
|
|
||||||
return byte(str, index + 1) + 0x100 * (t - 128) - 8192, index + 2
|
|
||||||
elseif t == 202 then
|
|
||||||
return nil, index + 1
|
|
||||||
elseif t == 203 then
|
|
||||||
return number_from_str(str, index)
|
|
||||||
elseif t == 204 then
|
|
||||||
return true, index + 1
|
|
||||||
elseif t == 205 then
|
|
||||||
return false, index + 1
|
|
||||||
elseif t == 206 then
|
|
||||||
local length, dataindex = deserialize_value(str, index + 1, visited)
|
|
||||||
local nextindex = dataindex + length
|
|
||||||
local substr = sub(str, dataindex, nextindex - 1)
|
|
||||||
visited[#visited + 1] = substr
|
|
||||||
return substr, nextindex
|
|
||||||
elseif t == 207 then
|
|
||||||
local count, nextindex = number_from_str(str, index + 1)
|
|
||||||
local ret = {}
|
|
||||||
visited[#visited + 1] = ret
|
|
||||||
for i = 1, count do
|
|
||||||
ret[i], nextindex = deserialize_value(str, nextindex, visited)
|
|
||||||
end
|
|
||||||
count, nextindex = number_from_str(str, nextindex)
|
|
||||||
for i = 1, count do
|
|
||||||
local k, v
|
|
||||||
k, nextindex = deserialize_value(str, nextindex, visited)
|
|
||||||
v, nextindex = deserialize_value(str, nextindex, visited)
|
|
||||||
ret[k] = v
|
|
||||||
end
|
|
||||||
return ret, nextindex
|
|
||||||
elseif t == 208 then
|
|
||||||
local ref, nextindex = number_from_str(str, index + 1)
|
|
||||||
return visited[ref], nextindex
|
|
||||||
elseif t == 209 then
|
|
||||||
local count
|
|
||||||
local name, nextindex = deserialize_value(str, index + 1, visited)
|
|
||||||
count, nextindex = number_from_str(str, nextindex)
|
|
||||||
local args = {}
|
|
||||||
for i = 1, count do
|
|
||||||
args[i], nextindex = deserialize_value(str, nextindex, visited)
|
|
||||||
end
|
|
||||||
local ret = deserializers[name](unpack(args))
|
|
||||||
visited[#visited + 1] = ret
|
|
||||||
return ret, nextindex
|
|
||||||
elseif t == 210 then
|
|
||||||
local length, dataindex = deserialize_value(str, index + 1, visited)
|
|
||||||
local nextindex = dataindex + length
|
|
||||||
local ret = loadstring(sub(str, dataindex, nextindex - 1))
|
|
||||||
visited[#visited + 1] = ret
|
|
||||||
return ret, nextindex
|
|
||||||
elseif t == 211 then
|
|
||||||
local res, nextindex = deserialize_value(str, index + 1, visited)
|
|
||||||
return resources_by_name[res], nextindex
|
|
||||||
elseif t == 212 then
|
|
||||||
return number_from_str(str, index)
|
|
||||||
else
|
|
||||||
error("Could not deserialize type byte " .. t .. ".")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function serialize(...)
|
|
||||||
local visited = {next = 1}
|
|
||||||
local accum = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
local x = select(i, ...)
|
|
||||||
types[type(x)](x, visited, accum)
|
|
||||||
end
|
|
||||||
return concat(accum)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function make_file_writer(file)
|
|
||||||
return setmetatable({}, {
|
|
||||||
__newindex = function(_, _, v)
|
|
||||||
file:write(v)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function serialize_to_file(path, mode, ...)
|
|
||||||
local file, err = io.open(path, mode)
|
|
||||||
assert(file, err)
|
|
||||||
local visited = {next = 1}
|
|
||||||
local accum = make_file_writer(file)
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
local x = select(i, ...)
|
|
||||||
types[type(x)](x, visited, accum)
|
|
||||||
end
|
|
||||||
-- flush the writer
|
|
||||||
file:flush()
|
|
||||||
file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function writeFile(path, ...)
|
|
||||||
return serialize_to_file(path, "wb", ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function appendFile(path, ...)
|
|
||||||
return serialize_to_file(path, "ab", ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function deserialize(str, index)
|
|
||||||
assert(type(str) == "string", "Expected string to deserialize.")
|
|
||||||
local vals = {}
|
|
||||||
index = index or 1
|
|
||||||
local visited = {}
|
|
||||||
local len = 0
|
|
||||||
local val
|
|
||||||
while index do
|
|
||||||
val, index = deserialize_value(str, index, visited)
|
|
||||||
if index then
|
|
||||||
len = len + 1
|
|
||||||
vals[len] = val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return vals, len
|
|
||||||
end
|
|
||||||
|
|
||||||
local function deserializeN(str, n, index)
|
|
||||||
assert(type(str) == "string", "Expected string to deserialize.")
|
|
||||||
n = n or 1
|
|
||||||
assert(type(n) == "number", "Expected a number for parameter n.")
|
|
||||||
assert(n > 0 and floor(n) == n, "N must be a poitive integer.")
|
|
||||||
local vals = {}
|
|
||||||
index = index or 1
|
|
||||||
local visited = {}
|
|
||||||
local len = 0
|
|
||||||
local val
|
|
||||||
while index and len < n do
|
|
||||||
val, index = deserialize_value(str, index, visited)
|
|
||||||
if index then
|
|
||||||
len = len + 1
|
|
||||||
vals[len] = val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vals[len + 1] = index
|
|
||||||
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
|
|
||||||
local str = file:read("*all")
|
|
||||||
file:close()
|
|
||||||
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
|
|
||||||
return setmetatable(ret, metatable)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function default_serialize(x)
|
|
||||||
assert(type(x) == "table",
|
|
||||||
"Default serialization for custom types only works for tables.")
|
|
||||||
local args = {}
|
|
||||||
local len = 0
|
|
||||||
for k, v in pairs(x) do
|
|
||||||
args[len + 1], args[len + 2] = k, v
|
|
||||||
len = len + 2
|
|
||||||
end
|
|
||||||
return unpack(args, 1, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Templating
|
|
||||||
|
|
||||||
local function normalize_template(template)
|
|
||||||
local ret = {}
|
|
||||||
for i = 1, #template do
|
|
||||||
ret[i] = template[i]
|
|
||||||
end
|
|
||||||
local non_array_part = {}
|
|
||||||
-- The non-array part of the template (nested templates) have to be deterministic, so they are sorted.
|
|
||||||
-- This means that inherently non deterministicly sortable keys (tables, functions) should NOT be used
|
|
||||||
-- in templates. Looking for way around this.
|
|
||||||
for k in pairs(template) do
|
|
||||||
if not_array_index(k, #template) then
|
|
||||||
non_array_part[#non_array_part + 1] = k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(non_array_part)
|
|
||||||
for i = 1, #non_array_part do
|
|
||||||
local name = non_array_part[i]
|
|
||||||
ret[#ret + 1] = {name, normalize_template(template[name])}
|
|
||||||
end
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
|
|
||||||
local function templatepart_serialize(part, argaccum, x, len)
|
|
||||||
local extras = {}
|
|
||||||
local extracount = 0
|
|
||||||
for k, v in pairs(x) do
|
|
||||||
extras[k] = v
|
|
||||||
extracount = extracount + 1
|
|
||||||
end
|
|
||||||
for i = 1, #part do
|
|
||||||
extracount = extracount - 1
|
|
||||||
if type(part[i]) == "table" then
|
|
||||||
extras[part[i][1]] = nil
|
|
||||||
len = templatepart_serialize(part[i][2], argaccum, x[part[i][1]], len)
|
|
||||||
else
|
|
||||||
extras[part[i]] = nil
|
|
||||||
len = len + 1
|
|
||||||
argaccum[len] = x[part[i]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if extracount > 0 then
|
|
||||||
argaccum[len + 1] = extras
|
|
||||||
else
|
|
||||||
argaccum[len + 1] = nil
|
|
||||||
end
|
|
||||||
return len + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function templatepart_deserialize(ret, part, values, vindex)
|
|
||||||
for i = 1, #part do
|
|
||||||
local name = part[i]
|
|
||||||
if type(name) == "table" then
|
|
||||||
local newret = {}
|
|
||||||
ret[name[1]] = newret
|
|
||||||
vindex = templatepart_deserialize(newret, name[2], values, vindex)
|
|
||||||
else
|
|
||||||
ret[name] = values[vindex]
|
|
||||||
vindex = vindex + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local extras = values[vindex]
|
|
||||||
if extras then
|
|
||||||
for k, v in pairs(extras) do
|
|
||||||
ret[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return vindex + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function template_serializer_and_deserializer(metatable, template)
|
|
||||||
return function(x)
|
|
||||||
argaccum = {}
|
|
||||||
local len = templatepart_serialize(template, argaccum, x, 0)
|
|
||||||
return unpack(argaccum, 1, len)
|
|
||||||
end, function(...)
|
|
||||||
local ret = {}
|
|
||||||
local len = select("#", ...)
|
|
||||||
local args = {...}
|
|
||||||
templatepart_deserialize(ret, template, args, 1)
|
|
||||||
return setmetatable(ret, metatable)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function register(metatable, name, serialize, deserialize)
|
|
||||||
name = name or metatable.name
|
|
||||||
serialize = serialize or metatable._serialize
|
|
||||||
deserialize = deserialize or metatable._deserialize
|
|
||||||
if not serialize then
|
|
||||||
if metatable._template then
|
|
||||||
local t = normalize_template(metatable._template)
|
|
||||||
serialize, deserialize = template_serializer_and_deserializer(metatable, t)
|
|
||||||
elseif not deserialize then
|
|
||||||
serialize = default_serialize
|
|
||||||
deserialize = default_deserialize(metatable)
|
|
||||||
else
|
|
||||||
serialize = metatable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
type_check(metatable, "table", "metatable")
|
|
||||||
type_check(name, "string", "name")
|
|
||||||
type_check(serialize, "function", "serialize")
|
|
||||||
type_check(deserialize, "function", "deserialize")
|
|
||||||
assert(not ids[metatable], "Metatable already registered.")
|
|
||||||
assert(not mts[name], ("Name %q already registered."):format(name))
|
|
||||||
mts[name] = metatable
|
|
||||||
ids[metatable] = name
|
|
||||||
serializers[name] = serialize
|
|
||||||
deserializers[name] = deserialize
|
|
||||||
return metatable
|
|
||||||
end
|
|
||||||
|
|
||||||
local function unregister(item)
|
|
||||||
local name, metatable
|
|
||||||
if type(item) == "string" then -- assume name
|
|
||||||
name, metatable = item, mts[item]
|
|
||||||
else -- assume metatable
|
|
||||||
name, metatable = ids[item], item
|
|
||||||
end
|
|
||||||
type_check(name, "string", "name")
|
|
||||||
type_check(metatable, "table", "metatable")
|
|
||||||
mts[name] = nil
|
|
||||||
ids[metatable] = nil
|
|
||||||
serializers[name] = nil
|
|
||||||
deserializers[name] = nil
|
|
||||||
return metatable
|
|
||||||
end
|
|
||||||
|
|
||||||
local function registerClass(class, name)
|
|
||||||
name = name or class.name
|
|
||||||
if class.__instanceDict then -- middleclass
|
|
||||||
register(class.__instanceDict, name)
|
|
||||||
else -- assume 30log or similar library
|
|
||||||
register(class, name)
|
|
||||||
end
|
|
||||||
return class
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
s = serialize,
|
|
||||||
d = deserialize,
|
|
||||||
dn = deserializeN,
|
|
||||||
r = readFile,
|
|
||||||
w = writeFile,
|
|
||||||
a = appendFile,
|
|
||||||
|
|
||||||
serialize = serialize,
|
|
||||||
deserialize = deserialize,
|
|
||||||
deserializeN = deserializeN,
|
|
||||||
readFile = readFile,
|
|
||||||
writeFile = writeFile,
|
|
||||||
appendFile = appendFile,
|
|
||||||
register = register,
|
|
||||||
unregister = unregister,
|
|
||||||
registerResource = registerResource,
|
|
||||||
unregisterResource = unregisterResource,
|
|
||||||
registerClass = registerClass
|
|
||||||
}
|
|
||||||
496
libs/bitser.lua
Normal file
496
libs/bitser.lua
Normal 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}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
local binser = require 'libs.binser'
|
local bitser = require 'libs.bitser'
|
||||||
local fs = love.filesystem
|
local fs = love.filesystem
|
||||||
|
|
||||||
function loadSave()
|
function loadSave()
|
||||||
@@ -6,11 +6,10 @@ function loadSave()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function loadFromFile(filename)
|
function loadFromFile(filename)
|
||||||
local save_data = fs.read(filename)
|
if fs.read(filename) == nil then
|
||||||
if save_data == nil then
|
|
||||||
return {} -- new object
|
return {} -- new object
|
||||||
end
|
end
|
||||||
return binser.deserialize(save_data)[1]
|
return bitser.loadLoveFile(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
function initConfig()
|
function initConfig()
|
||||||
@@ -25,5 +24,5 @@ function initConfig()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function saveConfig()
|
function saveConfig()
|
||||||
fs.write(CONFIG_FILE,binser.serialize(config))
|
bitser.dumpLoveFile(CONFIG_FILE, config)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
local NameEntryScene = Scene:extend()
|
local NameEntryScene = Scene:extend()
|
||||||
local Grid = require 'game.grid'
|
local Grid = require 'game.grid'
|
||||||
local binser = require 'libs.binser'
|
local bitser = require 'libs.bitser'
|
||||||
require 'load.save'
|
require 'load.save'
|
||||||
|
|
||||||
NameEntryScene.title = "Game Start"
|
NameEntryScene.title = "Game Start"
|
||||||
@@ -30,9 +30,8 @@ function NameEntryScene:new()
|
|||||||
self.name_entry = {config['last_entry']:sub(1,1),config['last_entry']:sub(2,2),config['last_entry']:sub(3,3)}
|
self.name_entry = {config['last_entry']:sub(1,1),config['last_entry']:sub(2,2),config['last_entry']:sub(3,3)}
|
||||||
self.entry_pos = 3
|
self.entry_pos = 3
|
||||||
end
|
end
|
||||||
score_file = love.filesystem.newFile(HIscoreFILE, 'r')
|
if love.filesystem.getInfo(HIscoreFILE) then
|
||||||
if score_file ~= nil then
|
self.hi_scores = bitser.loadLoveFile(HIscoreFILE)
|
||||||
self.hi_scores = binser.deserialize(score_file:read())[1]
|
|
||||||
else
|
else
|
||||||
self.hi_scores = {"TRO",0,"MIT",0,"ROM",0,"ITR",0,"OMI",0}
|
self.hi_scores = {"TRO",0,"MIT",0,"ROM",0,"ITR",0,"OMI",0}
|
||||||
end
|
end
|
||||||
@@ -122,9 +121,8 @@ function NameEntryScene:onInputPress(e)
|
|||||||
else
|
else
|
||||||
if self.entry_pos == 3 then
|
if self.entry_pos == 3 then
|
||||||
name = string.lower(self.name_entry[1]..self.name_entry[2]..self.name_entry[3])
|
name = string.lower(self.name_entry[1]..self.name_entry[2]..self.name_entry[3])
|
||||||
grade_history = love.filesystem.newFile(SAVE_DIR..name.."_grade_history.sav", 'r')
|
if love.filesystem.getInfo((SAVE_DIR..name.."_grade_history.sav")) then
|
||||||
if grade_history ~= nil then
|
grade_history = bitser.loadLoveFile(SAVE_DIR..name.."_grade_history.sav")
|
||||||
grade_history = binser.deserialize(grade_history:read())[1]
|
|
||||||
self.grade = grade_history[1]
|
self.grade = grade_history[1]
|
||||||
self.wins = grade_history[2]
|
self.wins = grade_history[2]
|
||||||
self.plays = grade_history[4]
|
self.plays = grade_history[4]
|
||||||
|
|||||||
Reference in New Issue
Block a user