Files
Techmino/parts/data.lua
MrZ_26 06f4bb4e1a 整 理 代 码
(应该没有改到字符串里面的东西吧…)起码不直接影响运行
2022-10-01 11:32:11 +08:00

422 lines
10 KiB
Lua

local int=math.floor
local char,byte=string.char,string.byte
local ins=table.insert
local BAG,FIELD,MISSION,CUSTOMENV,GAME=BAG,FIELD,MISSION,CUSTOMENV,GAME
local DATA={}
--Sep symbol: 33 (!)
--Safe char: 34~126
--[[
Count: 34~96
Block: 97~125
Encode: A[B] sequence, A = block ID, B = repeat times, no B means do not repeat.
Example: "abcdefg" is [SZJLTOI], "a^aDb)" is [Z*63,Z*37,S*10]
]]
function DATA.copySequence()
local str=""
local count=1
for i=1,#BAG+1 do
if BAG[i+1]~=BAG[i] or count==64 then
str=str..char(96+BAG[i])
if count>1 then
str=str..char(32+count)
count=1
end
else
count=count+1
end
end
return str
end
function DATA.pasteSequence(str)
TABLE.cut(BAG)
local b,reg
for i=1,#str do
b=byte(str,i)
if not reg then
if b>=97 and b<=125 then
reg=b-96
else
return
end
else
if b>=97 and b<=125 then
ins(BAG,reg)
reg=b-96
elseif b>=34 and b<=96 then
for _=1,b-32 do
ins(BAG,reg)
end
reg=false
end
end
end
if reg then
ins(BAG,reg)
end
return true
end
local fieldMeta={__index=function(self,h)
for i=#self+1,h do
self[i]={0,0,0,0,0,0,0,0,0,0}
end
return self[h]
end}
function DATA.newBoard(f)--Generate a new board
return setmetatable(f and TABLE.shift(f) or{},fieldMeta)
end
function DATA.copyBoard(page)--Copy the [page] board
local F=FIELD[page or 1]
local str=""
--Encode field
for y=1,#F do
local S=""
local L=F[y]
for x=1,10 do
S=S..char(L[x]+1)
end
str=str..S
end
return STRING.packBin(str)
end
function DATA.copyBoards()
local out={}
for i=1,#FIELD do
out[i]=DATA.copyBoard(i)
end
return table.concat(out,"!")
end
function DATA.pasteBoard(str,page)--Paste [str] data to [page] board
if not page then
page=1
end
if not FIELD[page] then
FIELD[page]=DATA.newBoard()
end
local F=FIELD[page]
--Decode
str=STRING.unpackBin(str)
if not str then return end
local fX,fY=1,1--*ptr for Field(r*10+(c-1))
local p=1
while true do
local b=byte(str,p)--1byte
--Str end
if not b then
if fX~=1 then
return
else
break
end
end
local id=b%32-1--Block id
if id>26 then return end--Illegal blockid
b=int(b/32)--Mode id
F[fY][fX]=id
if fX<10 then
fX=fX+1
else
fY=fY+1
if fY>20 then break end
fX=1
end
p=p+1
end
return true
end
--[[
Mission: 34~114
Count: 115~126
Encode: [A] or [AB] sequence, A = mission ID, B = repeat times, no B means do not repeat.
_1=01,_2=02,_3=03,_4=04,
A1=05,A2=06,A3=07,A4=08,
PC=09,
Z1=11,Z2=12,Z3=13,
S1=21,S2=22,S3=23,
J1=31,J2=32,J3=33,
L1=41,L2=42,L3=43,
T1=51,T2=52,T3=53,
O1=61,O2=62,O3=63,O4=64,
I1=71,I2=72,I3=73,I4=74,
]]
function DATA.copyMission()
local _
local str=""
local count=1
for i=1,#MISSION+1 do
if MISSION[i+1]~=MISSION[i] or count==13 then
_=33+MISSION[i]
str=str..char(_)
if count>1 then
str=str..char(113+count)
count=1
end
else
count=count+1
end
end
return str
end
function DATA.pasteMission(str)
local b
TABLE.cut(MISSION)
local reg
for i=1,#str do
b=byte(str,i)
if not reg then
if b>=34 and b<=114 then
reg=b-33
else
return
end
else
if b>=34 and b<=114 then
if ENUM_MISSION[reg] then
ins(MISSION,reg)
reg=b-33
else
TABLE.cut(MISSION)
return
end
elseif b>=115 and b<=126 then
for _=1,b-113 do
ins(MISSION,reg)
end
reg=false
end
end
end
if reg then
ins(MISSION,reg)
end
return true
end
function DATA.copyQuestArgs()
local ENV=CUSTOMENV
local str=""..
ENV.holdCount..
(ENV.ospin and "O" or "Z")..
(ENV.missionKill and "M" or "Z")..
ENV.sequence
return str
end
function DATA.pasteQuestArgs(str)
if #str<4 then return end
local ENV=CUSTOMENV
ENV.holdCount= str:byte(1)-48
ENV.ospin= str:byte(2)~=90
ENV.missionKill=str:byte(3)~=90
ENV.sequence= str:sub(4)
return true
end
--[[
Replay file:
a zlib-compressed json table
Replay data format (table):
{frame,event, frame,event, ...}
Replay data format (byte): (1 byte each period)
dt, event, dt, event, ...
all data range from 0 to 127
large value will be encoded as 1xxxxxxx(high)-1xxxxxxx-...-0xxxxxxx(low)
Example (decoded):
6,1, 20,-1, 0,2, 26,-2, 872,4, ...
This means:
Press key1 at 6f
Release key1 at 26f (6+20)
Press key2 at the same time (26+0)
Release key 2 after 26 frame (26+26)
Press key 4 after 872 frame (52+872)
...
]]
function DATA.dumpRecording(list,ptr)
local out=""
local buffer,buffer2=""
if not ptr then ptr=1 end
local prevFrm=list[ptr-2] or 0
while list[ptr] do
--Flush buffer
if #buffer>10 then
out=out..buffer
buffer=""
end
--Encode time
local t=list[ptr]-prevFrm
prevFrm=list[ptr]
if t>=128 then
buffer2=char(t%128)
t=int(t/128)
while t>=128 do
buffer2=char(128+t%128)..buffer2
t=int(t/128)
end
buffer=buffer..char(128+t)..buffer2
else
buffer=buffer..char(t)
end
--Encode event
t=list[ptr+1]
if t>=128 then
buffer2=char(t%128)
t=int(t/128)
while t>=128 do
buffer2=char(128+t%128)..buffer2
t=int(t/128)
end
buffer=buffer..char(128+t)..buffer2
else
buffer=buffer..char(t)
end
--Step
ptr=ptr+2
end
return out..buffer,ptr
end
function DATA.pumpRecording(str,L)
local len=#str
local p=1
local curFrm=L[#L-1] or 0
local code
while p<=len do
--Read delta time
code=0
local b=byte(str,p)
while b>=128 do
code=code*128+b-128
p=p+1
b=byte(str,p)
end
curFrm=curFrm+code*128+b
L[#L+1]=curFrm
p=p+1
local event=0
b=byte(str,p)
while b>=128 do
event=event*128+b-128
p=p+1
b=byte(str,p)
end
L[#L+1]=event*128+b
p=p+1
end
end
do--function DATA.saveReplay()
local noRecList={"custom","solo","round","techmino"}
local function _getModList()
local res={}
for _,v in next,GAME.mod do
if v.sel>0 then
ins(res,{v.no,v.sel})
end
end
return res
end
function DATA.saveReplay()
--Filtering modes that cannot be saved
for _,v in next,noRecList do
if GAME.curModeName:find(v) then
MES.new('error',"Cannot save recording of this mode now!")
return
end
end
--Write file
local fileName=os.date("replay/%Y_%m_%d_%H%M%S.rep")
if not love.filesystem.getInfo(fileName) then
love.filesystem.write(fileName,
love.data.compress('string','zlib',
JSON.encode{
date=os.date("%Y/%m/%d %H:%M:%S"),
mode=GAME.curModeName,
version=VERSION.string,
player=USERS.getUsername(USER.uid),
seed=GAME.seed,
setting=GAME.setting,
mod=_getModList(),
tasUsed=GAME.tasUsed,
}.."\n"..
DATA.dumpRecording(GAME.rep)
)
)
ins(REPLAY,1,DATA.parseReplay(fileName))
return true
else
MES.new('error',"Save failed: File already exists")
end
end
end
function DATA.parseReplay(fileName,ifFull)
local fileData
--Read file
fileData=love.filesystem.read(fileName)
return DATA.parseReplayData(fileName,fileData,ifFull)
end
function DATA.parseReplayData(fileName,fileData,ifFull)
local success,metaData,rep
if not (fileData and #fileData>0) then goto BREAK_cannotParse end
--Decompress file
success,fileData=pcall(love.data.decompress,'string','zlib',fileData)
if not success then goto BREAK_cannotParse end
--Load metadata
metaData,fileData=STRING.readLine(fileData)
metaData=JSON.decode(metaData)
if not metaData then goto BREAK_cannotParse end
--Convert ancient replays
metaData.mode=MODE_UPDATE_MAP[metaData.mode] or metaData.mode
if not MODES[metaData.mode] then goto BREAK_cannotParse end
--Create replay object
rep={
fileName=fileName,
available=true,
date=metaData.date,
mode=metaData.mode,
version=metaData.version,
player=metaData.player,
seed=metaData.seed,
setting=metaData.setting,
mod=metaData.mod,
tasUsed=metaData.tasUsed,
}
if ifFull then rep.data=fileData end
do return rep end
--Create unavailable replay object
::BREAK_cannotParse::
return{
fileName=fileName,
available=false,
}
end
return DATA