local f local Y local l local J=50 local A={[22]=18;[31]=8;[33]=28;[0]=3,[1]=13;[2]=23,[26]=33,[12]=1;[13]=6,[14]=10,[15]=16;[16]=20;[17]=26;[18]=30,[19]=36;[3]=0,[4]=2,[5]=4,[6]=7,[7]=9,[8]=12,[9]=14;[10]=17,[20]=19,[21]=22;[23]=24,[24]=27;[25]=29;[27]=32;[32]=34,[34]=37;[11]=5,[28]=11;[29]=15,[30]=21;[35]=25;[36]=31;[37]=35}local Q={[0]="\065\066\067";"\065\066\120";"\065\066\067","\065\066\067";"\065\066\067";"\065\066\120";"\065\066\067";"\065\066\120";"\065\066\067","\065\066\067";"\065\066\067";"\065\066\067","\065\066\067","\065\066\067";"\065\066\067","\065\066\067","\065\066\067","\065\066\067";"\065\066\067";"\065\066\067","\065\066\067","\065\066\067","\065\115\066\120","\065\066\067";"\065\066\067";"\065\066\067","\065\066\067","\065\066\067","\065\066\067","\065\066\067","\065\066\067","\065\115\066\120";"\065\115\066\120";"\065\066\067";"\065\066\067","\065\066\067";"\065\066\120","\065\066\067"}local N={[0]={b="\079\112\065\114\103\082";c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\075";c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\085";c="\079\112\065\114\103\085"},{b="\079\112\065\114\103\082";c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\085",c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\075",c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\082";c="\079\112\065\114\103\075"};{b="\079\112\065\114\103\075";c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\085";c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\075",c="\079\112\065\114\103\075"},{b="\079\112\065\114\103\085";c="\079\112\065\114\103\085"},{b="\079\112\065\114\103\082";c="\079\112\065\114\103\075"},{b="\079\112\065\114\103\075";c="\079\112\065\114\103\075"};{b="\079\112\065\114\103\075";c="\079\112\065\114\103\075"};{b="\079\112\065\114\103\075";c="\079\112\065\114\103\075"},{b="\079\112\065\114\103\075",c="\079\112\065\114\103\075"},{b="\079\112\065\114\103\075",c="\079\112\065\114\103\075"};{b="\079\112\065\114\103\075",c="\079\112\065\114\103\075"};{b="\079\112\065\114\103\082";c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\082",c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\082",c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\082",c="\079\112\065\114\103\082"},{b="\079\112\065\114\103\082";c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\075";c="\079\112\065\114\103\075"},{b="\079\112\065\114\103\075",c="\079\112\065\114\103\075"},{b="\079\112\065\114\103\075",c="\079\112\065\114\103\075"};{b="\079\112\065\114\103\082",c="\079\112\065\114\103\085"};{b="\079\112\065\114\103\082";c="\079\112\065\114\103\085"};{b="\079\112\065\114\103\085";c="\079\112\065\114\103\085"};{b="\079\112\065\114\103\085";c="\079\112\065\114\103\085"};{b="\079\112\065\114\103\085",c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\082",c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\082";c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\078",c="\079\112\065\114\103\085"},{b="\079\112\065\114\103\085";c="\079\112\065\114\103\085"};{b="\079\112\065\114\103\078";c="\079\112\065\114\103\078"};{b="\079\112\065\114\103\085";c="\079\112\065\114\103\078"},{b="\079\112\065\114\103\085",c="\079\112\065\114\103\078"}}local function n(f,Y,l,J)local A=0 for l=Y,l,J do A=A+string.byte(f,l,l)*256^(l-Y)end return A end local function i(f,Y,l,J)local A=-1^bit32.rshift(J,7)local Q=bit32.rshift(l,7)+bit32.lshift(bit32.band(J,127),1)local N=(f+bit32.lshift(Y,8))+bit32.lshift(bit32.band(l,127),16)local n=1 if Q==0 then if N==0 then return A*0 else n=0 Q=1 end elseif Q==127 then if N==0 then return A*(1/0)else return A*(0/0)end end return(A*2^(Q-127))*(1+n/8388608.0)end local function u(f,Y,l,J,A,Q,N,n)local i=-1^bit32.rshift(n,7)local u=bit32.lshift(bit32.band(n,127),4)+bit32.rshift(N,4)local a=bit32.band(N,15)*2.8147497671066e+14 local q=1 a=(((((a+Q*1099511627776.0)+A*4294967296.0)+J*16777216.0)+l*65536.0)+Y*256.0)+f if u==0 then if a==0 then return i*0 else q=0 u=1 end elseif u==2047 then if a==0 then return i*(1/0)else return i*(0/0)end end return(i*2^(u-1023))*(q+a/4.5035996273705e+15)end local function a(f,Y,l)return n(f,Y,l-1,1)end local function q(f,Y,l)return n(f,l-1,Y,-1)end local function X(f,Y)return i(string.byte(f,Y,Y+3))end local function w(f,Y)local l,J,A,Q=string.byte(f,Y,Y+3)return i(Q,A,J,l)end local function r(f,Y)return u(string.byte(f,Y,Y+7))end local function s(f,Y)local l,J,A,Q,N,n,i,a=string.byte(f,Y,Y+7)return u(a,i,n,N,Q,A,J,l)end local O={[4]={little=X,big=w};[8]={little=r;big=s}}local function H(f)local Y=f.index local l=string.byte(f.source,Y,Y)f.index=Y+1 return l end local function F(f,Y)local l=f.index+Y local J=string.sub(f.source,f.index,l-1)f.index=l return J end local function v(f)local Y=f:s_szt()local l if Y~=0 then l=string.sub(F(f,Y),1,-2)end return l end local function t(f,Y)return function(l)local J=l.index+f local A=Y(l.source,l.index,J)l.index=J return A end end local function R(f,Y)return function(l)local J=Y(l.source,l.index)l.index=l.index+f return J end end local function y(f)local Y=f:s_int()local l={}for Y=1,Y,1 do local J=f:s_ins()local n=bit32.band(J,63)local i=Q[n]local u=N[n]local a={value=J,op=A[n],A=bit32.band(bit32.rshift(J,6),255)}if i=="\065\066\067"then a.B=bit32.band(bit32.rshift(J,23),511)a.C=bit32.band(bit32.rshift(J,14),511)a.is_KB=u.b=="\079\112\065\114\103\075"and a.B>255 a.is_KC=u.c=="\079\112\065\114\103\075"and a.C>255 elseif i=="\065\066\120"then a.Bx=bit32.band(bit32.rshift(J,14),262143)a.is_K=u.b=="\079\112\065\114\103\075"elseif i=="\065\115\066\120"then a.sBx=bit32.band(bit32.rshift(J,14),262143)-131071 end l[Y]=a end return l end local function C(f)local Y=f:s_int()local l={}for Y=1,Y,1 do local J=H(f)local A if J==1 then A=H(f)~=0 elseif J==3 then A=f:s_num()elseif J==4 then A=v(f)end l[Y]=A end return l end local function B(f,Y)local J=f:s_int()local A={}for J=1,J,1 do A[J]=l(f,Y)end return A end local function U(f)local Y=f:s_int()local l={}for Y=1,Y,1 do l[Y]=f:s_int()end return l end local function V(f)local Y=f:s_int()local l={}for Y=1,Y,1 do l[Y]={varname=v(f),startpc=f:s_int();endpc=f:s_int()}end return l end local function x(f)local Y=f:s_int()local l={}for Y=1,Y,1 do l[Y]=v(f)end return l end function l(f,Y)local l={}local J=v(f)or Y l.source=J f:s_int()f:s_int()l.numupvals=H(f)l.numparams=H(f)H(f)H(f)l.code=y(f)l.const=C(f)l.subs=B(f,J)l.lines=U(f)V(f)x(f)for f,Y in ipairs(l.code)do if Y.is_K then Y.const=l.const[Y.Bx+1]else if Y.is_KB then Y.const_B=l.const[Y.B-255]end if Y.is_KC then Y.const_C=l.const[Y.C-255]end end end return l end function f(f)local Y local J local A local Q local N local n local i local u={index=1;source=f}assert(F(u,4)=="\027\076\117\097","\105\110\118\097\108\105\100 \076\117\097 \115\105\103\110\097\116\117\114\101")assert(H(u)==81,"\105\110\118\097\108\105\100 \076\117\097 \118\101\114\115\105\111\110")assert(H(u)==0,"\105\110\118\097\108\105\100 \076\117\097 \102\111\114\109\097\116")J=H(u)~=0 A=H(u)Q=H(u)N=H(u)n=H(u)i=H(u)~=0 Y=J and a or q u.s_int=t(A,Y)u.s_szt=t(Q,Y)u.s_ins=t(N,Y)if i then u.s_num=t(n,Y)elseif O[n]then u.s_num=R(n,O[n][J and"\108\105\116\116\108\101"or"\098\105\103"])else error("\117\110\115\117\112\112\111\114\116\101\100 \102\108\111\097\116 \115\105\122\101")end return l(u,"\064\118\105\114\116\117\097\108")end local function c(f,Y)for l,J in pairs(f)do if J.index>=Y then J.value=J.store[J.index]J.store=J J.index="\118\097\108\117\101"f[l]=nil end end end local function g(f,Y,l)local J=f[Y]if not J then J={index=Y;store=l}f[Y]=J end return J end local function M(...)return select("\035",...),{...}end local function L(f,Y)local l=f.source local J=f.lines[f.pc-1]local A,Q,N=string.match(Y or"","\094\040\046-\041\058\040\037\100\043\041\058\037\115\043\040\046\043\041")local n="\037\115\058\037\105\058 \091\037\115\058\037\105\093 \037\115"J=J or"\048"A=A or"\063"Q=Q or"\048"N=N or Y or""error(string.format(n,l,J,A,Q,N),0)end local function I(f)local l=f.code local Q=f.subs local N=f.env local n=f.upvals local i=f.varargs local u=-1 local a={}local q=f.stack local X=f.pc while true do local w=l[X]local r=w.op X=X+1 if r<18 then if r<8 then if r<3 then if r<1 then for f=w.A,w.B,1 do q[f]=nil end elseif r>1 then local f=n[w.B]q[w.A]=f.store[f.index]else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A]=f+Y end elseif r>3 then if r<6 then if r>4 then local f=w.A local Y=w.B local l if w.is_KC then l=w.const_C else l=q[w.C]end q[f+1]=q[Y]q[f]=q[Y][l]else q[w.A]=N[w.const]end elseif r>6 then local f if w.is_KC then f=w.const_C else f=q[w.C]end q[w.A]=q[w.B][f]else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A]=f-Y end else q[w.A]=q[w.B]end elseif r>8 then if r<13 then if r<10 then N[w.const]=q[w.A]elseif r>10 then if r<12 then local f=w.A local Y=w.B local l=w.C local J local A,Q if Y==0 then J=u-f else J=Y-1 end A,Q=M(q[f](table.unpack(q,f+1,f+J)))if l==0 then u=(f+A)-1 else A=l-1 end for Y=1,A,1 do q[(f+Y)-1]=Q[Y]end else local f=n[w.B]f.store[f.index]=q[w.A]end else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A]=f*Y end elseif r>13 then if r<16 then if r>14 then local f=w.A local Y=w.B local l if Y==0 then l=u-f else l=Y-1 end c(a,0)return M(q[f](table.unpack(q,f+1,f+l)))else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A][f]=Y end elseif r>16 then q[w.A]={}else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A]=f/Y end else q[w.A]=w.const end else local f=w.A local Y=q[f+2]local l=q[f]+Y local J=q[f+1]local A if Y==math.abs(Y)then A=l<=J else A=l>=J end if A then q[w.A]=l q[w.A+3]=l X=X+w.sBx end end elseif r>18 then if r<28 then if r<23 then if r<20 then q[w.A]=#q[w.B]elseif r>20 then if r<22 then local f=w.A local Y=w.B local l={}local J if Y==0 then J=(u-f)+1 else J=Y-1 end for Y=1,J,1 do l[Y]=q[(f+Y)-1]end c(a,0)return J,l else local f=q[w.B]for Y=w.B+1,w.C,1 do f=f..q[Y]end q[w.A]=f end else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A]=f%Y end elseif r>23 then if r<26 then if r>24 then c(a,w.A)else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end if(f==Y)==(w.A~=0)then X=X+l[X].sBx end X=X+1 end elseif r>26 then local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end if(f<Y)==(w.A~=0)then X=X+l[X].sBx end X=X+1 else local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end q[w.A]=f^Y end else q[w.A]=w.B~=0 if w.C~=0 then X=X+1 end end elseif r>28 then if r<33 then if r<30 then local f,Y if w.is_KB then f=w.const_B else f=q[w.B]end if w.is_KC then Y=w.const_C else Y=q[w.C]end if(f<=Y)==(w.A~=0)then X=X+l[X].sBx end X=X+1 elseif r>30 then if r<32 then local f=Q[w.Bx+1]local J=f.numupvals local i if J~=0 then i={}for f=1,J,1 do local Y=l[(X+f)-1]if Y.op==A[0]then i[f-1]=g(a,Y.B,q)elseif Y.op==A[4]then i[f-1]=n[Y.B]end end X=X+J end q[w.A]=Y(f,N,i)else local f=w.A local Y=w.B if not q[Y]==(w.C~=0)then X=X+1 else q[f]=q[Y]end end else q[w.A]=-q[w.B]end elseif r>33 then if r<36 then if r>34 then local f=w.A local Y=w.B if Y==0 then Y=i.size u=(f+Y)-1 end for Y=1,Y,1 do q[(f+Y)-1]=i.list[Y]end else local f=w.A local Y,l,J Y=assert(tonumber(q[f]),"\096\102\111\114\096 \105\110\105\116\105\097\108 \118\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")l=assert(tonumber(q[f+1]),"\096\102\111\114\096 \108\105\109\105\116 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")J=assert(tonumber(q[f+2]),"\096\102\111\114\096 \115\116\101\112 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")q[f]=Y-J q[f+1]=l q[f+2]=J X=X+w.sBx end elseif r>36 then local f=w.A local Y=w.C local l=w.B local A=q[f]local Q if l==0 then l=u-f end if Y==0 then Y=w[X].value X=X+1 end Q=(Y-1)*J for Y=1,l,1 do A[Y+Q]=q[f+Y]end else q[w.A]=not q[w.B]end else if not q[w.A]==(w.C~=0)then X=X+1 end end else local f=w.A local Y=q[f]local l=q[f+1]local J=q[f+2]local A=f+3 local Q q[A+2]=J q[A+1]=l q[A]=Y Q={Y(l,J)}for f=1,w.C,1 do q[(A+f)-1]=Q[f]end if q[A]~=nil then q[f+2]=q[A]else X=X+1 end end else X=X+w.sBx end f.pc=X end end function Y(f,Y,l)local J=f.code local A=f.subs local Q=f.lines local N=f.source local n=f.numparams local function i(...)local f={}local i={}local u=0 local a,q=M(...)local X local w,r,s for Y=1,n,1 do f[Y-1]=q[Y]end if n<a then u=a-n for f=1,u,1 do i[f]=q[n+f]end end X={varargs={list=i,size=u};code=J;subs=A;lines=Q;source=N;env=Y,upvals=l,stack=f;pc=1}w,r,s=pcall(I,X,...)if w then return table.unpack(s,1,r)else L(X,r)end return end return i end return function(l,J)return Y(f(l),J or{})end
--# selene: allow(divide_by_zero)
local stm_lua_bytecode
local wrap_lua_func
local stm_lua_func

