dobrograd-13-06-2022/garrysmod/addons/gmod-tools/lua/advdupe2/sh_codec_legacy.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

560 lines
No EOL
15 KiB
Lua
Raw Blame History

--[[
Title: Adv. Dupe 2 Codec Legacy Support
Desc: Facilitates opening of dupes from AD1 and earlier AD2 versions.
Author: emspike
Version: 2.0
]]
local pairs = pairs
local type = type
local tonumber = tonumber
local error = error
local Vector = Vector
local Angle = Angle
local unpack = unpack
local format = string.format
local char = string.char
local byte = string.byte
local sub = string.sub
local gsub = string.gsub
local find = string.find
local gmatch = string.gmatch
local match = string.match
local concat = table.concat
--[[
Name: GenerateDupeStamp
Desc: Generates an info table.
Params: <player> ply
Return: <table> stamp
]]
function AdvDupe2.GenerateDupeStamp(ply)
local stamp = {}
stamp.name = ply:GetName()
stamp.time = os.date("%I:%M %p")
stamp.date = os.date("%d %B %Y")
stamp.timezone = os.date("%z")
hook.Call("AdvDupe2_StampGenerated",GAMEMODE,stamp)
return stamp
end
local AD2FF = "AD2F%s\n%s\n%s"
local decode_types_v1, decode_types_v2
local tables = 0
local str,pos
local a,b,c,m,n,w,tblref
local function read_v2()
local t = byte(str, pos+1)
if t then
local dt = decode_types_v2[t]
if dt then
pos = pos + 1
return dt()
else
error(format("encountered invalid data type (%u)\n",t))
end
else
error("expected value, got EOF\n")
end
end
decode_types_v2 = {
[1 ] = function()
error("expected value, got terminator\n")
end,
[2 ] = function() -- table
m = find(str, "\1", pos)
if m then
w = sub(str, pos+1, m-1)
pos = m
else
error("expected table identifier, got EOF\n")
end
local t = {}
tables[w] = t
while true do
if byte(str, pos+1) == 1 then
pos = pos + 1
return t
else
t[read_v2()] = read_v2()
end
end
end,
[3 ] = function() -- array
m = find(str, "\1", pos)
if m then
w = sub(str, pos+1, m-1)
pos = m
else
error("expected table identifier, got EOF\n")
end
local t, i = {}, 1
tables[w] = t
while true do
if byte(str,pos+1) == 1 then
pos = pos+1
return t
else
t[i] = read_v2()
i = i + 1
end
end
end,
[4 ] = function() -- false boolean
return false
end,
[5 ] = function() -- true boolean
return true
end,
[6 ] = function() -- number
m = find(str, "\1", pos)
if m then
a = tonumber(sub(str, pos+1, m-1)) or 0
pos = m
return a
else
error("expected number, got EOF\n")
end
end,
[7 ] = function() -- string
m = find(str,"\1",pos)
if m then
w = sub(str, pos+1, m-1)
pos = m
return w
else
error("expected string, got EOF\n")
end
end,
[8 ] = function() -- Vector
m,n = find(str,".-\1.-\1.-\1", pos)
if m then
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Vector(tonumber(a), tonumber(b), tonumber(c))
else
error("expected vector, got EOF\n")
end
end,
[9 ] = function() -- Angle
m,n = find(str, ".-\1.-\1.-\1", pos)
if m then
a,b,c = match(str, "^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Angle(tonumber(a), tonumber(b), tonumber(c))
else
error("expected angle, got EOF\n")
end
end,
[10 ] = function() -- Table Reference
m = find(str,"\1",pos)
if m then
w = sub(str,pos+1,m-1)
pos = m
else
error("expected table identifier, got EOF\n")
end
tblref = tables[w]
if tblref then
return tblref
else
error(format("table identifier %s points to nil\n", w))
end
end
}
local function read_v1()
local t = byte(str,pos+1)
if t then
local dt = decode_types_v1[t]
if dt then
pos = pos + 1
return dt()
else
error(format("encountered invalid data type (%u)\n",t))
end
else
error("expected value, got EOF\n")
end
end
decode_types_v1 = {
[1 ] = function()
error("expected value, got terminator\n")
end,
[2 ] = function() -- table
local t = {}
while true do
if byte(str,pos+1) == 1 then
pos = pos+1
return t
else
t[read_v1()] = read_v1()
end
end
end,
[3 ] = function() -- array
local t, i = {}, 1
while true do
if byte(str,pos+1) == 1 then
pos = pos+1
return t
else
t[i] = read_v1()
i = i + 1
end
end
end,
[4 ] = function() -- false boolean
return false
end,
[5 ] = function() -- true boolean
return true
end,
[6 ] = function() -- number
m = find(str,"\1",pos)
if m then
a = tonumber(sub(str,pos+1,m-1)) or 0
pos = m
return a
else
error("expected number, got EOF\n")
end
end,
[7 ] = function() -- string
m = find(str,"\1",pos)
if m then
w = sub(str,pos+1,m-1)
pos = m
return w
else
error("expected string, got EOF\n")
end
end,
[8 ] = function() -- Vector
m,n = find(str,".-\1.-\1.-\1",pos)
if m then
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Vector(tonumber(a), tonumber(b), tonumber(c))
else
error("expected vector, got EOF\n")
end
end,
[9 ] = function() -- Angle
m,n = find(str,".-\1.-\1.-\1",pos)
if m then
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Angle(tonumber(a), tonumber(b), tonumber(c))
else
error("expected angle, got EOF\n")
end
end
}
local function deserialize_v1(data)
str = data
pos = 0
tables = {}
return read_v1()
end
local function deserialize_v2(data)
str = data
pos = 0
tables = {}
return read_v2()
end
local function lzwDecode(encoded)
local dictionary_length = 256
local dictionary = {}
for i = 0, 255 do
dictionary[i] = char(i)
end
local pos = 2
local decompressed = {}
local decompressed_length = 1
local index = byte(encoded)
local word = dictionary[index]
decompressed[decompressed_length] = dictionary[index]
local entry
local encoded_length = #encoded
local firstbyte --of an index
while pos <= encoded_length do
firstbyte = byte(encoded,pos)
if firstbyte > 252 then --now we know it's a length indicator for a multibyte index
index = 0
firstbyte = 256 - firstbyte
--[[if pos+firstbyte > encoded_length then --will test for performance impact
error("expected index got EOF")
end]]
for i = pos+firstbyte, pos+1, -1 do
index = bit.bor(bit.lshift(index, 8), byte(encoded,i))
end
pos = pos + firstbyte + 1
else
index = firstbyte
pos = pos + 1
end
entry = dictionary[index] or (word..sub(word,1,1))
decompressed_length = decompressed_length + 1
decompressed[decompressed_length] = entry
dictionary[dictionary_length] = word..sub(entry,1,1)
dictionary_length = dictionary_length + 1
word = entry
end
return concat(decompressed)
end
--http://en.wikipedia.org/wiki/Huffman_coding#Decompression
local invcodes = {[2]={[0]="\254"},[5]={[22]="\1",[11]="\2"},[6]={[13]="\7",[35]="\6",[37]="\5",[58]="\3",[31]="\8",[9]="\13",[51]="\9",[55]="\10",[57]="\4",[59]="\15"},[7]={[1]="\14",[15]="\16",[87]="\31",[89]="\30",[62]="\26",[17]="\27",[97]="\19",[19]="\43",[10]="\12",[39]="\33",[41]="\24",[82]="\40",[3]="\32",[46]="\41",[47]="\38",[94]="\25",[65]="\23",[50]="\39",[26]="\11",[7]="\28",[33]="\18",[61]="\17",[25]="\42"},[8]={[111]="\101",[162]="\29",[2]="\34",[133]="\21",[142]="\36",[5]="\20",[21]="\37",[170]="\44",[130]="\22",[66]="\35"},[9]={[241]="\121",[361]="\104",[365]="\184",[125]="\227",[373]="\198",[253]="\117",[381]="\57",[270]="\49",[413]="\80",[290]="\47",[294]="\115",[38]="\112",[429]="\74",[433]="\0",[437]="\48",[158]="\183",[453]="\107",[166]="\111",[469]="\182",[477]="\241",[45]="\86",[489]="\69",[366]="\100",[497]="\61",[509]="\76",[49]="\53",[390]="\78",[279]="\196",[283]="\70",[414]="\98",[53]="\55",[422]="\109",[233]="\79",[349]="\89",[369]="\52",[14]="\105",[238]="\56",[319]="\162",[323]="\83",[327]="\63",[458]="\65",[335]="\231",[339]="\225",[337]="\114",[347]="\193",[493]="\139",[23]="\209",[359]="\250",[490]="\68",[42]="\54",[63]="\91",[286]="\97",[254]="\50",[510]="\108",[109]="\73",[67]="\103",[255]="\122",[69]="\170",[70]="\110",[407]="\176",[411]="\119",[110]="\120",[83]="\146",[149]="\163",[151]="\224",[85]="\51",[155]="\177",[79]="\251",[27]="\118",[447]="\159",[451]="\228",[455]="\175",[383]="\174",[463]="\243",[467]="\157",[173]="\210",[475]="\167",[177]="\84",[90]="\45",[487]="\206",[93]="\226",[495]="\245",[207]="\64",[127]="\147",[191]="\155",[511]="\153",[195]="\208",[197]="\85",[199]="\178",[181]="\82",[102]="\116",[103]="\71",[285]="\144",[105]="\102",[211]="\199",[213]="\123",[301]="\66",[305]="\46",[219]="\137",[81]="\67",[91]="\88",[157]="\130",[325]="\95",[29]="\58",[231]="\201",[117]="\99",[341]="\222",[237]="\77",[239]="\211",[71]="\223"},[10]={[710]="\149",[245]="\60",[742]="\172",[774]="\81",[134]="\151",[917]="\145",[274]="\216",[405]="\242",[146]="\194",[838]="\246",[298]="\248",[870]="\189",[1013]="\150",[894]="\190",[326]="\244",[330]="\166",[334]="\217",[465]="\179",[346]="\59",[354]="\180",[966]="\212",[974]="\143",[370]="\148",[998]="\154",[625]="\138",[382]="\161",[194]="\141",[198]="\126",[402]="\96",[206]="\185",[586]="\129",[721]="\187",[610]="\135",[618]="\181",[626]="\72",[226]="\62",[454]="\127",[658]="\113",[462]="\164",[234]="\214",[474]="\140",[242]="\106",[714]="\188",[730]="\87",[498]="\237",[746]="\125",[754]="\229",[786]="\128",[202]="\93",[18]="\255",[810]="\173",[846]="\131",[74]="\192",[842]="\142",[977]="\252",[858]="\235",[78]="\134",[874]="\234",[882]="\90",[646]="\92",[1006]="\160",[126]="\165",[914]="\221",[718]="\94",[738]="\238",[638]="\197",[482]="\230",[34]="\220",[962]="\133",[6]="\213",[706]="\219",[986]="\171",[994]="\233",[866]="\200",[1010]="\247",[98]="\169",[518]="\236",[494]="\207",[230]="\205",[542]="\191",[501]="\202",[530]="\203",[450]="\204",[209]="\158",[106]="\186",[590]="\136",[218]="\232",[733]="\124",[309]="\168",[221]="\152",[757]="\240",[113]="\215",[114]="\156",[362]="\239",[486]="\132",[358]="\249",[262]="\75",[30]="\218",[821]="\195",[546]="\253"}}
local function huffmanDecode(encoded)
local h1,h2,h3 = byte(encoded, 1, 3)
if (not h3) or (#encoded < 4) then
error("invalid input")
end
local original_length = bit.bor(bit.lshift(h3,16), bit.lshift(h2,8), h1)
local encoded_length = #encoded+1
local decoded = {}
local decoded_length = 0
local buffer = 0
local buffer_length = 0
local code
local code_len = 2
local temp
local pos = 4
while decoded_length < original_length do
if code_len <= buffer_length then
temp = invcodes[code_len]
code = bit.band(buffer, bit.lshift(1, code_len)-1)
if temp and temp[code] then --most of the time temp is nil
decoded_length = decoded_length + 1
decoded[decoded_length] = temp[code]
buffer = bit.rshift(buffer, code_len)
buffer_length = buffer_length - code_len
code_len = 2
else
code_len = code_len + 1
if code_len > 10 then
error("malformed code")
end
end
else
buffer = bit.bor(buffer, bit.lshift(byte(encoded, pos), buffer_length))
buffer_length = buffer_length + 8
pos = pos + 1
if pos > encoded_length then
error("malformed code")
end
end
end
return concat(decoded)
end
local function invEscapeSub(str)
local escseq,body = match(str,"^(.-)\n(.-)$")
if not escseq then error("invalid input") end
return gsub(body,escseq,"\26")
end
local dictionary
local subtables
local function deserializeChunk(chunk)
local ctype,val = byte(chunk),sub(chunk,3)
if ctype == 89 then return dictionary[ val ]
elseif ctype == 86 then
local a,b,c = match(val,"^(.-),(.-),(.+)$")
return Vector( tonumber(a), tonumber(b), tonumber(c) )
elseif ctype == 65 then
local a,b,c = match(val,"^(.-),(.-),(.+)$")
return Angle( tonumber(a), tonumber(b), tonumber(c) )
elseif ctype == 84 then
local t = {}
local tv = subtables[val]
if not tv then
tv = {}
subtables[ val ] = tv
end
tv[#tv+1] = t
return t
elseif ctype == 78 then return tonumber(val)
elseif ctype == 83 then return gsub(sub(val,2,-2),"<EFBFBD>",";")
elseif ctype == 66 then return val == "t"
elseif ctype == 80 then return 1
end
error(format("AD1 deserialization failed: invalid chunk (%u:%s)",ctype,val))
end
local function deserializeAD1(dupestring)
dupestring = dupestring:Replace("\r\n", "\n")
local header, extraHeader, dupeBlock, dictBlock = dupestring:match("%[Info%]\n(.+)\n%[More Information%]\n(.+)\n%[Save%]\n(.+)\n%[Dict%]\n(.+)")
if not header then
error("unknown duplication format")
end
local info = {}
for k,v in header:gmatch("([^\n:]+):([^\n]+)") do
info[k] = v
end
local moreinfo = {}
for k,v in extraHeader:gmatch("([^\n:]+):([^\n]+)") do
moreinfo[k] = v
end
dictionary = {}
for k,v in dictBlock:gmatch("(.-):\"(.-)\"\n") do
dictionary[k] = v
end
local dupe = {}
for key,block in dupeBlock:gmatch("([^\n:]+):([^\n]+)") do
local tables = {}
subtables = {}
local head
for id,chunk in block:gmatch('(%w+){(.-)}') do
--check if this table is the trunk
if byte(id) == 72 then
id = sub(id,2)
head = id
end
tables[id] = {}
for kv in gmatch(chunk,'[^;]+') do
local k,v = match(kv,'(.-)=(.+)')
if k then
k = deserializeChunk( k )
v = deserializeChunk( v )
tables[id][k] = v
else
v = deserializeChunk( kv )
local tid = tables[id]
tid[#tid+1]=v
end
end
end
--Restore table references
for id,tbls in pairs( subtables ) do
for _,tbl in pairs( tbls ) do
if not tables[id] then error("attempt to reference a nonexistent table") end
for k,v in pairs(tables[id]) do
tbl[k] = v
end
end
end
dupe[key] = tables[ head ]
end
return dupe, info, moreinfo
end
--seperates the header and body and converts the header to a table
local function getInfo(str)
local last = str:find("\2")
if not last then
error("attempt to read AD2 file with malformed info block error 1")
end
local info = {}
local ss = str:sub(1,last-1)
for k,v in ss:gmatch("(.-)\1(.-)\1") do
info[k] = v
end
if info.check ~= "\r\n\t\n" then
if info.check == "\10\9\10" then
error("detected AD2 file corrupted in file transfer (newlines homogenized)(when using FTP, transfer AD2 files in image/binary mode, not ASCII/text mode)")
else
error("attempt to read AD2 file with malformed info block error 2")
end
end
return info, str:sub(last+2)
end
--decoders for individual versions go here
local versions = {}
versions[2] = function(encodedDupe)
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n", "\n")
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
local info, dupestring = getInfo(encodedDupe:sub(7))
return deserialize_v2(
lzwDecode(
huffmanDecode(
invEscapeSub(dupestring)
)
)
), info
end
versions[1] = function(encodedDupe)
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n", "\n")
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
local info, dupestring = getInfo(encodedDupe:sub(7))
return deserialize_v1(
lzwDecode(
huffmanDecode(
invEscapeSub(dupestring)
)
)
), info
end
versions[0] = deserializeAD1
AdvDupe2.LegacyDecoders = versions