Replace `binser with bitser`

This commit is contained in:
Squishy (C6H12O6+NaCl+H2O)
2024-04-12 21:00:57 +07:00
parent 09f59d929f
commit f56bde14b9
5 changed files with 514 additions and 720 deletions

View File

@@ -1,7 +1,7 @@
local Object = require 'libs.classic'
local bit = require("bit")
local lualzw = require 'libs.lualzw'
local binser = require 'libs.binser'
local bitser = require 'libs.bitser'
require 'funcs'
require 'load.save'
@@ -141,10 +141,8 @@ function GameMode:new(player_name, input_file, replay_grade)
end
function GameMode:readGradeHistory()
outfile = love.filesystem.newFile(SAVE_DIR..self.player_name.."_grade_history.sav", 'r')
if outfile ~= nil then
self.grade_history = binser.deserialize(outfile:read('a'))[1]
outfile:close()
if love.filesystem.getInfo(SAVE_DIR..self.player_name.."_grade_history.sav") then
self.grade_history = bitser.loadLoveFile(SAVE_DIR..self.player_name.."_grade_history.sav")
else
self.grade_history = {1,2,0,0}
end
@@ -153,17 +151,13 @@ function GameMode:readGradeHistory()
if self.grade > 1 then
temp_grade = copy(self.grade_history)
temp_grade[2] = 0
gradefile = love.filesystem.newFile(SAVE_DIR..self.player_name.."_grade_history.sav", 'w')
gradefile:write(binser.serialize(temp_grade))
gradefile:close()
end
bitser.dumpLoveFile(SAVE_DIR..self.player_name.."_grade_history.sav", temp_grade)
end
end
function GameMode:readHiScores()
outfile = love.filesystem.newFile(HIscoreFILE, 'r')
if outfile ~= nil then
self.hi_scores = binser.deserialize(outfile:read())[1]
outfile:close()
if love.filesystem.getInfo(HIscoreFILE) then
self.hi_scores = bitser.loadLoveFile(HIscoreFILE)
else
self.hi_scores = {"TRO",0,"MIT",0,"ROM",0,"ITR",0,"OMI",0}
end
@@ -231,9 +225,7 @@ function GameMode:updateHiScores()
self.hi_scores[score_position] = self.grade_score
hiscore_pos = {score_position-1, score_position}
end
local scoresfile = love.filesystem.newFile(HIscoreFILE, 'w')
scoresfile:write(binser.serialize(self.hi_scores))
scoresfile:close()
bitser.dumpLoveFile(HIscoreFILE, self.hi_scores)
return hiscore_pos
end
@@ -718,9 +710,7 @@ function GameMode:onGameOver()
self.grade_score = self.grade_score + self.speed_level
promo_string = self:updateGradeHistory()
hiscore_pos = self:updateHiScores()
gradefile = love.filesystem.newFile(SAVE_DIR..self.player_name.."_grade_history.sav", 'w')
gradefile:write(binser.serialize(self.grade_history))
gradefile:close()
bitser.dumpLoveFile(SAVE_DIR..self.player_name.."_grade_history.sav", self.grade_history)
self.did_grades = true
end
self:drawEndScoringInfo()

View File

@@ -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
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,4 +1,4 @@
local binser = require 'libs.binser'
local bitser = require 'libs.bitser'
local fs = love.filesystem
function loadSave()
@@ -6,11 +6,10 @@ function loadSave()
end
function loadFromFile(filename)
local save_data = fs.read(filename)
if save_data == nil then
if fs.read(filename) == nil then
return {} -- new object
end
return binser.deserialize(save_data)[1]
return bitser.loadLoveFile(filename)
end
function initConfig()
@@ -25,5 +24,5 @@ function initConfig()
end
function saveConfig()
fs.write(CONFIG_FILE,binser.serialize(config))
bitser.dumpLoveFile(CONFIG_FILE, config)
end

View File

@@ -1,6 +1,6 @@
local NameEntryScene = Scene:extend()
local Grid = require 'game.grid'
local binser = require 'libs.binser'
local bitser = require 'libs.bitser'
require 'load.save'
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.entry_pos = 3
end
score_file = love.filesystem.newFile(HIscoreFILE, 'r')
if score_file ~= nil then
self.hi_scores = binser.deserialize(score_file:read())[1]
if love.filesystem.getInfo(HIscoreFILE) then
self.hi_scores = bitser.loadLoveFile(HIscoreFILE)
else
self.hi_scores = {"TRO",0,"MIT",0,"ROM",0,"ITR",0,"OMI",0}
end
@@ -122,9 +121,8 @@ function NameEntryScene:onInputPress(e)
else
if self.entry_pos == 3 then
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 grade_history ~= nil then
grade_history = binser.deserialize(grade_history:read())[1]
if love.filesystem.getInfo((SAVE_DIR..name.."_grade_history.sav")) then
grade_history = bitser.loadLoveFile(SAVE_DIR..name.."_grade_history.sav")
self.grade = grade_history[1]
self.wins = grade_history[2]
self.plays = grade_history[4]