-- SETLIST config
local FIELDS_PER_FLUSH = 50

-- remap for better lookup
local OPCODE_RM = {
	-- level 1
	[22] = 18, -- JMP
	[31] = 8, -- FORLOOP
	[33] = 28, -- TFORLOOP
	-- level 2
	[0] = 3, -- MOVE
	[1] = 13, -- LOADK
	[2] = 23, -- LOADBOOL
	[26] = 33, -- TEST
	-- level 3
	[12] = 1, -- ADD
	[13] = 6, -- SUB
	[14] = 10, -- MUL
	[15] = 16, -- DIV
	[16] = 20, -- MOD
	[17] = 26, -- POW
	[18] = 30, -- UNM
	[19] = 36, -- NOT
	-- level 4
	[3] = 0, -- LOADNIL
	[4] = 2, -- GETUPVAL
	[5] = 4, -- GETGLOBAL
	[6] = 7, -- GETTABLE
	[7] = 9, -- SETGLOBAL
	[8] = 12, -- SETUPVAL
	[9] = 14, -- SETTABLE
	[10] = 17, -- NEWTABLE
	[20] = 19, -- LEN
	[21] = 22, -- CONCAT
	[23] = 24, -- EQ
	[24] = 27, -- LT
	[25] = 29, -- LE
	[27] = 32, -- TESTSET
	[32] = 34, -- FORPREP
	[34] = 37, -- SETLIST
	-- level 5
	[11] = 5, -- SELF
	[28] = 11, -- CALL
	[29] = 15, -- TAILCALL
	[30] = 21, -- RETURN
	[35] = 25, -- CLOSE
	[36] = 31, -- CLOSURE
	[37] = 35, -- VARARG
}

