local e local r local F=50 local E={[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 A={[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 I={[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 V={[1]=buffer.readu8,[2]=buffer.readu16;[4]=buffer.readu32}local R={[1]=buffer.writeu8,[2]=buffer.writeu16,[4]=buffer.writeu32}local function G(e,r,F)return V[F-r](e,r)end local function o(e,r)return function(F,E,A)local I,V=(A or r)*8,R[A]V(F,E,bit32.rshift(bit32.byteswap(G(F,E,I)),32-I))local o=e(F,E)V(F,E,bit32.rshift(bit32.byteswap(G(F,E,I)),32-I))return o end end local u={[4]={little=buffer.readf32,big=o(buffer.readf32)};[8]={little=buffer.readf64,big=o(buffer.readf64)}}local function b(e)local r=e.index local F=buffer.readu8(e.source,r)e.index=r+1 return F end local function T(e,r)local F=buffer.readstring(e.source,e.index,r)e.index+=r return F end local function l(e)local r=e:s_szt()local F if r~=0 then F=string.sub(T(e,r),1,-2)end return F end local function c(e,r)return function(F)local E=F.index+e local A=r(F.source,F.index,E)F.index=E return A end end local function K(e,r)return function(F)local E=r(F.source,F.index)F.index=F.index+e return E end end local function k(e)local r=e:s_int()local F=table.create(r)for r=1,r,1 do local V=e:s_ins()local R=bit32.band(V,63)local G=A[R]local o=I[R]local u={value=V;op=E[R],A=bit32.band(bit32.rshift(V,6),255)}if G=="\065\066\067"then u.B=bit32.band(bit32.rshift(V,23),511)u.C=bit32.band(bit32.rshift(V,14),511)u.is_KB=o.b=="\079\112\065\114\103\075"and u.B>255 u.is_KC=o.c=="\079\112\065\114\103\075"and u.C>255 if R==10 then local e=bit32.band(bit32.rshift(u.B,3),31)if e==0 then u.const=u.B else u.const=bit32.lshift(bit32.band(u.B,7)+8,e-1)end end elseif G=="\065\066\120"then u.Bx=bit32.band(bit32.rshift(V,14),262143)u.is_K=o.b=="\079\112\065\114\103\075"elseif G=="\065\115\066\120"then u.sBx=bit32.band(bit32.rshift(V,14),262143)-131071 end F[r]=u end return F end local function v(e)local r=e:s_int()local F=table.create(r)for r=1,r,1 do local E=b(e)local A if E==1 then A=b(e)~=0 elseif E==3 then A=e:s_num()elseif E==4 then A=l(e)end F[r]=A end return F end local function q(e,F)local E=e:s_int()local A=table.create(E)for E=1,E,1 do A[E]=r(e,F)end return A end local function d(e)local r=e:s_int()local F=table.create(r)for r=1,r,1 do F[r]=e:s_int()end return F end local function O(e)local r=e:s_int()local F=table.create(r)for r=1,r,1 do F[r]={varname=l(e);startpc=e:s_int();endpc=e:s_int()}end return F end local function s(e)local r=e:s_int()local F=table.create(r)for r=1,r,1 do F[r]=l(e)end return F end function r(e,r)local F={}local E=l(e)or r F.source=E e:s_int()e:s_int()F.num_upval=b(e)F.num_param=b(e)b(e)F.max_stack=b(e)F.code=k(e)F.const=v(e)F.subs=q(e,E)F.lines=d(e)O(e)s(e)for e,r in ipairs(F.code)do if r.is_K then r.const=F.const[r.Bx+1]else if r.is_KB then r.const_B=F.const[r.B-255]end if r.is_KC then r.const_C=F.const[r.C-255]end end end return F end local function Y(e)local F local E local A local I local V local R local l local k={index=0;source=typeof(e)=="\098\117\102\102\101\114"and e or buffer.fromstring(e)}assert(T(k,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(b(k)==81,"\105\110\118\097\108\105\100 \076\117\097 \118\101\114\115\105\111\110")assert(b(k)==0,"\105\110\118\097\108\105\100 \076\117\097 \102\111\114\109\097\116")E=b(k)~=0 A=b(k)I=b(k)V=b(k)R=b(k)l=b(k)~=0 F=E and G or o(G)k.s_int=c(A,F)k.s_szt=c(I,F)k.s_ins=c(V,F)if l then k.s_num=c(R,F)elseif u[R]then k.s_num=K(R,u[R][E 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 r(k,"\064\118\105\114\116\117\097\108")end local function h(e,r)for F,E in pairs(e)do if E.index>=r then E.value=E.store[E.index]E.store=E E.index="\118\097\108\117\101"e[F]=nil end end end local function X(e,r,F)local E=e[r]if not E then E={index=r;store=F}e[r]=E end return E end local function S(e,r)local F=e.source local E=e.lines[e.pc-1]error(string.format("\037\115\058\037\105\058 \037\115",F,E,r),0)end local H,L local function U(e,r,F)H=H or(game:GetService("\082\117\110\083\101\114\118\105\099\101")):IsClient()L=L or script local E=Instance.new("\065\099\116\111\114")local A=script:Clone()local I=script[H and"\076\111\099\097\108\082\117\110\110\101\114"or"\082\117\110\110\101\114"]:Clone()local V=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")local R=0 for e,r in A:GetChildren()do if r:IsA("\065\099\116\111\114")then r:Destroy()end end V.Name="\082\101\116\117\114\110\080\097\115\115"V.Parent=I A.Parent=I I.Disabled=false I.Parent=E E.Name="\077\117\108\116\105\116\104\114\101\097\100\082\117\110\110\101\114\095"..tostring(math.random())E.Parent=L task.defer(E.SendMessage,E,"\119\114\097\112\095\115\116\097\116\101",e,r,F)task.wait()return function(...)R=R+1 local e=R local r=coroutine.running()local F=V.Event:Connect(function(F,...)if F==e then coroutine.resume(r,...)end end)task.defer(E.SendMessage,E,"\114\117\110\095\099\097\108\108\098\097\099\107",e,...)local A=table.pack(coroutine.yield())F:Disconnect()if not A[1]then error(A[2],2)end return table.unpack(A,2,A.n)end end local function Q(r,A,I)local V=r.code local R=r.subs local G=r.vararg local o=-1 local u={}local b=r.memory local T=r.pc while true do local l=V[T]local c=l.op T=T+1 if c<18 then if c<8 then if c<3 then if c<1 then for e=l.A,l.B,1 do b[e]=nil end elseif c>1 then local e=I[l.B]b[l.A]=e.store[e.index]else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A]=e+r end elseif c>3 then if c<6 then if c>4 then local e=l.A local r=l.B local F if l.is_KC then F=l.const_C else F=b[l.C]end b[e+1]=b[r]b[e]=b[r][F]else b[l.A]=A[l.const]end elseif c>6 then local e if l.is_KC then e=l.const_C else e=b[l.C]end b[l.A]=b[l.B][e]else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A]=e-r end else b[l.A]=b[l.B]end elseif c>8 then if c<13 then if c<10 then A[l.const]=b[l.A]elseif c>10 then if c<12 then local e=l.A local r=l.B local F=l.C local E if r==0 then E=o-e else E=r-1 end local A=table.pack(b[e](table.unpack(b,e+1,e+E)))local I=A.n if F==0 then o=(e+I)-1 else I=F-1 end table.move(A,1,I,e,b)else local e=I[l.B]e.store[e.index]=b[l.A]end else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A]=e*r end elseif c>13 then if c<16 then if c>14 then local e=l.A local r=l.B local F if r==0 then F=o-e else F=r-1 end h(u,0)return b[e](table.unpack(b,e+1,e+F))else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A][e]=r end elseif c>16 then b[l.A]=table.create(l.const)else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A]=e/r end else b[l.A]=l.const end else local e=l.A local r=b[e+2]local F=b[e]+r local E=b[e+1]local A if r>=0 then A=F<=E else A=F>=E end if A then b[e]=F b[e+3]=F T=T+l.sBx end end elseif c>18 then if c<28 then if c<23 then if c<20 then b[l.A]=#b[l.B]elseif c>20 then if c<22 then local e=l.A local r=l.B local F if r==0 then F=(o-e)+1 else F=r-1 end h(u,0)return table.unpack(b,e,(e+F)-1)else local e,r=l.B,l.C local F,E=pcall(table.concat,b,"",e,r)if not F then E=b[e]for e=e+1,r,1 do E=E..b[e]end end b[l.A]=E end else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A]=e%r end elseif c>23 then if c<26 then if c>24 then h(u,l.A)else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end if(e==r)==(l.A~=0)then T=T+V[T].sBx end T=T+1 end elseif c>26 then local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end if(e<r)==(l.A~=0)then T=T+V[T].sBx end T=T+1 else local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end b[l.A]=e^r end else b[l.A]=l.B~=0 if l.C~=0 then T=T+1 end end elseif c>28 then if c<33 then if c<30 then local e,r if l.is_KB then e=l.const_B else e=b[l.B]end if l.is_KC then r=l.const_C else r=b[l.C]end if(e<=r)==(l.A~=0)then T=T+V[T].sBx end T=T+1 elseif c>30 then if c<32 then local r=R[l.Bx+1]local F=r.num_upval local G if F~=0 then G=table.create(F-1)for e=1,F,1 do local r=V[(T+e)-1]if r.op==E[0]then G[e-1]=X(u,r.B,b)elseif r.op==E[4]then G[e-1]=I[r.B]end end T=T+F end b[l.A]=(r.source and string.sub(r.source,1,39)=="\083\085\080\069\082\095\083\069\067\082\069\084\095\065\068\079\078\073\083\095\077\085\076\084\073\084\072\082\069\065\068\095\080\082\069\070\073\088\095")and U(r,A,G)or e(r,A,G)else local e=l.A local r=l.B if not b[r]~=(l.C~=0)then b[e]=b[r]T=T+V[T].sBx end T=T+1 end else b[l.A]=-b[l.B]end elseif c>33 then if c<36 then if c>34 then local e=l.A local r=l.B if r==0 then r=G.len o=(e+r)-1 end table.move(G.list,1,r,e,b)else local e=l.A local r,F,E r=assert(tonumber(b[e]),"\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")F=assert(tonumber(b[e+1]),"\096\102\111\114\096 \108\105\109\105\116 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")E=assert(tonumber(b[e+2]),"\096\102\111\114\096 \115\116\101\112 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")b[e]=r-E b[e+1]=F b[e+2]=E T=T+l.sBx end elseif c>36 then local e=l.A local r=l.C local E=l.B local A=b[e]local I if E==0 then E=o-e end if r==0 then r=l[T].value T=T+1 end I=(r-1)*F table.move(b,e+1,e+E,I+1,A)else b[l.A]=not b[l.B]end else if not b[l.A]~=(l.C~=0)then T=T+V[T].sBx end T=T+1 end else local e=l.A local r=b[e]local F=b[e+1]local E=b[e+2]local A=e+3 local I if not E and(not F and type(r)=="\116\097\098\108\101")then local A=pcall(getmetatable,r)and getmetatable(r)if not(type(A)=="\116\097\098\108\101"and rawget(A,"\095\095\099\097\108\108"))then r,F,E=type(A)=="\116\097\098\108\101"and rawget(A,"\095\095\105\116\101\114")or next,r,nil b[e],b[e+1],b[e+2]=r,F,E end end I={r(F,E)}table.move(I,1,l.C,A,b)if b[A]~=nil then b[e+2]=b[A]T=T+V[T].sBx end T=T+1 end else T=T+l.sBx end r.pc=T end end function e(e,r,F)local function E(...)local E=table.pack(...)local A=table.create(e.max_stack)local I={len=0,list={}}table.move(E,1,e.num_param,0,A)if e.num_param<E.n then local r=e.num_param+1 local F=E.n-e.num_param I.len=F table.move(E,r,(r+F)-1,1,I.list)end local V={vararg=I,memory=A,code=e.code;subs=e.subs;pc=1}local R=table.pack(pcall(Q,V,r,F))if R[1]then return table.unpack(R,2,R.n)else local r={pc=V.pc,source=e.source;lines=e.lines}S(r,R[2])return end end return E end return setmetatable({bc_to_state=Y,wrap_state=e;OPCODE_RM=E,OPCODE_T=A,OPCODE_M=I},{__call=function(r,F,E)return e(Y(F),E or{})end})
--!native
--!optimize 2
--!nolint TableOperations
--# selene: allow(multiple_statements, mixed_table)
--[[
FiOne
Copyright (C) 2021  Rerumu

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
]] --

local lua_wrap_state
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"},
}

local intiger_types = {
	[1] = buffer.readu8,
	[2] = buffer.readu16,
	[4] = buffer.readu32,
}

local intiger_write_type = {
	[1] = buffer.writeu8,
	[2] = buffer.writeu16,
	[4] = buffer.writeu32,
}

-- int rd_int(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(src, s, e)
	return intiger_types[e - s](src, s)
end

-- number big_endian(string src, int s)
-- @callback - Function to be called after bitswap
-- @byte_count - Lenght of the number
local function big_endian(callback, byte_count)
	return function(src, s, e)
		local e, write = (e or byte_count) * 8, intiger_write_type[e]
		write(src, s, bit32.rshift(bit32.byteswap(rd_int(src, s, e)), 32 - e))
		local n2 = callback(src, s)
		write(src, s, bit32.rshift(bit32.byteswap(rd_int(src, s, e)), 32 - e))

		return n2
	end
end

-- to avoid nested ifs in deserializing
local float_types = {
	[4] = {little = buffer.readf32, big = big_endian(buffer.readf32)},
	[8] = {little = buffer.readf64, big = big_endian(buffer.readf64)},
}

-- byte stm_byte(Stream S)
-- @S - Stream object to read from
local function stm_byte(S)
	local idx = S.index
	local bt = buffer.readu8(S.source, 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 str = buffer.readstring(S.source, S.index, len)

	S.index += len
	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_inst_list(S)
	local len = S:s_int()
	local list = table.create(len)

	for i = 1, len 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

			if op == 10 then -- decode NEWTABLE array size, store it as constant value
				local e = bit32.band(bit32.rshift(data.B, 3), 31)
				if e == 0 then
					data.const = data.B
				else
					data.const = bit32.lshift(bit32.band(data.B, 7) + 8, e - 1)
				end
			end
		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

		list[i] = data
	end

	return list
end

local function stm_const_list(S)
	local len = S:s_int()
	local list = table.create(len)

	for i = 1, len 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

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

	return list
end

local function stm_sub_list(S, src)
	local len = S:s_int()
	local list = table.create(len)

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

	return list
end

local function stm_line_list(S)
	local len = S:s_int()
	local list = table.create(len)

	for i = 1, len do list[i] = S:s_int() end

	return list
end

local function stm_loc_list(S)
	local len = S:s_int()
	local list = table.create(len)

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

	return list
end

local function stm_upval_list(S)
	local len = S:s_int()
	local list = table.create(len)

	for i = 1, len do list[i] = stm_lstring(S) end

	return list
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.num_upval = stm_byte(S) -- num upvalues
	proto.num_param = stm_byte(S) -- num params

	stm_byte(S) -- vararg flag
	proto.max_stack = stm_byte(S) -- max stack size

	proto.code = stm_inst_list(S)
	proto.const = stm_const_list(S)
	proto.subs = stm_sub_list(S, src)
	proto.lines = stm_line_list(S)

	stm_loc_list(S)
	stm_upval_list(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

local function lua_bc_to_state(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 = 0,
		source = typeof(src) == "buffer" and src or buffer.fromstring(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 or big_endian(rd_int)
	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, memory)
	local prev = list[index]

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

	return prev
end

local function on_lua_error(failed, err)
	local src = failed.source
	local line = failed.lines[failed.pc - 1]

	error(string.format("%s:%i: %s", src, line, err), 0)
end

-- ccuser44 added multithreading support
-- TODO: Create multiple threads for closure and use a module and/or round robin load balancer to distribute usage
local isClient, actorContainer
local function new_threaded_closure(proto, env, upval)
	isClient = isClient or game:GetService("RunService"):IsClient()
	actorContainer = actorContainer or script
	local actor = Instance.new("Actor")
	local clone = script:Clone()
	local runner = script[isClient and "LocalRunner" or "Runner"]:Clone()
	local event = Instance.new("BindableEvent")
	local tag = 0

	for _, v in clone:GetChildren() do
		if v:IsA("Actor") then
			v:Destroy()
		end
	end

	event.Name = "ReturnPass"
	event.Parent = runner
	clone.Parent = runner
	runner.Disabled = false
	runner.Parent = actor
	actor.Name = "MultithreadRunner_"..tostring(math.random())
	actor.Parent = actorContainer

	task.defer(actor.SendMessage, actor, "wrap_state", proto, env, upval)

	task.wait()

	return function(...)
		tag = tag + 1
		local currentTag = tag
		local routine = coroutine.running()
		local connection = event.Event:Connect(function(eventTag, ...)
			if eventTag == currentTag then
				coroutine.resume(routine, ...)
			end
		end)

		task.defer(actor.SendMessage, actor, "run_callback", currentTag, ...)

		local args = table.pack(coroutine.yield())
		connection:Disconnect()

		if not args[1] then
			error(args[2], 2)
		end

		return table.unpack(args, 2, args.n)
	end
end

local function run_lua_func(state, env, upvals)
	local code = state.code
	local subs = state.subs
	local vararg = state.vararg

	local top_index = -1
	local open_list = {}
	local memory = state.memory
	local pc = state.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 memory[i] = nil end
					elseif op > 1 then
						--[[GETUPVAL]]
						local uv = upvals[inst.B]

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

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

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

						memory[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 = memory[inst.C]
							end

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

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

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

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

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

						memory[inst.A] = lhs - rhs
					end
				else --[[MOVE]]
					memory[inst.A] = memory[inst.B]
				end
			elseif op > 8 then
				if op < 13 then
					if op < 10 then
						--[[SETGLOBAL]]
						env[inst.const] = memory[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

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

							local ret_list = table.pack(memory[A](table.unpack(memory, A + 1, A + params)))
							local ret_num = ret_list.n

							if C == 0 then
								top_index = A + ret_num - 1
							else
								ret_num = C - 1
							end

							table.move(ret_list, 1, ret_num, A, memory)
						else
							--[[SETUPVAL]]
							local uv = upvals[inst.B]

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

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

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

						memory[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 = top_index - A
							else
								params = B - 1
							end

							close_lua_upvalues(open_list, 0)

							return memory[A](table.unpack(memory, A + 1, A + params))
						else
							--[[SETTABLE]]
							local index, value

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

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

							memory[inst.A][index] = value
						end
					elseif op > 16 then
						--[[NEWTABLE]]
						memory[inst.A] = table.create(inst.const) -- inst.const contains array size
					else
						--[[DIV]]
						local lhs, rhs

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

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

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

				if step >= 0 then
					loops = index <= limit
				else
					loops = index >= limit
				end

				if loops then
					memory[A] = index
					memory[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]]
						memory[inst.A] = #memory[inst.B]
					elseif op > 20 then
						if op < 22 then
							--[[RETURN]]
							local A = inst.A
							local B = inst.B
							local len

							if B == 0 then
								len = top_index - A + 1
							else
								len = B - 1
							end

							close_lua_upvalues(open_list, 0)

							return table.unpack(memory, A, A + len - 1)
						else
							--[[CONCAT]]
							local B, C = inst.B, inst.C
							local success, str = pcall(table.concat, memory, "", B, C)

							if not success then
								str = memory[B]

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

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

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

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

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

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

							if inst.is_KC then
								rhs = inst.const_C
							else
								rhs = memory[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 = memory[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = memory[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 = memory[inst.B]
						end

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

						memory[inst.A] = lhs ^ rhs
					end
				else
					--[[LOADBOOL]]
					memory[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 = memory[inst.B]
						end

						if inst.is_KC then
							rhs = inst.const_C
						else
							rhs = memory[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.num_upval
							local uvlist

							if nups ~= 0 then
								uvlist = table.create(nups - 1)

								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(open_list, pseudo.B, memory)
									elseif pseudo.op == OPCODE_RM[4] then -- @GETUPVAL
										uvlist[i - 1] = upvals[pseudo.B]
									end
								end

								pc = pc + nups
							end

							memory[inst.A] = (sub.source and string.sub(sub.source, 1, 39) == "SUPER_SECRET_ADONIS_MULTITHREAD_PREFIX_") and new_threaded_closure(sub, env, uvlist) or lua_wrap_state(sub, env, uvlist)
						else
							--[[TESTSET]]
							local A = inst.A
							local B = inst.B

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

							if len == 0 then
								len = vararg.len
								top_index = A + len - 1
							end

							table.move(vararg.list, 1, len, A, memory)
						else
							--[[FORPREP]]
							local A = inst.A
							local init, limit, step

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

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

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

						if len == 0 then len = top_index - A end

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

						offset = (C - 1) * FIELDS_PER_FLUSH

						table.move(memory, A + 1, A + len, offset + 1, tab)
					else
						--[[NOT]]
						memory[inst.A] = not memory[inst.B]
					end
				else
					--[[TEST]]
					if (not memory[inst.A]) ~= (inst.C ~= 0) then pc = pc + code[pc].sBx end
					pc = pc + 1
				end
			else
				--[[TFORLOOP]]
				local A = inst.A
				local func = memory[A]
				local state = memory[A + 1]
				local index = memory[A + 2]
				local base = A + 3
				local vals

				-- === Luau compatibility - General iteration begin ===
				-- // ccuser44 added support for generic iteration
				-- (Please don't use general iteration in vanilla Lua code)
				if not index and not state and type(func) == "table" then
					local metatable = pcall(getmetatable, func) and getmetatable(func)

					if not (type(metatable) == "table" and rawget(metatable, "__call")) then
						func, state, index = (type(metatable) == "table" and rawget(metatable, "__iter") or next), func, nil
						memory[A], memory[A + 1], memory[A + 2] = func, state, index
					end
				end
				-- === Luau compatibility - General iteration end ===

				vals = {func(state, index)}

				table.move(vals, 1, inst.C, base, memory)

				if memory[base] ~= nil then
					memory[A + 2] = memory[base]
					pc = pc + code[pc].sBx
				end

				pc = pc + 1
			end
		else
			--[[JMP]]
			pc = pc + inst.sBx
		end

		state.pc = pc
	end
end

function lua_wrap_state(proto, env, upval)
	local function wrapped(...)
		local passed = table.pack(...)
		local memory = table.create(proto.max_stack)
		local vararg = {len = 0, list = {}}

		table.move(passed, 1, proto.num_param, 0, memory)

		if proto.num_param < passed.n then
			local start = proto.num_param + 1
			local len = passed.n - proto.num_param

			vararg.len = len
			table.move(passed, start, start + len - 1, 1, vararg.list)
		end

		local state = {vararg = vararg, memory = memory, code = proto.code, subs = proto.subs, pc = 1}

		local result = table.pack(pcall(run_lua_func, state, env, upval))

		if result[1] then
			return table.unpack(result, 2, result.n)
		else
			local failed = {pc = state.pc, source = proto.source, lines = proto.lines}

			on_lua_error(failed, result[2])

			return
		end
	end

	return wrapped
end

return setmetatable({
	bc_to_state = lua_bc_to_state,
	wrap_state = lua_wrap_state,
	OPCODE_RM = OPCODE_RM,
	OPCODE_T = OPCODE_T,
	OPCODE_M = OPCODE_M,
}, {__call = function(_, BCode, Env) -- Backwards compatibility for legacy rerubi usage
	return lua_wrap_state(lua_bc_to_state(BCode), Env or {})
end})