-- opcode types for getting values
local OPCODE_T = {
	[0] = 'ABC',
	'ABx',
	'ABC',
	'ABC',
	'ABC',
	'ABx',
	'ABC',
	'ABx',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'AsBx',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'ABC',
	'AsBx',
	'AsBx',
	'ABC',
	'ABC',
	'ABC',
	'ABx',
	'ABC',
}

local OPCODE_M = {
	[0] = {b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgK', c = 'OpArgN'},
	{b = 'OpArgU', c = 'OpArgU'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgU', c = 'OpArgN'},
	{b = 'OpArgK', c = 'OpArgN'},
	{b = 'OpArgR', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgN'},
	{b = 'OpArgU', c = 'OpArgN'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgU', c = 'OpArgU'},
	{b = 'OpArgR', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgR', c = 'OpArgR'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgK', c = 'OpArgK'},
	{b = 'OpArgR', c = 'OpArgU'},
	{b = 'OpArgR', c = 'OpArgU'},
	{b = 'OpArgU', c = 'OpArgU'},
	{b = 'OpArgU', c = 'OpArgU'},
	{b = 'OpArgU', c = 'OpArgN'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgR', c = 'OpArgN'},
	{b = 'OpArgN', c = 'OpArgU'},
	{b = 'OpArgU', c = 'OpArgU'},
	{b = 'OpArgN', c = 'OpArgN'},
	{b = 'OpArgU', c = 'OpArgN'},
	{b = 'OpArgU', c = 'OpArgN'},
}

-- int rd_int_basic(string src, int s, int e, int d)
-- @src - Source binary string
-- @s - Start index of a little endian integer
-- @e - End index of the integer
-- @d - Direction of the loop
local function rd_int_basic(src, s, e, d)
	local num = 0

	-- if bb[l] > 127 then -- signed negative
	-- 	num = num - 256 ^ l
	-- 	bb[l] = bb[l] - 128
	-- end

	for i = s, e, d do num = num + string.byte(src, i, i) * 256 ^ (i - s) end

	return num
end

-- float rd_flt_basic(byte f1..8)
-- @f1..4 - The 4 bytes composing a little endian float
local function rd_flt_basic(f1, f2, f3, f4)
	local sign = (-1) ^ bit32.rshift(f4, 7)
	local exp = bit32.rshift(f3, 7) + bit32.lshift(bit32.band(f4, 0x7F), 1)
	local frac = f1 + bit32.lshift(f2, 8) + bit32.lshift(bit32.band(f3, 0x7F), 16)
	local normal = 1

	if exp == 0 then
		if frac == 0 then
			return sign * 0
		else
			normal = 0
			exp = 1
		end
	elseif exp == 0x7F then
		if frac == 0 then
			return sign * (1 / 0)
		else
			return sign * (0 / 0)
		end
	end

	return sign * 2 ^ (exp - 127) * (1 + normal / 2 ^ 23)
end

-- double rd_dbl_basic(byte f1..8)
-- @f1..8 - The 8 bytes composing a little endian double
local function rd_dbl_basic(f1, f2, f3, f4, f5, f6, f7, f8)
	local sign = (-1) ^ bit32.rshift(f8, 7)
	local exp = bit32.lshift(bit32.band(f8, 0x7F), 4) + bit32.rshift(f7, 4)
	local frac = bit32.band(f7, 0x0F) * 2 ^ 48
	local normal = 1

	frac = frac + (f6 * 2 ^ 40) + (f5 * 2 ^ 32) + (f4 * 2 ^ 24) + (f3 * 2 ^ 16) + (f2 * 2 ^ 8) + f1 -- help

	if exp == 0 then
		if frac == 0 then
			return sign * 0
		else
			normal = 0
			exp = 1
		end
	elseif exp == 0x7FF then
		if frac == 0 then
			return sign * (1 / 0)
		else
			return sign * (0 / 0)
		end
	end

	return sign * 2 ^ (exp - 1023) * (normal + frac / 2 ^ 52)
end

-- int rd_int_le(string src, int s, int e)
-- @src - Source binary string
-- @s - Start index of a little endian integer
-- @e - End index of the integer
local function rd_int_le(src, s, e) return rd_int_basic(src, s, e - 1, 1) end

-- int rd_int_be(string src, int s, int e)
-- @src - Source binary string
-- @s - Start index of a big endian integer
-- @e - End index of the integer
local function rd_int_be(src, s, e) return rd_int_basic(src, e - 1, s, -1) end

-- float rd_flt_le(string src, int s)
-- @src - Source binary string
-- @s - Start index of little endian float
local function rd_flt_le(src, s) return rd_flt_basic(string.byte(src, s, s + 3)) end

-- float rd_flt_be(string src, int s)
-- @src - Source binary string
-- @s - Start index of big endian float
local function rd_flt_be(src, s)
	local f1, f2, f3, f4 = string.byte(src, s, s + 3)
	return rd_flt_basic(f4, f3, f2, f1)
end

-- double rd_dbl_le(string src, int s)
-- @src - Source binary string
-- @s - Start index of little endian double
local function rd_dbl_le(src, s) return rd_dbl_basic(string.byte(src, s, s + 7)) end

-- double rd_dbl_be(string src, int s)
-- @src - Source binary string
-- @s - Start index of big endian double
local function rd_dbl_be(src, s)
	local f1, f2, f3, f4, f5, f6, f7, f8 = string.byte(src, s, s + 7) -- same
	return rd_dbl_basic(f8, f7, f6, f5, f4, f3, f2, f1)
end

-- to avoid nested ifs in deserializing
local float_types = {
	[4] = {little = rd_flt_le, big = rd_flt_be},
	[8] = {little = rd_dbl_le, big = rd_dbl_be},
}

-- byte stm_byte(Stream S)
-- @S - Stream object to read from
local function stm_byte(S)
	local idx = S.index
	local bt = string.byte(S.source, idx, idx)

	S.index = idx + 1
	return bt
end

-- string stm_string(Stream S, int len)
-- @S - Stream object to read from
-- @len - Length of string being read
local function stm_string(S, len)
	local pos = S.index + len
	local str = string.sub(S.source, S.index, pos - 1)

	S.index = pos
	return str
end

-- string stm_lstring(Stream S)
-- @S - Stream object to read from
local function stm_lstring(S)
	local len = S:s_szt()
	local str

	if len ~= 0 then str = string.sub(stm_string(S, len), 1, -2) end

	return str
end

-- fn cst_int_rdr(string src, int len, fn func)
-- @len - Length of type for reader
-- @func - Reader callback
local function cst_int_rdr(len, func)
	return function(S)
		local pos = S.index + len
		local int = func(S.source, S.index, pos)
		S.index = pos

		return int
	end
end

-- fn cst_flt_rdr(string src, int len, fn func)
-- @len - Length of type for reader
-- @func - Reader callback
local function cst_flt_rdr(len, func)
	return function(S)
		local flt = func(S.source, S.index)
		S.index = S.index + len

		return flt
	end
end

local function stm_instructions(S)
	local size = S:s_int()
	local code = {}

	for i = 1, size do
		local ins = S:s_ins()
		local op = bit32.band(ins, 0x3F)
		local args = OPCODE_T[op]
		local mode = OPCODE_M[op]
		local data = {value = ins, op = OPCODE_RM[op], A = bit32.band(bit32.rshift(ins, 6), 0xFF)}

		if args == 'ABC' then
			data.B = bit32.band(bit32.rshift(ins, 23), 0x1FF)
			data.C = bit32.band(bit32.rshift(ins, 14), 0x1FF)
			data.is_KB = mode.b == 'OpArgK' and data.B > 0xFF -- post process optimization
			data.is_KC = mode.c == 'OpArgK' and data.C > 0xFF
		elseif args == 'ABx' then
			data.Bx = bit32.band(bit32.rshift(ins, 14), 0x3FFFF)
			data.is_K = mode.b == 'OpArgK'
		elseif args == 'AsBx' then
			data.sBx = bit32.band(bit32.rshift(ins, 14), 0x3FFFF) - 131071
		end

		code[i] = data
	end

	return code
end

local function stm_constants(S)
	local size = S:s_int()
	local consts = {}

	for i = 1, size do
		local tt = stm_byte(S)
		local k

		if tt == 1 then
			k = stm_byte(S) ~= 0
		elseif tt == 3 then
			k = S:s_num()
		elseif tt == 4 then
			k = stm_lstring(S)
		end

		consts[i] = k -- offset +1 during instruction decode
	end

	return consts
end

local function stm_subfuncs(S, src)
	local size = S:s_int()
	local sub = {}

	for i = 1, size do
		sub[i] = stm_lua_func(S, src) -- offset +1 in CLOSURE
	end

	return sub
end

local function stm_lineinfo(S)
	local size = S:s_int()
	local lines = {}

	for i = 1, size do lines[i] = S:s_int() end

	return lines
end

local function stm_locvars(S)
	local size = S:s_int()
	local locvars = {}

	for i = 1, size do locvars[i] = {varname = stm_lstring(S), startpc = S:s_int(), endpc = S:s_int()} end

	return locvars
end

local function stm_upvals(S)
	local size = S:s_int()
	local upvals = {}

	for i = 1, size do upvals[i] = stm_lstring(S) end

	return upvals
end

function stm_lua_func(S, psrc)
	local proto = {}
	local src = stm_lstring(S) or psrc -- source is propagated

	proto.source = src -- source name

	S:s_int() -- line defined
	S:s_int() -- last line defined

	proto.numupvals = stm_byte(S) -- num upvalues
	proto.numparams = stm_byte(S) -- num params

	stm_byte(S) -- vararg flag
	stm_byte(S) -- max stack size

	proto.code = stm_instructions(S)
	proto.const = stm_constants(S)
	proto.subs = stm_subfuncs(S, src)
	proto.lines = stm_lineinfo(S)

	stm_locvars(S)
	stm_upvals(S)

	-- post process optimization
	for _, v in ipairs(proto.code) do
		if v.is_K then
			v.const = proto.const[v.Bx + 1] -- offset for 1 based index
		else
			if v.is_KB then v.const_B = proto.const[v.B - 0xFF] end

			if v.is_KC then v.const_C = proto.const[v.C - 0xFF] end
		end
	end

	return proto
end

function stm_lua_bytecode(src)
	-- func reader
	local rdr_func

	-- header flags
	local little
	local size_int
	local size_szt
	local size_ins
	local size_num
	local flag_int

	-- stream object
	local stream = {
		-- data
		index = 1,
		source = src,
	}

	assert(stm_string(stream, 4) == '\27Lua', 'invalid Lua signature')
	assert(stm_byte(stream) == 0x51, 'invalid Lua version')
	assert(stm_byte(stream) == 0, 'invalid Lua format')

	little = stm_byte(stream) ~= 0
	size_int = stm_byte(stream)
	size_szt = stm_byte(stream)
	size_ins = stm_byte(stream)
	size_num = stm_byte(stream)
	flag_int = stm_byte(stream) ~= 0

	rdr_func = little and rd_int_le or rd_int_be
	stream.s_int = cst_int_rdr(size_int, rdr_func)
	stream.s_szt = cst_int_rdr(size_szt, rdr_func)
	stream.s_ins = cst_int_rdr(size_ins, rdr_func)

	if flag_int then
		stream.s_num = cst_int_rdr(size_num, rdr_func)
	elseif float_types[size_num] then
		stream.s_num = cst_flt_rdr(size_num, float_types[size_num][little and 'little' or 'big'])
	else
		error('unsupported float size')
	end

	return stm_lua_func(stream, '@virtual')
end

local function close_lua_upvalues(list, index)
	for i, uv in pairs(list) do
		if uv.index >= index then
			uv.value = uv.store[uv.index] -- store value
			uv.store = uv
			uv.index = 'value' -- self reference
			list[i] = nil
		end
	end
end

local function open_lua_upvalue(list, index, stack)
	local prev = list[index]

	if not prev then
		prev = {index = index, store = stack}
		list[index] = prev
	end

	return prev
end

local function wrap_lua_variadic(...) return select('#', ...), {...} end

local function on_lua_error(exst, err)
	local src = exst.source
	local line = exst.lines[exst.pc - 1]
	local psrc, pline, pmsg = string.match(err or '', '^(.-):(%d+):%s+(.+)')
	local fmt = '%s:%i: [%s:%i] %s'

	line = line or '0'
	psrc = psrc or '?'
	pline = pline or '0'
	pmsg = pmsg or err or ''

	error(string.format(fmt, src, line, psrc, pline, pmsg), 0)
end

local function exec_lua_func(exst)
	-- localize for easy lookup
	local code = exst.code
	local subs = exst.subs
	local env = exst.env
	local upvs = exst.upvals
	local vargs = exst.varargs

	-- state variables
	local stktop = -1
	local openupvs = {}
	local stack = exst.stack
	local pc = exst.pc

	while true do
		local inst = code[pc]
		local op = inst.op
		pc = pc + 1

		if op < 18 then
			if op < 8 then
				if op < 3 then
					if op < 1 then
						--[[LOADNIL]]
						for i = inst.A, inst.B do stack[i] = nil end
					elseif op > 1 then
						--[[GETUPVAL]]
						local uv = upvs[inst.B]

						stack[inst.A] = uv.store[uv.index]
					else
						--[[ADD]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						stack[inst.A] = lhs + rhs
					end
				elseif op > 3 then
					if op < 6 then
						if op > 4 then
							--[[SELF]]
							local A = inst.A
							local B = inst.B
							local index

							if inst.is_KC then
								index = inst.const_C
							else
								index = stack[inst.C]
							end

							stack[A + 1] = stack[B]
							stack[A] = stack[B][index]
						else
							--[[GETGLOBAL]]
							stack[inst.A] = env[inst.const]
						end
					elseif op > 6 then
						--[[GETTABLE]]
						local index

						if inst.is_KC then
							index = inst.const_C
						else
							index = stack[inst.C]
						end

						stack[inst.A] = stack[inst.B][index]
					else
						--[[SUB]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						stack[inst.A] = lhs - rhs
					end
				else --[[MOVE]]
					stack[inst.A] = stack[inst.B]
				end
			elseif op > 8 then
				if op < 13 then
					if op < 10 then
						--[[SETGLOBAL]]
						env[inst.const] = stack[inst.A]
					elseif op > 10 then
						if op < 12 then
							--[[CALL]]
							local A = inst.A
							local B = inst.B
							local C = inst.C
							local params
							local sz_vals, l_vals

							if B == 0 then
								params = stktop - A
							else
								params = B - 1
							end

							sz_vals, l_vals = wrap_lua_variadic(stack[A](table.unpack(stack, A + 1, A + params)))

							if C == 0 then
								stktop = A + sz_vals - 1
							else
								sz_vals = C - 1
							end

							for i = 1, sz_vals do stack[A + i - 1] = l_vals[i] end
						else
							--[[SETUPVAL]]
							local uv = upvs[inst.B]

							uv.store[uv.index] = stack[inst.A]
						end
					else
						--[[MUL]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						stack[inst.A] = lhs * rhs
					end
				elseif op > 13 then
					if op < 16 then
						if op > 14 then
							--[[TAILCALL]]
							local A = inst.A
							local B = inst.B
							local params

							if B == 0 then
								params = stktop - A
							else
								params = B - 1
							end

							close_lua_upvalues(openupvs, 0)
							return wrap_lua_variadic(stack[A](table.unpack(stack, A + 1, A + params)))
						else
							--[[SETTABLE]]
							local index, value

							if inst.is_KB then
								index = inst.const_B
							else
								index = stack[inst.B]
							end

							if inst.is_KC then
								value = inst.const_C
							else
								value = stack[inst.C]
							end

							stack[inst.A][index] = value
						end
					elseif op > 16 then
						--[[NEWTABLE]]
						stack[inst.A] = {}
					else
						--[[DIV]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						stack[inst.A] = lhs / rhs
					end
				else
					--[[LOADK]]
					stack[inst.A] = inst.const
				end
			else
				--[[FORLOOP]]
				local A = inst.A
				local step = stack[A + 2]
				local index = stack[A] + step
				local limit = stack[A + 1]
				local loops

				if step == math.abs(step) then
					loops = index <= limit
				else
					loops = index >= limit
				end

				if loops then
					stack[inst.A] = index
					stack[inst.A + 3] = index
					pc = pc + inst.sBx
				end
			end
		elseif op > 18 then
			if op < 28 then
				if op < 23 then
					if op < 20 then
						--[[LEN]]
						stack[inst.A] = #stack[inst.B]
					elseif op > 20 then
						if op < 22 then
							--[[RETURN]]
							local A = inst.A
							local B = inst.B
							local vals = {}
							local size

							if B == 0 then
								size = stktop - A + 1
							else
								size = B - 1
							end

							for i = 1, size do vals[i] = stack[A + i - 1] end

							close_lua_upvalues(openupvs, 0)
							return size, vals
						else
							--[[CONCAT]]
							local str = stack[inst.B]

							for i = inst.B + 1, inst.C do str = str .. stack[i] end

							stack[inst.A] = str
						end
					else
						--[[MOD]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						stack[inst.A] = lhs % rhs
					end
				elseif op > 23 then
					if op < 26 then
						if op > 24 then
							--[[CLOSE]]
							close_lua_upvalues(openupvs, inst.A)
						else
							--[[EQ]]
							local lhs, rhs

							if inst.is_KB then
								lhs = inst.const_B
							else
								lhs = stack[inst.B]
							end

							if inst.is_KC then
								rhs = inst.const_C
							else
								rhs = stack[inst.C]
							end

							if (lhs == rhs) == (inst.A ~= 0) then pc = pc + code[pc].sBx end

							pc = pc + 1
						end
					elseif op > 26 then
						--[[LT]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						if (lhs < rhs) == (inst.A ~= 0) then pc = pc + code[pc].sBx end

						pc = pc + 1
					else
						--[[POW]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						stack[inst.A] = lhs ^ rhs
					end
				else
					--[[LOADBOOL]]
					stack[inst.A] = inst.B ~= 0

					if inst.C ~= 0 then pc = pc + 1 end
				end
			elseif op > 28 then
				if op < 33 then
					if op < 30 then
						--[[LE]]
						local lhs, rhs

						if inst.is_KB then
							lhs = inst.const_B
						else
							lhs = stack[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = stack[inst.C]
						end

						if (lhs <= rhs) == (inst.A ~= 0) then pc = pc + code[pc].sBx end

						pc = pc + 1
					elseif op > 30 then
						if op < 32 then
							--[[CLOSURE]]
							local sub = subs[inst.Bx + 1] -- offset for 1 based index
							local nups = sub.numupvals
							local uvlist

							if nups ~= 0 then
								uvlist = {}

								for i = 1, nups do
									local pseudo = code[pc + i - 1]

									if pseudo.op == OPCODE_RM[0] then -- @MOVE
										uvlist[i - 1] = open_lua_upvalue(openupvs, pseudo.B, stack)
									elseif pseudo.op == OPCODE_RM[4] then -- @GETUPVAL
										uvlist[i - 1] = upvs[pseudo.B]
									end
								end

								pc = pc + nups
							end

							stack[inst.A] = wrap_lua_func(sub, env, uvlist)
						else
							--[[TESTSET]]
							local A = inst.A
							local B = inst.B

							if (not stack[B]) == (inst.C ~= 0) then
								pc = pc + 1
							else
								stack[A] = stack[B]
							end
						end
					else
						--[[UNM]]
						stack[inst.A] = -stack[inst.B]
					end
				elseif op > 33 then
					if op < 36 then
						if op > 34 then
							--[[VARARG]]
							local A = inst.A
							local size = inst.B

							if size == 0 then
								size = vargs.size
								stktop = A + size - 1
							end

							for i = 1, size do stack[A + i - 1] = vargs.list[i] end
						else
							--[[FORPREP]]
							local A = inst.A
							local init, limit, step

							init = assert(tonumber(stack[A]), '`for` initial value must be a number')
							limit = assert(tonumber(stack[A + 1]), '`for` limit must be a number')
							step = assert(tonumber(stack[A + 2]), '`for` step must be a number')

							stack[A] = init - step
							stack[A + 1] = limit
							stack[A + 2] = step

							pc = pc + inst.sBx
						end
					elseif op > 36 then
						--[[SETLIST]]
						local A = inst.A
						local C = inst.C
						local size = inst.B
						local tab = stack[A]
						local offset

						if size == 0 then size = stktop - A end

						if C == 0 then
							C = inst[pc].value
							pc = pc + 1
						end

						offset = (C - 1) * FIELDS_PER_FLUSH

						for i = 1, size do tab[i + offset] = stack[A + i] end
					else
						--[[NOT]]
						stack[inst.A] = not stack[inst.B]
					end
				else
					--[[TEST]]
					if (not stack[inst.A]) == (inst.C ~= 0) then pc = pc + 1 end
				end
			else
				--[[TFORLOOP]]
				local A = inst.A
				local func = stack[A]
				local state = stack[A + 1]
				local index = stack[A + 2]
				local base = A + 3
				local vals

				stack[base + 2] = index
				stack[base + 1] = state
				stack[base] = func

				vals = {func(state, index)}

				for i = 1, inst.C do stack[base + i - 1] = vals[i] end

				if stack[base] ~= nil then
					stack[A + 2] = stack[base]
				else
					pc = pc + 1
				end
			end
		else
			--[[JMP]]
			pc = pc + inst.sBx
		end

		exst.pc = pc
	end
end

function wrap_lua_func(state, env, upvals)
	local st_code = state.code
	local st_subs = state.subs
	local st_lines = state.lines
	local st_source = state.source
	local st_numparams = state.numparams

	local function exec_wrap(...)
		local stack = {}
		local varargs = {}
		local sizevarg = 0
		local sz_args, l_args = wrap_lua_variadic(...)

		local exst
		local ok, err, vals

		for i = 1, st_numparams do stack[i - 1] = l_args[i] end

		if st_numparams < sz_args then
			sizevarg = sz_args - st_numparams
			for i = 1, sizevarg do varargs[i] = l_args[st_numparams + i] end
		end

		exst = {
			varargs = {list = varargs, size = sizevarg},
			code = st_code,
			subs = st_subs,
			lines = st_lines,
			source = st_source,
			env = env,
			upvals = upvals,
			stack = stack,
			pc = 1,
		}

		ok, err, vals = pcall(exec_lua_func, exst, ...)

		if ok then
			return table.unpack(vals, 1, err)
		else
			on_lua_error(exst, err)
		end

		return -- explicit "return nothing"
	end

	return exec_wrap
end

return function(BCode, Env)
	return wrap_lua_func(stm_lua_bytecode(BCode), Env or {})
end
